summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-30 14:10:15 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:17 -0300
commit70ea4cb543df4724a977d7e8c10f2b5f470df94f (patch)
tree6cd3d9399a9d954ca629d0c2d212b0a90d3ecd9f
parent3cfc0d921184b5bf25e7855e22003b1ec230c32d (diff)
downloadpardes-70ea4cb543df4724a977d7e8c10f2b5f470df94f.tar.gz
pardes-70ea4cb543df4724a977d7e8c10f2b5f470df94f.zip
Kill with a name and nothing running says "Kill: nothing running" and succeeds, as bare Kill does
Bare `Kill` with nothing running is a message, not a failure: a script's Kill has nothing left to do. `Kill make` in the same state failed the write with "no running command has that first word", as though make were the mistake. With nothing running at all, named or not, Kill is now the message. It still fails when commands run but none has the word. The two tests that used `Kill zzz` as a builtin that always fails now use `Unmount zzz`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--docs/fs.md6
-rw-r--r--src/builtins.zig9
-rw-r--r--src/ninep/ctl.zig22
3 files changed, 26 insertions, 11 deletions
diff --git a/docs/fs.md b/docs/fs.md
index 616fb3fb..33cfd581 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -260,8 +260,10 @@ goes on below `exit N` until it lets go of the pty. `Kill` (root ctl)
stops the commands pardes started: bare, all; `Kill make ls`, those whose
line starts with one of the words. For a command pane it signals the whole
line, `&` jobs included; for a line typed into a shell only the foreground
-job (SIGTERM), and the shell decides the rest. With nothing running it says
-`Kill: nothing running`. Kill does not reach a REPL's code: use `sig INT` on
+job (SIGTERM), and the shell decides the rest. With nothing running, named
+or not, it says `Kill: nothing running` and the write succeeds; words that
+name none of what runs fail it (`Kill: no running command has that first
+word`). Kill does not reach a REPL's code: use `sig INT` on
its `pty/ctl`.
## The root ctl
diff --git a/src/builtins.zig b/src/builtins.zig
index 5a2037cf..ba67766d 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -609,11 +609,13 @@ pub const Kill = struct {
const names = std.mem.trim(u8, c.arg orelse "", " \t\r\n");
var stopped = false;
var jobless = false;
+ var running = false;
for (c.p.panes, 0..) |slot, id| {
const pane = slot orelse continue;
// A command pane's child is the command: the whole of its
// process group goes, as acme's Kill ends the process.
if (pane.command) |line| if (!pane.command_done) {
+ running = true;
const said = std.mem.trim(u8, line, " \t");
const first = said[0 .. std.mem.indexOfAny(u8, said, " \t") orelse said.len];
if (names.len > 0) {
@@ -627,6 +629,7 @@ pub const Kill = struct {
continue;
};
if (!panes.terminal.commandRunning(pane)) continue;
+ running = true;
const sent = pane.sent_command.?;
if (names.len > 0) {
var words = std.mem.tokenizeAny(u8, names, " \t");
@@ -640,9 +643,11 @@ pub const Kill = struct {
}
if (stopped) return;
if (jobless) return c.p.reportFailure(c.id, "Kill: no job to signal (its shell runs it without job control)");
- if (names.len > 0)
+ // Said, but no failure: a script's Kill, named or not, has nothing
+ // left to do. Only commands running, none of them the one named, is.
+ if (names.len > 0 and running)
c.p.reportFailure(c.id, "Kill: no running command has that first word")
- else // said, but no failure: a script's Kill has nothing left to do
+ else
c.p.setMessage(c.id, "Kill: nothing running");
}
};
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 4312653c..9a95a10f 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -1952,12 +1952,12 @@ test "a builtin that fails a ctl write logs only its err, so the same failure ag
p.update(.tick); // the pane's `new` is logged first
while (p.nextEffect()) |_| {}
const ctl_node = @intFromEnum(tree.TopFile.ctl);
- for (0..3) |_| try testing.expectEqual(Status.err, wr(p, ctl_node, "Kill zzz\n").reply.status);
+ for (0..3) |_| try testing.expectEqual(Status.err, wr(p, ctl_node, "Unmount zzz\n").reply.status);
const log = @intFromEnum(tree.TopFile.log);
const h = call(p, .{ .tag = 1, .op = .open, .node = log }).reply.handle;
const text = call(p, .{ .tag = 2, .op = .read, .node = log, .handle = h, .size = 1 << 16 }).bytes;
- try testing.expect(std.mem.endsWith(u8, text, "err - ctl: Kill: no running command has that first word (x3)\n"));
- try testing.expect(std.mem.indexOf(u8, text, "msg - Kill") == null);
+ try testing.expect(std.mem.endsWith(u8, text, "err - ctl: Unmount: no such mount zzz (x3)\n"));
+ try testing.expect(std.mem.indexOf(u8, text, "msg - Unmount") == null);
_ = call(p, .{ .tag = 3, .op = .release, .node = log, .handle = h });
}
@@ -2517,6 +2517,14 @@ test "a look at file:3:0 is refused as addr 3:0 is, a column counting from 1" {
try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .look), "/test.txt:3:1\n").reply.status);
}
+test "Kill with a name and nothing running says so and does not fail, as bare Kill does" {
+ const p = try withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const r = wr(p, @intFromEnum(tree.TopFile.ctl), "Kill zzz\n");
+ try testing.expectEqual(Status.ok, r.reply.status);
+ try testing.expect(th.logHas(p, "Kill: nothing running"));
+}
+
test "a line starting with # runs as nothing at an exec or a ctl, silently" {
const p = try withFile(testing.allocator, "x\n");
defer p.deinit();
@@ -3315,9 +3323,9 @@ test "a builtin that fails through exec, tagexec or a column's exec fails the wr
while (p.nextEffect()) |_| {}
const serial = serialOf(p);
for ([_]u64{ root_exec, Node.of(serial, .exec), @intFromEnum(tree.TopFile.tagexec), Node.ofCol(pardes.layout.columnSerial(p, 0), .exec) }) |node| {
- const failed = wr(p, node, "Kill zzz\n");
+ const failed = wr(p, node, "Unmount zzz\n");
try testing.expectEqual(Status.err, failed.reply.status);
- try testing.expect(std.mem.indexOf(u8, failed.reply.ename, "Kill: no running command has that first word") != null);
+ try testing.expect(std.mem.indexOf(u8, failed.reply.ename, "Unmount: no such mount zzz") != null);
}
const log = @intFromEnum(tree.TopFile.log);
const h = call(p, .{ .tag = 1, .op = .open, .node = log }).reply.handle;
@@ -3325,8 +3333,8 @@ test "a builtin that fails through exec, tagexec or a column's exec fails the wr
try testing.expect(std.mem.indexOf(u8, text, "msg ") == null);
// The root's exec ran at the pane, so its err and the pane's exec's are
// the same record, counted.
- try testing.expectEqual(@as(usize, 3), std.mem.count(u8, text, "Kill: no running command has that first word"));
- try testing.expect(std.mem.indexOf(u8, text, "first word (x2)") != null);
+ try testing.expectEqual(@as(usize, 3), std.mem.count(u8, text, "Unmount: no such mount zzz"));
+ try testing.expect(std.mem.indexOf(u8, text, "mount zzz (x2)") != null);
_ = call(p, .{ .tag = 3, .op = .release, .node = log, .handle = h });
// A control character is refused with its reason.
try testing.expectEqualStrings(e_control, wr(p, root_exec, "Msg a\x01b\n").reply.ename);