summaryrefslogtreecommitdiff
path: root/lib/inifile
diff options
context:
space:
mode:
authorOri Bernstein <ori@eigenstate.org>2015-11-24 22:28:27 -0800
committerOri Bernstein <ori@eigenstate.org>2015-11-24 22:28:27 -0800
commitda67a46215a8c4f16e958e86354c7514132abc92 (patch)
treeed7dbd26081b6b8e150903b3109a7e4f39cd2a04 /lib/inifile
parenta57ce36f628ff500404b41064bd2a95c7bfa510d (diff)
downloadmc-da67a46215a8c4f16e958e86354c7514132abc92.tar.gz
Add ini file parser.
Diffstat (limited to 'lib/inifile')
-rw-r--r--lib/inifile/access.myr29
-rw-r--r--lib/inifile/bld.sub10
-rw-r--r--lib/inifile/parse.myr153
-rw-r--r--lib/inifile/types.myr13
-rw-r--r--lib/inifile/write.myr12
5 files changed, 217 insertions, 0 deletions
diff --git a/lib/inifile/access.myr b/lib/inifile/access.myr
new file mode 100644
index 0000000..09d526f
--- /dev/null
+++ b/lib/inifile/access.myr
@@ -0,0 +1,29 @@
+use std
+use bio
+
+use "types.use"
+
+pkg inifile =
+ /* key getting/setting */
+ const get : (ini : inifile#, sect : byte[:], key : byte[:] -> std.option(byte[:]))
+ const getv : (ini : inifile#, sect : byte[:], key : byte[:], val : byte[:] -> byte[:])
+ const has : (ini : inifile#, sect : byte[:], key : byte[:] -> bool)
+ const put : (ini : inifile#, sect : byte[:], key : byte[:], val : byte[:] -> void)
+;;
+
+const get = {ini, sect, key
+ -> std.htget(ini.elts, (sect, key))
+}
+
+const getv = {ini, sect, key, val
+ -> std.htgetv(ini.elts, (sect, key), val)
+}
+
+const has = {ini, sect, key
+ -> std.hthas(ini.elts, (sect, key))
+}
+
+const put = {ini, sect, key, val
+ std.htput(ini.elts, (std.sldup(sect), std.sldup(key)), std.sldup(val))
+}
+
diff --git a/lib/inifile/bld.sub b/lib/inifile/bld.sub
new file mode 100644
index 0000000..976b01b
--- /dev/null
+++ b/lib/inifile/bld.sub
@@ -0,0 +1,10 @@
+lib inifile =
+ access.myr
+ parse.myr
+ types.myr
+ write.myr
+
+ lib ../std:std
+ lib ../sys:sys
+ lib ../bio:bio
+;;
diff --git a/lib/inifile/parse.myr b/lib/inifile/parse.myr
new file mode 100644
index 0000000..928ebf1
--- /dev/null
+++ b/lib/inifile/parse.myr
@@ -0,0 +1,153 @@
+use std
+use bio
+
+use "types.use"
+
+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.Fail e: -> `std.Fail `Fileerr
+ ;;
+}
+
+const free = {ini
+ var kl
+
+ kl = std.htkeys(ini.elts)
+ for (sect, key) in kl
+ std.slfree(std.htgetv(ini.elts, (sect, key), ""))
+ std.slfree(sect)
+ std.slfree(key)
+ ;;
+ std.slfree(kl)
+ 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.Fail 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] castto(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:$-1]))
+ | `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)
+}
+
diff --git a/lib/inifile/types.myr b/lib/inifile/types.myr
new file mode 100644
index 0000000..3df3d50
--- /dev/null
+++ b/lib/inifile/types.myr
@@ -0,0 +1,13 @@
+use std
+
+pkg inifile =
+ type error = union
+ `Fileerr
+ `Parseerr int
+ `Dupkey int
+ ;;
+
+ type inifile = struct
+ elts : std.htab((byte[:], byte[:]), byte[:])#
+ ;;
+;;
diff --git a/lib/inifile/write.myr b/lib/inifile/write.myr
new file mode 100644
index 0000000..d17f005
--- /dev/null
+++ b/lib/inifile/write.myr
@@ -0,0 +1,12 @@
+use std
+use bio
+
+use "types.use"
+
+pkg inifile =
+ const write : (ini : inifile#, path : byte[:] -> bool)
+;;
+
+const write = {ini, path
+ -> true
+}