hare

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

commit ef0c281b86c873957449b05a0304cd9b41eceea2
parent 7d4af1172ba0fa65d3307e160ad76c1ca1287df6
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun, 28 Aug 2022 11:22:33 +0200

unix::passwd: add get*id, improve *_finish

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Munix/passwd/group.ha | 43++++++++++++++++++++++++++++++++++++-------
Munix/passwd/passwd.ha | 46+++++++++++++++++++++++++++++++++++++++-------
2 files changed, 75 insertions(+), 14 deletions(-)

diff --git a/unix/passwd/group.ha b/unix/passwd/group.ha @@ -21,7 +21,7 @@ export type grent = struct { }; // Reads a Unix-like group entry from an [[io::handle]]. The caller must free -// the result using [[grent_finish]]. +// the return value using [[grent_finish]]. export fn nextgr(in: io::handle) (grent | io::EOF | io::error | invalid) = { let line = match (bufio::scanline(in)?) { case let ln: []u8 => @@ -60,7 +60,7 @@ export fn nextgr(in: io::handle) (grent | io::EOF | io::error | invalid) = { }; // Frees resources associated with [[grent]]. -export fn grent_finish(ent: grent) void = { +export fn grent_finish(ent: *grent) void = { free(ent.name); free(ent.userlist); }; @@ -91,11 +91,40 @@ export fn getgroup(name: str) (grent | void) = { if (ent.name == name) { return ent; } else { - grent_finish(ent); + grent_finish(&ent); }; }; +}; + +// Looks up a group by ID 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 getgid(gid: uint) (grent | void) = { + let file = match (os::open("/etc/group")) { + case let f: io::file => + yield f; + case => + abort("Unable to open /etc/group"); + }; + defer io::close(file)!; + + for (true) { + let ent = match (nextgr(file)) { + case let e: grent => + yield e; + case io::EOF => + break; + case => + abort("Invalid entry in /etc/group"); + }; - return; + if (ent.gid == gid) { + return ent; + } else { + grent_finish(&ent); + }; + }; }; @test fn nextgr() void = { @@ -105,7 +134,7 @@ export fn getgroup(name: str) (grent | void) = { "video:x:986:alex,wmuser"), io::mode::READ); let ent = nextgr(&buf) as grent; - defer grent_finish(ent); + defer grent_finish(&ent); assert(ent.name == "root"); assert(ent.password == "x"); @@ -114,7 +143,7 @@ export fn getgroup(name: str) (grent | void) = { assert(ent.userlist[0] == "root"); let ent = nextgr(&buf) as grent; - defer grent_finish(ent); + defer grent_finish(&ent); assert(ent.name == "mail"); assert(ent.password == "x"); @@ -122,7 +151,7 @@ export fn getgroup(name: str) (grent | void) = { assert(len(ent.userlist) == 0); let ent = nextgr(&buf) as grent; - defer grent_finish(ent); + defer grent_finish(&ent); assert(ent.name == "video"); assert(ent.password == "x"); diff --git a/unix/passwd/passwd.ha b/unix/passwd/passwd.ha @@ -27,7 +27,7 @@ export type pwent = struct { }; // Reads a Unix-like password entry from an [[io::handle]]. The caller must free -// the result using [[pwent_finish]]. +// the return value using [[pwent_finish]]. export fn nextpw(file: io::handle) (pwent | io::EOF | io::error | invalid) = { let line = match (bufio::scanline(file)?) { case io::EOF => @@ -75,8 +75,8 @@ export fn nextpw(file: io::handle) (pwent | io::EOF | io::error | invalid) = { }; }; -// Frees resources associated with [[pwent]]. -export fn pwent_finish(ent: pwent) void = { +// Frees resources associated with a [[pwent]]. +export fn pwent_finish(ent: *pwent) void = { // pwent fields are sliced from one allocated string returned by // bufio::scanline. Freeing the first field frees the entire string in // one go. @@ -85,7 +85,7 @@ export fn pwent_finish(ent: pwent) void = { // Looks up a user by name in a Unix-like password file. It expects a password // database file at /etc/passwd. Aborts if that file doesn't exist or is not -// properly formatted. +// properly formatted. The return value must be freed with [[pwent_finish]]. // // See [[nextpw]] for low-level parsing API. export fn getuser(username: str) (pwent | void) = { @@ -110,20 +110,52 @@ export fn getuser(username: str) (pwent | void) = { if (ent.username == username) { return ent; } else { - pwent_finish(ent); + pwent_finish(&ent); }; }; return; }; +// Looks up a user by ID in a Unix-like password file. It expects a password +// database file at /etc/passwd. Aborts if that file doesn't exist or is not +// properly formatted. The return value must be freed with [[pwent_finish]]. +// +// See [[nextpw]] for low-level parsing API. +export fn getuid(uid: uint) (pwent | void) = { + let file = match (os::open("/etc/passwd")) { + case let f: io::file => + yield f; + case => + abort("Can't open /etc/passwd"); + }; + defer io::close(file)!; + + for (true) { + let ent = match (nextpw(file)) { + case let e: pwent => + yield e; + case io::EOF => + break; + case => + abort("Invalid entry in /etc/passwd"); + }; + + if (ent.uid == uid) { + return ent; + } else { + pwent_finish(&ent); + }; + }; +}; + @test fn nextpw() void = { let buf = bufio::fixed(strings::toutf8( "sircmpwn:x:1000:1000:sircmpwn's comment:/home/sircmpwn:/bin/mrsh\n" "alex:x:1001:1001::/home/alex:/bin/zsh"), io::mode::READ); let ent = nextpw(&buf) as pwent; - defer pwent_finish(ent); + defer pwent_finish(&ent); assert(ent.username == "sircmpwn"); assert(ent.password == "x"); @@ -134,7 +166,7 @@ export fn getuser(username: str) (pwent | void) = { assert(ent.shell == "/bin/mrsh"); let ent = nextpw(&buf) as pwent; - defer pwent_finish(ent); + defer pwent_finish(&ent); assert(ent.username == "alex"); assert(ent.password == "x");