diff options
Diffstat (limited to 'src/host_io.zig')
| -rw-r--r-- | src/host_io.zig | 66 |
1 files changed, 58 insertions, 8 deletions
diff --git a/src/host_io.zig b/src/host_io.zig index 8ddb0c90..4ebac9ee 100644 --- a/src/host_io.zig +++ b/src/host_io.zig @@ -1255,7 +1255,12 @@ pub const ChildEnv = struct { return line[0 .. std.mem.indexOfScalar(u8, line, '=') orelse line.len]; } - pub fn build(slots: *Slots) ?[*:null]const ?[*:0]const u8 { + /// Which pager a child is given where the user's environment names none: + /// a command pane's one-shot shell `cat` (its output is a pane already), + /// a terminal's shell `pardes -` (a +Pager pane) unless `Pager off`. + pub const Paging = enum { command, terminal, terminal_off }; + + pub fn build(slots: *Slots, paging: Paging) ?[*:null]const ?[*:0]const u8 { var n: usize = 0; var i: usize = 0; while (libc.environ[i]) |entry| : (i += 1) { @@ -1268,9 +1273,15 @@ pub const ChildEnv = struct { slots[n] = entry; n += 1; } - // No pager to wait in: a pty/run or a command pane's output is read - // whole, and less would hold it at its first screen for ever. Only - // where the inherited environment chose none. + // No pager to wait in (less holds a run at its first screen for + // ever): a command pane's output is read whole, through cat; a + // terminal's goes to a +Pager pane. Only where the inherited + // environment chose none. + const defaults: []const [*:0]const u8 = switch (paging) { + .command => &cat_pagers, + .terminal => pardesPagers(), + .terminal_off => &.{}, + }; for (defaults) |default| { const set = for (libc.environ[0..i]) |entry| { if (std.mem.eql(u8, name(entry.?), name(default))) break true; @@ -1288,8 +1299,27 @@ pub const ChildEnv = struct { return @ptrCast(slots); } - /// Set for a pane's child only when the user's environment has none. - const defaults = [_][*:0]const u8{ "PAGER=cat", "GIT_PAGER=cat" }; + const cat_pagers = [_][*:0]const u8{ "PAGER=cat", "GIT_PAGER=cat" }; + + /// `PAGER='<this pardes> -'`, its own path so no PATH lookup can find + /// another; plain `pardes -` where the path cannot be read or quoted. + fn pardesPagers() []const [*:0]const u8 { + const S = struct { + var pager: [std.fs.max_path_bytes + 16:0]u8 = undefined; + var git_pager: [std.fs.max_path_bytes + 16:0]u8 = undefined; + var entries: [2][*:0]const u8 = undefined; + var ready = false; + }; + if (!S.ready) { + var exe_buf: [std.fs.max_path_bytes]u8 = undefined; + const n = if (comptime builtin.os.tag == .linux) libc.readlink("/proc/self/exe", &exe_buf, exe_buf.len) else -1; + const exe: []const u8 = if (n > 0 and @as(usize, @intCast(n)) < exe_buf.len and std.mem.indexOfScalar(u8, exe_buf[0..@intCast(n)], '\'') == null) exe_buf[0..@intCast(n)] else "pardes"; + S.entries[0] = if (std.fmt.bufPrintSentinel(&S.pager, "PAGER='{s}' -", .{exe}, 0)) |t| t.ptr else |_| "PAGER=pardes -"; + S.entries[1] = if (std.fmt.bufPrintSentinel(&S.git_pager, "GIT_PAGER='{s}' -", .{exe}, 0)) |t| t.ptr else |_| "GIT_PAGER=pardes -"; + S.ready = true; + } + return &S.entries; + } }; test "the child environment replaces the launcher's terminal identity exactly once" { @@ -1314,7 +1344,24 @@ test "the child environment replaces the launcher's terminal identity exactly on defer _ = unsetenv("PARDES_CHILD_ENV_PROBE"); var slots: ChildEnv.Slots = undefined; - const envp = ChildEnv.build(&slots) orelse return error.EnvironmentTooLarge; + // Where the user named no pager: a terminal's shell pages into a + // +Pager pane (`pardes -`), or, Pager off, as the environment says; a + // command pane's through cat. A pager the user named is kept. + for ([_]ChildEnv.Paging{ .terminal, .terminal_off }) |paging| { + var shell_slots: ChildEnv.Slots = undefined; + const shell_envp = ChildEnv.build(&shell_slots, paging) orelse return error.EnvironmentTooLarge; + var said: usize = 0; + var j: usize = 0; + while (shell_envp[j]) |entry| : (j += 1) { + const line = std.mem.span(entry); + if (std.mem.startsWith(u8, line, "PAGER=") and std.c.getenv("PAGER") == null) { + try std.testing.expect(paging == .terminal and std.mem.endsWith(u8, line, " -")); + said += 1; + } + } + if (std.c.getenv("PAGER") == null) try std.testing.expectEqual(@as(usize, if (paging == .terminal) 1 else 0), said); + } + const envp = ChildEnv.build(&slots, .command) orelse return error.EnvironmentTooLarge; var terms: usize = 0; var colorterms: usize = 0; var probes: usize = 0; @@ -1442,7 +1489,10 @@ pub fn forkShell( // not call setenv, whose malloc can deadlock against a pty reader thread // that held the heap when the fork took its snapshot. var env_slots: ChildEnv.Slots = undefined; - const envp = ChildEnv.build(&env_slots); + const paging: ChildEnv.Paging = if (one_line != null) + .command + else if (core) |c| (if (c.settings.pager == .off) .terminal_off else .terminal) else .terminal; + const envp = ChildEnv.build(&env_slots, paging); const ws = posix.winsize{ .row = rows, .col = cols, .xpixel = 0, .ypixel = 0 }; // The child says here why it could not become the shell (its chdir or // its exec failed, as for a script whose interpreter is not there): the |
