shithub: libini

Download patch

ref: e922ef10cc79b48a7085ec76f343bee5859be94b
author: sirjofri <[email protected]>
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, &section))
+			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);
+}