diff --git a/src/ErrContext.zig b/src/ErrCtx.zig similarity index 68% rename from src/ErrContext.zig rename to src/ErrCtx.zig index 3020eef..d28f0f8 100644 --- a/src/ErrContext.zig +++ b/src/ErrCtx.zig @@ -1,5 +1,5 @@ -// ErrContext is a way to pass friendly error messages to the user. -// Preallocates memory. +// ErrCtx is a way to pass friendly error messages to the user. +// Preallocates memory. Discards messages that do not fit. const std = @import("std"); const mem = std.mem; @@ -7,15 +7,15 @@ const math = std.math; const BoundedArray = std.BoundedArray; const capacity = 1 << 16; // 64K -const ErrContext = @This(); +const ErrCtx = @This(); buf: BoundedArray(u8, capacity) = BoundedArray(u8, capacity).init(0) catch unreachable, overflow: bool = false, -pub fn write(self: *ErrContext, bytes: []const u8) error{Overflow}!usize { +pub fn write(self: *ErrCtx, bytes: []const u8) error{}!usize { const can_add = capacity - self.buf.len; - if (can_add == 0) return error.Overflow; + if (can_add == 0) return bytes.len; self.overflow = bytes.len > can_add; self.buf.appendSliceAssumeCapacity(bytes[0..math.min(bytes.len, can_add)]); @@ -23,19 +23,16 @@ pub fn write(self: *ErrContext, bytes: []const u8) error{Overflow}!usize { // to be ignored in the iterator anyway. _ = self.buf.append(0) catch null; - if (self.overflow) - return error.Overflow; - return bytes.len; } -const Writer = std.io.Writer(*ErrContext, error{Overflow}, write); +const Writer = std.io.Writer(*ErrCtx, error{}, write); -pub fn writer(self: *ErrContext) Writer { +pub fn writer(self: *ErrCtx) Writer { return Writer{ .context = self }; } -pub fn iterator(self: *const ErrContext) mem.SplitIterator(u8) { +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; return mem.split(u8, slice[0..last_byte], "\x00"); @@ -44,13 +41,13 @@ pub fn iterator(self: *const ErrContext) mem.SplitIterator(u8) { const testing = std.testing; test "basics" { - var ctx = ErrContext{}; + var ctx = ErrCtx{}; var wr = ctx.writer(); try wr.writeAll("0" ** 10); try wr.writeAll("1" ** 10); - try testing.expectError(error.Overflow, wr.writeAll("3" ** capacity)); - try testing.expectError(error.Overflow, wr.writeAll("foo")); + try wr.writeAll("3" ** capacity); + try wr.writeAll("foo"); var it = ctx.iterator(); try testing.expectEqualSlices(u8, it.next().?, "0" ** 10); @@ -63,11 +60,11 @@ test "basics" { } test "almost overflow" { - var ctx = ErrContext{}; + var ctx = ErrCtx{}; var wr = ctx.writer(); try wr.writeAll("0" ** (capacity - 2)); - try testing.expectError(error.Overflow, wr.writeAll("11")); + try wr.writeAll("11"); var it = ctx.iterator(); try testing.expectEqualSlices(u8, it.next().?, "0" ** (capacity - 2)); diff --git a/src/Group.zig b/src/Group.zig index 59eead0..dfe15d4 100644 --- a/src/Group.zig +++ b/src/Group.zig @@ -62,6 +62,11 @@ pub const FromReaderError = error{ InvalidRecord, OutOfMemory } || os.ReadError; pub fn fromReader(allocator: Allocator, reader: anytype) FromReaderError![]Group { var groups = ArrayList(Group).init(allocator); + errdefer { + for (groups.items) |*group| group.deinit(allocator); + groups.deinit(); + } + var member_ptrs = ArrayList([]const u8).init(allocator); defer member_ptrs.deinit(); var line = ArrayList(u8).init(allocator); diff --git a/src/User.zig b/src/User.zig index a873599..36a59d2 100644 --- a/src/User.zig +++ b/src/User.zig @@ -9,6 +9,7 @@ const BoundedArray = std.BoundedArray; const pw_passwd = "x\x00"; const User = @This(); +const ErrCtx = @import("ErrCtx.zig"); const PackedUser = @import("PackedUser.zig"); const validate = @import("validate.zig"); @@ -24,19 +25,29 @@ pub fn clone( self: *const User, allocator: Allocator, ) error{OutOfMemory}!User { + const name = try allocator.dupe(u8, self.name); + errdefer allocator.free(name); + const gecos = try allocator.dupe(u8, self.gecos); + errdefer allocator.free(gecos); + const home = try allocator.dupe(u8, self.home); + errdefer allocator.free(home); + const shell = try allocator.dupe(u8, self.shell); + errdefer allocator.free(shell); + return User{ .uid = self.uid, .gid = self.gid, - .name = try allocator.dupe(u8, self.name), - .gecos = try allocator.dupe(u8, self.gecos), - .home = try allocator.dupe(u8, self.home), - .shell = try allocator.dupe(u8, self.shell), + .name = name, + .gecos = gecos, + .home = home, + .shell = shell, }; } // fromLine accepts a line of /etc/passwd (with or without the EOL) and makes a // User. -fn fromLine(allocator: Allocator, line: []const u8) error{ InvalidRecord, OutOfMemory }!User { +fn fromLine(allocator: Allocator, err_ctx: anytype, line: []const u8) error{ InvalidRecord, OutOfMemory }!User { + _ = err_ctx; var it = mem.split(u8, line, ":"); const name = it.next() orelse return error.InvalidRecord; _ = it.next() orelse return error.InvalidRecord; // password @@ -113,12 +124,16 @@ pub fn deinit(self: *User, allocator: Allocator) void { self.* = undefined; } -pub fn fromReader(allocator: Allocator, reader: anytype) ![]User { +pub fn fromReader(allocator: Allocator, err_ctx: anytype, reader: anytype) ![]User { var users = ArrayList(User).init(allocator); + errdefer { + for (users.items) |*user| user.deinit(allocator); + users.deinit(); + } + var buf: [max_line_len + 1]u8 = undefined; - // TODO: catch and interpret error while (try reader.readUntilDelimiterOrEof(buf[0..], '\n')) |line| { - const user = try fromLine(allocator, line); + var user = try fromLine(allocator, err_ctx, line); try users.append(user); } return users.toOwnedSlice(); diff --git a/src/test_all.zig b/src/test_all.zig index 1c32d05..da8fd45 100644 --- a/src/test_all.zig +++ b/src/test_all.zig @@ -4,7 +4,7 @@ test "turbonss test suite" { _ = @import("compress.zig"); _ = @import("Corpus.zig"); _ = @import("DB.zig"); - _ = @import("ErrContext.zig"); + _ = @import("ErrCtx.zig"); _ = @import("Group.zig"); _ = @import("header.zig"); _ = @import("libnss.zig"); diff --git a/src/unix2db/main.zig b/src/unix2db/main.zig index 5547d15..02a0a58 100644 --- a/src/unix2db/main.zig +++ b/src/unix2db/main.zig @@ -12,6 +12,7 @@ const User = @import("../User.zig"); const Group = @import("../Group.zig"); const Corpus = @import("../Corpus.zig"); const DB = @import("../DB.zig"); +const ErrCtx = @import("../ErrCtx.zig"); const usage = \\usage: turbonss-unix2db [options] @@ -65,15 +66,22 @@ fn execute( const groupFname = result.argFlag("--group") orelse "./group"; const outFile = result.argFlag("--output") orelse "./db.turbo"; - std.debug.print("passwd file name: {s}\n", .{passwdFname}); + //std.debug.print("passwd file name: {s}\n", .{passwdFname}); var passwdFile = try fs.cwd().openFile(passwdFname, .{ .mode = .read_only }); defer passwdFile.close(); var groupFile = try fs.cwd().openFile(groupFname, .{ .mode = .read_only }); defer groupFile.close(); - var users = try User.fromReader(allocator, passwdFile.reader()); + var err_ctx = ErrCtx{}; + + var users = try User.fromReader( + allocator, + err_ctx.writer(), + passwdFile.reader(), + ); defer for (users) |*user| user.deinit(allocator); defer allocator.free(users); + var groups = try Group.fromReader(allocator, groupFile.reader()); defer for (groups) |*group| group.deinit(allocator); defer allocator.free(groups); @@ -116,7 +124,7 @@ test "invalid argument" { } test "smoke test" { - if (true) return error.SkipZigTest; + //if (true) return error.SkipZigTest; const allocator = testing.allocator; var stderr = ArrayList(u8).init(allocator);