summaryrefslogtreecommitdiff
path: root/src/ninep/events.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 09:13:55 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit36241daca2aaf90cc8933dffd95fc3cd6216e2b7 (patch)
treea24873db3188e64f699b828a764b813f29f02de6 /src/ninep/events.zig
parenta550753fb60be3cdc040c521ddaab9022f5d3248 (diff)
downloadpardes-36241daca2aaf90cc8933dffd95fc3cd6216e2b7.tar.gz
pardes-36241daca2aaf90cc8933dffd95fc3cd6216e2b7.zip
/index shows names as the log does, one line of UTF-8, and a newline in a name as \n
/index printed a name as it was, so a directory with a newline in it split its row in two, and bytes not UTF-8 went through; the log turned the newline into a space, silently another name. Both now show a name one way: a newline `\n`, other controls, DEL and C1 a space, bytes not UTF-8 `\xNN`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/events.zig')
-rw-r--r--src/ninep/events.zig48
1 files changed, 36 insertions, 12 deletions
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index f58d970e..45569b22 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -145,9 +145,11 @@ pub fn announce(p: *Pardes) void {
/// Records `<kind> <serial> <name>`.
pub fn noteLog(p: *Pardes, kind: LogKind, pane: *Pane) void {
- var buf: [4096 + 64]u8 = undefined;
- const name = pane_files.nameOf(pane);
- pushLog(p, std.fmt.bufPrint(&buf, "{s} {d} {s}\n", .{ @tagName(kind), pane.serial, name[0..@min(name.len, 4096)] }) catch return);
+ var buf: [4 * 4096 + 64]u8 = undefined;
+ var name_buf: [4 * 4096]u8 = undefined;
+ // As /index shows it: a newline in the name is `\n`.
+ const name = shown(pane_files.nameOf(pane), &name_buf);
+ pushLog(p, std.fmt.bufPrint(&buf, "{s} {d} {s}\n", .{ @tagName(kind), pane.serial, name }) catch return);
}
/// Records `msg <serial> <text>` for what the editor said, `-` for no pane.
@@ -342,16 +344,37 @@ fn followerRead(p: *Pardes, seq: u64) bool {
/// The log is one ring that records whether or not anyone reads it. A record
/// is one line: a newline in a message or a name would read as two records.
-/// A record as one line of text: a control character, DEL or a C1 control
-/// (U+0080-U+009F) is a space each, and a byte that is not UTF-8 is
-/// written `\xNN`, so a reader splitting on newlines and decoding UTF-8
-/// never trips. The record's own newline, last, is kept.
+/// A record as one line of text (shown): the record's own newline, last,
+/// is kept.
fn sanitize(record: []const u8, out: []u8) []u8 {
+ // A newline in a message is a space (its words go on); a name's own
+ // newline is escaped before it gets here (noteLog).
+ const w = shownAs(record[0 .. record.len - 1], out[0 .. out.len - 1], false).len;
+ out[w] = '\n';
+ return out[0 .. w + 1];
+}
+
+/// `text` as one line of UTF-8 a reader can split and decode, as /log and
+/// /index show names: a newline is `\n` (a name with one stays readable as
+/// what it is, not run into the next), any other control character, DEL
+/// or C1 control (U+0080-U+009F) a space, and a byte that is not UTF-8
+/// `\xNN`. `out` of 4 bytes a byte of `text` holds it all.
+pub fn shown(text: []const u8, out: []u8) []u8 {
+ return shownAs(text, out, true);
+}
+
+fn shownAs(text: []const u8, out: []u8, escape_newline: bool) []u8 {
var w: usize = 0;
var i: usize = 0;
- const body = record[0 .. record.len - 1];
- while (i < body.len and w + 4 < out.len) {
+ const body = text;
+ while (i < body.len and w + 4 <= out.len) {
const c = body[i];
+ if (c == '\n' and escape_newline) {
+ @memcpy(out[w..][0..2], "\\n");
+ w += 2;
+ i += 1;
+ continue;
+ }
if (c < ' ' or c == 0x7f) {
out[w] = ' ';
w += 1;
@@ -371,13 +394,12 @@ fn sanitize(record: []const u8, out: []u8) []u8 {
i += 2;
continue;
}
- if (w + n + 1 > out.len) break;
+ if (w + n > out.len) break;
@memcpy(out[w..][0..n], body[i..][0..n]);
w += n;
i += n;
}
- out[w] = '\n';
- return out[0 .. w + 1];
+ return out[0..w];
}
fn pushLog(p: *Pardes, raw: []u8) void {
@@ -1285,6 +1307,8 @@ test "a long msg record is cut between words at its cap, with an ellipsis" {
test "a record is one line of UTF-8: DEL and C1 are spaces, bytes not UTF-8 are escaped" {
var out: [64]u8 = undefined;
+ var name: [64]u8 = undefined;
+ try testing.expectEqualStrings("/tmp/two\\nlines", shown("/tmp/two\nlines", &name));
try testing.expectEqualStrings("msg - a b c \\xff d\n", sanitize("msg - a\x7fb\xc2\x85c \xff d\n", &out));
try testing.expectEqualStrings("msg - caf\xc3\xa9\n", sanitize("msg - caf\xc3\xa9\n", &out));
}