diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/host_io.zig | 37 |
1 files changed, 34 insertions, 3 deletions
diff --git a/src/host_io.zig b/src/host_io.zig index 25bd61e1..53e1628d 100644 --- a/src/host_io.zig +++ b/src/host_io.zig @@ -792,7 +792,7 @@ pub const Shell = struct { pub const Spawn = struct { path: [*:0]const u8, - argv: [4:null]?[*:0]const u8, + argv: [6:null]?[*:0]const u8, }; // Resolve in the parent. The path buffer and prompt files must survive through exec. @@ -803,7 +803,24 @@ pub const Shell = struct { .fish => if (prompt_rcs.fishCommand()) |command| .{ "-C", command.ptr } else .{ null, null }, .none => .{ null, null }, }; - return .{ .path = path, .argv = .{ path, marks[0], marks[1], null } }; + return .{ .path = path, .argv = .{ path, marks[0], marks[1], null, null, null } }; + } + + /// A command pane's shell runs its one line with job control on, so a + /// job the line puts in the background has a process group of its own. + /// The shell leads the terminal's session, and when it exits the kernel + /// hangs up the terminal's foreground group: with job control off that + /// is the shell's own, the job's too, and the job died with the command + /// (nohup'd or not; only setsid got out, and not always in time). A + /// shell pardes does not know runs the line as it is. + pub fn commandArgv(path: [*:0]const u8, line: [*:0]const u8) [6:null]?[*:0]const u8 { + const bin = std.mem.span(path); + const base = if (std.mem.lastIndexOfScalar(u8, bin, '/')) |s| bin[s + 1 ..] else bin; + const posix_shells = [_][]const u8{ "bash", "sh", "dash", "zsh", "ksh", "mksh", "yash" }; + for (posix_shells) |name| if (std.mem.eql(u8, base, name)) + return .{ path, "-m", "-c", line, null, null }; + if (family(bin) == .fish) return .{ path, "-C", "status job-control full", "-c", line, null }; + return .{ path, "-c", line, null, null, null }; } /// A shell by its path or its name in the usual directories, or null. @@ -840,6 +857,20 @@ pub const Shell = struct { return @ptrCast(buf); } + test "a command pane's line runs with job control on in the shells that have it" { + const want = struct { + fn check(path: [*:0]const u8, words: []const []const u8) !void { + const argv = commandArgv(path, "sleep 9 &"); + for (words, 0..) |w, i| try std.testing.expectEqualStrings(w, std.mem.span(argv[i].?)); + try std.testing.expect(argv[words.len] == null); + } + }; + try want.check("/usr/bin/bash", &.{ "/usr/bin/bash", "-m", "-c", "sleep 9 &" }); + try want.check("/bin/sh", &.{ "/bin/sh", "-m", "-c", "sleep 9 &" }); + try want.check("/usr/bin/fish", &.{ "/usr/bin/fish", "-C", "status job-control full", "-c", "sleep 9 &" }); + try want.check("/usr/bin/nu", &.{ "/usr/bin/nu", "-c", "sleep 9 &" }); + } + test "a path is taken at its word, a name is looked up, and both pick their own marks" { if (builtin.os.tag == .windows) return; var buf: [std.fs.max_path_bytes]u8 = undefined; @@ -1070,7 +1101,7 @@ pub fn forkShell( if (line.len > exec_command_max or std.mem.indexOfScalar(u8, line, 0) != null) return error.InvalidCommand; @memcpy(command_buf[0..line.len], line); command_buf[line.len] = 0; - spawn.argv = .{ spawn.path, "-c", command_buf[0..line.len :0].ptr, null }; + spawn.argv = Shell.commandArgv(spawn.path, command_buf[0..line.len :0].ptr); } var helper_buf: [4096]u8 = undefined; const mounted = one_line == null and if (core) |c| if (c.panes[pane]) |pn| pn.v9fs_on_spawn else false else false; |
