mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
Fix tmux control block terminator parsing
This commit is contained in:
@@ -48,6 +48,11 @@ pub const Parser = struct {
|
||||
block,
|
||||
};
|
||||
|
||||
const BlockTerminator = enum {
|
||||
end,
|
||||
err,
|
||||
};
|
||||
|
||||
pub fn deinit(self: *Parser) void {
|
||||
// If we're in a broken state, we already deinited
|
||||
// the buffer, so we don't need to do anything.
|
||||
@@ -116,10 +121,8 @@ pub const Parser = struct {
|
||||
)) |v| v + 1 else 0;
|
||||
const line = written[idx..];
|
||||
|
||||
if (std.mem.startsWith(u8, line, "%end") or
|
||||
std.mem.startsWith(u8, line, "%error"))
|
||||
{
|
||||
const err = std.mem.startsWith(u8, line, "%error");
|
||||
if (parseBlockTerminator(line)) |terminator| {
|
||||
const err = terminator == .err;
|
||||
const output = std.mem.trimRight(u8, written[0..idx], "\r\n");
|
||||
|
||||
// If it is an error then log it.
|
||||
@@ -144,6 +147,36 @@ pub const Parser = struct {
|
||||
|
||||
const ParseError = error{RegexError};
|
||||
|
||||
/// Block payload is raw data, so a line only terminates a block if it
|
||||
/// exactly matches tmux's `%end`/`%error` guard-line shape.
|
||||
fn parseBlockTerminator(line_raw: []const u8) ?BlockTerminator {
|
||||
var line = line_raw;
|
||||
if (line.len > 0 and line[line.len - 1] == '\r') {
|
||||
line = line[0 .. line.len - 1];
|
||||
}
|
||||
|
||||
var fields = std.mem.tokenizeScalar(u8, line, ' ');
|
||||
const cmd = fields.next() orelse return null;
|
||||
const terminator: BlockTerminator = if (std.mem.eql(u8, cmd, "%end"))
|
||||
.end
|
||||
else if (std.mem.eql(u8, cmd, "%error"))
|
||||
.err
|
||||
else
|
||||
return null;
|
||||
|
||||
const time = fields.next() orelse return null;
|
||||
const command_id = fields.next() orelse return null;
|
||||
const flags = fields.next() orelse return null;
|
||||
const extra = fields.next();
|
||||
|
||||
_ = std.fmt.parseInt(usize, time, 10) catch return null;
|
||||
_ = std.fmt.parseInt(usize, command_id, 10) catch return null;
|
||||
_ = std.fmt.parseInt(usize, flags, 10) catch return null;
|
||||
if (extra != null) return null;
|
||||
|
||||
return terminator;
|
||||
}
|
||||
|
||||
fn parseNotification(self: *Parser) ParseError!?Notification {
|
||||
assert(self.state == .notification);
|
||||
|
||||
@@ -597,6 +630,81 @@ test "tmux begin/end data" {
|
||||
try testing.expectEqualStrings("hello\nworld", n.block_end);
|
||||
}
|
||||
|
||||
test "tmux block payload may start with %end" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Parser = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%begin 1 1 1\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%end not really\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("hello\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%end 1 1 1") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
try testing.expect(n == .block_end);
|
||||
try testing.expectEqualStrings("%end not really\nhello", n.block_end);
|
||||
}
|
||||
|
||||
test "tmux block payload may start with %error" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Parser = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%begin 1 1 1\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%error not really\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("hello\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%end 1 1 1") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
try testing.expect(n == .block_end);
|
||||
try testing.expectEqualStrings("%error not really\nhello", n.block_end);
|
||||
}
|
||||
|
||||
test "tmux block may terminate with real %error after misleading payload" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Parser = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%begin 1 1 1\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%error not really\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("hello\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%error 1 1 1") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
try testing.expect(n == .block_err);
|
||||
try testing.expectEqualStrings("%error not really\nhello", n.block_err);
|
||||
}
|
||||
|
||||
test "tmux block terminator requires exact token count" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Parser = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%begin 1 1 1\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%end 1 1 1 trailing\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("hello\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%end 1 1 1") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
try testing.expect(n == .block_end);
|
||||
try testing.expectEqualStrings("%end 1 1 1 trailing\nhello", n.block_end);
|
||||
}
|
||||
|
||||
test "tmux block terminator requires numeric metadata" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Parser = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%begin 1 1 1\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%end foo bar baz\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("hello\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%end 1 1 1") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
try testing.expect(n == .block_end);
|
||||
try testing.expectEqualStrings("%end foo bar baz\nhello", n.block_end);
|
||||
}
|
||||
|
||||
test "tmux output" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
Reference in New Issue
Block a user