#include #include #include #include #include "conf.h" typedef struct _SField { char *name; int type; union { char *str_value; int int_value; double double_value; HTable table_value; } value; struct _SField *next; } SField; typedef struct _STable { SField *fields; char *stat_buf; } STable; typedef struct _STableIterator { HTable table; SField *cur_field; int before_start; char *stat_buf; } STableIterator; HTable table_create() { HTable table; table = (STable*)calloc(1, sizeof(STable)); return table; } static void free_field(SField *field) { if (! field) return; if (field->name) free(field->name); switch (field->type) { case TYPE_STRING: free(field->value.str_value); break; case TYPE_TABLE: table_free(field->value.table_value); break; } free(field); } static SField* find_field(HTable table, const char *name) { SField *f; if ((! table) || (! name)) return NULL; f = table->fields; while (f) { if (f->name && (! strcmp(f->name, name))) return f; f = f->next; } return NULL; } int table_remove_field(HTable table, char *field) { SField *f, *pf; if ((! table) || (! field)) return -1; f = find_field(table, field); if (f) { if (table->fields == f) table->fields = f->next; else { pf = table->fields; while (pf && (pf->next != f)) pf = pf->next; if (pf) pf->next = f->next; } free_field(f); } return 0; } static SField* add_empty_field(HTable table, const char *field) { SField *f, *nf; if ((! table) || (! field)) return NULL; f = find_field(table, field); if (f) table_remove_field(table, f->name); nf = (SField*)calloc(1, sizeof(SField)); if (! nf) return NULL; nf->name = strdup(field); f = table->fields; if (f) { while (f->next) f = f->next; f->next = nf; } else table->fields = nf; return nf; } int table_set_str(HTable table, const char *field, const char *value) { SField *f; if ((! table) || (! field) || (! value)) return -1; f = add_empty_field(table, field); if (! f) return -1; f->type = TYPE_STRING; f->value.str_value = strdup(value); return 0; } int table_set_int(HTable table, const char *field, int value) { SField *f; if ((! table) || (! field)) return -1; f = add_empty_field(table, field); if (! f) return -1; f->type = TYPE_INT; f->value.int_value = value; return 0; } int table_set_double(HTable table, const char *field, double value) { SField *f; if ((! table) || (! field)) return -1; f = add_empty_field(table, field); if (! f) return -1; f->type = TYPE_FLOAT; f->value.double_value = value; return 0; } int table_set_table(HTable table, const char *field, HTable value) { SField *f; if ((! table) || (! field) || (! value)) return -1; f = add_empty_field(table, field); if (! f) return -1; f->type = TYPE_TABLE; f->value.table_value = value; return 0; } static int skip_spaces(char *buf, long int *pos); static int skip_multy_comment(char *buf, long int *pos) { while (buf[*pos]) { if (buf[*pos] == '*') { (*pos)++; if (buf[*pos] == '/') { (*pos)++; return skip_spaces(buf, pos); } } (*pos)++; } return -1; } static int skip_line_comment(char *buf, long int *pos) { while (buf[*pos] && ((buf[*pos]) != '\n')) (*pos)++; return skip_spaces(buf, pos); } static int is_cspace(char *buf, long int *pos) { if (isspace(buf[*pos])) return 1; if (buf[*pos] == '/') { if ((buf[(*pos) + 1] == '*') || (buf[(*pos) + 1] == '/')) return 1; } return 0; } static int skip_spaces(char *buf, long int *pos) { while (buf[*pos] && isspace(buf[*pos])) (*pos)++; if (buf[*pos] == '/') { (*pos)++; if (buf[*pos] == '*') { (*pos)++; return skip_multy_comment(buf, pos); } else if (buf[*pos] == '/') { (*pos)++; return skip_line_comment(buf, pos); } else return -1; } return 0; } static int is_symbol(char sym) { static char syms[] = { '=', '{', '}', ';', '\'', '"', ',', '/', '\\' }; unsigned int i; for (i = 0; i < sizeof(syms) / sizeof(char); i++) { if (sym == syms[i]) return 1; } return 0; } static char* read_string(char* buf, long int *pos) { int allocated=50, len=1; char *b; b = (char*)malloc(allocated); if (! b) return NULL; b[0] = buf[*pos]; (*pos)++; while (buf[*pos]) { if (len + 3 > allocated) { char *s = (char*)realloc(b, allocated+=20); if (! s) { free(b); return NULL; } b = s; } if ((buf[*pos] == '\\') && (buf[*pos + 1] == b[0])) { b[len++] = '\\'; b[len] = b[0]; } else { b[len++] = buf[*pos]; if (buf[*pos] == b[0]) { b[len] = 0; (*pos)++; return b; } } (*pos)++; } free(b); return NULL; } static char* read_word(char* buf, long int *pos) { int allocated=50, len=0; char *b; b = (char*)malloc(allocated); if (! b) return NULL; while (buf[*pos] && (! is_cspace(buf, pos)) && (! is_symbol(buf[*pos]))) { if (len + 3 > allocated) { char *s = (char*)realloc(b, allocated+=20); if (! s) { free(b); return NULL; } b = s; } b[len++] = buf[*pos]; (*pos)++; } if (! len) { free(b); return NULL; } b[len] = 0; return b; } static char* read_token(char *buf, long int *pos) { if (skip_spaces(buf, pos) || (! buf[*pos])) return NULL; if ((buf[*pos] == '\'') || (buf[*pos] == '"')) return read_string(buf, pos); if (is_symbol(buf[*pos])) { char *b = (char*)malloc(2); if (! b) return NULL; b[0] = buf[*pos]; b[1] = 0; (*pos)++; return b; } return read_word(buf, pos); } static long int get_file_size(FILE *f) { long int len; if (fseek(f, 0L, SEEK_END)) return -1; len = ftell(f); if (fseek(f, 0L, SEEK_SET)) return -1; return len; } static char* read_file(const char *filename, long int *length) { FILE *f; char *buf; long int len; f = fopen(filename, "rb"); if (! f) return NULL; len = get_file_size(f); if (len <= 0) { fclose(f); return NULL; } buf = (char*)malloc(len + 1); if (! buf) { fclose(f); return NULL; } if (fread(buf, 1, len, f) != (unsigned)len) { free(buf); fclose(f); return NULL; } buf[len] = 0; fclose(f); *length = len + 1; return buf; } static int get_next_index(HTable table) { SField *f; int v, max_v=-1; char *endptr; for (f = table->fields; f; f = f->next) { v = strtol(f->name, &endptr, 10); if ((! f->name[0]) || (endptr[0])) continue; if (v > max_v) max_v = v; } return max_v + 1; } static int is_ident(char *str) { char *p; if (! str) return 0; if (strlen(str) < 1) return 0; if (! isalpha(str[0])) return 0; for (p = str; *p; p++) if (! (isalnum(*p) || (*p == '_') || (*p == '.'))) return 0; return 1; } static int is_string_value(char *value) { int len; if ((value[0] == '\'') || (value[0] == '"')) { if ((len = strlen(value)) >= 2) { if (value[len - 1] == value[0]) return 1; } } return 0; } static int is_int_value(char *value) { char *p; for (p = value; *p; p++) { if (! isdigit(*p)) { if (p != value) return 0; else if (! ((*p == '+') || (*p == '-'))) return 0; } } return 1; } static int is_float_value(char *value) { char *p; int pcount = 0; for (p = value; *p; p++) { if (! isdigit(*p)) { if (p != value) { if (*p != '.') return 0; else { pcount++; if (pcount > 1) return 0; } } else { if (! ((*p == '+') || (*p == '-'))) return 0; } } } return 1; } static int get_value_type(char *value) { if ((! value) || (! value[0])) return -1; if (is_string_value(value)) return TYPE_STRING; if (! strcmp(value, "{")) return TYPE_TABLE; if (is_int_value(value)) return TYPE_INT; if (is_float_value(value)) return TYPE_FLOAT; return -1; } static char* unescape_string(char *str) { char *buf, quote, *p; int allocated=50, len=0; buf = (char*)malloc(allocated); quote = str[0]; p = str + 1; while ((*p) && (*p != quote)) { if (len + 10 > allocated) { char *s = (char*)realloc(buf, allocated+=20); if (! s) { free(buf); return NULL; } buf = s; } if (*p == '\\') { p++; switch (*p) { case 'n': buf[len++] = '\n'; break; case 't': buf[len++] = '\t'; break; case 'r': buf[len++] = '\r'; break; default: buf[len++] = *p; } } else buf[len++] = *p; p++; } buf[len] = 0; if ((*p == quote) && (*(p + 1))) { free(buf); return NULL; } return buf; } static int parse_table(HTable table, char *buf, long int *pos, int top_level); static int add_field(HTable table, char *name, char *value, char *buf, long int *pos) { int value_type; char *s; HTable tbl; value_type = get_value_type(value); switch (value_type) { case TYPE_INT: table_set_int(table, name, atoi(value)); break; case TYPE_STRING: s = unescape_string(value); if (! s) return -1; table_set_str(table, name, s); free(s); break; case TYPE_FLOAT: table_set_double(table, name, atof(value)); break; case TYPE_TABLE: tbl = table_create(); if (table_set_table(table, name, tbl)) { table_free(tbl); return -1; } if (parse_table(tbl, buf, pos, 0)) return -1; break; default: return -1; } return 0; } static int add_field_to_array(HTable table, char *token, char *buf, long int *pos) { int idx = get_next_index(table); char b[100]; sprintf(b, "%i", idx); return add_field(table, b, token, buf, pos); } /* * TODO: add field as in array highly unoptimized. it scans for max index * number on every element addition, converting idexes from strings * to numbers where it possible. optimizations method: process * indexes in batch */ static int parse_table(HTable table, char *buf, long int *pos, int top_level) { char *token=NULL; char *name; int res; while (1) { if (! token) token = read_token(buf, pos); if (! token) { if (top_level) return 0; else return -1; } if (! strcmp(token, "{")) { if (add_field_to_array(table, token, buf, pos)) { free(token); return -1; } free(token); token = read_token(buf, pos); if (! token) { if (top_level) return 0; else return -1; } if ((! strcmp(token, ";")) || (! strcmp(token, ","))) { free(token); token = NULL; } continue; } else if (! strcmp(token, "}")) { free(token); if (top_level) return -1; else return 0; } name = token; token = read_token(buf, pos); if (token && (! strcmp(token, "="))) { free(token); if (! is_ident(name)) { free(name); return -1; } token = read_token(buf, pos); if (! token) { free(name); return -1; } res = add_field(table, name, token, buf, pos); free(name); free(token); if (res) { return -1; } token = read_token(buf, pos); if (! token) { if (top_level) return 0; else return -1; } if ((! strcmp(token, ";")) || (! strcmp(token, ","))) { free(token); token = NULL; } else if (strcmp(token, "}")) { free(token); return -1; } } else { if ((! token) && (! top_level)) { free(name); return -1; } if (! (token && ((! strcmp(token, ";")) || (! strcmp(token, ",")) || (! strcmp(token, "}"))))) { free(name); if (token) free(token); return -1; } res = add_field_to_array(table, name, buf, pos); free(name); if (res) { free(token); return -1; } if ((! token) || (! strcmp(token, "}"))) { if (token) free(token); return 0; } if (token && ((! strcmp(token, ",")) || ((! strcmp(token, ";"))))) { free(token); token = NULL; } } } return 0; } HTable table_read(const char *filename) { long int len, pos; char *buf; HTable table; int res; buf = read_file(filename, &len); if (! buf) return NULL; pos = 0; table = table_create(); res = parse_table(table, buf, &pos, 1); if (res) { table_free(table); table = NULL; } free(buf); return table; } void table_free(HTable table) { SField *f, *nf; if (! table) return; f = table->fields; while (f) { nf = f->next; free_field(f); f = nf; } if (table->stat_buf) free(table->stat_buf); free(table); } HTableIterator table_get_iter(HTable table) { HTableIterator it; it = (HTableIterator)calloc(sizeof(STableIterator), 1); if (! it) return NULL; it->table = table; it->before_start = 1; return it; } void table_free_iter(HTableIterator iterator) { if (! iterator) return; if (iterator->stat_buf) free(iterator->stat_buf); free(iterator); } int table_iter_next(HTableIterator iterator) { if (! iterator) return 0; if (iterator->before_start) { iterator->before_start = 0; iterator->cur_field = iterator->table->fields; } else { if (iterator->cur_field) iterator->cur_field = iterator->cur_field->next; } if (! iterator->cur_field) return 0; return 1; } char* table_iter_get_name(HTableIterator iterator) { if ((! iterator) || (! iterator->cur_field)) return NULL; return iterator->cur_field->name; } int table_iter_get_type(HTableIterator iterator) { if ((! iterator) || (! iterator->cur_field)) return -1; return iterator->cur_field->type; } static char* encode_string(char *str) { char *buf, *s; int allocated, len; if (! str) return strdup("''"); allocated = 20; buf = (char*)malloc(allocated); if (! buf) return NULL; buf[0] = '\''; len = 1; for (s = str; *s; s++) { if (len + 2 < allocated - 2) { char *m = (char*)realloc(buf, allocated += 20); if (! m) { free(buf); return NULL; } buf = m; } switch (*s) { case '\n': buf[len++] = '\\'; buf[len++] = 'n'; break; case '\r': buf[len++] = '\\'; buf[len++] = 'r'; break; case '\'': buf[len++] = '\\'; buf[len++] = '\''; break; case '\\': buf[len++] = '\\'; buf[len++] = '\\'; break; default: buf[len++] = *s; } } buf[len++] = '\''; buf[len] = 0; return buf; } static char* print_field(SField *field, int butify, int spaces) { char buf[100]; if (! field) return NULL; switch (field->type) { case TYPE_STRING: return encode_string(field->value.str_value); case TYPE_INT: sprintf(buf, "%i", field->value.int_value); return strdup(buf); case TYPE_FLOAT: sprintf(buf, "%g", field->value.double_value); if (! strchr(buf, '.')) strcat(buf, ".0"); return strdup(buf); case TYPE_TABLE: return table_to_str(field->value.table_value, 1, butify, spaces); } return NULL; } static int isSimpleArray(HTable table) { SField *f; char buf[50]; int i; for (f = table->fields, i = 0; f; f = f->next, i++) { sprintf(buf, "%i", i); if (! (f && f->name && (! strcmp(f->name, buf)))) return 0; } return 1; } #define APPEND_BUF(buf, s) { int l = strlen(s); \ if (len + l > allocated - 5) { \ char *nb; \ allocated += l + 50; \ nb = (char*)realloc(buf, allocated); \ if (! nb) { \ free(buf); \ return NULL; \ } \ buf = nb; \ } \ strcat(buf, s); \ len += l; \ } #define APPEND_SPACES(buf, n) \ for (int i = 0; i < n; i++) { APPEND_BUF(buf, " "); } char* table_to_str(HTable table, int print_braces, int butify, int spaces) { char *b, *fs; int len=0, allocated; SField *f; if (! table) return NULL; allocated = 100; b = (char*)malloc(allocated); if (! b) return NULL; if (print_braces) { if (butify) strcpy(b, "{\n"); else strcpy(b, "{ "); } else strcpy(b, ""); len = strlen(b); int printNames = ! isSimpleArray(table); for (f = table->fields; f; f = f->next) { if (butify) { APPEND_SPACES(b, spaces); } else if (f != table->fields) { APPEND_BUF(b, " "); } if (printNames) { APPEND_BUF(b, f->name); APPEND_BUF(b, " = "); } fs = print_field(f, butify, spaces + 4); APPEND_BUF(b, fs); free(fs); if (butify) { APPEND_BUF(b, ";\n"); } else { APPEND_BUF(b, ";"); } } if (print_braces) { if (! butify) { APPEND_BUF(b, " }"); } else { APPEND_SPACES(b, spaces); APPEND_BUF(b, "}"); } } return b; } static char* get_field_str(SField *field, int *err) { char buf[100]; if (err) *err = 0; if (! field) { if (err) *err = 1; return NULL; } switch (field->type) { case TYPE_STRING: return strdup(field->value.str_value); case TYPE_INT: sprintf(buf, "%i", field->value.int_value); return strdup(buf); case TYPE_FLOAT: sprintf(buf, "%f", field->value.double_value); return strdup(buf); case TYPE_TABLE: return table_to_str(field->value.table_value, 1, 0, 0);; } if (err) *err = 1; return NULL; } char* table_iter_get_str(HTableIterator iterator, int *err) { if ((! iterator) || (! iterator->cur_field)) { if (err) *err = 1; return NULL; } return get_field_str(iterator->cur_field, err); } char* table_iter_get_strs(HTableIterator iterator, int *err) { if ((! iterator) || (! iterator->cur_field)) { if (err) *err = 1; return NULL; } if (iterator->stat_buf) free(iterator->stat_buf); iterator->stat_buf = get_field_str(iterator->cur_field, err); return iterator->stat_buf; } static int get_field_int(SField *field, int *err) { char *endptr; int n; if (err) *err = 0; if (! field) { if (err) *err = 1; return 0; } switch (field->type) { case TYPE_STRING: n = strtol(field->value.str_value, &endptr, 10); if ((! field->value.str_value[0]) || (endptr[0])) { if (err) *err = 1; return 0; } return n; case TYPE_INT: return field->value.int_value; case TYPE_FLOAT: return (int)field->value.double_value; } if (err) *err = 1; return 0; } int table_iter_get_int(HTableIterator iterator, int *err) { if ((! iterator) || (! iterator->cur_field)) { if (err) *err = 1; return 0; } return get_field_int(iterator->cur_field, err); } static double get_field_double(SField *field, int *err) { char *endptr; double n; if (err) *err = 0; if (! field) { if (err) *err = 1; return 0.0; } switch (field->type) { case TYPE_STRING: n = strtod(field->value.str_value, &endptr); if ((! field->value.str_value[0]) || (endptr[0])) { if (err) *err = 1; return 0.0; } return n; case TYPE_INT: return field->value.int_value; case TYPE_FLOAT: return field->value.double_value; } if (err) *err = 1; return 0.0; } double table_iter_get_double(HTableIterator iterator, int *err) { if ((! iterator) || (! iterator->cur_field)) { if (err) *err = 1; return 0.0; } return get_field_double(iterator->cur_field, err); } HTable table_iter_get_table(HTableIterator iterator, int *err) { if ((! iterator) || (! iterator->cur_field)) { if (err) *err = 1; return NULL; } if (iterator->cur_field->type != TYPE_TABLE) { if (err) *err = 1; return NULL; } if (err) *err = 0; return iterator->cur_field->value.table_value; } int table_is_field_exists(HTable table, char *name) { SField *f; f = find_field(table, name); if (f) return 1; else return 0; } int table_get_field_type(HTable table, char *field) { SField *f; f = find_field(table, field); if (! f) return -1; return f->type; } char* table_get_str(HTable table, char *field, char *dflt, int *err) { SField *f; char *s; int e; f = find_field(table, field); if (! f) { if (err) *err = 1; if (dflt) return strdup(dflt); else return NULL; } s = get_field_str(f, &e); if (err) *err = e; if (e) { if (dflt) return strdup(dflt); else return NULL; } else { if (s) return strdup(s); else return NULL; } } char* table_get_strs(HTable table, char *field, char *dflt, int *err) { SField *f; char *s; int e; if (! table) { if (err) *err = 1; return dflt; } if (table->stat_buf) { free(table->stat_buf); table->stat_buf = NULL; } f = find_field(table, field); if (! f) { if (err) *err = 1; return dflt; } s = get_field_str(f, &e); table->stat_buf = s; if (err) *err = e; if (e) s = dflt; return s; } int table_get_int(HTable table, char *field, int dflt, int *err) { SField *f; int v, e; f = find_field(table, field); if (! f) { if (err) *err = 1; return dflt; } v = get_field_int(f, &e); if (err) *err = e; if (e) v = dflt; return v; } double table_get_double(HTable table, char *field, double dflt, int *err) { SField *f; double v; int e; f = find_field(table, field); if (! f) { if (err) *err = 1; return dflt; } v = get_field_double(f, &e); if (err) *err = e; if (e) v = dflt; return v; } HTable table_get_table(HTable table, char *field, HTable dflt, int *err) { SField *f; HTable v; f = find_field(table, field); if ((! f) || (f->type != TYPE_TABLE)) { if (err) *err = 1; return dflt; } v = f->value.table_value; if (err) *err = 0; return v; } int table_append_str(HTable table, const char *val) { int idx; char b[100]; if ((! table) || (! val)) return -1; idx = get_next_index(table); sprintf(b, "%i", idx); table_set_str(table, b, val); return 0; } int table_append_int(HTable table, int val) { int idx; char b[100]; if ((! table) || (! val)) return -1; idx = get_next_index(table); sprintf(b, "%i", idx); table_set_int(table, b, val); return 0; } int table_append_double(HTable table, double val) { int idx; char b[100]; if ((! table) || (! val)) return -1; idx = get_next_index(table); sprintf(b, "%i", idx); table_set_double(table, b, val); return 0; } int table_append_table(HTable table, HTable val) { int idx; char b[100]; if ((! table) || (! val)) return -1; idx = get_next_index(table); sprintf(b, "%i", idx); table_set_table(table, b, val); return 0; }