summaryrefslogtreecommitdiff
path: root/src/pardes.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/pardes.zig')
-rw-r--r--src/pardes.zig36
1 files changed, 29 insertions, 7 deletions
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