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 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
|
// fromLine accepts a line of /etc/passwd (with or without the EOL) and makes a
|
||||||
// User.
|
// User.
|
||||||
fn fromLine(allocator: Allocator, line: []const u8) error{ InvalidRecord, OutOfMemory }!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.
|
// the line must be exhaustive.
|
||||||
if (it.next() != null) return error.InvalidRecord;
|
if (it.next() != null) return error.InvalidRecord;
|
||||||
|
|
||||||
const uid = fmt.parseInt(u32, uids) orelse return error.InvalidRecord;
|
const uid = fmt.parseInt(u32, uids, 10) catch return error.InvalidRecord;
|
||||||
const gid = fmt.parseInt(u32, gids) orelse return error.InvalidRecord;
|
const gid = fmt.parseInt(u32, gids, 10) catch return error.InvalidRecord;
|
||||||
|
|
||||||
try validate.utf8(name);
|
try validate.utf8(name);
|
||||||
try validate.utf8(gecos);
|
try validate.utf8(gecos);
|
||||||
@ -105,6 +84,28 @@ fn strlen(self: *const User) usize {
|
|||||||
self.shell.len;
|
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.
|
// length of all string-data fields, assuming they are zero-terminated.
|
||||||
// Does not include password, since that's always static 'x\00'.
|
// Does not include password, since that's always static 'x\00'.
|
||||||
pub fn strlenZ(self: *const User) usize {
|
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 {
|
pub fn fromReader(allocator: Allocator, reader: anytype) ![]User {
|
||||||
var users = ArrayList(User).init(allocator);
|
var users = ArrayList(User).init(allocator);
|
||||||
var scratch = ArrayList(u8).init(allocator);
|
var buf: [max_line_len]u8 = undefined;
|
||||||
defer scratch.deinit();
|
// TODO: catch and interpret error
|
||||||
while (true) {
|
while (try reader.readUntilDelimiterOrEof(buf[0..], '\n')) |line| {
|
||||||
const line = reader.readUntilDelimiterArrayList(&scratch, '\n', maxInt(usize));
|
const user = try fromLine(allocator, line);
|
||||||
_ = line;
|
try users.append(user);
|
||||||
}
|
}
|
||||||
_ = users;
|
return users.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CUser = extern struct {
|
pub const CUser = extern struct {
|
||||||
|
@ -35,7 +35,7 @@ pub fn ParseResult(comptime flags: []const Flag) type {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Remaining args after the recognized flags
|
/// Remaining args after the recognized flags
|
||||||
args: [][*:0]const u8,
|
args: []const [*:0]const u8,
|
||||||
/// Data obtained from parsed flags
|
/// Data obtained from parsed flags
|
||||||
flag_data: [flags.len]FlagData = blk: {
|
flag_data: [flags.len]FlagData = blk: {
|
||||||
// Init all flags to false/null
|
// 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 ret: ParseResult(flags) = .{ .args = undefined };
|
||||||
|
|
||||||
var arg_idx: usize = 0;
|
var arg_idx: usize = 0;
|
||||||
|
@ -1,10 +1,85 @@
|
|||||||
const std = @import("std");
|
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;
|
const testing = std.testing;
|
||||||
|
|
||||||
test "stub" {
|
test "invalid argument" {
|
||||||
_ = flags;
|
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