From 31b5bb2d727214df89a60ac4bec4baedcb9caf21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Tue, 7 Jun 2022 05:32:21 +0300 Subject: [PATCH] Group.fromReader --- src/Group.zig | 44 +++++++++++++++++++++++++++++++++++++++++++- src/User.zig | 9 +++++---- src/unix2db/main.zig | 3 +++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/Group.zig b/src/Group.zig index 57961f7..674818d 100644 --- a/src/Group.zig +++ b/src/Group.zig @@ -1,8 +1,11 @@ const std = @import("std"); - +const os = std.os; const mem = std.mem; +const fmt = std.fmt; const meta = std.meta; +const maxInt = std.math.maxInt; const Allocator = mem.Allocator; +const ArrayList = std.ArrayList; const Group = @This(); gid: u32, @@ -55,6 +58,45 @@ pub fn deinit(self: *Group, allocator: Allocator) void { self.* = undefined; } +pub const FromReaderError = error{ InvalidRecord, OutOfMemory } || os.ReadError; + +pub fn fromReader(allocator: Allocator, reader: anytype) FromReaderError![]Group { + var groups = ArrayList(Group).init(allocator); + var member_ptrs = ArrayList([]const u8).init(allocator); + defer member_ptrs.deinit(); + var line = ArrayList(u8).init(allocator); + defer line.deinit(); + + while (true) { + // TODO: catch and interpret different errors + const max = std.math.maxInt(u32); + reader.readUntilDelimiterArrayList(&line, '\n', max) catch |err| switch (err) { + error.EndOfStream => break, + error.StreamTooLong => unreachable, + else => |e| return e, + }; + + var it = mem.split(u8, line.items, ":"); + const name = it.next() orelse return error.InvalidRecord; + _ = it.next() orelse return error.InvalidRecord; // password + const gids = it.next() orelse return error.InvalidRecord; + const members_commas = it.next() orelse return error.InvalidRecord; + // the line must be exhaustive. + if (it.next() != null) return error.InvalidRecord; + + const gid = fmt.parseInt(u32, gids, 10) catch return error.InvalidRecord; + + var members_it = mem.split(u8, members_commas, ","); + while (members_it.next()) |member| + try member_ptrs.append(member); + + try groups.append(try init(allocator, gid, name, member_ptrs.items)); + member_ptrs.shrinkRetainingCapacity(0); + } + + return groups.toOwnedSlice(); +} + // suggested buffer size in bytes if all strings were zero-terminated // (for CGroup). pub fn strlenZ(self: *const Group) usize { diff --git a/src/User.zig b/src/User.zig index a5a0bbd..c6bf370 100644 --- a/src/User.zig +++ b/src/User.zig @@ -43,15 +43,14 @@ pub fn clone( }; } -const line_fmt = "{s}:x:{d}:{d}:{s}:{s}:{s}\n"; - // 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 { var it = mem.split(u8, line, ":"); + const name = it.next() orelse return error.InvalidRecord; + _ = it.next() orelse return error.InvalidRecord; // password const uids = it.next() orelse return error.InvalidRecord; const gids = it.next() orelse return error.InvalidRecord; - const name = it.next() orelse return error.InvalidRecord; const gecos = it.next() orelse return error.InvalidRecord; const home = it.next() orelse return error.InvalidRecord; const shell = it.next() orelse return error.InvalidRecord; @@ -84,6 +83,8 @@ fn strlen(self: *const User) usize { self.shell.len; } +const line_fmt = "{s}:x:{d}:{d}:{s}:{s}:{s}\n"; + const max_line_len = fmt.count(line_fmt, .{ max_user.name, max_user.uid, @@ -120,7 +121,7 @@ pub fn deinit(self: *User, allocator: Allocator) void { pub fn fromReader(allocator: Allocator, reader: anytype) ![]User { var users = ArrayList(User).init(allocator); - var buf: [max_line_len]u8 = undefined; + 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); diff --git a/src/unix2db/main.zig b/src/unix2db/main.zig index e33895d..0970446 100644 --- a/src/unix2db/main.zig +++ b/src/unix2db/main.zig @@ -9,6 +9,7 @@ const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator; const flags = @import("../flags.zig"); const User = @import("../User.zig"); +const Group = @import("../Group.zig"); const usage = \\usage: turbonss-unix2db [options] @@ -66,8 +67,10 @@ fn execute( defer groupFile.close(); var users = try User.fromReader(allocator, passwdFile.reader()); + var groups = try Group.fromReader(allocator, groupFile.reader()); try stderr.print("read {d} users\n", .{users.len}); + try stderr.print("read {d} groups\n", .{groups.len}); return 0; }