From 44e48ddc2785b037abd202a8d38b2ef2e8c36600 Mon Sep 17 00:00:00 2001 From: quou Date: Sat, 14 Dec 2024 23:15:34 +1100 Subject: initial commit --- cfg/Makefile | 21 +++++ cfg/cfgparse.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cfg/cfgparse.h | 62 +++++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 cfg/Makefile create mode 100644 cfg/cfgparse.c create mode 100644 cfg/cfgparse.h (limited to 'cfg') 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 +#include +#include + +#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 -- cgit v1.2.3-54-g00ecf