hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 4298b6824c2b42026fbf7147eb267cb3c312756f
parent ae4bcefee4ba0ca0b03b603daf310059da8f726c
Author: Alexey Yerin <yyp@disroot.org>
Date:   Sun, 11 Apr 2021 19:36:27 +0300

unix/passwd: add /etc/group parser

Diffstat:
Mscripts/gen-stdlib | 1+
Mstdlib.mk | 2++
Aunix/passwd/group.ha | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 120 insertions(+), 0 deletions(-)

diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -622,6 +622,7 @@ unix() { unix_passwd() { gen_srcs unix::passwd \ + group.ha \ passwd.ha \ types.ha gen_ssa unix::passwd bufio io os strconv strings diff --git a/stdlib.mk b/stdlib.mk @@ -818,6 +818,7 @@ $(HARECACHE)/unix/unix.ssa: $(stdlib_unix_srcs) $(stdlib_rt) $(stdlib_errors) # unix::passwd stdlib_unix_passwd_srcs= \ + $(STDLIB)/unix/passwd/group.ha \ $(STDLIB)/unix/passwd/passwd.ha \ $(STDLIB)/unix/passwd/types.ha @@ -1673,6 +1674,7 @@ $(TESTCACHE)/unix/unix.ssa: $(testlib_unix_srcs) $(testlib_rt) $(testlib_errors) # unix::passwd testlib_unix_passwd_srcs= \ + $(STDLIB)/unix/passwd/group.ha \ $(STDLIB)/unix/passwd/passwd.ha \ $(STDLIB)/unix/passwd/types.ha diff --git a/unix/passwd/group.ha b/unix/passwd/group.ha @@ -0,0 +1,117 @@ +use bufio; +use io; +use os; +use strconv; +use strings; + +// A Unix-like group file entry. +export type grent = struct { + // Name of the group + name: str, + // Optional encrypted password + password: str, + // Numerical group ID + gid: uint, + // List of usernames that are members of this group, comma separated + userlist: str, +}; + +// Reads a Unix-like group entry from a stream. The caller must free the result +// using [grent_finish]. +export fn nextgr(stream: *io::stream) (grent | io::EOF | io::error | invalid) = { + let line = match (bufio::scanline(stream)?) { + ln: []u8 => ln, + io::EOF => return io::EOF, + }; + let line = match (strings::try_fromutf8(line)) { + s: str => s, + * => return invalid, + }; + + let fields = strings::split(line, ":"); + defer free(fields); + + if (len(fields) != 4) { + return invalid; + }; + + let gid = match (strconv::stou(fields[2])) { + u: uint => u, + * => return invalid, + }; + + return grent { + // Borrows the return value of bufio::scanline + name = fields[0], + password = fields[1], + gid = gid, + userlist = fields[3], + }; +}; + +// Frees resources associated with [grent]. +export fn grent_finish(ent: grent) void = { + free(ent.name); +}; + +// Looks up a group by name in a Unix-like group file. It expects a such file at +// /etc/group. Aborts if that file doesn't exist or is not properly formatted. +// +// See [nextgr] for low-level parsing API. +export fn getgroup(name: str) (grent | void) = { + let file = match (os::open("/etc/group")) { + s: *io::stream => s, + * => abort("Unable to open /etc/group"), + }; + defer io::close(file); + + for (true) { + let ent = match (nextgr(file)) { + e: grent => e, + io::EOF => break, + * => abort("Invalid entry in /etc/group"), + }; + defer grent_finish(ent); + + if (ent.name == name) { + return ent; + }; + }; + + return; +}; + +@test fn nextgr() void = { + let buf = bufio::fixed(strings::toutf8( + "root:x:0:root\n" + "mail:x:12:\n" + "video:x:986:alex,wmuser"), io::mode::READ); + defer free(buf); + + let ent = nextgr(buf) as grent; + defer grent_finish(ent); + + assert(ent.name == "root"); + assert(ent.password == "x"); + assert(ent.gid == 0); + assert(ent.userlist == "root"); + + let ent = nextgr(buf) as grent; + defer grent_finish(ent); + + assert(ent.name == "mail"); + assert(ent.password == "x"); + assert(ent.gid == 12); + assert(ent.userlist == ""); + + let ent = nextgr(buf) as grent; + defer grent_finish(ent); + + assert(ent.name == "video"); + assert(ent.password == "x"); + assert(ent.gid == 986); + assert(ent.userlist == "alex,wmuser"); + + // No more entries + assert(nextgr(buf) is io::EOF); +};