einstein/conf.cpp

1450 lines
27 KiB
C++

#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#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;
}