diff --git a/.gitignore b/.gitignore index dfe5b37..7599683 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .zig-cache/ zig-out/ +zig-pkg/ .vscode/ .dim-out/ \ No newline at end of file diff --git a/build.zig.zon b/build.zig.zon index c710cf7..9961fd3 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -4,12 +4,12 @@ .fingerprint = 0x9947018c924eecb2, .dependencies = .{ .zfat = .{ - .url = "git+https://github.com/ZigEmbeddedGroup/zfat.git#0571b0d8c8cc4fcb037a1d5e7ea5666cb2f83ddf", - .hash = "zfat-0.15.0-SNNK9RRqcgCLQ5mjXghbB6mokzcHORsGnY7GtbfOt2k3", + .url = "git+https://github.com/khitiara/zfat?ref=0.15#4ce2a6a95d4410573dcc1c159085f554465aea04", + .hash = "zfat-0.15.0-SNNK9QCZcgBdWwpwf_lzSuKP12w2HZQnIQwT4qn8TfZi", }, .args = .{ - .url = "git+https://github.com/ikskuh/zig-args.git#8ae26b44a884ff20dca98ee84c098e8f8e94902f", - .hash = "args-0.0.0-CiLiqojRAACGzDRO7A9dw7kWSchNk29caJZkXuMCb0Cn", + .url = "git+https://github.com/khitiara/zig-args#4085c9be55dfd2eeb6893694510a2ce97e89f9fe", + .hash = "args-0.0.0-CiLiqiLSAAArS0NVwnYLAarU-u1csj8zAkg5-q4pa1n8", }, }, .paths = .{ diff --git a/src/BuildInterface.zig b/src/BuildInterface.zig index c1aedc5..feee4d4 100644 --- a/src/BuildInterface.zig +++ b/src/BuildInterface.zig @@ -26,18 +26,20 @@ pub fn createDisk(dimmer: Interface, size: u64, content: Content) std.Build.Lazy const write_files = b.addWriteFiles(); - const script_source, const variables = renderContent(write_files, b.allocator, content); + const script_source, const variables = renderContent(write_files, b.allocator, content, dimmer.builder.graph.io); const script_file = write_files.add("image.dis", script_source); const compile_script = b.addRunArtifact(dimmer.dimmer_exe); + compile_script.setCwd(script_file.dirname()); + _ = compile_script.addPrefixedDepFileOutputArg("--deps-file=", "image.d"); compile_script.addArg(b.fmt("--size={d}", .{size})); compile_script.addPrefixedFileArg("--script=", script_file); - compile_script.addPrefixedDirectoryArg("--script-root=", .{ .cwd_relative = "." }); + // compile_script.addPrefixedDirectoryArg("--script-root=", .{ .cwd_relative = "." }); const result_file = compile_script.addPrefixedOutputFileArg("--output=", "disk.img"); @@ -67,6 +69,7 @@ fn renderContent( wfs: *std.Build.Step.WriteFile, allocator: std.mem.Allocator, content: Content, + io: std.Io, ) struct { []const u8, ContentWriter.VariableMap } { var code: std.Io.Writer.Allocating = .init(allocator); defer code.deinit(); @@ -79,7 +82,7 @@ fn renderContent( .vars = &variables, }; - cw.render(content) catch @panic("out of memory"); + cw.render(content, io) catch @panic("out of memory"); const source = std.mem.trim( u8, @@ -107,7 +110,7 @@ const ContentWriter = struct { code: *std.Io.Writer, vars: *VariableMap, - fn render(cw: ContentWriter, content: Content) !void { + fn render(cw: ContentWriter, content: Content, io: std.Io) !void { // Always insert some padding before and after: try cw.code.writeAll(" "); errdefer cw.code.writeAll(" ") catch {}; @@ -122,7 +125,7 @@ const ContentWriter = struct { }, .paste_file => |data| { - try cw.code.print("paste-file {f}", .{cw.fmtLazyPath(data, .file)}); + try cw.code.print("paste-file {f}", .{cw.fmtLazyPath(data, .file, io)}); }, .mbr_part_table => |data| { @@ -130,7 +133,7 @@ const ContentWriter = struct { if (data.bootloader) |loader| { try cw.code.writeAll(" bootloader "); - try cw.render(loader.*); + try cw.render(loader.*, io); try cw.code.writeAll("\n"); } @@ -151,7 +154,7 @@ const ContentWriter = struct { try cw.code.print(" size {d}\n", .{size}); } try cw.code.writeAll(" contains"); - try cw.render(part.data); + try cw.render(part.data, io); try cw.code.writeAll("\n"); try cw.code.writeAll(" endpart\n"); } else { @@ -183,6 +186,9 @@ const ContentWriter = struct { if (part.name) |name| { try cw.code.print(" name \"{f}\"\n", .{std.zig.fmtString(name)}); } + if (part.part_guid) |pg| { + try cw.code.print(" guid \"{s}\"", .{&pg}); + } if (part.offset) |offset| { try cw.code.print(" offset {d}\n", .{offset}); } @@ -190,7 +196,7 @@ const ContentWriter = struct { try cw.code.print(" size {d}\n", .{size}); } try cw.code.writeAll(" contains"); - try cw.render(part.data); + try cw.render(part.data, io); try cw.code.writeAll("\n"); try cw.code.writeAll(" endpart\n"); } @@ -208,14 +214,14 @@ const ContentWriter = struct { }); } - try cw.renderFileSystemTree(data.tree); + try cw.renderFileSystemTree(data.tree, io); try cw.code.writeAll("endfat\n"); }, } } - fn renderFileSystemTree(cw: ContentWriter, fs: FileSystem) !void { + fn renderFileSystemTree(cw: ContentWriter, fs: FileSystem, io: std.Io) !void { for (fs.items) |item| { switch (item) { .empty_dir => |dir| try cw.code.print("mkdir {f}\n", .{ @@ -224,16 +230,16 @@ const ContentWriter = struct { .copy_dir => |copy| try cw.code.print("copy-dir {f} {f}\n", .{ fmtPath(copy.destination), - cw.fmtLazyPath(copy.source, .directory), + cw.fmtLazyPath(copy.source, .directory, io), }), .copy_file => |copy| try cw.code.print("copy-file {f} {f}\n", .{ fmtPath(copy.destination), - cw.fmtLazyPath(copy.source, .file), + cw.fmtLazyPath(copy.source, .file, io), }), .include_script => |script| try cw.code.print("!include {f}\n", .{ - cw.fmtLazyPath(script, .file), + cw.fmtLazyPath(script, .file, io), }), } } @@ -280,7 +286,7 @@ const ContentWriter = struct { } }; const LazyPathFormatter = std.fmt.Alt( - struct { ContentWriter, std.Build.LazyPath, UsageHint }, + struct { ContentWriter, std.Build.LazyPath, UsageHint, std.Io }, formatLazyPath, ); const UsageHint = enum { file, directory }; @@ -289,8 +295,9 @@ const ContentWriter = struct { cw: ContentWriter, path: std.Build.LazyPath, hint: UsageHint, + io: std.Io, ) LazyPathFormatter { - return .{ .data = .{ cw, path, hint } }; + return .{ .data = .{ cw, path, hint, io } }; } fn fmtPath(path: []const u8) PathFormatter { @@ -298,10 +305,10 @@ const ContentWriter = struct { } fn formatLazyPath( - data: struct { ContentWriter, std.Build.LazyPath, UsageHint }, + data: struct { ContentWriter, std.Build.LazyPath, UsageHint, std.Io }, writer: *std.Io.Writer, ) std.Io.Writer.Error!void { - const cw, const path, const hint = data; + const cw, const path, const hint, const io = data; switch (path) { .cwd_relative, @@ -314,12 +321,12 @@ const ContentWriter = struct { const rel_path = path.getPath2(cw.wfs.step.owner, &cw.wfs.step); const full_path = if (!std.fs.path.isAbsolute(rel_path)) - std.fs.cwd().realpathAlloc(cw.wfs.step.owner.allocator, rel_path) catch @panic("oom") + std.Io.Dir.cwd().realPathFileAlloc(io, rel_path, cw.wfs.step.owner.allocator) catch @panic("oom") else rel_path; if (!std.fs.path.isAbsolute(full_path)) { - const cwd = std.fs.cwd().realpathAlloc(cw.wfs.step.owner.allocator, ".") catch @panic("oom"); + const cwd = std.Io.Dir.cwd().realPathFileAlloc(io, ".", cw.wfs.step.owner.allocator) catch @panic("oom"); std.debug.print("non-absolute path detected for {t}: cwd=\"{f}\" path=\"{f}\"\n", .{ path, std.zig.fmtString(cwd), @@ -414,6 +421,7 @@ pub const GptPartTable = struct { guid: [36]u8, }, name: ?[]const u8 = null, + part_guid: ?[36]u8 = null, size: ?u64 = null, offset: ?u64 = null, data: Content, @@ -439,7 +447,7 @@ pub const FatFs = struct { pub const FileSystemBuilder = struct { b: *std.Build, - list: std.ArrayListUnmanaged(FileSystem.Item), + list: std.ArrayList(FileSystem.Item), pub fn init(b: *std.Build) FileSystemBuilder { return FileSystemBuilder{ diff --git a/src/Parser.zig b/src/Parser.zig index edfdc13..d656f73 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -18,18 +18,19 @@ pub const Error = Tokenizer.Error || error{ UnknownDirective, OutOfMemory, InvalidEscapeSequence, + Canceled, }; pub const IO = struct { - fetch_file_fn: *const fn (io: *const IO, std.mem.Allocator, path: []const u8) error{ FileNotFound, IoError, OutOfMemory, InvalidPath }![]const u8, - resolve_variable_fn: *const fn (io: *const IO, name: []const u8) error{UnknownVariable}![]const u8, + fetch_file_fn: *const fn (stdio: std.Io, io: *const IO, std.mem.Allocator, path: []const u8) error{ FileNotFound, IoError, OutOfMemory, InvalidPath, Canceled }![]const u8, + resolve_variable_fn: *const fn (stdio: std.Io, io: *const IO, name: []const u8) error{UnknownVariable}![]const u8, - pub fn fetch_file(io: *const IO, allocator: std.mem.Allocator, path: []const u8) error{ FileNotFound, IoError, OutOfMemory, InvalidPath }![]const u8 { - return io.fetch_file_fn(io, allocator, path); + pub fn fetch_file(io: *const IO, stdio: std.Io, allocator: std.mem.Allocator, path: []const u8) error{ FileNotFound, IoError, OutOfMemory, InvalidPath, Canceled }![]const u8 { + return io.fetch_file_fn(stdio,io, allocator, path); } - pub fn resolve_variable(io: *const IO, name: []const u8) error{UnknownVariable}![]const u8 { - return io.resolve_variable_fn(io, name); + pub fn resolve_variable(io: *const IO, stdio: std.Io, name: []const u8) error{UnknownVariable}![]const u8 { + return io.resolve_variable_fn(stdio,io, name); } }; @@ -84,10 +85,10 @@ pub fn push_source(parser: *Parser, options: struct { }; } -pub fn push_file(parser: *Parser, include_path: []const u8) !void { +pub fn push_file(parser: *Parser, stdio: std.Io, include_path: []const u8) !void { const abs_include_path = try parser.get_include_path(parser.arena.allocator(), include_path); - const file_contents = try parser.io.fetch_file(parser.arena.allocator(), abs_include_path); + const file_contents = try parser.io.fetch_file(stdio , parser.arena.allocator(), abs_include_path); const index = parser.file_stack.len; parser.file_stack.len += 1; @@ -120,14 +121,14 @@ pub fn get_include_path(parser: Parser, allocator: std.mem.Allocator, rel_includ return abs_include_path; } -pub fn next(parser: *Parser) (Error || error{UnexpectedEndOfFile})![]const u8 { - return if (try parser.next_or_eof()) |word| +pub fn next(parser: *Parser, stdio: std.Io) (Error || error{UnexpectedEndOfFile})![]const u8 { + return if (try parser.next_or_eof(stdio)) |word| word else error.UnexpectedEndOfFile; } -pub fn next_or_eof(parser: *Parser) Error!?[]const u8 { +pub fn next_or_eof(parser: *Parser, stdio: std.Io) Error!?[]const u8 { fetch_loop: while (parser.file_stack.len > 0) { const top = &parser.file_stack[parser.file_stack.len - 1]; @@ -141,7 +142,7 @@ pub fn next_or_eof(parser: *Parser) Error!?[]const u8 { switch (token.type) { .whitespace, .comment => unreachable, - .word, .variable, .string => return try parser.resolve_value( + .word, .variable, .string => return try parser.resolve_value(stdio, token.type, top.tokenizer.get_text(token), ), @@ -152,14 +153,14 @@ pub fn next_or_eof(parser: *Parser) Error!?[]const u8 { if (std.mem.eql(u8, directive, "!include")) { if (try fetch_token(&top.tokenizer)) |path_token| { const rel_include_path = switch (path_token.type) { - .word, .variable, .string => try parser.resolve_value( + .word, .variable, .string => try parser.resolve_value(stdio, path_token.type, top.tokenizer.get_text(path_token), ), .comment, .directive, .whitespace => return error.BadDirective, }; - try parser.push_file(rel_include_path); + try parser.push_file(stdio, rel_include_path); } else { return error.ExpectedIncludePath; } @@ -189,11 +190,11 @@ fn fetch_token(tok: *Tokenizer) Tokenizer.Error!?Token { } } -fn resolve_value(parser: *Parser, token_type: TokenType, text: []const u8) ![]const u8 { +fn resolve_value(parser: *Parser, stdio: std.Io, token_type: TokenType, text: []const u8) ![]const u8 { return switch (token_type) { .word => text, - .variable => try parser.io.resolve_variable( + .variable => try parser.io.resolve_variable(stdio, text[1..], ), @@ -208,10 +209,12 @@ fn resolve_value(parser: *Parser, token_type: TokenType, text: []const u8) ![]co if (!has_includes) return content_slice; - var unescaped: std.array_list.Managed(u8) = .init(parser.arena.allocator()); - defer unescaped.deinit(); + const allocator = parser.arena.allocator(); - try unescaped.ensureTotalCapacityPrecise(content_slice.len); + var unescaped = std.ArrayList(u8).empty; + defer unescaped.deinit(allocator); + + try unescaped.ensureTotalCapacityPrecise(allocator, content_slice.len); { var i: usize = 0; @@ -220,7 +223,7 @@ fn resolve_value(parser: *Parser, token_type: TokenType, text: []const u8) ![]co i += 1; if (c != '\\') { - try unescaped.append(c); + try unescaped.append(allocator, c); continue; } @@ -233,20 +236,20 @@ fn resolve_value(parser: *Parser, token_type: TokenType, text: []const u8) ![]co errdefer std.log.err("invalid escape sequence: \\{s}", .{[_]u8{esc_code}}); switch (esc_code) { - 'r' => try unescaped.append('\r'), - 'n' => try unescaped.append('\n'), - 't' => try unescaped.append('\t'), - '\\' => try unescaped.append('\\'), - '\"' => try unescaped.append('\"'), - '\'' => try unescaped.append('\''), - 'e' => try unescaped.append('\x1B'), + 'r' => try unescaped.append(allocator, '\r'), + 'n' => try unescaped.append(allocator, '\n'), + 't' => try unescaped.append(allocator, '\t'), + '\\' => try unescaped.append(allocator, '\\'), + '\"' => try unescaped.append(allocator, '\"'), + '\'' => try unescaped.append(allocator, '\''), + 'e' => try unescaped.append(allocator, '\x1B'), else => return error.InvalidEscapeSequence, } } } - return try unescaped.toOwnedSlice(); + return try unescaped.toOwnedSlice(allocator); }, .comment, .directive, .whitespace => unreachable, diff --git a/src/components/EmptyData.zig b/src/components/EmptyData.zig index da335f0..6393a1f 100644 --- a/src/components/EmptyData.zig +++ b/src/components/EmptyData.zig @@ -8,14 +8,16 @@ const dim = @import("../dim.zig"); const EmptyData = @This(); -pub fn parse(ctx: dim.Context) !dim.Content { +pub fn parse(ctx: dim.Context, stdio: std.Io) !dim.Content { _ = ctx; + _ = stdio; return .create_handle(undefined, .create(@This(), .{ .render_fn = render, })); } -fn render(self: *EmptyData, stream: *dim.BinaryStream) dim.Content.RenderError!void { +fn render(self: *EmptyData, io: std.Io, stream: *dim.BinaryStream) dim.Content.RenderError!void { _ = self; _ = stream; + _ = io; } diff --git a/src/components/FillData.zig b/src/components/FillData.zig index 82b8cb4..f902471 100644 --- a/src/components/FillData.zig +++ b/src/components/FillData.zig @@ -9,19 +9,21 @@ const FillData = @This(); fill_value: u8, -pub fn parse(ctx: dim.Context) !dim.Content { +pub fn parse(ctx: dim.Context, stdio: std.Io) !dim.Content { const pf = try ctx.alloc_object(FillData); pf.* = .{ - .fill_value = try ctx.parse_integer(u8, 0), + .fill_value = try ctx.parse_integer(stdio, u8, 0), }; return .create_handle(pf, .create(@This(), .{ .render_fn = render, })); } -fn render(self: *FillData, stream: *dim.BinaryStream) dim.Content.RenderError!void { - try stream.writer().writeByteNTimes( +fn render(self: *FillData, io: std.Io, stream: *dim.BinaryStream) dim.Content.RenderError!void { + var writer = stream.writer(io, &.{}); + writer.interface.splatByteAll( self.fill_value, stream.length, - ); + ) catch return error.Overflow; // TODO FIX we don't know actually why this failed. + // std.Io.Writer only returns error.WriteFailed. } diff --git a/src/components/PasteFile.zig b/src/components/PasteFile.zig index 3cbd9e7..fec4b8a 100644 --- a/src/components/PasteFile.zig +++ b/src/components/PasteFile.zig @@ -5,16 +5,16 @@ const PasteFile = @This(); file_handle: dim.FileName, -pub fn parse(ctx: dim.Context) !dim.Content { +pub fn parse(ctx: dim.Context, stdio: std.Io) !dim.Content { const pf = try ctx.alloc_object(PasteFile); pf.* = .{ - .file_handle = try ctx.parse_file_name(), + .file_handle = try ctx.parse_file_name(stdio), }; return .create_handle(pf, .create(@This(), .{ .render_fn = render, })); } -fn render(self: *PasteFile, stream: *dim.BinaryStream) dim.Content.RenderError!void { - try self.file_handle.copy_to(stream); +fn render(self: *PasteFile, io: std.Io, stream: *dim.BinaryStream) dim.Content.RenderError!void { + try self.file_handle.copy_to(io, stream); } diff --git a/src/components/fs/FatFileSystem.zig b/src/components/fs/FatFileSystem.zig index 4c35085..13b3490 100644 --- a/src/components/fs/FatFileSystem.zig +++ b/src/components/fs/FatFileSystem.zig @@ -18,8 +18,8 @@ ops: std.array_list.Managed(common.FsOperation), sector_align: ?c_uint = null, cluster_size: ?u32 = null, -pub fn parse(ctx: dim.Context) !dim.Content { - const fat_type = try ctx.parse_enum(FatType); +pub fn parse(ctx: dim.Context, stdio: std.Io) !dim.Content { + const fat_type = try ctx.parse_enum(stdio,FatType); const pf = try ctx.alloc_object(FAT); pf.* = .{ @@ -32,7 +32,7 @@ pub fn parse(ctx: dim.Context) !dim.Content { .updater = .init(ctx, pf), }; - try common.parse_ops(ctx, "endfat", &appender); + try common.parse_ops(ctx, stdio, "endfat", &appender); try appender.updater.validate(); @@ -59,7 +59,7 @@ const Appender = struct { try self.fat.ops.append(op); } - pub fn parse_custom_op(self: *@This(), ctx: dim.Context, str_op: []const u8) !void { + pub fn parse_custom_op(self: *@This(), stdio: std.Io, ctx: dim.Context, str_op: []const u8) !void { const Op = enum { label, fats, @@ -72,16 +72,17 @@ const Appender = struct { .{str_op}, ); switch (op) { - .label => try self.updater.set(.label, try ctx.parse_string()), - .fats => try self.updater.set(.fats, try ctx.parse_enum(fatfs.FatTables)), - .@"root-size" => try self.updater.set(.rootdir_size, try ctx.parse_integer(c_uint, 0)), - .@"sector-align" => try self.updater.set(.sector_align, try ctx.parse_integer(c_uint, 0)), - .@"cluster-size" => try self.updater.set(.cluster_size, try ctx.parse_integer(u32, 0)), + .label => try self.updater.set(.label, try ctx.parse_string(stdio)), + .fats => try self.updater.set(.fats, try ctx.parse_enum(stdio, fatfs.FatTables)), + .@"root-size" => try self.updater.set(.rootdir_size, try ctx.parse_integer(stdio,c_uint, 0)), + .@"sector-align" => try self.updater.set(.sector_align, try ctx.parse_integer(stdio, c_uint, 0)), + .@"cluster-size" => try self.updater.set(.cluster_size, try ctx.parse_integer(stdio, u32, 0)), } } }; -fn render(self: *FAT, stream: *dim.BinaryStream) dim.Content.RenderError!void { +fn render(self: *FAT, io: std.Io, stream: *dim.BinaryStream) dim.Content.RenderError!void { + fatfs.io = io; var bsd: BinaryStreamDisk = .{ .stream = stream }; const min_size, const max_size = self.format_as.get_size_limits(); @@ -154,7 +155,7 @@ fn render(self: *FAT, stream: *dim.BinaryStream) dim.Content.RenderError!void { const wrapper = AtomicOps{}; for (self.ops.items) |op| { - try op.execute(wrapper); + try op.execute(io, wrapper); } } @@ -241,7 +242,7 @@ const AtomicOps = struct { defer fs_file.close(); var fs_file_buffer: [1024]u8 = undefined; - var adapter = fs_file.writer(&fs_file_buffer); + var adapter = fs_file.writer( &fs_file_buffer); _ = try reader.streamRemaining(&adapter.writer); @@ -273,25 +274,26 @@ const BinaryStreamDisk = struct { return disk_getStatus(intf); } - fn disk_read(intf: *fatfs.Disk, buff: [*]u8, sector: fatfs.LBA, count: c_uint) fatfs.Disk.Error!void { + fn disk_read(intf: *fatfs.Disk, io: std.Io, buff: [*]u8, sector: fatfs.LBA, count: c_uint) fatfs.Disk.Error!void { const bsd: *BinaryStreamDisk = @fieldParentPtr("disk", intf); - bsd.stream.read(block_size * sector, buff[0 .. count * block_size]) catch |err| { + bsd.stream.read(io, block_size * sector, buff[0 .. count * block_size]) catch |err| { bsd.disk_error = err; return error.IoError; }; } - fn disk_write(intf: *fatfs.Disk, buff: [*]const u8, sector: fatfs.LBA, count: c_uint) fatfs.Disk.Error!void { + fn disk_write(intf: *fatfs.Disk, io: std.Io, buff: [*]const u8, sector: fatfs.LBA, count: c_uint) fatfs.Disk.Error!void { const bsd: *BinaryStreamDisk = @fieldParentPtr("disk", intf); - bsd.stream.write(block_size * sector, buff[0 .. count * block_size]) catch |err| { + bsd.stream.write(io, block_size * sector, buff[0 .. count * block_size]) catch |err| { bsd.disk_error = err; return error.IoError; }; } - fn disk_ioctl(intf: *fatfs.Disk, cmd: fatfs.IoCtl, buff: [*]u8) fatfs.Disk.Error!void { + fn disk_ioctl(intf: *fatfs.Disk, io: std.Io, cmd: fatfs.IoCtl, buff: [*]u8) fatfs.Disk.Error!void { + _ = io; const bsd: *BinaryStreamDisk = @fieldParentPtr("disk", intf); switch (cmd) { diff --git a/src/components/fs/common.zig b/src/components/fs/common.zig index 0efdc7b..fe7048e 100644 --- a/src/components/fs/common.zig +++ b/src/components/fs/common.zig @@ -26,9 +26,9 @@ pub const FsOperation = union(enum) { contents: dim.Content, }, - pub fn execute(op: FsOperation, executor: anytype) !void { + pub fn execute(op: FsOperation, io: std.Io, executor: anytype) !void { const exec: Executor(@TypeOf(executor)) = .init(executor); - try exec.execute(op); + try exec.execute(io, op); } }; @@ -42,30 +42,30 @@ fn Executor(comptime T: type) type { return .{ .inner = wrapped }; } - fn execute(exec: Exec, op: FsOperation) dim.Content.RenderError!void { + fn execute(exec: Exec, io: std.Io, op: FsOperation) dim.Content.RenderError!void { switch (op) { .make_dir => |data| { try exec.recursive_mkdir(data.path); }, .copy_file => |data| { - var handle = data.source.open() catch |err| switch (err) { + var handle = data.source.open(io) catch |err| switch (err) { error.FileNotFound => return, // open() already reported the error else => |e| return e, }; - defer handle.close(); + defer handle.close(io); var buffer: [1024]u8 = undefined; - var adapter = handle.reader(&buffer); + var adapter = handle.reader(io, &buffer); try exec.add_file(data.path, &adapter.interface); }, .copy_dir => |data| { - var iter_dir = data.source.open_dir() catch |err| switch (err) { + var iter_dir = data.source.open_dir(io) catch |err| switch (err) { error.FileNotFound => return, // open() already reported the error else => |e| return e, }; - defer iter_dir.close(); + defer iter_dir.close(io); var walker_memory: [16384]u8 = undefined; var temp_allocator: std.heap.FixedBufferAllocator = .init(&walker_memory); @@ -75,7 +75,7 @@ fn Executor(comptime T: type) type { var walker = try iter_dir.walk(temp_allocator.allocator()); defer walker.deinit(); - while (walker.next() catch |err| return walk_err(err)) |entry| { + while (walker.next(io) catch |err| return walk_err(err)) |entry| { const path = std.fmt.bufPrintZ(&path_memory, "{s}/{s}", .{ data.path, entry.path, @@ -91,11 +91,11 @@ fn Executor(comptime T: type) type { .rel_path = entry.basename, }; - var file = try fname.open(); - defer file.close(); + var file = try fname.open(io); + defer file.close(io); var buffer: [1024]u8 = undefined; - var adapter = file.reader(&buffer); + var adapter = file.reader(io, &buffer); try exec.add_file(path, &adapter.interface); }, @@ -106,8 +106,8 @@ fn Executor(comptime T: type) type { else => { var realpath_buffer: [std.fs.max_path_bytes]u8 = undefined; - std.log.warn("cannot copy file {!s}: {s} is not a supported file type!", .{ - entry.dir.realpath(entry.path, &realpath_buffer), + std.log.warn("cannot copy file {s}: {s} is not a supported file type!", .{ + if (entry.dir.realPathFile(io,entry.path, &realpath_buffer)) |l| realpath_buffer[0..l] else |e| @errorName(e), @tagName(entry.kind), }); }, @@ -121,7 +121,7 @@ fn Executor(comptime T: type) type { var bs: dim.BinaryStream = .init_buffer(buffer); - try data.contents.render(&bs); + try data.contents.render(io, &bs); var reader: std.Io.Reader = .fixed(buffer); @@ -157,27 +157,25 @@ fn Executor(comptime T: type) type { try exec.inner.mkdir(path); } - fn walk_err(err: (std.fs.Dir.OpenError || std.mem.Allocator.Error)) dim.Content.RenderError { + fn walk_err(err: (std.Io.Dir.OpenError || std.mem.Allocator.Error)) dim.Content.RenderError { return switch (err) { - error.InvalidUtf8 => error.InvalidPath, - error.InvalidWtf8 => error.InvalidPath, - error.BadPathName => error.InvalidPath, - error.NameTooLong => error.InvalidPath, + error.BadPathName, error.NameTooLong => error.InvalidPath, error.OutOfMemory => error.OutOfMemory, error.FileNotFound => error.FileNotFound, - error.DeviceBusy => error.IoError, - error.AccessDenied => error.IoError, - error.SystemResources => error.IoError, - error.NoDevice => error.IoError, - error.Unexpected => error.IoError, - error.NetworkNotFound => error.IoError, - error.SymLinkLoop => error.IoError, - error.ProcessFdQuotaExceeded => error.IoError, - error.SystemFdQuotaExceeded => error.IoError, - error.NotDir => error.IoError, - error.ProcessNotFound, + error.Canceled => error.Canceled, + + // error.DeviceBusy, + error.AccessDenied, + error.SystemResources, + error.NoDevice, + error.Unexpected, + error.NetworkNotFound, + error.SymLinkLoop, + error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded, + error.NotDir, error.PermissionDenied, => error.IoError, }; @@ -185,8 +183,8 @@ fn Executor(comptime T: type) type { }; } -fn parse_path(ctx: dim.Context) ![:0]const u8 { - const path = try ctx.parse_string(); +fn parse_path(ctx: dim.Context, stdio: std.Io) ![:0]const u8 { + const path = try ctx.parse_string(stdio); if (path.len == 0) { try ctx.report_nonfatal_error("Path cannot be empty!", .{}); @@ -218,41 +216,41 @@ fn parse_path(ctx: dim.Context) ![:0]const u8 { return try normalize(ctx.get_arena(), path); } -pub fn parse_ops(ctx: dim.Context, end_seq: []const u8, handler: anytype) !void { +pub fn parse_ops(ctx: dim.Context, stdio: std.Io, end_seq: []const u8, handler: anytype) !void { while (true) { - const opsel = try ctx.parse_string(); + const opsel = try ctx.parse_string(stdio); if (std.mem.eql(u8, opsel, end_seq)) return; if (std.mem.eql(u8, opsel, "mkdir")) { - const path = try parse_path(ctx); + const path = try parse_path(ctx, stdio); try handler.append_common_op(FsOperation{ .make_dir = .{ .path = path }, }); } else if (std.mem.eql(u8, opsel, "copy-dir")) { - const path = try parse_path(ctx); - const src = try ctx.parse_file_name(); + const path = try parse_path(ctx, stdio); + const src = try ctx.parse_file_name(stdio); try handler.append_common_op(FsOperation{ .copy_dir = .{ .path = path, .source = src }, }); } else if (std.mem.eql(u8, opsel, "copy-file")) { - const path = try parse_path(ctx); - const src = try ctx.parse_file_name(); + const path = try parse_path(ctx, stdio); + const src = try ctx.parse_file_name(stdio); try handler.append_common_op(FsOperation{ .copy_file = .{ .path = path, .source = src }, }); } else if (std.mem.eql(u8, opsel, "create-file")) { - const path = try parse_path(ctx); - const size = try ctx.parse_mem_size(); - const contents = try ctx.parse_content(); + const path = try parse_path(ctx, stdio); + const size = try ctx.parse_mem_size(stdio); + const contents = try ctx.parse_content(stdio); try handler.append_common_op(FsOperation{ .create_file = .{ .path = path, .size = size, .contents = contents }, }); } else { - try handler.parse_custom_op(ctx, opsel); + try handler.parse_custom_op(stdio, ctx, opsel); } } } diff --git a/src/components/part/GptPartitionTable.zig b/src/components/part/GptPartitionTable.zig index daff951..9656baa 100644 --- a/src/components/part/GptPartitionTable.zig +++ b/src/components/part/GptPartitionTable.zig @@ -10,7 +10,7 @@ disk_id: ?Guid, legacy_bootable: bool = false, partitions: []Partition, -pub fn parse(ctx: dim.Context) !dim.Content { +pub fn parse(ctx: dim.Context, stdio: std.Io) !dim.Content { const pt = try ctx.alloc_object(PartTable); pt.* = .{ .disk_id = null, @@ -19,7 +19,7 @@ pub fn parse(ctx: dim.Context) !dim.Content { var partitions: std.ArrayList(Partition) = .empty; loop: while (true) { - const kw = try ctx.parse_enum(enum { + const kw = try ctx.parse_enum(stdio, enum { guid, part, endgpt, @@ -27,14 +27,14 @@ pub fn parse(ctx: dim.Context) !dim.Content { }); switch (kw) { .guid => { - const guid_str = try ctx.parse_string(); + const guid_str = try ctx.parse_string(stdio); if (guid_str.len != 36) return ctx.report_fatal_error("Invalid disk GUID: wrong length", .{}); pt.disk_id = Guid.parse(guid_str[0..36].*) catch |err| return ctx.report_fatal_error("Invalid disk GUID: {}", .{err}); }, - .part => (try partitions.addOne(ctx.get_arena())).* = try parsePartition(ctx), + .part => (try partitions.addOne(ctx.get_arena())).* = try parsePartition(ctx, stdio), .@"legacy-bootable" => pt.legacy_bootable = true, .endgpt => break :loop, } @@ -74,7 +74,7 @@ pub fn parse(ctx: dim.Context) !dim.Content { })); } -fn parsePartition(ctx: dim.Context) !Partition { +fn parsePartition(ctx: dim.Context, stdio: std.Io) !Partition { var part: Partition = .{ .type = undefined, .part_id = null, @@ -98,7 +98,7 @@ fn parsePartition(ctx: dim.Context) !Partition { }) = .init(ctx, &part); parse_loop: while (true) { - const kw = try ctx.parse_enum(enum { + const kw = try ctx.parse_enum(stdio, enum { type, name, guid, @@ -109,7 +109,7 @@ fn parsePartition(ctx: dim.Context) !Partition { }); switch (kw) { .type => { - const type_name = try ctx.parse_string(); + const type_name = try ctx.parse_string(stdio); const type_guid = known_types.get(type_name) orelse blk: { if (type_name.len == 36) if (Guid.parse(type_name[0..36].*)) |guid| break :blk guid else |_| {}; @@ -122,13 +122,13 @@ fn parsePartition(ctx: dim.Context) !Partition { try updater.set(.type, type_guid); }, .name => { - const string = try ctx.parse_string(); + const string = try ctx.parse_string(stdio); const name = stringToName(string) catch return error.BadStringLiteral; try updater.set(.name, name); }, .guid => { - const string = try ctx.parse_string(); + const string = try ctx.parse_string(stdio); if (string.len != 36) return ctx.report_fatal_error("Invalid partition GUID: wrong length", .{}); @@ -136,9 +136,9 @@ fn parsePartition(ctx: dim.Context) !Partition { return ctx.report_fatal_error("Invalid partition GUID: {}", .{err}); }); }, - .size => try updater.set(.size, try ctx.parse_mem_size()), - .offset => try updater.set(.offset, try ctx.parse_mem_size()), - .contains => try updater.set(.contains, try ctx.parse_content()), + .size => try updater.set(.size, try ctx.parse_mem_size(stdio)), + .offset => try updater.set(.offset, try ctx.parse_mem_size(stdio)), + .contains => try updater.set(.contains, try ctx.parse_content(stdio)), .endpart => break :parse_loop, } } @@ -148,8 +148,9 @@ fn parsePartition(ctx: dim.Context) !Partition { return part; } -pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderError!void { - const random = std.crypto.random; +pub fn render(table: *PartTable, io: std.Io, stream: *dim.BinaryStream) dim.Content.RenderError!void { + var r: std.Random.IoSource = .{ .io = io }; + const random = r.interface(); const lba_len = stream.length / block_size; const secondary_pth_lba = lba_len - 1; @@ -207,11 +208,11 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr pe_block[0x38..].* = @bitCast(partition.name); pe_crc.update(&pe_block); - try stream.write(block_size * 2 + pe_ofs, &pe_block); - try stream.write(block_size * secondary_pe_array_lba + pe_ofs, &pe_block); + try stream.write(io, block_size * 2 + pe_ofs, &pe_block); + try stream.write(io, block_size * secondary_pe_array_lba + pe_ofs, &pe_block); var sub_view = try stream.slice(offset, size); - try partition.contains.render(&sub_view); + try partition.contains.render(io, &sub_view); pe_ofs += 0x80; } @@ -220,8 +221,8 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr @memset(&pe_block, 0); for (table.partitions.len..0x80) |_| { pe_crc.update(&pe_block); - try stream.write(block_size * 2 + pe_ofs, &pe_block); - try stream.write(block_size * secondary_pe_array_lba + pe_ofs, &pe_block); + try stream.write(io, block_size * 2 + pe_ofs, &pe_block); + try stream.write(io, block_size * secondary_pe_array_lba + pe_ofs, &pe_block); pe_ofs += 0x80; } } @@ -273,9 +274,9 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr std.mem.writeInt(u32, backup_gpt_header[0x10..0x14], backup_gpt_header_crc32, .little); // CRC32 of backup header // write everything we generated to disk - try mbr.render(stream); - try stream.write(block_size, &gpt_header_block); - try stream.write(block_size * secondary_pth_lba, &backup_gpt_header_block); + try mbr.render(io, stream); + try stream.write(io, block_size, &gpt_header_block); + try stream.write(io, block_size * secondary_pth_lba, &backup_gpt_header_block); } pub const Guid = extern struct { diff --git a/src/components/part/MbrPartitionTable.zig b/src/components/part/MbrPartitionTable.zig index 2245de4..a0b7009 100644 --- a/src/components/part/MbrPartitionTable.zig +++ b/src/components/part/MbrPartitionTable.zig @@ -13,7 +13,7 @@ bootloader: ?dim.Content, disk_id: ?u32, partitions: [4]?Partition, -pub fn parse(ctx: dim.Context) !dim.Content { +pub fn parse(ctx: dim.Context, stdio: std.Io) !dim.Content { const pf = try ctx.alloc_object(PartTable); pf.* = .{ .bootloader = null, @@ -29,14 +29,14 @@ pub fn parse(ctx: dim.Context) !dim.Content { var next_part_id: usize = 0; var last_part_id: ?usize = null; while (next_part_id < pf.partitions.len) { - const kw = try ctx.parse_enum(enum { + const kw = try ctx.parse_enum(stdio, enum { bootloader, part, ignore, }); switch (kw) { .bootloader => { - const bootloader_content = try ctx.parse_content(); + const bootloader_content = try ctx.parse_content(stdio); if (pf.bootloader != null) { try ctx.report_nonfatal_error("mbr-part.bootloader specified twice!", .{}); } @@ -47,7 +47,7 @@ pub fn parse(ctx: dim.Context) !dim.Content { next_part_id += 1; }, .part => { - pf.partitions[next_part_id] = try parse_partition(ctx); + pf.partitions[next_part_id] = try parse_partition(ctx, stdio); last_part_id = next_part_id; next_part_id += 1; }, @@ -83,7 +83,7 @@ pub fn parse(ctx: dim.Context) !dim.Content { })); } -fn parse_partition(ctx: dim.Context) !Partition { +fn parse_partition(ctx: dim.Context, stdio: std.Io) !Partition { var part: Partition = .{ .offset = null, .size = null, @@ -99,7 +99,7 @@ fn parse_partition(ctx: dim.Context) !Partition { }) = .init(ctx, &part); parse_loop: while (true) { - const kw = try ctx.parse_enum(enum { + const kw = try ctx.parse_enum(stdio, enum { type, bootable, size, @@ -109,7 +109,7 @@ fn parse_partition(ctx: dim.Context) !Partition { }); try switch (kw) { .type => { - const part_name = try ctx.parse_string(); + const part_name = try ctx.parse_string(stdio); const encoded = if (std.fmt.parseInt(u8, part_name, 0)) |value| value @@ -125,9 +125,9 @@ fn parse_partition(ctx: dim.Context) !Partition { try updater.set(.type, encoded); }, .bootable => updater.set(.bootable, true), - .size => updater.set(.size, try ctx.parse_mem_size()), - .offset => updater.set(.offset, try ctx.parse_mem_size()), - .contains => updater.set(.contains, try ctx.parse_content()), + .size => updater.set(.size, try ctx.parse_mem_size(stdio)), + .offset => updater.set(.offset, try ctx.parse_mem_size(stdio)), + .contains => updater.set(.contains, try ctx.parse_content(stdio)), .endpart => break :parse_loop, }; } @@ -137,7 +137,7 @@ fn parse_partition(ctx: dim.Context) !Partition { return part; } -pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderError!void { +pub fn render(table: *PartTable, io: std.Io, stream: *dim.BinaryStream) dim.Content.RenderError!void { const last_part_id = blk: { var last: usize = 0; for (table.partitions, 0..) |p, i| { @@ -161,7 +161,7 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr if (table.bootloader) |bootloader| { var sector: dim.BinaryStream = .init_buffer(&boot_sector); - try bootloader.render(§or); + try bootloader.render(io, §or); const upper_limit: u64 = if (table.disk_id != null) 0x01B8 @@ -238,7 +238,7 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr boot_sector[0x01FE] = 0x55; boot_sector[0x01FF] = 0xAA; - try stream.write(0, &boot_sector); + try stream.write(io,0, &boot_sector); } for (part_infos, table.partitions) |maybe_info, maybe_part| { @@ -247,7 +247,7 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr var sub_view = try stream.slice(info.offset, info.size); - try part.contains.render(&sub_view); + try part.contains.render(io, &sub_view); } } diff --git a/src/dim.zig b/src/dim.zig index d96260d..ea31ae1 100644 --- a/src/dim.zig +++ b/src/dim.zig @@ -61,18 +61,20 @@ const usage = const VariableMap = std.StringArrayHashMapUnmanaged([]const u8); -var global_deps_file: ?std.fs.File = null; +var global_deps_file: ?std.Io.File = null; var global_deps_buffer: []u8 = undefined; -var global_deps_file_writer: std.fs.File.Writer = undefined; +var global_deps_file_writer: std.Io.File.Writer = undefined; var global_deps_writer: *std.Io.Writer = undefined; -pub fn main() !u8 { +pub fn main(init: std.process.Init) !u8 { var gpa_impl: std.heap.DebugAllocator(.{}) = .init; defer _ = gpa_impl.deinit(); const gpa = gpa_impl.allocator(); - const opts = try args.parseForCurrentProcess(Options, gpa, .print); + const io_iface = init.io; + + const opts = try args.parseForCurrentProcess(Options, gpa, init.minimal.args, .print); defer opts.deinit(); const options = opts.options; @@ -83,8 +85,7 @@ pub fn main() !u8 { var var_map: VariableMap = .empty; defer var_map.deinit(gpa); - var env_map = try std.process.getEnvMap(gpa); - defer env_map.deinit(); + var env_map = init.environ_map; if (options.@"import-env") { var iter = env_map.iterator(); @@ -114,29 +115,32 @@ pub fn main() !u8 { return fatal("--size must be given!"); } - var current_dir = try std.fs.cwd().openDir(".", .{}); - defer current_dir.close(); + var current_dir = try std.Io.Dir.cwd().openDir(io_iface, ".", .{}); + defer current_dir.close(io_iface); - const script_source = try current_dir.readFileAlloc(gpa, script_path, max_script_size); + const script_source = try current_dir.readFileAlloc(io_iface, script_path, gpa, .limited(max_script_size)); defer gpa.free(script_source); if (options.@"deps-file") |deps_file_path| { - global_deps_file = try std.fs.cwd().createFile(deps_file_path, .{}); + global_deps_file = try std.Io.Dir.cwd().createFile(io_iface, deps_file_path, .{}); global_deps_buffer = try gpa.alloc(u8, 1024); - global_deps_file_writer = global_deps_file.?.writer(global_deps_buffer); + global_deps_file_writer = global_deps_file.?.writer(io_iface, global_deps_buffer); global_deps_writer = &global_deps_file_writer.interface; + const script_realpath = current_dir.realPathFileAlloc(io_iface, script_path, gpa) catch |e| std.debug.panic("failed to determine real path for dependency file: {s}", .{@errorName(e)}); + defer gpa.free(script_realpath); + try global_deps_writer.print( \\{s}: {s} , .{ output_path, - script_path, + script_realpath, }); } defer if (global_deps_file) |deps_file| { global_deps_file_writer.end() catch {}; - deps_file.close(); + deps_file.close(io_iface); gpa.free(global_deps_buffer); }; @@ -167,7 +171,7 @@ pub fn main() !u8 { .contents = script_source, }); - const root_content: Content = env.parse_content() catch |err| switch (err) { + const root_content: Content = env.parse_content(io_iface) catch |err| switch (err) { error.FatalConfigError => return 1, else => |e| return e, @@ -180,14 +184,14 @@ pub fn main() !u8 { env.mode = .execute; { - var output_file = try current_dir.createFile(output_path, .{ .read = true }); - defer output_file.close(); + var output_file = try current_dir.createFile(io_iface, output_path, .{ .read = true }); + defer output_file.close(io_iface); - try output_file.setEndPos(size_limit); + try output_file.setLength(io_iface, size_limit); var stream: BinaryStream = .init_file(output_file, size_limit); - try root_content.render(&stream); + try root_content.render(io_iface, &stream); } if (global_deps_file != null) { @@ -201,10 +205,10 @@ pub fn main() !u8 { return 0; } -pub fn declare_file_dependency(path: []const u8) !void { +pub fn declare_file_dependency(stdio: std.Io, path: []const u8) !void { if (global_deps_file == null) return; - const stat = std.fs.cwd().statFile(path) catch |err| switch (err) { + const stat = std.Io.Dir.cwd().statFile(stdio, path, .{}) catch |err| switch (err) { error.IsDir => return, else => |e| return e, }; @@ -249,14 +253,14 @@ pub const Context = struct { return error.FatalConfigError; } - pub fn parse_string(ctx: Context) Environment.ParseError![]const u8 { - const str = try ctx.env.parser.next(); + pub fn parse_string(ctx: Context, stdio: std.Io) Environment.ParseError![]const u8 { + const str = try ctx.env.parser.next(stdio); // std.debug.print("token: '{f}'\n", .{std.zig.fmtString(str)}); return str; } - pub fn parse_file_name(ctx: Context) Environment.ParseError!FileName { - const rel_path = try ctx.parse_string(); + pub fn parse_file_name(ctx: Context, stdio: std.Io) Environment.ParseError!FileName { + const rel_path = try ctx.parse_string(stdio); const abs_path = try ctx.env.parser.get_include_path(ctx.env.arena, rel_path); @@ -267,10 +271,10 @@ pub const Context = struct { }; } - pub fn parse_enum(ctx: Context, comptime E: type) Environment.ParseError!E { + pub fn parse_enum(ctx: Context, stdio: std.Io, comptime E: type) Environment.ParseError!E { if (@typeInfo(E) != .@"enum") @compileError("parse_enum requires an enum type!"); - const tag_name = try ctx.parse_string(); + const tag_name = try ctx.parse_string(stdio); const converted = std.meta.stringToEnum( E, tag_name, @@ -290,32 +294,32 @@ pub const Context = struct { return error.InvalidEnumTag; } - pub fn parse_integer(ctx: Context, comptime I: type, base: u8) Environment.ParseError!I { + pub fn parse_integer(ctx: Context, stdio: std.Io, comptime I: type, base: u8) Environment.ParseError!I { if (@typeInfo(I) != .int) @compileError("parse_integer requires an integer type!"); return std.fmt.parseInt( I, - try ctx.parse_string(), + try ctx.parse_string(stdio), base, ) catch return error.InvalidNumber; } - pub fn parse_mem_size(ctx: Context) Environment.ParseError!u64 { - const str = try ctx.parse_string(); + pub fn parse_mem_size(ctx: Context, stdio: std.Io) Environment.ParseError!u64 { + const str = try ctx.parse_string(stdio); const ds: DiskSize = try .parse(str); return ds.size_in_bytes(); } - pub fn parse_content(ctx: Context) Environment.ParseError!Content { - const content_type_str = try ctx.env.parser.next(); + pub fn parse_content(ctx: Context, stdio: std.Io) Environment.ParseError!Content { + const content_type_str = try ctx.env.parser.next(stdio); inline for (content_types) |tn| { const name, const impl = tn; if (std.mem.eql(u8, name, content_type_str)) { - const content: Content = try impl.parse(ctx); + const content: Content = try impl.parse(ctx, stdio); return content; } @@ -388,7 +392,7 @@ const Environment = struct { arena: std.mem.Allocator, allocator: std.mem.Allocator, parser: *Parser, - include_base: std.fs.Dir, + include_base: std.Io.Dir, vars: *const VariableMap, error_flag: bool = false, mode: enum { parse, execute } = .parse, @@ -398,10 +402,10 @@ const Environment = struct { .resolve_variable_fn = resolve_var, }, - fn parse_content(env: *Environment) ParseError!Content { + fn parse_content(env: *Environment, stdio: std.Io) ParseError!Content { var ctx = Context{ .env = env }; - return try ctx.parse_content(); + return try ctx.parse_content(stdio); } fn report_error(env: *Environment, comptime fmt: []const u8, params: anytype) error{OutOfMemory}!void { @@ -412,16 +416,16 @@ const Environment = struct { } } - fn fetch_file(io: *const Parser.IO, allocator: std.mem.Allocator, path: []const u8) error{ FileNotFound, IoError, OutOfMemory, InvalidPath }![]const u8 { + fn fetch_file(stdio: std.Io, io: *const Parser.IO, allocator: std.mem.Allocator, path: []const u8) error{ FileNotFound, IoError, OutOfMemory, InvalidPath, Canceled }![]const u8 { const env: *const Environment = @fieldParentPtr("io", io); - const contents = env.include_base.readFileAlloc(allocator, path, max_script_size) catch |err| switch (err) { + const contents = env.include_base.readFileAlloc(stdio, path, allocator, .limited(max_script_size)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.FileNotFound => { const ctx = Context{ .env = @constCast(env) }; var buffer: [std.fs.max_path_bytes]u8 = undefined; try ctx.report_nonfatal_error("failed to open file: \"{f}/{f}\"", .{ - std.zig.fmtString(env.include_base.realpath(".", &buffer) catch return error.FileNotFound), + std.zig.fmtString(buffer[0 .. env.include_base.realPath(stdio, &buffer) catch return error.FileNotFound]), std.zig.fmtString(path), }); return error.FileNotFound; @@ -435,12 +439,13 @@ const Environment = struct { .root_dir = env.include_base, .rel_path = path, }; - try name.declare_dependency(); + try name.declare_dependency(stdio); return contents; } - fn resolve_var(io: *const Parser.IO, name: []const u8) error{UnknownVariable}![]const u8 { + fn resolve_var(stdio: std.Io, io: *const Parser.IO, name: []const u8) error{UnknownVariable}![]const u8 { + _ = stdio; const env: *const Environment = @fieldParentPtr("io", io); return env.vars.get(name) orelse return error.UnknownVariable; } @@ -451,42 +456,42 @@ const Environment = struct { /// /// pub const Content = struct { - pub const RenderError = FileName.OpenError || std.Io.Reader.Error || BinaryStream.WriteError || error{ + pub const RenderError = FileName.OpenError || std.Io.Reader.Error || std.Io.Writer.Error || BinaryStream.WriteError || error{ ConfigurationError, OutOfBounds, OutOfMemory, - ReadFailed, - WriteFailed, + Canceled, }; pub const GuessError = FileName.GetSizeError; obj: *anyopaque, vtable: *const VTable, - pub const empty: Content = @import("components/EmptyData.zig").parse(undefined) catch unreachable; + pub const empty: Content = @import("components/EmptyData.zig").parse(undefined, undefined) catch unreachable; pub fn create_handle(obj: *anyopaque, vtable: *const VTable) Content { return .{ .obj = obj, .vtable = vtable }; } /// Emits the content into a binary stream. - pub fn render(content: Content, stream: *BinaryStream) RenderError!void { - try content.vtable.render_fn(content.obj, stream); + pub fn render(content: Content, io: std.Io, stream: *BinaryStream) RenderError!void { + try content.vtable.render_fn(content.obj, io, stream); } pub const VTable = struct { - render_fn: *const fn (*anyopaque, *BinaryStream) RenderError!void, + render_fn: *const fn (*anyopaque, std.Io, *BinaryStream) RenderError!void, pub fn create( comptime Container: type, comptime funcs: struct { - render_fn: *const fn (*Container, *BinaryStream) RenderError!void, + render_fn: *const fn (*Container, std.Io, *BinaryStream) RenderError!void, }, ) *const VTable { const Wrap = struct { - fn render(self: *anyopaque, stream: *BinaryStream) RenderError!void { + fn render(self: *anyopaque, io: std.Io, stream: *BinaryStream) RenderError!void { return funcs.render_fn( @ptrCast(@alignCast(self)), + io, stream, ); } @@ -500,26 +505,25 @@ pub const Content = struct { pub const FileName = struct { env: *Environment, - root_dir: std.fs.Dir, + root_dir: std.Io.Dir, rel_path: []const u8, - pub const OpenError = error{ FileNotFound, InvalidPath, IoError }; + pub const OpenError = error{ FileNotFound, InvalidPath, IoError, Canceled }; - pub fn open(name: FileName) OpenError!FileHandle { - const file = name.root_dir.openFile(name.rel_path, .{}) catch |err| switch (err) { + pub fn open(name: FileName, stdio: std.Io) OpenError!FileHandle { + const file = name.root_dir.openFile(stdio, name.rel_path, .{}) catch |err| switch (err) { error.FileNotFound => { var buffer: [std.fs.max_path_bytes]u8 = undefined; - name.env.report_error("failed to open \"{f}/{f}\": not found", .{ - std.zig.fmtString(name.root_dir.realpath(".", &buffer) catch |e| @errorName(e)), - std.zig.fmtString(name.rel_path), + name.env.report_error("failed to open \"{f}\": not found", .{ + std.zig.fmtString(if (name.root_dir.realPath(stdio, &buffer)) |l| buffer[0..l] else |e| @errorName(e)), }) catch |e| std.debug.assert(e == error.OutOfMemory); return error.FileNotFound; }, + error.Canceled => return error.Canceled, + error.NameTooLong, - error.InvalidWtf8, error.BadPathName, - error.InvalidUtf8, => return error.InvalidPath, error.NoSpaceLeft, @@ -530,7 +534,6 @@ pub const FileName = struct { error.WouldBlock, error.NoDevice, error.Unexpected, - error.SharingViolation, error.PathAlreadyExists, error.PipeBusy, error.NetworkNotFound, @@ -540,36 +543,35 @@ pub const FileName = struct { error.SystemFdQuotaExceeded, error.IsDir, error.NotDir, - error.FileLocksNotSupported, + error.FileLocksUnsupported, error.FileBusy, - error.ProcessNotFound, error.PermissionDenied, => return error.IoError, }; - try name.declare_dependency(); + try name.declare_dependency(stdio); return .{ .file = file }; } - pub fn open_dir(name: FileName) OpenError!std.fs.Dir { - const dir = name.root_dir.openDir(name.rel_path, .{ .iterate = true }) catch |err| switch (err) { + pub fn open_dir(name: FileName, stdio: std.Io) OpenError!std.Io.Dir { + const dir = name.root_dir.openDir(stdio, name.rel_path, .{ .iterate = true }) catch |err| switch (err) { error.FileNotFound => { var buffer: [std.fs.max_path_bytes]u8 = undefined; name.env.report_error("failed to open \"{f}/{f}\": not found", .{ - std.zig.fmtString(name.root_dir.realpath(".", &buffer) catch |e| @errorName(e)), + std.zig.fmtString(if (name.root_dir.realPath(stdio, &buffer)) |l| buffer[0..l] else |e| @errorName(e)), std.zig.fmtString(name.rel_path), }) catch |e| std.debug.assert(e == error.OutOfMemory); return error.FileNotFound; }, + error.Canceled => return error.Canceled, + error.NameTooLong, - error.InvalidWtf8, error.BadPathName, - error.InvalidUtf8, => return error.InvalidPath, - error.DeviceBusy, + // error.DeviceBusy, error.AccessDenied, error.SystemResources, error.NoDevice, @@ -579,24 +581,24 @@ pub const FileName = struct { error.ProcessFdQuotaExceeded, error.SystemFdQuotaExceeded, error.NotDir, - error.ProcessNotFound, error.PermissionDenied, => return error.IoError, }; - try name.declare_dependency(); + try name.declare_dependency(stdio); return dir; } - pub fn declare_dependency(name: FileName) OpenError!void { + pub fn declare_dependency(name: FileName, stdio: std.Io) OpenError!void { var buffer: [std.fs.max_path_bytes]u8 = undefined; - const realpath = name.root_dir.realpath( + const realpath = name.root_dir.realPathFile( + stdio, name.rel_path, &buffer, ) catch |e| std.debug.panic("failed to determine real path for dependency file: {s}", .{@errorName(e)}); - declare_file_dependency(realpath) catch |e| std.debug.panic("Failed to write to deps file: {s}", .{@errorName(e)}); + declare_file_dependency(stdio, buffer[0..realpath]) catch |e| std.debug.panic("Failed to write to deps file: {s}", .{@errorName(e)}); } pub const GetSizeError = error{ FileNotFound, InvalidPath, IoError }; @@ -605,9 +607,7 @@ pub const FileName = struct { error.FileNotFound => return error.FileNotFound, error.NameTooLong, - error.InvalidWtf8, error.BadPathName, - error.InvalidUtf8, => return error.InvalidPath, error.NoSpaceLeft, @@ -636,42 +636,42 @@ pub const FileName = struct { return stat.size; } - pub fn copy_to(file: FileName, stream: *BinaryStream) (OpenError || error{ ReadFailed, WriteFailed })!void { - var handle = try file.open(); - defer handle.close(); + pub fn copy_to(file: FileName, io: std.Io, stream: *BinaryStream) (OpenError || error{ ReadFailed, WriteFailed })!void { + var handle = try file.open(io); + defer handle.close(io); - var file_reader = handle.file.reader(&.{}); + var file_reader = handle.file.reader(io, &.{}); var buffer: [8192]u8 = undefined; - var writer = stream.writer().adaptToNewApi(&buffer); + var writer = stream.writer(io, &buffer); - _ = try file_reader.interface.streamRemaining(&writer.new_interface); + _ = try file_reader.interface.streamRemaining(&writer.interface); } }; pub const FileHandle = struct { - file: std.fs.File, + file: std.Io.File, - pub fn close(fd: *FileHandle) void { - fd.file.close(); + pub fn close(fd: *FileHandle, io: std.Io) void { + fd.file.close(io); fd.* = undefined; } - pub fn reader(fd: FileHandle, buffer: []u8) std.fs.File.Reader { - return fd.file.reader(buffer); + pub fn reader(fd: FileHandle, io: std.Io, buffer: []u8) std.Io.File.Reader { + return fd.file.reader(io, buffer); } }; pub const BinaryStream = struct { pub const WriteError = error{ Overflow, IoError }; - pub const ReadError = error{ Overflow, IoError }; - pub const Writer = std.io.GenericWriter(*BinaryStream, WriteError, write_some); + pub const WriterError = std.Io.Writer.Error; + pub const ReadError = error{ Overflow, IoError, Canceled }; backing: Backing, virtual_offset: u64 = 0, - /// Max number of bytes that an be written + /// Max number of bytes that can be written length: u64, /// Constructs a BinaryStream from a slice. @@ -683,7 +683,7 @@ pub const BinaryStream = struct { } /// Constructs a BinaryStream from a file. - pub fn init_file(file: std.fs.File, max_len: u64) BinaryStream { + pub fn init_file(file: std.Io.File, max_len: u64) BinaryStream { return .{ .backing = .{ .file = .{ @@ -717,7 +717,7 @@ pub const BinaryStream = struct { }; } - pub fn read(bs: *BinaryStream, offset: u64, data: []u8) ReadError!void { + pub fn read(bs: *BinaryStream, io: std.Io, offset: u64, data: []u8) ReadError!void { const end_pos = offset + data.len; if (end_pos > bs.length) return error.Overflow; @@ -725,14 +725,14 @@ pub const BinaryStream = struct { switch (bs.backing) { .buffer => |ptr| @memcpy(data, ptr[@intCast(offset)..][0..data.len]), .file => |state| { - const len = state.file.pread(data, state.base + offset) catch return error.IoError; + const len = state.file.readPositionalAll(io, data, state.base + offset) catch return error.IoError; if (len != data.len) return error.Overflow; }, } } - pub fn write(bs: *BinaryStream, offset: u64, data: []const u8) WriteError!void { + pub fn write(bs: *BinaryStream, io: std.Io, offset: u64, data: []const u8) WriteError!void { const end_pos = offset + data.len; if (end_pos > bs.length) return error.Overflow; @@ -740,9 +740,7 @@ pub const BinaryStream = struct { switch (bs.backing) { .buffer => |ptr| @memcpy(ptr[@intCast(offset)..][0..data.len], data), .file => |state| { - const len = state.file.pwrite(data, state.base + offset) catch return error.IoError; - if (len != data.len) - return error.Overflow; + state.file.writePositionalAll(io, data, state.base + offset) catch return error.IoError; }, } } @@ -753,16 +751,51 @@ pub const BinaryStream = struct { bs.virtual_offset = offset; } - pub fn writer(bs: *BinaryStream) Writer { - return .{ .context = bs }; + pub fn writer(bs: *BinaryStream, stdio: std.Io, buffer: []u8) Writer { + return .{ + .interface = .{ + .vtable = &.{ + .drain = Writer.drain, + }, + .buffer = buffer, + }, + .stream = bs, + .stdio = stdio, + }; } - fn write_some(stream: *BinaryStream, data: []const u8) WriteError!usize { + pub const Writer = struct { + interface: std.Io.Writer, + stream: *BinaryStream, + stdio: std.Io, + + pub fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize { + const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); + + var written: usize = 0; + + for (data[0 .. data.len - 1]) |bytes| { + written += try w.stream.write_some(w.stdio, bytes); + } + + const pattern = data[data.len - 1]; + switch (pattern.len) { + 0 => {}, + else => for (0..splat) |_| { + written += try w.stream.write_some(w.stdio, pattern); + }, + } + + return written; + } + }; + + fn write_some(stream: *BinaryStream, stdio: std.Io, data: []const u8) std.Io.Writer.Error!usize { const remaining_len = stream.length - stream.virtual_offset; const written_len: usize = @intCast(@min(remaining_len, data.len)); - try stream.write(stream.virtual_offset, data[0..written_len]); + stream.write(stdio, stream.virtual_offset, data[0..written_len]) catch return error.WriteFailed; stream.virtual_offset += written_len; return written_len; @@ -770,7 +803,7 @@ pub const BinaryStream = struct { pub const Backing = union(enum) { file: struct { - file: std.fs.File, + file: std.Io.File, base: u64, }, buffer: [*]u8, @@ -848,7 +881,7 @@ pub const DiskSize = enum(u64) { const prefix, const suffix = .{ divided[0 .. divided.len - 3], - std.mem.trimRight(u8, divided[divided.len - 3 ..], "0"), + std.mem.trimEnd(u8, divided[divided.len - 3 ..], "0"), }; if (suffix.len > 0) {