ref: bff5417fece9ae857ba09de40f1a0823bead52fc
dir: /lib/inifile/parse.myr/
use std use bio use "types" pkg inifile = /* reading */ const load : (path : byte[:] -> std.result(inifile#, error)) const loadf : (file : std.fd -> std.result(inifile#, error)) const free : (ini : inifile# -> void) ;; type parser = struct line : int sect : byte[:] err : std.option(error) ;; const load = {path match std.open(path, std.Oread) | `std.Ok fd: -> loadf(fd) | `std.Err e: -> `std.Err `Fileerr ;; } const free = {ini for ((sect, key), val) : std.byhtkeyvals(ini.elts) std.slfree(val) std.slfree(sect) std.slfree(key) ;; for s : ini.sects std.slfree(s) ;; std.slfree(ini.sects) std.htfree(ini.elts) std.free(ini) } const loadf = {fd var p : parser# var ini var f ini = std.mk([ .elts = std.mkht(keyhash, keyeq) ]) p = std.mk([ .line = 1, .sect = "", .err = `std.None ]) f = bio.mkfile(fd, bio.Rd) while true match bio.readln(f) | `bio.Eof: break | `bio.Ok ln: if !parseline(p, ini, ln) break ;; | `bio.Err e: p.err = `std.Some `Fileerr break ;; p.line++ ;; match p.err | `std.None: std.slfree(p.sect) -> `std.Ok ini | `std.Some e: free(ini) -> `std.Err e ;; } const parseline = {p, ini, ln ln = std.strstrip(ln) /* remove comments */ match std.strfind(ln, ";") | `std.Some idx: ln = ln[:idx] | `std.None: ;; match std.strfind(ln, "#") | `std.Some idx: ln = ln[:idx] | `std.None: ;; /* skip empty lines */ if ln.len == 0 -> true ;; match (ln[0] : char) | '[': -> parsesection(p, ini, ln) | _: -> parsekvp(p, ini, ln) ;; } const parsesection = {p, ini, ln match std.strfind(ln, "]") | `std.Some idx: if idx != ln.len - 1 p.err = `std.Some (`Parseerr p.line) -> false ;; std.slfree(p.sect) p.sect = std.sldup(std.strstrip(ln[1:idx])) match std.lsearch(ini.sects, p.sect, std.strcmp) | `std.Some s: /* nothing */ | `std.None: std.slpush(&ini.sects, std.sldup(p.sect)) ;; | `std.None: p.err = `std.Some (`Parseerr p.line) -> false ;; -> true } const parsekvp = {p, ini, ln var key, val match std.strfind(ln, "=") | `std.None: p.err = `std.Some (`Parseerr p.line) -> false | `std.Some idx: key = std.strstrip(ln[:idx]) val = std.strstrip(ln[idx+1:]) if std.hthas(ini.elts, (p.sect, key)) p.err = `std.Some(`Dupkey p.line) -> false ;; std.htput(ini.elts, (std.sldup(p.sect), std.sldup(key)), std.sldup(val)) -> true ;; } const keyhash = {k var sect, key (sect, key) = k -> std.strhash(sect) ^ std.strhash(key) } const keyeq = {a, b var s1, k1 var s2, k2 (s1, k1) = a (s2, k2) = a -> std.streq(s1, s2) && std.streq(k1, k2) }