diff options
| -rw-r--r-- | src/builtins.zig | 3 | ||||
| -rw-r--r-- | src/look.zig | 10 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 23 |
3 files changed, 33 insertions, 3 deletions
diff --git a/src/builtins.zig b/src/builtins.zig index 3f4a680c..59fc140d 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -1339,7 +1339,8 @@ pub const Grep = struct { pub fn run(c: Ctx) void { const pat = std.mem.trim(u8, c.arg orelse "", " \t\r\n"); if (pat.len > 0) { - _ = look.runSearch(c.p, c.id, pat, .grep, .top, true) catch |err| c.p.reportError(c.id, "grep", err); + const rows = look.runSearch(c.p, c.id, pat, .grep, .top, false) catch |err| return c.p.reportError(c.id, "grep", err); + if (rows == 0) c.p.reportFailure(c.id, "grep: no match"); return; } c.p.startPrompt(c.pane, &c.pane.body, .{ .search = config.grep_marker }); diff --git a/src/look.zig b/src/look.zig index 87dcede3..ba022bef 100644 --- a/src/look.zig +++ b/src/look.zig @@ -612,8 +612,14 @@ pub fn runSearch(p: *Pardes, id: usize, pat_raw: []const u8, kind: Search, start roots[nroots] = d; nroots += 1; } + // A pane's directory that is not there (a scratch named into one + // not made yet, a peer's) has nothing to grep: passed over, not the + // search's failure. for (roots[0..nroots]) |r| - out_len += try filesystem.grep(arena, p.gpa, r, dir, pat, out[out_len..]); + out_len += filesystem.grep(arena, p.gpa, r, dir, pat, out[out_len..]) catch |err| switch (err) { + error.FileNotFound, error.NotDir, error.AccessDenied => continue, + else => return err, + }; } else if (kind == .find) { out_len = try filesystem.find(arena, dir, pat, out); } else if (pane.hasPdf()) { @@ -655,7 +661,7 @@ pub fn runSearch(p: *Pardes, id: usize, pat_raw: []const u8, kind: Search, start nrows += 1; } } - if (!open_empty and kind == .text and nrows == 0) return 0; + if (!open_empty and (kind == .text or kind == .grep) and nrows == 0) return 0; const content = try p.gpa.dupe(u8, out[0..out_len]); // the buffer records WHICH search filled it, pattern and all: Find and // Grep are builtins (words you can execute), the bare `/` is a key diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 1ef9a2f6..330b9cb7 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -1819,6 +1819,29 @@ test "a builtin that needs its argument, written bare to an exec, is EINVAL as t } } +test "Grep with no hit says grep: no match and opens nothing, a missing pane directory passed over" { + const p = try withFile(testing.allocator, "x\n"); + defer p.deinit(); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.writeFile(testing.io, .{ .sub_path = "a.txt", .data = "alpha\n" }); + var dir_buf: [4096]u8 = undefined; + const dir = dir_buf[0..try tmp.dir.realPath(testing.io, &dir_buf)]; + var line: [4200]u8 = undefined; + _ = wr(p, Node.of(serialOf(p), .name), try std.fmt.bufPrint(&line, "{s}/a.txt\n", .{dir})); + // A second pane in a directory that is not there. + const other = try th.newPane(p); + _ = wr(p, Node.of(other, .name), "/tmp/pardes-no-such-dir-zz/b.txt\n"); + var count: usize = 0; + for (p.panes) |slot| count += @intFromBool(slot != null); + const missed = wr(p, Node.of(serialOf(p), .exec), "Grep zzqqnomatchzz\n"); + try testing.expectEqual(E.IO, missed.errno()); + try testing.expectEqualStrings("grep: no match", missed.reply.ename); + var after: usize = 0; + for (p.panes) |slot| after += @intFromBool(slot != null); + try testing.expectEqual(count, after); +} + test "size is monotonic: growing is never refused, and a size once taken is taken again" { const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 60 }); defer p.deinit(); |
