Group.fromReader

This commit is contained in:
Motiejus Jakštys 2022-06-07 05:32:21 +03:00
parent e238db9b60
commit 31b5bb2d72
3 changed files with 51 additions and 5 deletions

View File

@ -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 {

View File

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

View File

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