ref: 6c63e71880f8bcdf597548e8bbaba165c4fe7d38
dir: /ini.c/
#include <u.h> #include <libc.h> #include <bio.h> #include "ini.h" #define REALLOC_SLACK 32 // #define DUMPINPUT typedef struct Array Array; struct Array { int num; void **items; int maxnum; }; void Ainit(Array *a); int Aaddunique(Array *a, void *item); int Aaddstrunique(Array *a, char *item); int Aappenditem(Array *a, void *item); int Afind(Array *a, void *item); char* Afindstr(Array *a, char *item); void* Aget(Array *a, int i); void** Aarr(Array *a); int Anum(Array *a); void Ashrink(Array *a); int Aset(Array *a, int index, void *item); typedef struct Property Property; struct Property { char *key; char *value; char *section; }; typedef struct ArrayProperty ArrayProperty; struct ArrayProperty { char *key; Array values; char *section; }; typedef struct IniConfig IniConfig; struct IniConfig { Array sections; Array properties; Array arrayproperties; }; Array configs; int seceq(char* left, char* right) { if (left == nil && right == nil) return 1; if (left == right) return 1; if (left != nil && right != nil) return strcmp(left, right) == 0; return 0; } char* getinivalue(int config, char *section, char *key) { if (!key) return nil; IniConfig *c = (IniConfig*)Aget(&configs, config); if (!c) { werrstr("invalid config: %r"); return nil; } for (int i = 0; i < Anum(&c->properties); i++) { Property *p = (Property*)Aget(&c->properties, i); if (seceq(p->section, section) && strcmp(p->key, key) == 0) return p->value; } werrstr("unable to find key value pair within section"); return nil; } char** getiniarray(int config, char *section, char *key, int *num) { if (!key) { *num = 0; return nil; } IniConfig *c = (IniConfig*)Aget(&configs, config); if (!c) sysfatal("invalid config: %r"); for (int i = 0; i < Anum(&c->arrayproperties); i++) { ArrayProperty *p = (ArrayProperty*)Aget(&c->arrayproperties, i); if (seceq(p->section, section) && strcmp(p->key+1, key) == 0) { *num = Anum(&p->values); return (char**)Aarr(&p->values); } } *num = 0; return nil; } 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), int forcelower, int config) { 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, config); } Bterm(bio); return 1; } void configparseline(char *section, char *key, char *value, int config) { char *sec; Property *prop; ArrayProperty *arr; IniConfig *c = (IniConfig*)Aget(&configs, config); sec = nil; if (section != nil) { int sid = Aaddstrunique(&c->sections, section); sec = (char*)Aget(&c->sections, sid); } if (*key == '+') { #ifdef DUMPINPUT fprint(2, "]]array key: %s\n", key); #endif arr = nil; for (int i = 0; i < Anum(&c->arrayproperties); i++) { ArrayProperty *p = (ArrayProperty*)Aget(&c->arrayproperties, i); if (seceq(p->section, sec) && strcmp(p->key, key) == 0) { arr = p; #ifdef DUMPINPUT fprint(2, "]] key exists\n"); #endif break; } } if (!arr) { arr = malloc(sizeof(ArrayProperty)); if (!arr) sysfatal("error: %r"); arr->key = key; Ainit(&arr->values); arr->section = sec; Aaddunique(&c->arrayproperties, arr); #ifdef DUMPINPUT fprint(2, "]] new key\n"); #endif } Aaddunique(&arr->values, value); #ifdef DUMPINPUT fprint(2, "]] %s | %s | num: %d\n", arr->section, arr->key, Anum(&arr->values)); #endif } else { #ifdef DUMPINPUT fprint(2, "]]prop key: %s\n", key); #endif prop = nil; for (int i = 0; i < Anum(&c->properties); i++) { Property *p = (Property*)Aget(&c->properties, i); if (seceq(p->section, sec) && strcmp(p->key, key) == 0) { prop = p; #ifdef DUMPINPUT fprint(2, "]] key exists\n"); #endif break; } } if (!prop) { prop = malloc(sizeof(Property)); if (!prop) sysfatal("error: %r"); prop->key = key; prop->section = sec; Aaddunique(&c->properties, prop); #ifdef DUMPINPUT fprint(2, "]] new key\n"); #endif } prop->value = value; #ifdef DUMPINPUT fprint(2, "]] %s | %s | %s\n", prop->section, prop->key, prop->value); #endif } } void shrinkconfig(int id) { IniConfig *c = (IniConfig*)Aget(&configs, id); if (!c) return; Ashrink(&c->sections); Ashrink(&c->properties); Ashrink(&c->arrayproperties); } int iniconfig(char *file, int forcelower) // returns ID of config entry { int id; if (!configs.items) Ainit(&configs); IniConfig *ini = malloc(sizeof(IniConfig)); if (!ini) sysfatal("error: %r"); Ainit(&ini->sections); Ainit(&ini->properties); Ainit(&ini->arrayproperties); id = Aappenditem(&configs, ini); if (parseini(file, configparseline, forcelower, id)) { //shrinkconfig(id); return id; } return -1; } int freeini(int config) { IniConfig *c = (IniConfig*)Aget(&configs, config); if (!c) sysfatal("invalid config: %r"); for (int i = 0; i < Anum(&c->properties); i++) { Property *p = (Property*)Aget(&c->properties, i); free(p->key); p->key = nil; free(p->value); p->value = nil; free(p); Aset(&c->properties, i, nil); } for (int i = 0; i < Anum(&c->arrayproperties); i++) { ArrayProperty *p = (ArrayProperty*)Aget(&c->arrayproperties, i); for (int j = 0; j < Anum(&p->values); j++) { free(Aget(&p->values, j)); Aset(&p->values, j, nil); } free(p->key); p->key = nil; free(p); Aset(&c->arrayproperties, i, nil); } for (int i = 0; i < Anum(&c->sections); i++) { free(Aget(&c->sections, i)); Aset(&c->sections, i, nil); } free(c); if (Aset(&configs, config, nil)) return 1; werrstr("cannot free ini config: %r"); return 0; } int Afindstrid(Array *a, char *item) { for (int i = 0; i < a->num; i++) { if (strcmp((char*)a->items[i], item) == 0) return i; } return -1; } char* Afindstr(Array *a, char *item) { int i = Afindstrid(a, item); return Aget(a, i); } int Afind(Array *a, void *item) { for (int i = 0; i < a->num; i++) { if (item == a->items[i]) return i; } return -1; } int Aappenditem(Array *a, void *item) { if (a->num + 1 > a->maxnum) { a->maxnum += REALLOC_SLACK; a->items = realloc(a->items, a->maxnum); if (!a->items) sysfatal("error: %r"); } a->items[a->num] = item; a->num++; return a->num - 1; } int Aaddunique(Array *a, void *item) { int it = Afind(a, item); if (it >= 0) return it; return Aappenditem(a, item); } int Aaddstrunique(Array *a, char *item) { int it = Afindstrid(a, item); if (it >= 0) return it; return Aappenditem(a, item); } void* Aget(Array *a, int i) { if (i < 0) return nil; if (i < a->num) return a->items[i]; werrstr("array index out of bounds"); return nil; } void** Aarr(Array *a) { return a->items; } int Anum(Array *a) { return a->num; } void Ashrink(Array *a) { a->items = realloc(a->items, a->num); if (!a->items) sysfatal("error: %r"); a->maxnum = a->num; } int Aset(Array *a, int index, void *item) { if (index >= a->num) { werrstr("bad index"); return 0; } a->items[index] = item; return 1; } void Ainit(Array *a) { a->num = 0; a->maxnum = 0; a->items = malloc(0); if (!a->items) sysfatal("error: %r"); }