summaryrefslogtreecommitdiff
path: root/cfg
diff options
context:
space:
mode:
authorquou <quou@disroot.org>2024-12-14 23:15:34 +1100
committerquou <quou@disroot.org>2024-12-14 23:19:17 +1100
commit44e48ddc2785b037abd202a8d38b2ef2e8c36600 (patch)
treef58887ce48f7fdbf6dcca365b2a1b02a34e1b355 /cfg
initial commit
Diffstat (limited to 'cfg')
-rw-r--r--cfg/Makefile21
-rw-r--r--cfg/cfgparse.c236
-rw-r--r--cfg/cfgparse.h62
3 files changed, 319 insertions, 0 deletions
diff --git a/cfg/Makefile b/cfg/Makefile
new file mode 100644
index 0000000..e10e70f
--- /dev/null
+++ b/cfg/Makefile
@@ -0,0 +1,21 @@
+.POSIX:
+target = libcfg.a
+includes = -I../qstd
+cflags = -std=c90 -pedantic -Wall -Wextra $(DEBUG_COMPILE_FLAG) $(includes)
+lflags = $(DEBUG_LINK_FLAG)
+
+objects = cfgparse.o
+
+.PHONY: all clean
+
+all: $(target)
+
+cfgparse.o: cfgparse.c cfgparse.h
+ $(CC) -c $(cflags) cfgparse.c -o cfgparse.o
+
+$(target): $(objects)
+ $(AR) -rcs $(target) $(objects)
+
+clean:
+ rm -f $(target)
+ rm -f $(objects)
diff --git a/cfg/cfgparse.c b/cfg/cfgparse.c
new file mode 100644
index 0000000..4dc6dbb
--- /dev/null
+++ b/cfg/cfgparse.c
@@ -0,0 +1,236 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cfgparse.h"
+
+static const char* next_line(const char* line) {
+ const char* c;
+ for (c = line; *c != '\n'; c++) {
+ if (!*c) return 0;
+ }
+ return c + 1;
+}
+
+static int is_object(
+ const char* line,
+ const char** start,
+ int* len
+) {
+ const char* c;
+ *start = line;
+ if (line[0] != '[') return 0;
+ *start = line + 1;
+ for (
+ c = line, *len = 0;
+ *c && *c != '\n';
+ c++, *len = *len + 1
+ ) if (*c == ']') {
+ *len = *len - 1;
+ return 1;
+ }
+ return 0;
+}
+
+static int is_iden(char c) {
+ return
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c == '_');
+}
+
+static int is_prop(
+ const char* line,
+ const char** start,
+ int* len
+) {
+ const char* c;
+ *start = line;
+ for (
+ c = line, *len = 0;
+ *c && *c != '\n';
+ c++, *len = *len + 1
+ ) if (*c == ':' || (*c == ' ' && c[1] == ':')) {
+ *len = *len;
+ return 1;
+ } else if (!is_iden(*c)) return 0;
+ return 0;
+}
+
+static void fill_prop_string(
+ cfg_Prop* p,
+ Arena* arena,
+ const char* start,
+ int len
+) {
+ p->type = cfg_type_string;
+ p->as.str = arena_alloc(arena, len + 1);
+ memcpy(p->as.str, start, len);
+ p->as.str[len] = 0;
+}
+
+static void fill_prop_number(
+ cfg_Prop* p,
+ const char* start,
+ int len
+) {
+ (void)len;
+ p->type = cfg_type_int;
+ p->as.num = (int)strtol(start, 0, 10);
+}
+
+static void fill_prop_data(
+ cfg_Prop* p,
+ Arena* arena,
+ const char* start,
+ int len
+) {
+ int i, size;
+ char s[3];
+ s[2] = 0;
+ size = len / 2;
+ p->type = cfg_type_data;
+ p->as.data.data = arena_alloc(arena, size);
+ p->as.data.size = size;
+ for (i = 0; i < size; i++) {
+ s[0] = start[i * 2];
+ s[1] = start[i * 2 + 1];
+ sscanf(s, "%x", (unsigned*)&p->as.data.data[i]);
+ }
+}
+
+static void fill_prop_val(
+ cfg_Prop* p,
+ Arena* arena,
+ const char* start
+) {
+ const char* c;
+ int len;
+ for (; *start && (*start == ' ' || *start == '\t'); start++);
+ for (len = 0, c = start; *c && *c != '\n'; c++, len++);
+ switch (start[0]) {
+ case '0': case '1': case '2':
+ case '3': case '4': case '5':
+ case '6': case '7': case '8':
+ case '9': case '-':
+ fill_prop_number(p, start, len);
+ break;
+ case '$':
+ fill_prop_data(p, arena, start + 1, len - 1);
+ break;
+ default:
+ fill_prop_string(p, arena, start, len);
+ break;
+ }
+}
+
+const char* prop_start(const char* line) {
+ const char* s = line;
+ while (*s && *s != ':') s++;
+ s++;
+ if (*s == ' ') s++;
+ return s;
+}
+
+cfg_Object* cfg_parse(const char* src, Arena* arena) {
+ const char* line, * start;
+ int len;
+ cfg_Object* root = 0, * obj = 0, * pobj;
+ cfg_Prop* prop = 0, * pprop;
+ for (line = src; line; line = next_line(line)) {
+ len = 0;
+ if (line[0] == '#') continue;
+ if (is_object(line, &start, &len) || line == src) {
+ pobj = obj;
+ obj = arena_alloc(arena, sizeof *obj);
+ obj->prop_count = 0;
+ obj->props = 0;
+ obj->next = 0;
+ memcpy(obj->name, start, len);
+ obj->name[len] = 0;
+ if (!root)
+ root = obj;
+ else
+ pobj->next = obj;
+ } else if (!obj) continue;
+ if (is_prop(line, &start, &len)) {
+ pprop = prop;
+ prop = arena_alloc(arena, sizeof *prop);
+ prop->next = 0;
+ memcpy(prop->name, start, len);
+ prop->name[len] = 0;
+ fill_prop_val(prop, arena, prop_start(line));
+ obj->prop_count++;
+ if (!obj->props)
+ obj->props = prop;
+ else
+ pprop->next = prop;
+ }
+ }
+ return root;
+}
+
+const cfg_Prop* find_prop(
+ const cfg_Object* obj,
+ const char* name
+) {
+ const cfg_Prop* p;
+ for (p = obj->props; p; p = p->next) {
+ if (!strcmp(p->name, name)) {
+ return p;
+ }
+ }
+ return 0;
+}
+
+const cfg_Prop* find_prop_of(
+ const cfg_Object* obj,
+ const char* name,
+ cfg_Type type
+) {
+ const cfg_Prop* p;
+ for (p = obj->props; p; p = p->next) {
+ if (p->type == type && !strcmp(p->name, name)) {
+ return p;
+ }
+ }
+ return 0;
+}
+
+const cfg_Object* find_child(
+ const cfg_Object* obj,
+ const char* name
+) {
+ const cfg_Object* ch = obj;
+ while (ch) {
+ if (!strcmp(ch->name, name)) return ch;
+ ch = ch->next;
+ }
+ return 0;
+}
+
+int find_int_default(
+ const cfg_Object* obj,
+ const char* name,
+ int def
+) {
+ const cfg_Prop* prop;
+ prop = find_prop_of(obj, name, cfg_type_int);
+ if (prop) {
+ return prop->as.num;
+ }
+ return def;
+}
+
+const char* find_string_default(
+ const cfg_Object* obj,
+ const char* name,
+ const char* def
+) {
+ const cfg_Prop* prop;
+ prop = find_prop_of(obj, name, cfg_type_string);
+ if (prop) {
+ return prop->as.str;
+ }
+ return def;
+}
diff --git a/cfg/cfgparse.h b/cfg/cfgparse.h
new file mode 100644
index 0000000..4865b48
--- /dev/null
+++ b/cfg/cfgparse.h
@@ -0,0 +1,62 @@
+#ifndef cfgparse_h
+#define cfgparse_h
+
+#include "memory.h"
+
+typedef enum {
+ cfg_type_int,
+ cfg_type_string,
+ cfg_type_data,
+ cfg_type_none
+} cfg_Type;
+
+typedef struct {
+ int size;
+ char* data;
+} cfg_Data;
+
+typedef struct cfg_Prop {
+ char name[28];
+ cfg_Type type;
+ union {
+ int num;
+ cfg_Data data;
+ char* str;
+ } as;
+ struct cfg_Prop* next;
+} cfg_Prop;
+
+typedef struct cfg_Object {
+ char name[28];
+ int prop_count;
+ cfg_Prop* props;
+ struct cfg_Object* next;
+} cfg_Object;
+
+cfg_Object* cfg_parse(const char* src, Arena* arena);
+const cfg_Prop* find_prop(
+ const cfg_Object* obj,
+ const char* name
+);
+const cfg_Prop* find_prop_of(
+ const cfg_Object* obj,
+ const char* name,
+ cfg_Type type
+);
+const cfg_Object* find_child(
+ const cfg_Object* obj,
+ const char* name
+);
+
+int find_int_default(
+ const cfg_Object* obj,
+ const char* name,
+ int def
+);
+const char* find_string_default(
+ const cfg_Object* obj,
+ const char* name,
+ const char* def
+);
+
+#endif