summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 12:35:40 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit2d9f40667197ac775d5215ffd164c800f83cab4c (patch)
tree7db6fb0ead682803ad406909c3c2cb81def335d0
parentd8b35f18817faaba340059cc2bd88ab3b3bc919b (diff)
downloadpardes-2d9f40667197ac775d5215ffd164c800f83cab4c.tar.gz
pardes-2d9f40667197ac775d5215ffd164c800f83cab4c.zip
A pattern with a newline and an anchor mvzr would misread is refused EINVAL, as the doc says
Its words named no malformed input, so 9ns read the refusal as EIO, and Edit's failed EIO directly. It now says bad regular expression: ..., and Edit's failure with those words is EINVAL too. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--src/ninep/addr.zig10
-rw-r--r--src/ninep/ctl.zig5
-rw-r--r--src/regexp.zig2
-rw-r--r--src/sam_edit.zig2
4 files changed, 16 insertions, 3 deletions
diff --git a/src/ninep/addr.zig b/src/ninep/addr.zig
index 09ef2b66..f60c07b1 100644
--- a/src/ninep/addr.zig
+++ b/src/ninep/addr.zig
@@ -377,6 +377,16 @@ test "an address is evaluated from the current one, as acme's are" {
try testing.expectEqualStrings(e_order, th.wr(q, qaddr, "/a/,/b/").reply.ename);
}
+test "a pattern with a newline and an anchor mvzr would misread is refused EINVAL, by addr and by Edit" {
+ const p = try th.withFile(testing.allocator, "a\nfoo\n");
+ defer p.deinit();
+ const r = th.wr(p, Node.of(th.serialOf(p), .addr), "/(^|\\n)foo/");
+ try testing.expectEqual(E.INVAL, r.errno());
+ try testing.expectEqualStrings(regexp_.Regex.e_anchor, r.reply.ename);
+ const e = th.wr(p, Node.of(th.serialOf(p), .ctl), "Edit ,x/(^|\\n)foo/d\n");
+ try testing.expectEqual(E.INVAL, e.errno());
+}
+
test "a row's range, L:C-L2:C2 or L:C-C2, is an address through its last character" {
const p = try th.withFile(testing.allocator, "one\ntwo three\nfour\n");
defer p.deinit();
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 978ce707..b79b1ab8 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -250,7 +250,10 @@ pub fn captured(p: *Pardes, req: Req, id: usize, exec: bool, text: []const u8, i
/// there (`no such`, `not found`, the words 9ns reads as ENOENT too), else
/// EIO.
fn failureErrno(failure: []const u8) u16 {
- return if (std.mem.indexOf(u8, failure, "no such") != null or std.mem.indexOf(u8, failure, "not found") != null) E.NOENT else E.IO;
+ 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.
+ if (std.mem.indexOf(u8, failure, "bad regular expression") != null) return E.INVAL;
+ return E.IO;
}
pub fn resultsLen(p: *Pardes) u64 {
diff --git a/src/regexp.zig b/src/regexp.zig
index 64e1d75a..27b3c0ba 100644
--- a/src/regexp.zig
+++ b/src/regexp.zig
@@ -41,7 +41,7 @@ pub const Regex = struct {
/// patterns take 45-80 ms, and a Debug build is ten times slower.
pub const budget: u64 = if (builtin.mode == .Debug) 4_000_000 else 32_000_000;
- pub const e_anchor = "in a pattern with \\n, ^ can only come first and $ only just before a \\n";
+ pub const e_anchor = "bad regular expression: in a pattern with \\n, ^ can only come first and $ only just before a \\n";
/// `Anchor`: a pattern that names a newline has `^` other than first,
/// or `$` other than just before a `\n`, which mvzr would read as the
diff --git a/src/sam_edit.zig b/src/sam_edit.zig
index cd2998ef..a88728aa 100644
--- a/src/sam_edit.zig
+++ b/src/sam_edit.zig
@@ -735,7 +735,7 @@ test "an Edit that fails halfway changes nothing, and says why in acme's words"
.{ "w /tmp/x", "w is not supported in pardes" },
.{ ",s/(a)/\\1/", "no \\1: mvzr keeps no submatches" },
.{ "1 m 1,2", "move overlaps itself" },
- .{ ",x/(^|\\n)foo/d", "in a pattern with \\n, ^ can only come first and $ only just before a \\n" },
+ .{ ",x/(^|\\n)foo/d", "bad regular expression: in a pattern with \\n, ^ can only come first and $ only just before a \\n" },
}) |c| {
var why: Why = .{};
try std.testing.expectError(error.Edit, run(arena_state.allocator(), "foo a\nfoo\n", .{}, "t", c[0], &why));