This commit is contained in:
snarmph 2024-11-29 16:10:48 +01:00
parent 82aee127b0
commit a92b119549
99 changed files with 6922 additions and 5723 deletions

279
ini.c Normal file
View file

@ -0,0 +1,279 @@
#include "ini.h"
#include "warnings/colla_warn_beg.h"
#include <assert.h>
#include "strstream.h"
static void ini__parse(arena_t *arena, ini_t *ini, const iniopts_t *options);
ini_t iniParse(arena_t *arena, strview_t filename, const iniopts_t *options) {
file_t fp = fileOpen(*arena, filename, FILE_READ);
ini_t out = iniParseFile(arena, fp, options);
fileClose(fp);
return out;
}
ini_t iniParseFile(arena_t *arena, file_t file, const iniopts_t *options) {
str_t data = fileReadWholeStrFP(arena, file);
return iniParseStr(arena, strv(data), options);
}
ini_t iniParseStr(arena_t *arena, strview_t str, const iniopts_t *options) {
ini_t out = {
.text = str,
.tables = NULL,
};
ini__parse(arena, &out, options);
return out;
}
bool iniIsValid(ini_t *ctx) {
return ctx && !strvIsEmpty(ctx->text);
}
initable_t *iniGetTable(ini_t *ctx, strview_t name) {
initable_t *t = ctx ? ctx->tables : NULL;
while (t) {
if (strvEquals(t->name, name)) {
return t;
}
t = t->next;
}
return NULL;
}
inivalue_t *iniGet(initable_t *ctx, strview_t key) {
inivalue_t *v = ctx ? ctx->values : NULL;
while (v) {
if (strvEquals(v->key, key)) {
return v;
}
v = v->next;
}
return NULL;
}
iniarray_t iniAsArr(arena_t *arena, inivalue_t *value, char delim) {
strview_t v = value ? value->value : STRV_EMPTY;
if (!delim) delim = ' ';
strview_t *beg = (strview_t *)arena->current;
usize count = 0;
usize start = 0;
for (usize i = 0; i < v.len; ++i) {
if (v.buf[i] == delim) {
strview_t arrval = strvTrim(strvSub(v, start, i));
if (!strvIsEmpty(arrval)) {
strview_t *newval = alloc(arena, strview_t);
*newval = arrval;
++count;
}
start = i + 1;
}
}
strview_t last = strvTrim(strvSub(v, start, SIZE_MAX));
if (!strvIsEmpty(last)) {
strview_t *newval = alloc(arena, strview_t);
*newval = last;
++count;
}
return (iniarray_t){
.values = beg,
.count = count,
};
}
uint64 iniAsUInt(inivalue_t *value) {
strview_t v = value ? value->value : STRV_EMPTY;
instream_t in = istrInitLen(v.buf, v.len);
uint64 out = 0;
if (!istrGetU64(&in, &out)) {
out = 0;
}
return out;
}
int64 iniAsInt(inivalue_t *value) {
strview_t v = value ? value->value : STRV_EMPTY;
instream_t in = istrInitLen(v.buf, v.len);
int64 out = 0;
if (!istrGetI64(&in, &out)) {
out = 0;
}
return out;
}
double iniAsNum(inivalue_t *value) {
strview_t v = value ? value->value : STRV_EMPTY;
instream_t in = istrInitLen(v.buf, v.len);
double out = 0;
if (!istrGetDouble(&in, &out)) {
out = 0.0;
}
return out;
}
bool iniAsBool(inivalue_t *value) {
strview_t v = value ? value->value : STRV_EMPTY;
instream_t in = istrInitLen(v.buf, v.len);
bool out = 0;
if (!istrGetBool(&in, &out)) {
out = false;
}
return out;
}
// == PRIVATE FUNCTIONS ==============================================================================
#define INIPUSH(head, tail, val) \
do { \
if (!head) { \
head = val; \
tail = val; \
} \
else { \
tail->next = val; \
val = tail; \
} \
} while (0)
static iniopts_t ini__get_options(const iniopts_t *options) {
iniopts_t out = {
.key_value_divider = '=',
};
#define SETOPT(v) out.v = options->v ? options->v : out.v
if (options) {
SETOPT(key_value_divider);
SETOPT(merge_duplicate_keys);
SETOPT(merge_duplicate_tables);
}
#undef SETOPT
return out;
}
static void ini__add_value(arena_t *arena, initable_t *table, instream_t *in, iniopts_t *opts) {
assert(table);
strview_t key = strvTrim(istrGetView(in, opts->key_value_divider));
istrSkip(in, 1);
strview_t value = strvTrim(istrGetViewEither(in, strv("\n#;")));
istrSkip(in, 1);
inivalue_t *newval = NULL;
if (opts->merge_duplicate_keys) {
newval = table->values;
while (newval) {
if (strvEquals(newval->key, key)) {
break;
}
newval = newval->next;
}
}
if (newval) {
newval->value = value;
}
else {
newval = alloc(arena, inivalue_t);
newval->key = key;
newval->value = value;
if (!table->values) {
table->values = newval;
}
else {
table->tail->next = newval;
}
table->tail = newval;
}
}
static void ini__add_table(arena_t *arena, ini_t *ctx, instream_t *in, iniopts_t *options) {
istrSkip(in, 1); // skip [
strview_t name = istrGetView(in, ']');
istrSkip(in, 1); // skip ]
initable_t *table = NULL;
if (options->merge_duplicate_tables) {
table = ctx->tables;
while (table) {
if (strvEquals(table->name, name)) {
break;
}
table = table->next;
}
}
if (!table) {
table = alloc(arena, initable_t);
table->name = name;
if (!ctx->tables) {
ctx->tables = table;
}
else {
ctx->tail->next = table;
}
ctx->tail = table;
}
istrIgnoreAndSkip(in, '\n');
while (!istrIsFinished(*in)) {
switch (istrPeek(in)) {
case '\n': // fallthrough
case '\r':
return;
case '#': // fallthrough
case ';':
istrIgnoreAndSkip(in, '\n');
break;
default:
ini__add_value(arena, table, in, options);
break;
}
}
}
static void ini__parse(arena_t *arena, ini_t *ini, const iniopts_t *options) {
iniopts_t opts = ini__get_options(options);
initable_t *root = alloc(arena, initable_t);
root->name = INI_ROOT;
ini->tables = root;
ini->tail = root;
instream_t in = istrInitLen(ini->text.buf, ini->text.len);
while (!istrIsFinished(in)) {
istrSkipWhitespace(&in);
switch (istrPeek(&in)) {
case '[':
ini__add_table(arena, ini, &in, &opts);
break;
case '#': // fallthrough
case ';':
istrIgnoreAndSkip(&in, '\n');
break;
default:
ini__add_value(arena, ini->tables, &in, &opts);
break;
}
}
}
#undef INIPUSH
#include "warnings/colla_warn_end.h"