diff options
| -rw-r--r-- | src/look.zig | 13 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 19 |
2 files changed, 30 insertions, 2 deletions
diff --git a/src/look.zig b/src/look.zig index 238e479f..922504c0 100644 --- a/src/look.zig +++ b/src/look.zig @@ -1238,6 +1238,15 @@ test "a miss names a long path by its start and its end, its middle given up to try std.testing.expectEqualStrings("short/path.txt", missText("short/path.txt", &cut)); } +/// A file that is there but would not open: said with its path, `look: +/// <path>: permission denied`, the path cut in its middle when long. +fn lookOpenFailed(p: *Pardes, id: usize, path: []const u8, err: anyerror) void { + if (@import("Messages.zig").dialReason(err) != null) return p.reportError(id, path, err); + var cut: [miss_path_cap + "…".len]u8 = undefined; + var said: [miss_path_cap + 16]u8 = undefined; + p.reportError(id, std.fmt.bufPrint(&said, "look: {s}", .{missText(path, &cut)}) catch "look", err); +} + fn missedPage(p: *Pardes, id: usize, path: []const u8, page: usize) void { var cut: [miss_path_cap + "…".len]u8 = undefined; missed(p, id, "{s} has no page {d}", .{ missText(path, &cut), page }); @@ -1761,7 +1770,7 @@ pub fn lookAt(p: *Pardes, id: usize, operand_raw: []const u8) void { if (!focusPaneByPath(p, target.path, target.at)) { const free = p.freeSlot() orelse return p.reportError(id, "look", error.NoPaneSlots); const nt = panes.File.open(p, free, target.path, target.at.line) catch |err| - return p.reportError(id, if (@import("Messages.zig").dialReason(err) != null) target.path else "look", err); + return lookOpenFailed(p, id, target.path, err); if (target.at.col > 0) nt.body.cur_col = @intCast(target.at.col - 1); const failures = p.fs.failures; exec.placeNew(p, id, id, free, .doc); @@ -1783,7 +1792,7 @@ pub fn lookAt(p: *Pardes, id: usize, operand_raw: []const u8) void { if (focusPaneByPath(p, target.path, .{})) return; const free = p.freeSlot() orelse return p.reportError(id, "look", error.NoPaneSlots); const made = panes.Image.create(p, free, target.path, &.{}) catch |err| - return p.reportError(id, if (@import("Messages.zig").dialReason(err) != null) target.path else "look", err); + return lookOpenFailed(p, id, target.path, err); // One it cannot read (corrupt, truncated, not what its name says) // is said, a failure, rather than shown as a blank pane. if (!@import("image.zig").readable(made.image.?.raw)) { diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 9a1a8da1..4a0f94c2 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -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 look at a file it may not read fails the write naming the file" { + if (comptime !pardes.hosted) return; + 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 = "locked.txt", .data = "secret\n", .flags = .{ .permissions = .fromMode(0o000) } }); + var dir_buf: [4096]u8 = undefined; + const dir = dir_buf[0..try tmp.dir.realPath(testing.io, &dir_buf)]; + var line: [4200]u8 = undefined; + const path = try std.fmt.bufPrint(&line, "{s}/locked.txt", .{dir}); + var want: [4200]u8 = undefined; + const expected = try std.fmt.bufPrint(&want, "look: {s}: permission denied", .{path}); + var with_newline: [4300]u8 = undefined; + const refused = wr(p, root_look, try std.fmt.bufPrint(&with_newline, "{s}\n", .{path})); + if (refused.reply.status == .ok) return error.SkipZigTest; // root reads anything + try testing.expectEqualStrings(expected, refused.reply.ename); +} + 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"); |
