mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
Add ghostty_paste_encode() which encodes paste data for writing to the terminal pty. It strips unsafe control bytes, wraps in bracketed paste sequences when requested, and replaces newlines with carriage returns for unbracketed mode. The input buffer is modified in place and the encoded result is written to a caller-provided output buffer, following the same buffer/out_written pattern as the other encode functions like ghostty_size_report_encode. Update the c-vt-paste example with an encode_example() demonstrating the new function and add corresponding @snippet references in the header documentation.
130 lines
4.2 KiB
Zig
130 lines
4.2 KiB
Zig
const std = @import("std");
|
|
const lib = @import("../lib.zig");
|
|
const paste = @import("../../input/paste.zig");
|
|
const Result = @import("result.zig").Result;
|
|
|
|
pub fn is_safe(data: ?[*]const u8, len: usize) callconv(lib.calling_conv) bool {
|
|
const slice: []const u8 = if (data) |v| v[0..len] else &.{};
|
|
return paste.isSafe(slice);
|
|
}
|
|
|
|
pub fn encode(
|
|
data: ?[*]u8,
|
|
data_len: usize,
|
|
bracketed: bool,
|
|
out_: ?[*]u8,
|
|
out_len: usize,
|
|
out_written: *usize,
|
|
) callconv(lib.calling_conv) Result {
|
|
const slice: []u8 = if (data) |v| v[0..data_len] else &.{};
|
|
const result = paste.encode(slice, .{ .bracketed = bracketed });
|
|
|
|
const total = result[0].len + result[1].len + result[2].len;
|
|
out_written.* = total;
|
|
|
|
const out: []u8 = if (out_) |o| o[0..out_len] else &.{};
|
|
if (out.len < total) return .out_of_space;
|
|
|
|
var offset: usize = 0;
|
|
for (result) |segment| {
|
|
@memcpy(out[offset..][0..segment.len], segment);
|
|
offset += segment.len;
|
|
}
|
|
|
|
return .success;
|
|
}
|
|
|
|
test "encode bracketed" {
|
|
const testing = std.testing;
|
|
const input = try testing.allocator.dupe(u8, "hello");
|
|
defer testing.allocator.free(input);
|
|
var buf: [64]u8 = undefined;
|
|
var written: usize = 0;
|
|
const result = encode(input.ptr, input.len, true, &buf, buf.len, &written);
|
|
try testing.expectEqual(.success, result);
|
|
try testing.expectEqualStrings("\x1b[200~hello\x1b[201~", buf[0..written]);
|
|
}
|
|
|
|
test "encode unbracketed no newlines" {
|
|
const testing = std.testing;
|
|
const input = try testing.allocator.dupe(u8, "hello");
|
|
defer testing.allocator.free(input);
|
|
var buf: [64]u8 = undefined;
|
|
var written: usize = 0;
|
|
const result = encode(input.ptr, input.len, false, &buf, buf.len, &written);
|
|
try testing.expectEqual(.success, result);
|
|
try testing.expectEqualStrings("hello", buf[0..written]);
|
|
}
|
|
|
|
test "encode unbracketed newlines" {
|
|
const testing = std.testing;
|
|
const input = try testing.allocator.dupe(u8, "hello\nworld");
|
|
defer testing.allocator.free(input);
|
|
var buf: [64]u8 = undefined;
|
|
var written: usize = 0;
|
|
const result = encode(input.ptr, input.len, false, &buf, buf.len, &written);
|
|
try testing.expectEqual(.success, result);
|
|
try testing.expectEqualStrings("hello\rworld", buf[0..written]);
|
|
}
|
|
|
|
test "encode strip unsafe bytes" {
|
|
const testing = std.testing;
|
|
const input = try testing.allocator.dupe(u8, "hel\x1blo\x00world");
|
|
defer testing.allocator.free(input);
|
|
var buf: [64]u8 = undefined;
|
|
var written: usize = 0;
|
|
const result = encode(input.ptr, input.len, true, &buf, buf.len, &written);
|
|
try testing.expectEqual(.success, result);
|
|
try testing.expectEqualStrings("\x1b[200~hel lo world\x1b[201~", buf[0..written]);
|
|
}
|
|
|
|
test "encode with insufficient buffer" {
|
|
const testing = std.testing;
|
|
const input = try testing.allocator.dupe(u8, "hello");
|
|
defer testing.allocator.free(input);
|
|
var buf: [1]u8 = undefined;
|
|
var written: usize = 0;
|
|
const result = encode(input.ptr, input.len, true, &buf, buf.len, &written);
|
|
try testing.expectEqual(.out_of_space, result);
|
|
try testing.expectEqual(17, written);
|
|
}
|
|
|
|
test "encode with null buffer" {
|
|
const testing = std.testing;
|
|
const input = try testing.allocator.dupe(u8, "hello");
|
|
defer testing.allocator.free(input);
|
|
var written: usize = 0;
|
|
const result = encode(input.ptr, input.len, true, null, 0, &written);
|
|
try testing.expectEqual(.out_of_space, result);
|
|
try testing.expectEqual(17, written);
|
|
}
|
|
|
|
test "is_safe with safe data" {
|
|
const testing = std.testing;
|
|
const safe = "hello world";
|
|
try testing.expect(is_safe(safe.ptr, safe.len));
|
|
}
|
|
|
|
test "is_safe with newline" {
|
|
const testing = std.testing;
|
|
const unsafe = "hello\nworld";
|
|
try testing.expect(!is_safe(unsafe.ptr, unsafe.len));
|
|
}
|
|
|
|
test "is_safe with bracketed paste end" {
|
|
const testing = std.testing;
|
|
const unsafe = "hello\x1b[201~world";
|
|
try testing.expect(!is_safe(unsafe.ptr, unsafe.len));
|
|
}
|
|
|
|
test "is_safe with empty data" {
|
|
const testing = std.testing;
|
|
const empty = "";
|
|
try testing.expect(is_safe(empty.ptr, 0));
|
|
}
|
|
|
|
test "is_safe with null empty data" {
|
|
const testing = std.testing;
|
|
try testing.expect(is_safe(null, 0));
|
|
}
|