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); } }