#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; 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 == ':') { *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_bool( cfg_Prop* p, const char* start, int len ) { (void)len; p->type = cfg_type_int; if (!memcmp("true", start, 4)) p->as.num = 1; else p->as.num = 0; } 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 '"': fill_prop_string(p, arena, start + 1, len - 1); break; 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 'f': case 't': fill_prop_bool(p, start, len); break; case '$': fill_prop_data(p, arena, start + 1, len - 1); break; default: p->type = cfg_type_none; break; } } 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 (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, line + len + 1); 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; } 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; }