mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 00:36:07 +03:00
Merge pull request #742 from mitchellh/fuzz-crashes
Fix more fuzz crashes
This commit is contained in:
@ -159,7 +159,10 @@ pub const Action = union(enum) {
|
|||||||
const value = @field(self, u_field.name);
|
const value = @field(self, u_field.name);
|
||||||
switch (@TypeOf(value)) {
|
switch (@TypeOf(value)) {
|
||||||
// Unicode
|
// Unicode
|
||||||
u21 => try std.fmt.format(writer, "'{u}'", .{value}),
|
u21 => try std.fmt.format(writer, "'{u}' (U+{X})", .{ value, value }),
|
||||||
|
|
||||||
|
// Byte
|
||||||
|
u8 => try std.fmt.format(writer, "0x{x}", .{value}),
|
||||||
|
|
||||||
// Note: we don't do ASCII (u8) because there are a lot
|
// Note: we don't do ASCII (u8) because there are a lot
|
||||||
// of invisible characters we don't want to handle right
|
// of invisible characters we don't want to handle right
|
||||||
|
@ -443,8 +443,8 @@ pub fn restoreCursor(self: *Terminal) void {
|
|||||||
self.screen.cursor.pen = saved.pen;
|
self.screen.cursor.pen = saved.pen;
|
||||||
self.screen.charset = saved.charset;
|
self.screen.charset = saved.charset;
|
||||||
self.modes.set(.origin, saved.origin);
|
self.modes.set(.origin, saved.origin);
|
||||||
self.screen.cursor.x = saved.x;
|
self.screen.cursor.x = @min(saved.x, self.cols - 1);
|
||||||
self.screen.cursor.y = saved.y;
|
self.screen.cursor.y = @min(saved.y, self.rows - 1);
|
||||||
self.screen.cursor.pending_wrap = saved.pending_wrap;
|
self.screen.cursor.pending_wrap = saved.pending_wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -766,6 +766,11 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
|
|
||||||
// Attach zero-width characters to our cell as grapheme data.
|
// Attach zero-width characters to our cell as grapheme data.
|
||||||
if (width == 0) {
|
if (width == 0) {
|
||||||
|
// If we have grapheme clustering enabled, we don't blindly attach
|
||||||
|
// any zero width character to our cells and we instead just ignore
|
||||||
|
// it.
|
||||||
|
if (self.modes.get(.grapheme_cluster)) return;
|
||||||
|
|
||||||
// If we're at cell zero, then this is malformed data and we don't
|
// If we're at cell zero, then this is malformed data and we don't
|
||||||
// print anything or even store this. Zero-width characters are ALWAYS
|
// print anything or even store this. Zero-width characters are ALWAYS
|
||||||
// attached to some other non-zero-width character at the time of
|
// attached to some other non-zero-width character at the time of
|
||||||
@ -883,10 +888,10 @@ fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
|
|||||||
// single-width characters into that.
|
// single-width characters into that.
|
||||||
if (cell.attrs.wide) {
|
if (cell.attrs.wide) {
|
||||||
const x = self.screen.cursor.x + 1;
|
const x = self.screen.cursor.x + 1;
|
||||||
assert(x < self.cols);
|
if (x < self.cols) {
|
||||||
|
|
||||||
const spacer_cell = row.getCellPtr(x);
|
const spacer_cell = row.getCellPtr(x);
|
||||||
spacer_cell.* = self.screen.cursor.pen;
|
spacer_cell.* = self.screen.cursor.pen;
|
||||||
|
}
|
||||||
|
|
||||||
if (self.screen.cursor.y > 0 and self.screen.cursor.x <= 1) {
|
if (self.screen.cursor.y > 0 and self.screen.cursor.x <= 1) {
|
||||||
self.clearWideSpacerHead();
|
self.clearWideSpacerHead();
|
||||||
@ -1341,10 +1346,12 @@ pub fn deleteChars(self: *Terminal, count: usize) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eraseChars(self: *Terminal, count: usize) void {
|
pub fn eraseChars(self: *Terminal, count_req: usize) void {
|
||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
|
const count = @max(count_req, 1);
|
||||||
|
|
||||||
// This resets the pending wrap state
|
// This resets the pending wrap state
|
||||||
self.screen.cursor.pending_wrap = false;
|
self.screen.cursor.pending_wrap = false;
|
||||||
|
|
||||||
@ -2170,6 +2177,31 @@ test "Terminal: print over wide spacer tail" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: zero width chars with grapheme clustering can be put in their own cell" {
|
||||||
|
var t = try init(testing.allocator, 5, 5);
|
||||||
|
defer t.deinit(testing.allocator);
|
||||||
|
|
||||||
|
// Enable grapheme clustering
|
||||||
|
t.modes.set(.grapheme_cluster, true);
|
||||||
|
|
||||||
|
try t.print('x');
|
||||||
|
try t.print(0x7F); // zero-width control character
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("x", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = t.screen.getRow(.{ .screen = 0 });
|
||||||
|
{
|
||||||
|
const cell = row.getCell(0);
|
||||||
|
try testing.expectEqual(@as(u32, 'x'), cell.char);
|
||||||
|
try testing.expect(!cell.attrs.wide);
|
||||||
|
try testing.expect(!cell.attrs.grapheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: VS15 to make narrow character" {
|
test "Terminal: VS15 to make narrow character" {
|
||||||
var t = try init(testing.allocator, 5, 5);
|
var t = try init(testing.allocator, 5, 5);
|
||||||
defer t.deinit(testing.allocator);
|
defer t.deinit(testing.allocator);
|
||||||
@ -4757,6 +4789,23 @@ test "Terminal: eraseChars simple operation" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: eraseChars minimum one" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
for ("ABC") |c| try t.print(c);
|
||||||
|
t.setCursorPos(1, 1);
|
||||||
|
t.eraseChars(0);
|
||||||
|
try t.print('X');
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("XBC", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: eraseChars beyond screen edge" {
|
test "Terminal: eraseChars beyond screen edge" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
@ -5016,6 +5065,24 @@ test "Terminal: saveCursor origin mode" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: saveCursor resize" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 10, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
t.setCursorPos(1, 10);
|
||||||
|
t.saveCursor();
|
||||||
|
try t.resize(alloc, 5, 5);
|
||||||
|
t.restoreCursor();
|
||||||
|
try t.print('X');
|
||||||
|
|
||||||
|
{
|
||||||
|
var str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings(" X", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: setProtectedMode" {
|
test "Terminal: setProtectedMode" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 3, 3);
|
var t = try init(alloc, 3, 3);
|
||||||
|
@ -197,7 +197,7 @@ pub const Parser = struct {
|
|||||||
.b = @truncate(rgb[2]),
|
.b = @truncate(rgb[2]),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else if (slice.len >= 2 and slice[1] == 5) {
|
} else if (slice.len >= 3 and slice[1] == 5) {
|
||||||
self.idx += 2;
|
self.idx += 2;
|
||||||
return Attribute{
|
return Attribute{
|
||||||
.@"256_fg" = @truncate(slice[2]),
|
.@"256_fg" = @truncate(slice[2]),
|
||||||
@ -225,7 +225,7 @@ pub const Parser = struct {
|
|||||||
.b = @truncate(rgb[2]),
|
.b = @truncate(rgb[2]),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else if (slice.len >= 2 and slice[1] == 5) {
|
} else if (slice.len >= 3 and slice[1] == 5) {
|
||||||
self.idx += 2;
|
self.idx += 2;
|
||||||
return Attribute{
|
return Attribute{
|
||||||
.@"256_bg" = @truncate(slice[2]),
|
.@"256_bg" = @truncate(slice[2]),
|
||||||
@ -532,3 +532,15 @@ test "sgr: underline, bg, and fg" {
|
|||||||
try testing.expectEqual(Attribute.Underline.single, v.underline);
|
try testing.expectEqual(Attribute.Underline.single, v.underline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "sgr: direct color fg missing color" {
|
||||||
|
// This used to crash
|
||||||
|
var p: Parser = .{ .params = &[_]u16{ 38, 5 }, .colon = false };
|
||||||
|
while (p.next()) |_| {}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "sgr: direct color bg missing color" {
|
||||||
|
// This used to crash
|
||||||
|
var p: Parser = .{ .params = &[_]u16{ 48, 5 }, .colon = false };
|
||||||
|
while (p.next()) |_| {}
|
||||||
|
}
|
||||||
|
@ -154,7 +154,7 @@ pub fn Stream(comptime Handler: type) type {
|
|||||||
else
|
else
|
||||||
log.warn("unimplemented invokeCharset: {x}", .{c}),
|
log.warn("unimplemented invokeCharset: {x}", .{c}),
|
||||||
|
|
||||||
else => log.warn("invalid C0 character, ignoring: {x}", .{c}),
|
else => log.warn("invalid C0 character, ignoring: 0x{x}", .{c}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user