diff options
Diffstat (limited to 'src/ninep/tree.zig')
| -rw-r--r-- | src/ninep/tree.zig | 17 |
1 files changed, 15 insertions, 2 deletions
diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index e566482c..95e46ab3 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -165,6 +165,9 @@ pub const Open = struct { /// A `name` open has named its pane: one name an open, so a second line /// on it (bash writes `printf 'a\nb\n'` a line at a time) is refused. named: bool = false, + /// A line over 1 MiB was refused: what follows of it, through its + /// newline, is dropped rather than run as a line of its own. + discarding: bool = false, pub const Replay = struct { off: u64, bytes: []u8 }; @@ -1176,7 +1179,16 @@ fn write(p: *Pardes, req: Req, target: Target) Reply { if (resultsFile(target) or ctlFile(target)) for (req.data) |c| if ((c < ' ' and c != '\t' and c != '\n' and c != '\r') or c == 0x7f) return failText(req.tag, E.INVAL, ctl.e_control); const o = (if (linesFile(target)) openOf(p, req) else null) orelse return writeNow(p, req, target); if (o.what != .lines and o.what != .ctl) return writeNow(p, req, target); - o.pending.appendSlice(p.gpa, req.data) catch return Reply.fail(req.tag, E.NOMEM); + // The rest of a line refused for its length goes through its newline + // unread: its one refusal said it, and its tail is no command of its own. + var data = req.data; + if (o.discarding) { + const nl = std.mem.indexOfScalar(u8, data, '\n') orelse return .{ .tag = req.tag, .written = @intCast(req.data.len) }; + o.discarding = false; + data = data[nl + 1 ..]; + if (data.len == 0) return .{ .tag = req.tag, .written = @intCast(req.data.len) }; + } + o.pending.appendSlice(p.gpa, data) catch return Reply.fail(req.tag, E.NOMEM); var end = ctl.completeEnd(p, o.pending.items); // A tail with no newline is a whole line when the write is the whole of // what its client wrote (shorter than a Twrite can carry): it runs now, @@ -1197,7 +1209,8 @@ fn write(p: *Pardes, req: Req, target: Target) Reply { if (end == 0) { if (o.pending.items.len <= pending_cap) return .{ .tag = req.tag, .written = @intCast(req.data.len) }; o.pending.clearRetainingCapacity(); - return failText(req.tag, E.INVAL, "a line or Edit block over 1 MiB"); + o.discarding = true; + return failText(req.tag, E.INVAL, "invalid write: a line or Edit block over 1 MiB"); } if (target == .pane and target.pane.file == .name) { if (o.named or std.mem.count(u8, o.pending.items[0..end], "\n") > 1) { |
