mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-12 10:48:39 +03:00
107 lines
3.6 KiB
Zig
107 lines
3.6 KiB
Zig
const std = @import("std");
|
|
const testing = std.testing;
|
|
|
|
/// Writer that escapes characters that shells treat specially to reduce the
|
|
/// risk of injection attacks or other such weirdness. Specifically excludes
|
|
/// linefeeds so that they can be used to delineate lists of file paths.
|
|
///
|
|
/// T should be a Zig type that follows the `std.io.Writer` interface.
|
|
pub fn ShellEscapeWriter(comptime T: type) type {
|
|
return struct {
|
|
child_writer: T,
|
|
|
|
fn write(self: *ShellEscapeWriter(T), data: []const u8) error{Error}!usize {
|
|
var count: usize = 0;
|
|
for (data) |byte| {
|
|
const buf = switch (byte) {
|
|
'\\',
|
|
'"',
|
|
'\'',
|
|
'$',
|
|
'`',
|
|
'*',
|
|
'?',
|
|
' ',
|
|
'|',
|
|
'(',
|
|
')',
|
|
=> &[_]u8{ '\\', byte },
|
|
else => &[_]u8{byte},
|
|
};
|
|
self.child_writer.writeAll(buf) catch return error.Error;
|
|
count += 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
const Writer = std.io.Writer(*ShellEscapeWriter(T), error{Error}, write);
|
|
|
|
pub fn writer(self: *ShellEscapeWriter(T)) Writer {
|
|
return .{ .context = self };
|
|
}
|
|
};
|
|
}
|
|
|
|
test "shell escape 1" {
|
|
var buf: [128]u8 = undefined;
|
|
var fmt = std.io.fixedBufferStream(&buf);
|
|
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
|
|
const writer = shell.writer();
|
|
try writer.writeAll("abc");
|
|
try testing.expectEqualStrings("abc", fmt.getWritten());
|
|
}
|
|
|
|
test "shell escape 2" {
|
|
var buf: [128]u8 = undefined;
|
|
var fmt = std.io.fixedBufferStream(&buf);
|
|
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
|
|
const writer = shell.writer();
|
|
try writer.writeAll("a c");
|
|
try testing.expectEqualStrings("a\\ c", fmt.getWritten());
|
|
}
|
|
|
|
test "shell escape 3" {
|
|
var buf: [128]u8 = undefined;
|
|
var fmt = std.io.fixedBufferStream(&buf);
|
|
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
|
|
const writer = shell.writer();
|
|
try writer.writeAll("a?c");
|
|
try testing.expectEqualStrings("a\\?c", fmt.getWritten());
|
|
}
|
|
|
|
test "shell escape 4" {
|
|
var buf: [128]u8 = undefined;
|
|
var fmt = std.io.fixedBufferStream(&buf);
|
|
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
|
|
const writer = shell.writer();
|
|
try writer.writeAll("a\\c");
|
|
try testing.expectEqualStrings("a\\\\c", fmt.getWritten());
|
|
}
|
|
|
|
test "shell escape 5" {
|
|
var buf: [128]u8 = undefined;
|
|
var fmt = std.io.fixedBufferStream(&buf);
|
|
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
|
|
const writer = shell.writer();
|
|
try writer.writeAll("a|c");
|
|
try testing.expectEqualStrings("a\\|c", fmt.getWritten());
|
|
}
|
|
|
|
test "shell escape 6" {
|
|
var buf: [128]u8 = undefined;
|
|
var fmt = std.io.fixedBufferStream(&buf);
|
|
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
|
|
const writer = shell.writer();
|
|
try writer.writeAll("a\"c");
|
|
try testing.expectEqualStrings("a\\\"c", fmt.getWritten());
|
|
}
|
|
|
|
test "shell escape 7" {
|
|
var buf: [128]u8 = undefined;
|
|
var fmt = std.io.fixedBufferStream(&buf);
|
|
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
|
|
const writer = shell.writer();
|
|
try writer.writeAll("a(1)");
|
|
try testing.expectEqualStrings("a\\(1\\)", fmt.getWritten());
|
|
}
|