summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dump.zig2
-rw-r--r--src/exec.zig6
-rw-r--r--src/ninep/pty.zig2
-rw-r--r--src/pardes.zig36
4 files changed, 36 insertions, 10 deletions
diff --git a/src/dump.zig b/src/dump.zig
index 54b77451..7b27876e 100644
--- a/src/dump.zig
+++ b/src/dump.zig
@@ -848,7 +848,7 @@ fn initDump(gpa: std.mem.Allocator, opts: Options, zon_bytes: []const u8, previo
restored.command_status = t.status;
}
if (std.mem.startsWith(u8, src.tag, "TTY ")) restored.body.mode = .tty;
- if (revive) p.emit(.{ .spawn = .{ .pane = @intCast(i), .serial = restored.serial, .cwd = .from(t.cwd) } });
+ if (revive) p.emitSpawn(i, restored.serial, t.cwd);
break :terminal restored;
},
.file => try pardes.panes.File.restore(p, i, src),
diff --git a/src/exec.zig b/src/exec.zig
index 73413910..c2d1be21 100644
--- a/src/exec.zig
+++ b/src/exec.zig
@@ -348,6 +348,10 @@ fn spawnTtyWithMount(p: *Pardes, from: usize, v9fs: bool) ?*Pane {
return null;
};
const nt = p.newShell(free, p.execDir(from)) catch |err| {
+ if (err == error.PathTooLong) {
+ p.reportFailure(from, std.fmt.comptimePrint("Tty: its directory is longer than a path may be ({d} bytes)", .{pardes.memory.limits.host_path_cap}));
+ return null;
+ }
p.reportError(from, "Tty", err);
return null;
};
@@ -663,7 +667,7 @@ fn runCommand(p: *Pardes, from: usize, line: []const u8) ?usize {
if (panes.Terminal.onAlternateScreen(pane)) panes.Terminal.feedOutput(p, pane, "\x1b[?1049l");
panes.Terminal.feedOutput(p, pane, "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?2004l\x1b[?25h\x1b[0m");
echoCommand(p, pane, line);
- p.emit(.{ .spawn = .{ .pane = @intCast(id), .serial = pane.serial, .cwd = .from(pane.cwdSlice()) } });
+ p.emitSpawn(id, pane.serial, pane.cwdSlice());
noteRun(p, pane, "run", line);
return id;
}
diff --git a/src/ninep/pty.zig b/src/ninep/pty.zig
index 6b2abd0a..046a456a 100644
--- a/src/ninep/pty.zig
+++ b/src/ninep/pty.zig
@@ -87,7 +87,7 @@ fn verb(p: *Pardes, id: usize, line: []const u8, apply: bool) bool {
if (words.next() != null) return false;
if (pane.cwdSlice().len > pardes.effect_path_cap) return false;
if (!apply) return true;
- p.emit(.{ .spawn = .{ .pane = @intCast(id), .serial = pane.serial, .cwd = .from(pane.cwdSlice()) } });
+ p.emitSpawn(id, pane.serial, pane.cwdSlice());
},
}
return true;
diff --git a/src/pardes.zig b/src/pardes.zig
index 9f192004..a55a22e0 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -2139,10 +2139,19 @@ test "owned cwd preserves long paths aliases and failed updates" {
const original = pane.cwdSlice();
try pane.setOwnedCwd(original);
try std.testing.expectEqualStrings(&path, pane.cwdSlice());
- const effects = p.effects_len;
+ // A terminal's directory may be as long as a path is (PATH_MAX): one
+ // past the effect's buffer goes out of line (spawn_dirs); only one past
+ // the host's path limit is too long.
const free = p.freeSlot().?;
- try std.testing.expectError(error.PathTooLong, p.newShell(free, path[0 .. effect_path_cap + 1]));
+ _ = try p.newShell(free, path[0..]);
+ try std.testing.expectEqualStrings(&path, p.spawn_dirs[free].?);
+ try p.removePane(free, null);
+ while (p.nextEffect()) |_| {}
+ var too_long: [limits.host_path_cap + 1]u8 = @splat('d');
+ too_long[0] = '/';
+ try std.testing.expectError(error.PathTooLong, p.newShell(free, &too_long));
try std.testing.expect(p.panes[free] == null);
+ const effects = p.effects_len;
exec.saveTo(p, 0, "file.txt");
try std.testing.expectEqual(effects, p.effects_len);
try std.testing.expect(std.mem.indexOf(u8, pane.msg[0..pane.msg_len], "path too long") != null);
@@ -4304,6 +4313,9 @@ pub const Pardes = struct {
/// (`sessionDir`), as acme's row and column tags do.
exec_header: bool = false,
session_dir: [limits.host_path_cap]u8 = undefined,
+ /// A spawn's directory too long for the effect's inline buffer, by pane
+ /// slot: the effect then carries none, and perform gives the host this.
+ spawn_dirs: [MAX_PANES]?[]u8 = @splat(null),
session_dir_len: ?u16 = null,
/// acme's activecol (dat.c:37), by column serial, 0 for none: the
/// column last typed in (acme.c:487), clicked in with the select button
@@ -4647,6 +4659,7 @@ pub const Pardes = struct {
if (p.last_dump) |d| gpa.free(d);
if (p.custom_theme) |theme_value| std.zon.parse.free(gpa, theme_value);
if (p.chord_arg) |a| gpa.free(a);
+ for (p.spawn_dirs) |d| if (d) |dir| gpa.free(dir);
if (p.pipe.wait) |*wait| wait.deinit(gpa);
p.locations_cache.deinit(p.tree_sitter_gpa);
p.fs.deinit(gpa);
@@ -4789,20 +4802,29 @@ pub const Pardes = struct {
pub fn newShell(p: *Pardes, id: usize, cwd: []const u8) !*Pane {
std.debug.assert(p.panes[id] == null);
- if (cwd.len > effect_path_cap) return error.PathTooLong;
+ if (cwd.len > limits.host_path_cap) return error.PathTooLong;
const pane = try panes.Terminal.create(p.gpa, p.screen_w, p.screen_h);
panes.Terminal.armShellSpawn(pane);
p.installPane(id, pane);
- p.emit(.{ .spawn = .{ .pane = @intCast(id), .serial = pane.serial, .cwd = .from(cwd) } });
+ p.emitSpawn(id, pane.serial, cwd);
return pane;
}
+ /// A child for pane `id` in `cwd`: a directory up to PATH_MAX, the ones
+ /// past the effect's inline buffer kept here for perform to give.
+ pub fn emitSpawn(p: *Pardes, id: usize, serial: u32, cwd: []const u8) void {
+ if (p.spawn_dirs[id]) |old| p.gpa.free(old);
+ p.spawn_dirs[id] = null;
+ if (cwd.len > effect_path_cap) p.spawn_dirs[id] = p.gpa.dupe(u8, cwd) catch null;
+ p.emit(.{ .spawn = .{ .pane = @intCast(id), .serial = serial, .cwd = .from(if (cwd.len > effect_path_cap) "" else cwd) } });
+ }
+
/// A command pane: a terminal whose child is `$SHELL -c line` rather
/// than an interactive shell, left showing its output and `exit N` when
/// it is done (exec.zig runCommand).
pub fn newCommand(p: *Pardes, id: usize, cwd: []const u8, line: []const u8) !*Pane {
std.debug.assert(p.panes[id] == null);
- if (cwd.len > effect_path_cap) return error.PathTooLong;
+ if (cwd.len > limits.host_path_cap) return error.PathTooLong;
const owned = try p.gpa.dupe(u8, line);
errdefer p.gpa.free(owned);
const pane = try panes.Terminal.create(p.gpa, p.screen_w, p.screen_h);
@@ -4813,7 +4835,7 @@ pub const Pardes = struct {
// directory's commands reuse it; the child's own cd does not move it.
pane.setOwnedCwd(cwd) catch {};
p.installPane(id, pane);
- p.emit(.{ .spawn = .{ .pane = @intCast(id), .serial = pane.serial, .cwd = .from(cwd) } });
+ p.emitSpawn(id, pane.serial, cwd);
return pane;
}
@@ -5225,7 +5247,7 @@ pub const Pardes = struct {
switch (e) {
// The host reads the pane (its command, its shell) as it forks,
// so the slot must still hold the pane the spawn was for.
- .spawn => |s| if (if (p.panes[s.pane]) |pane| pane.serial != s.serial else true) {} else if (v.spawn) |f| f(p.host.ctx, s.pane, s.cwd.slice()) else {
+ .spawn => |s| if (if (p.panes[s.pane]) |pane| pane.serial != s.serial else true) {} else if (v.spawn) |f| f(p.host.ctx, s.pane, if (s.cwd.len == 0) (p.spawn_dirs[s.pane] orelse "") else s.cwd.slice()) else {
p.fallback.spawned[s.pane] = true;
},
// A pane with no child is silent: nothing invents output on its