diff --git a/src/ErrCtx.zig b/src/ErrCtx.zig index da15f70..7fab231 100644 --- a/src/ErrCtx.zig +++ b/src/ErrCtx.zig @@ -35,10 +35,30 @@ pub fn writer(self: *ErrCtx) Writer { return Writer{ .context = self }; } -pub fn err(self: *ErrCtx, comptime format: []const u8, args: anytype) void { +pub fn print(self: *ErrCtx, comptime format: []const u8, args: anytype) void { self.writer().print(format, args) catch unreachable; } +pub fn returnf( + self: *ErrCtx, + comptime format: []const u8, + args: anytype, + comptime ret: anytype, +) @TypeOf(ret) { + self.writer().print(format, args) catch unreachable; + return ret; +} + +pub fn wrap(self: *ErrCtx, comptime msg: []const u8) *ErrCtx { + self.writer().writeAll(msg) catch unreachable; + return self; +} + +pub fn wrapf(self: *ErrCtx, comptime format: []const u8, args: anytype) *ErrCtx { + self.writer().print(format, args) catch unreachable; + return self; +} + pub fn iterator(self: *const ErrCtx) mem.SplitIterator(u8) { const slice = self.buf.constSlice(); const last_byte = if (slice[slice.len - 1] == '0') slice.len - 1 else slice.len; diff --git a/src/User.zig b/src/User.zig index db3e30b..56e0e97 100644 --- a/src/User.zig +++ b/src/User.zig @@ -58,7 +58,7 @@ fn fromLine(allocator: Allocator, err_ctx: *ErrCtx, line: []const u8) error{ Inv // all fields are set if (shell == null) { - err_ctx.err("too few user fields in line: {s}", .{line}); + err_ctx.print("too few user fields in line: {s}", .{line}); return error.InvalidRecord; } @@ -67,19 +67,33 @@ fn fromLine(allocator: Allocator, err_ctx: *ErrCtx, line: []const u8) error{ Inv return error.InvalidRecord; const uid = fmt.parseInt(u32, uids.?, 10) catch { - err_ctx.err("bad uid: {s}", .{uids.?}); - return error.InvalidRecord; + return err_ctx.returnf("bad uid: {s}", .{uids.?}, error.InvalidRecord); }; const gid = fmt.parseInt(u32, gids.?, 10) catch { - err_ctx.err("bad uid: {s}", .{gids.?}); - return error.InvalidRecord; + return err_ctx.returnf("bad uid: {s}", .{gids.?}, error.InvalidRecord); }; - validate.utf8(name.?) catch err_ctx.err("name is invalid utf8: {s}", .{name.?}); - validate.utf8(gecos.?) catch err_ctx.err("gecos is invalid utf8: {s}", .{gecos.?}); - validate.utf8(home.?) catch err_ctx.err("home is invalid utf8: {s}", .{home.?}); - validate.utf8(shell.?) catch err_ctx.err("shell is invalid utf8: {s}", .{shell.?}); + validate.utf8(name.?) catch return err_ctx.returnf( + "name is invalid utf8: {s}", + .{name.?}, + error.InvalidRecord, + ); + validate.utf8(gecos.?) catch return err_ctx.returnf( + "gecos is invalid utf8: {s}", + .{gecos.?}, + error.InvalidRecord, + ); + validate.utf8(home.?) catch return err_ctx.returnf( + "home is invalid utf8: {s}", + .{home.?}, + error.InvalidRecord, + ); + validate.utf8(shell.?) catch return err_ctx.returnf( + "shell is invalid utf8: {s}", + .{shell.?}, + error.InvalidRecord, + ); if (err_ctx.dirty) return error.InvalidRecord; diff --git a/src/unix2db/main.zig b/src/unix2db/main.zig index 1e2400f..08f6018 100644 --- a/src/unix2db/main.zig +++ b/src/unix2db/main.zig @@ -28,16 +28,16 @@ pub fn main() !void { const argv: []const [*:0]const u8 = os.argv; const gpa = GeneralPurposeAllocator(.{}); - const return_code = execute(gpa, argv[1..]) catch |err| { - try io.getStdErr().writeAll("uncaught error: {s}\n", @intToError(err)); - os.exit(1); - }; + const stderr = try io.getStdErr(); + const stdout = try io.getStdOut(); + const return_code = execute(gpa, stdout, stderr, argv[1..]); os.exit(return_code); } fn execute( allocator: Allocator, + stdout: anytype, stderr: anytype, argv: []const [*:0]const u8, ) u8 { @@ -52,7 +52,7 @@ fn execute( }; if (result.boolFlag("-h")) { - io.getStdOut().writeAll(usage) catch return 1; + stdout.writeAll(usage) catch return 1; return 0; } @@ -66,62 +66,72 @@ fn execute( const groupFname = result.argFlag("--group") orelse "./group"; const outFile = result.argFlag("--output") orelse "./db.turbo"; - // to catch a specific file.OpenError, wait for + // to catch a specific file.OpenError, hold thumbs for // https://github.com/ziglang/zig/issues/2473 + var errc = ErrCtx{}; var passwdFile = fs.cwd().openFile( passwdFname, .{ .mode = .read_only }, - ) catch |err| { - stderr.print("Error opening {s}: {s}\n", .{ passwdFname, @errorName(err) }) catch {}; - return 1; - }; + ) catch |err| return fail(errc.wrapf("open {s}", .{passwdFname}), stderr, err); + defer passwdFile.close(); - var groupFile = fs.cwd().openFile(groupFname, .{ .mode = .read_only }) catch |err| { - stderr.print("Error opening {s}: {s}\n", .{ groupFname, @errorName(err) }) catch {}; - return 1; - }; + var groupFile = fs.cwd().openFile(groupFname, .{ .mode = .read_only }) catch |err| + return fail(errc.wrapf("open {s}", .{groupFname}), stderr, err); defer groupFile.close(); - var err_ctx = ErrCtx{}; - var users = try User.fromReader( + var users = User.fromReader( allocator, - &err_ctx, + &errc, passwdFile.reader(), - ); - errdefer { - stderr.print("ERROR", .{}) catch {}; - var it = err_ctx.rev(); - while (it.next()) |err| - stderr.print(": {s}", .{err}) catch {}; - stderr.print("\n", .{}) catch {}; - } + ) catch |err| return fail(errc.wrap("read users"), stderr, err); + defer for (users) |*user| user.deinit(allocator); defer allocator.free(users); - var groups = try Group.fromReader(allocator, groupFile.reader()); + var groups = Group.fromReader( + allocator, + groupFile.reader(), + ) catch |err| return fail(errc.wrap("read groups"), stderr, err); defer for (groups) |*group| group.deinit(allocator); defer allocator.free(groups); - var corpus = try Corpus.init(allocator, users, groups); + var corpus = Corpus.init(allocator, users, groups) catch |err| + return fail(errc.wrap("init corpus"), stderr, err); + defer corpus.deinit(); - var db = try DB.fromCorpus(allocator, &corpus); + var db = DB.fromCorpus(allocator, &corpus) catch |err| + return fail(errc.wrap("construct db from corpus"), stderr, err); defer db.deinit(allocator); - const fd = try os.open(outFile, os.O.WRONLY | os.O.TRUNC | os.O.CREAT, 0644); + const fd = os.open(outFile, os.O.WRONLY | os.O.TRUNC | os.O.CREAT, 0644) catch |err| + return fail(errc.wrapf("open for writing {s}", .{outFile}), stderr, err); errdefer os.close(fd); - const len = try os.writev(fd, db.iov().constSlice()); - try os.fsync(fd); + const len = os.writev(fd, db.iov().constSlice()) catch |err| + return fail(errc.wrapf("writev to {s}", .{outFile}), stderr, err); + os.fsync(fd) catch |err| + return fail(errc.wrapf("fsync {s}", .{outFile}), stderr, err); os.close(fd); - try stderr.print("total {d} bytes. groups={d} users={d}\n", .{ + stderr.print("total {d} bytes. groups={d} users={d}\n", .{ len, users.len, groups.len, - }); + }) catch return 1; + return 0; } +fn fail(errc: *ErrCtx, stderr: anytype, err: anytype) u8 { + stderr.print("ERROR {s}", .{@errorName(err)}) catch {}; + var it = errc.rev(); + while (it.next()) |msg| + stderr.print(": {s}", .{msg}) catch {}; + + stderr.print("\n", .{}) catch {}; + return 1; +} + const testing = std.testing; test "invalid argument" { @@ -129,8 +139,12 @@ test "invalid argument" { const args = &[_][*:0]const u8{"--invalid-argument"}; var stderr = ArrayList(u8).init(allocator); defer stderr.deinit(); + var stderrw = stderr.writer(); + var stdout = ArrayList(u8).init(allocator); + defer stdout.deinit(); + var stdoutw = stdout.writer(); - const exit_code = execute(allocator, stderr.writer(), args[0..]); + const exit_code = execute(allocator, stdoutw, stderrw, args[0..]); try testing.expectEqual(@as(u8, 1), exit_code); try testing.expect(mem.startsWith( u8,