diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ninep/ctl.zig | 21 | ||||
| -rw-r--r-- | src/ninep/events.zig | 10 |
2 files changed, 30 insertions, 1 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 31265a94..aa309ad1 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -296,7 +296,7 @@ pub fn captured(p: *Pardes, req: Req, id: usize, exec: bool, text: []const u8, i /// The errno a builtin's failure answers with: ENOENT for what is not /// there (`no such`, `not found`, the words 9ns reads as ENOENT too), else /// EIO. -fn failureErrno(failure: []const u8) u16 { +pub fn failureErrno(failure: []const u8) u16 { if (std.mem.indexOf(u8, failure, "no such") != null or std.mem.indexOf(u8, failure, "not found") != null) return E.NOENT; // A pattern refused (Edit's too) is malformed input, as 9ns reads it; // so is a word this build has not (`invalid: acme's Put ...`). @@ -1760,6 +1760,25 @@ test "exec Save writes the named file through the host" { try testing.expectEqualStrings(pane.file.?.content, p.fallback.get("/tmp/renamed.txt").?); } +test "a refused Del written back through event fails that write, EIO, with its err" { + const gpa = testing.allocator; + const p = try withFile(gpa, "text\n"); + defer p.deinit(); + const serial = serialOf(p); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .body), "more\n").reply.status); // unsaved + const event = Node.of(serial, .event); + const h = call(p, .{ .tag = 1, .op = .open, .node = event }).reply.handle; + defer _ = call(p, .{ .tag = 9, .op = .release, .node = event, .handle = h }); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .exec), "Del\n").reply.status); + const record = call(p, .{ .tag = 2, .op = .read, .node = event, .handle = h, .size = 256 }).bytes; + var copy: [256]u8 = undefined; + @memcpy(copy[0..record.len], record); + const back = wr(p, event, copy[0..record.len]); + try testing.expectEqual(E.IO, back.errno()); + try testing.expect(std.mem.indexOf(u8, back.reply.ename, "Modified") != null); + try testing.expect(p.paneBySerial(serial) != null); +} + test "a pane's own look and exec go to the event reader holding it, which writes them back to act" { const gpa = testing.allocator; const p = try withFile(gpa, "Msg routed\n"); diff --git a/src/ninep/events.zig b/src/ninep/events.zig index 4d1bbe64..1ffbaab9 100644 --- a/src/ninep/events.zig +++ b/src/ninep/events.zig @@ -6,6 +6,7 @@ const exec = @import("../exec.zig"); const look = @import("../look.zig"); const cloud9 = @import("cloud9"); const tree = @import("tree.zig"); +const ctl = @import("ctl.zig"); const pane_files = @import("pane.zig"); const dump = @import("../dump.zig"); @@ -736,6 +737,11 @@ pub fn writeEvent(p: *Pardes, asked: Req, id: usize) Reply { } if (check.i != req.data.len) return tree.failText(req.tag, E.INVAL, tree.e_bad_event ++ ": a record is <origin><action><q0> <q1> <flag> <count> <text>, a line each"); } + // What a record written back runs fails the write as a click written + // to exec would (a refused Del: EIO and its err), not said and lost. + p.fs.capturing = true; + p.fs.failure_len = 0; + defer p.fs.capturing = false; var run: EventReader = .{ .data = req.data }; while (run.next()) |r| { const live = p.paneBySerial(serial) orelse break; @@ -815,6 +821,10 @@ pub fn writeEvent(p: *Pardes, asked: Req, id: usize) Reply { else => unreachable, } } + if (p.fs.failure_len > 0) { + const failure = p.fs.failure[0..p.fs.failure_len]; + return tree.failText(req.tag, ctl.failureErrno(failure), std.fmt.bufPrint(&p.fs.ename, "{s}", .{failure}) catch failure); + } return .{ .tag = req.tag, .written = @intCast(asked.data.len) }; } |
