unix2db: first stab at flag parsing
This commit is contained in:
parent
20bfa60f26
commit
e238db9b60
59
src/User.zig
59
src/User.zig
@ -45,27 +45,6 @@ pub fn clone(
|
||||
|
||||
const line_fmt = "{s}:x:{d}:{d}:{s}:{s}:{s}\n";
|
||||
|
||||
const max_line_len = fmt.count(line_fmt, .{
|
||||
max_user.uid,
|
||||
max_user.gid,
|
||||
max_user.gecos,
|
||||
max_user.home,
|
||||
max_user.shell,
|
||||
});
|
||||
|
||||
// toPasswdLine formats the user in /etc/passwd format, including the EOL
|
||||
fn toLine(self: *const User) BoundedArray(u8, max_line_len) {
|
||||
var result = BoundedArray(u8, max_line_len);
|
||||
fmt.bufPrint(result.slice(), line_fmt, .{
|
||||
self.uid,
|
||||
self.gid,
|
||||
self.gecos,
|
||||
self.home,
|
||||
self.shell,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@ -79,8 +58,8 @@ fn fromLine(allocator: Allocator, line: []const u8) error{ InvalidRecord, OutOfM
|
||||
// the line must be exhaustive.
|
||||
if (it.next() != null) return error.InvalidRecord;
|
||||
|
||||
const uid = fmt.parseInt(u32, uids) orelse return error.InvalidRecord;
|
||||
const gid = fmt.parseInt(u32, gids) orelse return error.InvalidRecord;
|
||||
const uid = fmt.parseInt(u32, uids, 10) catch return error.InvalidRecord;
|
||||
const gid = fmt.parseInt(u32, gids, 10) catch return error.InvalidRecord;
|
||||
|
||||
try validate.utf8(name);
|
||||
try validate.utf8(gecos);
|
||||
@ -105,6 +84,28 @@ fn strlen(self: *const User) usize {
|
||||
self.shell.len;
|
||||
}
|
||||
|
||||
const max_line_len = fmt.count(line_fmt, .{
|
||||
max_user.name,
|
||||
max_user.uid,
|
||||
max_user.gid,
|
||||
max_user.gecos,
|
||||
max_user.home,
|
||||
max_user.shell,
|
||||
});
|
||||
|
||||
// toPasswdLine formats the user in /etc/passwd format, including the EOL
|
||||
fn toLine(self: *const User) BoundedArray(u8, max_line_len) {
|
||||
var result = BoundedArray(u8, max_line_len);
|
||||
fmt.bufPrint(result.slice(), line_fmt, .{
|
||||
self.uid,
|
||||
self.gid,
|
||||
self.gecos,
|
||||
self.home,
|
||||
self.shell,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// length of all string-data fields, assuming they are zero-terminated.
|
||||
// Does not include password, since that's always static 'x\00'.
|
||||
pub fn strlenZ(self: *const User) usize {
|
||||
@ -119,13 +120,13 @@ pub fn deinit(self: *User, allocator: Allocator) void {
|
||||
|
||||
pub fn fromReader(allocator: Allocator, reader: anytype) ![]User {
|
||||
var users = ArrayList(User).init(allocator);
|
||||
var scratch = ArrayList(u8).init(allocator);
|
||||
defer scratch.deinit();
|
||||
while (true) {
|
||||
const line = reader.readUntilDelimiterArrayList(&scratch, '\n', maxInt(usize));
|
||||
_ = line;
|
||||
var buf: [max_line_len]u8 = undefined;
|
||||
// TODO: catch and interpret error
|
||||
while (try reader.readUntilDelimiterOrEof(buf[0..], '\n')) |line| {
|
||||
const user = try fromLine(allocator, line);
|
||||
try users.append(user);
|
||||
}
|
||||
_ = users;
|
||||
return users.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub const CUser = extern struct {
|
||||
|
@ -35,7 +35,7 @@ pub fn ParseResult(comptime flags: []const Flag) type {
|
||||
};
|
||||
|
||||
/// Remaining args after the recognized flags
|
||||
args: [][*:0]const u8,
|
||||
args: []const [*:0]const u8,
|
||||
/// Data obtained from parsed flags
|
||||
flag_data: [flags.len]FlagData = blk: {
|
||||
// Init all flags to false/null
|
||||
@ -73,7 +73,7 @@ pub fn ParseResult(comptime flags: []const Flag) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn parse(args: [][*:0]const u8, comptime flags: []const Flag) !ParseResult(flags) {
|
||||
pub fn parse(args: []const [*:0]const u8, comptime flags: []const Flag) !ParseResult(flags) {
|
||||
var ret: ParseResult(flags) = .{ .args = undefined };
|
||||
|
||||
var arg_idx: usize = 0;
|
||||
|
@ -1,10 +1,85 @@
|
||||
const std = @import("std");
|
||||
const flags = @import("../flags.zig");
|
||||
const fs = std.fs;
|
||||
const io = std.io;
|
||||
const mem = std.mem;
|
||||
const os = std.os;
|
||||
const ArrayList = std.ArrayList;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator;
|
||||
|
||||
pub fn main() !void {}
|
||||
const flags = @import("../flags.zig");
|
||||
const User = @import("../User.zig");
|
||||
|
||||
const usage =
|
||||
\\usage: turbonss-unix2db [options]
|
||||
\\
|
||||
\\ -h Print this help message and exit
|
||||
\\ --passwd Path to passwd file (default: ./passwd)
|
||||
\\ --group Path to group file (default: ./group)
|
||||
\\
|
||||
;
|
||||
|
||||
pub fn main() !void {
|
||||
// This line is here because of https://github.com/ziglang/zig/issues/7807
|
||||
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);
|
||||
};
|
||||
|
||||
os.exit(return_code);
|
||||
}
|
||||
|
||||
fn execute(
|
||||
allocator: Allocator,
|
||||
stderr: anytype,
|
||||
argv: []const [*:0]const u8,
|
||||
) !u8 {
|
||||
const result = flags.parse(argv, &[_]flags.Flag{
|
||||
.{ .name = "-h", .kind = .boolean },
|
||||
.{ .name = "--passwd", .kind = .arg },
|
||||
.{ .name = "--group", .kind = .arg },
|
||||
}) catch {
|
||||
try stderr.writeAll(usage);
|
||||
return 1;
|
||||
};
|
||||
|
||||
if (result.boolFlag("-h")) {
|
||||
try io.getStdOut().writeAll(usage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (result.args.len != 0) {
|
||||
try stderr.print("ERROR: unknown option '{s}'\n", .{result.args[0]});
|
||||
try stderr.writeAll(usage);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const passwd = result.argFlag("--passwd") orelse "./passwd";
|
||||
const group = result.argFlag("--group") orelse "./group";
|
||||
|
||||
var passwdFile = try fs.cwd().openFile(passwd, .{ .mode = .read_only });
|
||||
defer passwdFile.close();
|
||||
var groupFile = try fs.cwd().openFile(group, .{ .mode = .read_only });
|
||||
defer groupFile.close();
|
||||
|
||||
var users = try User.fromReader(allocator, passwdFile.reader());
|
||||
|
||||
try stderr.print("read {d} users\n", .{users.len});
|
||||
return 0;
|
||||
}
|
||||
|
||||
const testing = std.testing;
|
||||
|
||||
test "stub" {
|
||||
_ = flags;
|
||||
test "invalid argument" {
|
||||
const allocator = testing.allocator;
|
||||
const args = &[_][*:0]const u8{"--invalid-argument"};
|
||||
var stderr = ArrayList(u8).init(allocator);
|
||||
defer stderr.deinit();
|
||||
|
||||
const exit_code = try execute(allocator, stderr.writer(), args[0..]);
|
||||
try testing.expectEqual(@as(u8, 1), exit_code);
|
||||
try testing.expect(mem.startsWith(u8, stderr.items, "ERROR: unknown "));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user