ref: e922ef10cc79b48a7085ec76f343bee5859be94b
author: sirjofri <sirjofri@sirjofri.de>
date: Sat Feb 18 11:14:01 EST 2023
adds first working version with only one simple parsing library function
--- /dev/null
+++ b/README
@@ -1,0 +1,14 @@
+Parse INI files
+
+
+Usage:
+
+ void
+ parseline(char *section, char *key, char *value) {
+ print("%s: %s -> %s\n", section, key, value);
+ }
+
+ parseini("file.ini", parseline, 0);
+
+
+See also the ini.man file for more details.
\ No newline at end of file
--- /dev/null
+++ b/ini.c
@@ -1,0 +1,94 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+void
+tolowercase(char *c)
+{
+ while (*c != 0) {
+ *c = tolower(*c);
+ c++;
+ }
+}
+
+int
+extractsection(char *line, char **section)
+{
+ char *start, *end;
+
+ start = strchr(line, '[');
+ end = strchr(line, ']');
+
+ if (start == nil || end == nil)
+ return 0;
+
+ *section = malloc(end-start);
+ if (!section)
+ sysfatal("error allocating section string: %r");
+
+ if (!memcpy(*section, start+1, end-start-1))
+ sysfatal("error copying section string: %r");
+
+ (*section)[end-start-1] = 0;
+ return 1;
+}
+
+int
+extractkeyvalue(char *line, char **key, char **value)
+{
+ char *arg[2];
+
+ arg[0] = nil;
+ arg[1] = nil;
+
+ if (getfields(line, arg, 2, 1, "=") == 0)
+ return 0;
+
+ *key = arg[0] ? strdup(arg[0]) : nil;
+ *value = arg[1] ? strdup(arg[1]) : nil;
+
+ if (!(key && value))
+ sysfatal("error copying key/value strings: %r");
+ return 1;
+}
+
+int
+parseini(char *file, void (*f)(char*,char*,char*), int forcelower)
+{
+ char *line;
+ int callback;
+ Biobuf *bio = Bopen(file, OREAD);
+
+ char *section = nil;
+ char *key = nil;
+ char *value = nil;
+
+ if (!bio)
+ return 0;
+
+ while (line = Brdstr(bio, '\n', 1)) {
+ if (!line)
+ return 1;
+
+ if (forcelower)
+ tolowercase(line);
+
+ callback = 0;
+ if (*line == ';')
+ goto endline;
+
+ if (extractsection(line, §ion))
+ goto endline;
+
+ if (callback = extractkeyvalue(line, &key, &value))
+ goto endline;
+
+endline:
+ free(line);
+ if (callback && f)
+ f(section, key, value);
+ }
+ Bterm(bio);
+
+ return 1;
+}
\ No newline at end of file
--- /dev/null
+++ b/ini.h
@@ -1,0 +1,1 @@
+int parseini(char *file, void (*f)(char*,char*,char*), int forcelower);
\ No newline at end of file
--- /dev/null
+++ b/ini.man
@@ -1,0 +1,30 @@
+.TH INI 2
+.SH NAME
+parseini \- parse ini files
+.SH SYNOPSIS
+.B #include <ini.h>
+.PP
+.B
+int parseini(char *file, void (*f)(char*, char*, char*), int forcelower)
+.PP
+.SH DESCRIPTION
+This library function provides simple parsing of INI files using
+.IR bio (2).
+For each key-value pair within the INI file it calls the provided callback function
+.IR f .
+.PP
+The callback function gets the section, key and value as parameters.
+.PP
+The
+.I forcelower
+flag tells the function to return all strings as lowercase strings, which can be helpful for some cases. Otherwise, all strings are returned as entered in the INI file.
+.SH SOURCE
+.B /sys/src/libini
+.SH SEE ALSO
+.IR bio (2)
+.SH DIAGNOSTICS
+.I Parseini
+returns 0 on failure and sets
+.IR errstr .
+.SH BUGS
+Sure.
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,32 @@
+</$objtype/mkfile
+
+LIB=$objtype-libini.a
+OFILES=ini.$O
+HFILES=/sys/include/ini.h
+
+</sys/src/cmd/mksyslib
+
+$O.out: test.$O ini.$O
+ $LD -o $target $prereq
+
+test.$O: test.c ini.h
+ $CC $CFLAGS test.c
+
+test:V: $O.out
+ cat <<EOF >test.ini
+ [Category A]
+ keyA=valueA
+ keyA=valueB
+ keyB=valueA
+ keyB=valueB
+
+ ; Comment
+
+ [Category B]
+ keyC=valueA
+ keyC=valueB
+ keyD=valueA
+ keyD=valueB
+ EOF
+ $O.out test.ini
+ rm test.ini
--- /dev/null
+++ b/test.c
@@ -1,0 +1,32 @@
+#include <u.h>
+#include <libc.h>
+#include "ini.h"
+
+void
+parseline(char *section, char *key, char *value)
+{
+ print("section: '%s', key: '%s', value: '%s'\n", section, key, value);
+}
+
+void
+main(int, char **argv)
+{
+ char *file = argv[1];
+ char *status = nil;
+ char *err = "error";
+
+ print("Testing forcelower=0\n");
+
+ if (!parseini(file, parseline, 0)) {
+ fprint(2, "error: %r\n");
+ status = err;
+ }
+
+ print("Testing forcelower=1\n");
+
+ if (!parseini(file, parseline, 1)) {
+ fprint(2, "error: %r\n");
+ status = err;
+ }
+ exits(status);
+}