summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/fs.md5
-rw-r--r--src/ninep/pane.zig15
2 files changed, 17 insertions, 3 deletions
diff --git a/docs/fs.md b/docs/fs.md
index 36fc7d7a..8e70af3e 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -496,7 +496,10 @@ selection, and leaves `look` reading back empty.
`/pane/<n>/name` reads the pane's file name (a terminal's directory) and
writing it renames the buffer; a relative name resolves against the pane's
-directory. `body` appends on write and replaces on truncating open. A
+directory. The write is the name and its newline, nothing trimmed: a blank
+inside a name is taken (`two words.zig`), but one at either end, or a
+control character, is refused, `bad character in file name` (EINVAL), as
+acme refuses a blank (xfid.c:650), rather than quietly cut off. `body` appends on write and replaces on truncating open. A
terminal's `body` is its history as plain text, frozen per open, in logical
lines: a row the terminal wrapped is joined back to the row before it (the
wrap is ghostty's, as `pty/run`'s output unwraps), and the last line ends
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig
index 61642999..d8329ccc 100644
--- a/src/ninep/pane.zig
+++ b/src/ninep/pane.zig
@@ -591,10 +591,18 @@ fn writeFlag(p: *Pardes, req: Req, pane: *Pane, file: PaneFile) Reply {
/// Writing `name` renames the buffer: a relative name resolves against the
/// pane's directory, as the tag's name field does.
+/// acme's refusal of a name with a blank or a control character in it
+/// (editors/acme/xfid.c:650-652). A blank inside a name is taken here, as
+/// pardes names files with spaces; one at either end is refused, not
+/// quietly cut off, so the name a script wrote is the name it gets.
+const e_name_char = "bad character in file name";
+
fn writeName(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply {
- const name = std.mem.trim(u8, req.data, " \t\r\n");
+ // One line: its newline ends it, as `echo` writes it.
+ const name = if (std.mem.endsWith(u8, req.data, "\n")) req.data[0 .. req.data.len - 1] else req.data;
if (name.len == 0) return Reply.fail(req.tag, E.INVAL);
- for (name) |c| if (c < ' ') return Reply.fail(req.tag, E.INVAL);
+ for (name) |c| if (c < ' ') return tree.failText(req.tag, E.INVAL, e_name_char);
+ if (name[0] == ' ' or name[name.len - 1] == ' ') return tree.failText(req.tag, E.INVAL, e_name_char);
if (fileOf(pane) == null) return tree.failText(req.tag, E.PERM, if (pane.isTerminal())
"rename not allowed: a terminal is named by its shell's directory; cd there, or Tty in another"
else
@@ -1095,6 +1103,9 @@ test "name reads the file name and writing it promotes a scratch without touchin
try testing.expectEqualStrings("existing target\n", target);
for ([_][]const u8{ "", "\n", "bad\x01name\n" }) |bad|
try testing.expectEqual(E.INVAL, wr(p, name, bad).errno());
+ // A blank at either end is refused as acme refuses one, not cut off.
+ for ([_][]const u8{ "trailing.zig \n", " leading.zig\n", "tab\t.zig\n" }) |bad|
+ try testing.expectEqualStrings("bad character in file name", wr(p, name, bad).reply.ename);
try testing.expectEqualStrings(path, pane.file.?.path);
try testing.expectEqual(Status.ok, wr(p, name, "two words.zig\n").reply.status);
const spaced = try std.fmt.bufPrint(&path_buffer, "{s}/two words.zig", .{directory});