summaryrefslogtreecommitdiff
path: root/src/ninep/tree.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/ninep/tree.zig')
-rw-r--r--src/ninep/tree.zig17
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) {