233 lines
6.9 KiB
Zig
233 lines
6.9 KiB
Zig
|
const std = @import("std");
|
||
|
const builtin = @import("builtin");
|
||
|
const CircularBuffer = @import("circular-buffer").CircularBuffer;
|
||
|
|
||
|
fn sink(fd: std.posix.fd_t, done: *bool) void {
|
||
|
var buf: [1]u8 = undefined;
|
||
|
var count: usize = 0;
|
||
|
while (true) {
|
||
|
const read = std.posix.read(fd, &buf) catch {
|
||
|
done.* = true;
|
||
|
return;
|
||
|
};
|
||
|
if (read == 0) {
|
||
|
done.* = true;
|
||
|
return;
|
||
|
}
|
||
|
done.* = buf[0] == 0x03;
|
||
|
count +%= read;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn main() !void {
|
||
|
const fd = try std.posix.open("/dev/tty", .{ .ACCMODE = .RDWR }, 0);
|
||
|
const state = try std.posix.tcgetattr(fd);
|
||
|
var raw = state;
|
||
|
// see termios(3)
|
||
|
raw.iflag.IGNBRK = false;
|
||
|
raw.iflag.BRKINT = false;
|
||
|
raw.iflag.PARMRK = false;
|
||
|
raw.iflag.ISTRIP = false;
|
||
|
raw.iflag.INLCR = false;
|
||
|
raw.iflag.IGNCR = false;
|
||
|
raw.iflag.ICRNL = false;
|
||
|
raw.iflag.IXON = false;
|
||
|
|
||
|
raw.oflag.OPOST = false;
|
||
|
|
||
|
raw.lflag.ECHO = false;
|
||
|
raw.lflag.ECHONL = false;
|
||
|
raw.lflag.ICANON = false;
|
||
|
raw.lflag.ISIG = false;
|
||
|
raw.lflag.IEXTEN = false;
|
||
|
|
||
|
raw.cflag.CSIZE = .CS8;
|
||
|
raw.cflag.PARENB = false;
|
||
|
|
||
|
raw.cc[@intFromEnum(std.posix.V.MIN)] = 1;
|
||
|
raw.cc[@intFromEnum(std.posix.V.TIME)] = 0;
|
||
|
try std.posix.tcsetattr(fd, .FLUSH, raw);
|
||
|
|
||
|
defer {
|
||
|
std.posix.tcsetattr(fd, .FLUSH, state) catch |err| {
|
||
|
std.log.err("couldn't restore terminal: {}", .{err});
|
||
|
};
|
||
|
if (builtin.os.tag != .macos) // closing /dev/tty may block indefinitely on macos
|
||
|
std.posix.close(fd);
|
||
|
}
|
||
|
|
||
|
var done: bool = false;
|
||
|
var thread = try std.Thread.spawn(.{}, sink, .{ fd, &done });
|
||
|
thread.detach();
|
||
|
|
||
|
var cb = CircularBuffer(u20){};
|
||
|
|
||
|
const seed = seed: {
|
||
|
var buf: [4096]u8 = undefined;
|
||
|
var fba = std.heap.FixedBufferAllocator.init(&buf);
|
||
|
|
||
|
var args = try std.process.argsWithAllocator(fba.allocator());
|
||
|
defer args.deinit();
|
||
|
|
||
|
_ = args.next();
|
||
|
|
||
|
while (args.next()) |arg| {
|
||
|
break :seed try std.fmt.parseUnsigned(u64, arg, 0);
|
||
|
}
|
||
|
|
||
|
var seed: u64 = undefined;
|
||
|
try std.posix.getrandom(std.mem.asBytes(&seed));
|
||
|
break :seed seed;
|
||
|
};
|
||
|
|
||
|
var tmp = try std.fs.openDirAbsolute("/tmp", .{});
|
||
|
defer tmp.close();
|
||
|
|
||
|
var prng = std.rand.DefaultPrng.init(seed);
|
||
|
var random = prng.random();
|
||
|
|
||
|
var fn_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||
|
const filename = try std.fmt.bufPrint(
|
||
|
&fn_buf,
|
||
|
"kcp-fuzzer-0x{x:0>16}-0x{x:0>16}.txt",
|
||
|
.{
|
||
|
@abs(std.time.timestamp()),
|
||
|
seed,
|
||
|
},
|
||
|
);
|
||
|
|
||
|
var iteration: usize = 0;
|
||
|
while (!done) : (iteration +%= 1) {
|
||
|
if (iteration & 0x3ff == 0) {
|
||
|
_ = try std.posix.write(fd, ".");
|
||
|
}
|
||
|
|
||
|
var kcp_buf: [512]u8 = undefined;
|
||
|
var kcp_fbs = std.io.fixedBufferStream(&kcp_buf);
|
||
|
var kcp_writer = kcp_fbs.writer();
|
||
|
try kcp_writer.print("\x1b]21", .{});
|
||
|
for (0..random.int(u3)) |_| {
|
||
|
const keys = enum {
|
||
|
foreground,
|
||
|
background,
|
||
|
selection_background,
|
||
|
selection_foreground,
|
||
|
cursor,
|
||
|
cursor_text,
|
||
|
visual_bell,
|
||
|
transparent_background_color1,
|
||
|
transparent_background_color2,
|
||
|
transparent_background_color3,
|
||
|
transparent_background_color4,
|
||
|
transparent_background_color5,
|
||
|
transparent_background_color6,
|
||
|
transparent_background_color7,
|
||
|
transparent_background_color8,
|
||
|
};
|
||
|
try kcp_writer.print(";{s}=", .{@tagName(random.enumValue(keys))});
|
||
|
switch (random.enumValue(enum {
|
||
|
color1,
|
||
|
color2,
|
||
|
color3,
|
||
|
color4,
|
||
|
color5,
|
||
|
color6,
|
||
|
color7,
|
||
|
color8,
|
||
|
color9,
|
||
|
query,
|
||
|
empty,
|
||
|
})) {
|
||
|
.color1 => try kcp_writer.print(
|
||
|
"rgb:{x:0>1}/{x:0>1}/{x:0>1}",
|
||
|
.{
|
||
|
random.int(u4),
|
||
|
random.int(u4),
|
||
|
random.int(u4),
|
||
|
},
|
||
|
),
|
||
|
.color2 => try kcp_writer.print(
|
||
|
"rgb:{x:0>2}/{x:0>2}/{x:0>2}",
|
||
|
.{
|
||
|
random.int(u8),
|
||
|
random.int(u8),
|
||
|
random.int(u8),
|
||
|
},
|
||
|
),
|
||
|
.color3 => try kcp_writer.print(
|
||
|
"rgb:{x:0>3}/{x:0>3}/{x:0>3}",
|
||
|
.{
|
||
|
random.int(u12),
|
||
|
random.int(u12),
|
||
|
random.int(u12),
|
||
|
},
|
||
|
),
|
||
|
.color4 => try kcp_writer.print(
|
||
|
"rgb:{x:0>4}/{x:0>4}/{x:0>4}",
|
||
|
.{
|
||
|
random.int(u16),
|
||
|
random.int(u16),
|
||
|
random.int(u16),
|
||
|
},
|
||
|
),
|
||
|
.color5 => try kcp_writer.print(
|
||
|
"#{x:0>1}{x:0>1}{x:0>1}",
|
||
|
.{
|
||
|
random.int(u4),
|
||
|
random.int(u4),
|
||
|
random.int(u4),
|
||
|
},
|
||
|
),
|
||
|
.color6 => try kcp_writer.print(
|
||
|
"#{x:0>2}{x:0>2}{x:0>2}",
|
||
|
.{
|
||
|
random.int(u8),
|
||
|
random.int(u8),
|
||
|
random.int(u8),
|
||
|
},
|
||
|
),
|
||
|
.color7 => try kcp_writer.print(
|
||
|
"#{x:0>3}{x:0>3}{x:0>3}",
|
||
|
.{
|
||
|
random.int(u12),
|
||
|
random.int(u12),
|
||
|
random.int(u12),
|
||
|
},
|
||
|
),
|
||
|
.color8 => try kcp_writer.print(
|
||
|
"#{x:0>4}{x:0>4}{x:0>4}",
|
||
|
.{
|
||
|
random.int(u16),
|
||
|
random.int(u16),
|
||
|
random.int(u16),
|
||
|
},
|
||
|
),
|
||
|
.color9 => try kcp_writer.print(
|
||
|
"rgbi:{d:4.2}/{d:4.2}/{d:4.2}",
|
||
|
.{
|
||
|
random.float(f32),
|
||
|
random.float(f32),
|
||
|
random.float(f32),
|
||
|
},
|
||
|
),
|
||
|
.query => try kcp_writer.writeByte('?'),
|
||
|
.empty => {},
|
||
|
}
|
||
|
}
|
||
|
try kcp_writer.writeAll("\x1b\\");
|
||
|
|
||
|
const kcp = kcp_fbs.getWritten();
|
||
|
|
||
|
for (kcp) |c| cb.pushByte(c);
|
||
|
|
||
|
var file = try tmp.atomicFile(filename, .{});
|
||
|
defer file.deinit();
|
||
|
|
||
|
try cb.write(file.file.writer());
|
||
|
|
||
|
try file.finish();
|
||
|
|
||
|
_ = try std.posix.write(fd, kcp);
|
||
|
}
|
||
|
}
|