summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fs.zig3
-rw-r--r--src/ninep/ctl.zig18
-rw-r--r--src/ninep/events.zig8
3 files changed, 27 insertions, 2 deletions
diff --git a/src/fs.zig b/src/fs.zig
index 6a4c0a33..4414c150 100644
--- a/src/fs.zig
+++ b/src/fs.zig
@@ -1303,6 +1303,9 @@ pub const Namespace = struct {
/// host work fails (a Save, a Dump), takes it back out of the log, so
/// an announcement stands only for a builtin that did what it said.
announced_seq: ?u64 = null,
+ /// The serial of the pane the root's exec or look last ran a line at
+ /// (the keyboard's), 0 before one: its err record names that pane.
+ ran_at: u32 = 0,
/// Every failure said (Messages.reportFailure), counted: a builtin
/// that said one while it ran failed.
failures: u32 = 0,
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 7c68210a..4f52d661 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -170,6 +170,7 @@ pub fn completeEnd(p: *Pardes, data: []const u8) usize {
/// A null `serial` clicks at whichever pane is active as each line runs,
/// which is what the root's own look and exec mean; a serial stays put.
pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool, in_tag: bool) Reply {
+ p.fs.ran_at = 0;
for ([2]bool{ false, true }) |apply| {
if (apply) p.fs.results_len = 0;
var it = Messages.init(p, req.data);
@@ -181,6 +182,7 @@ pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool, in_tag: bool) Rep
if (!apply) continue;
const id = if (serial) |s| p.paneBySerial(s) orelse break else p.active;
if (p.panes[id] == null) return Reply.fail(req.tag, E.NOENT);
+ p.fs.ran_at = p.panes[id].?.serial;
p.fs.no_pane_slot = false;
if (captured(p, req, id, exec, text, in_tag)) |refusal| return refusal;
// A pane it would have opened had no slot: the write fails, and
@@ -1728,6 +1730,17 @@ test "Mount dials its peer and fails, mounting nothing, when none answers; a loo
try testing.expect(th.logHas(p, "look: /n/gone/f.txt: dial failed: no answer"));
}
+test "a root exec that fails logs its err against the pane it ran at" {
+ const p = try withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const f = &p.panes[0].?.file.?;
+ f.saved_revision = f.revision -% 1;
+ f.saved_hash = 0;
+ try testing.expect(wr(p, root_exec, "Exit\n").errno() != 0);
+ var want: [32]u8 = undefined;
+ try testing.expect(th.logHas(p, try std.fmt.bufPrint(&want, "err {d} exec: ", .{serialOf(p)})));
+}
+
test "size is monotonic: growing is never refused, and a size once taken is taken again" {
const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 60 });
defer p.deinit();
@@ -1983,7 +1996,10 @@ test "a builtin that fails through exec, tagexec or a column's exec fails the wr
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.indexOf(u8, text, "msg ") == null);
- try testing.expectEqual(@as(usize, 4), std.mem.count(u8, text, "Kill: no running command has that first word"));
+ // 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);
_ = 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);
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index fb0141f4..98799774 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -187,7 +187,13 @@ pub fn noteError(p: *Pardes, req: Req, reply: Reply) void {
if (req.node & tree.sources.archive_node != 0) file = "src";
if (req.node != pardes.filesystem.os_root and req.node & pardes.filesystem.os_node == 0 and req.node & tree.sources.archive_node == 0) {
if (tree.Node.target(req.node)) |t| switch (t) {
- .top => |f| file = f.fileName(),
+ .top => |f| {
+ file = f.fileName();
+ // The root's exec or look ran at the keyboard's pane: its
+ // failure is that pane's, as a click there would be.
+ if ((f == .exec or f == .look) and p.fs.ran_at != 0)
+ serial = std.fmt.bufPrint(&name, "{d}", .{p.fs.ran_at}) catch "-";
+ },
.col => |c| {
serial = "-";
file = (if (c.file == .dir)