ref: 15392a525cae61a9fc06d40cd4846664512661b2
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.Ordonly)
| `std.Ok fd: -> loadf(fd)
| `std.Err e: -> `std.Err `Fileerr
;;
}
const free = {ini
for ((sect, key), val) in std.byhtkeyvals(ini.elts)
std.slfree(val)
std.slfree(sect)
std.slfree(key)
;;
for s in 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)
}