summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ninep/ctl.zig21
-rw-r--r--src/ninep/events.zig10
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) };
}