#include "util.h"
#include "expat/expat.h"
config_t config_new(void)
{
config_t c;
c = (config_t) malloc(sizeof(struct config_st));
memset(c, 0, sizeof(struct config_st));
c->hash = xhash_new(501);
c->nads = nad_cache_new();
return c;
}
struct build_data
{
nad_t nad;
int depth;
};
static void _config_startElement(void *arg, const char *name, const char **atts)
{
struct build_data *bd = (struct build_data *) arg;
int i = 0;
nad_append_elem(bd->nad, -1, (char *) name, bd->depth);
while(atts[i] != NULL)
{
nad_append_attr(bd->nad, -1, (char *) atts[i], (char *) atts[i + 1]);
i += 2;
}
bd->depth++;
}
static void _config_endElement(void *arg, const char *name)
{
struct build_data *bd = (struct build_data *) arg;
bd->depth--;
}
static void _config_charData(void *arg, const char *str, int len)
{
struct build_data *bd = (struct build_data *) arg;
nad_append_cdata(bd->nad, (char *) str, len, bd->depth);
}
int config_load(config_t c, char *file)
{
struct build_data bd;
FILE *f;
XML_Parser p;
int done, len, end, i, j, attr;
char buf[1024], *next;
struct nad_elem_st **path;
config_elem_t elem;
f = fopen(file, "r");
if(f == NULL)
{
fprintf(stderr, "config_load: couldn't open %s for reading: %s\n", file, strerror(errno));
return 1;
}
p = XML_ParserCreate(NULL);
if(p == NULL)
{
fprintf(stderr, "config_load: couldn't allocate XML parser\n");
fclose(f);
return 1;
}
bd.nad = nad_new(c->nads);
bd.depth = 0;
XML_SetUserData(p, (void *) &bd);
XML_SetElementHandler(p, _config_startElement, _config_endElement);
XML_SetCharacterDataHandler(p, _config_charData);
for(;;)
{
len = fread(buf, 1, 1024, f);
if(ferror(f))
{
fprintf(stderr, "config_load: read error: %s\n", strerror(errno));
XML_ParserFree(p);
fclose(f);
nad_free(bd.nad);
return 1;
}
done = feof(f);
if(!XML_Parse(p, buf, len, done))
{
fprintf(stderr, "config_load: parse error at line %d: %s\n", XML_GetCurrentLineNumber(p), XML_ErrorString(XML_GetErrorCode(p)));
XML_ParserFree(p);
fclose(f);
nad_free(bd.nad);
return 1;
}
if(done)
break;
}
XML_ParserFree(p);
fclose(f);
path = NULL;
len = 0, end = 0;
for(i = 1; i < bd.nad->ecur; i++)
{
if(end <= bd.nad->elems[i].depth)
{
end = bd.nad->elems[i].depth + 1;
path = (struct nad_elem_st **) realloc((void *) path, sizeof(struct nad_elem_st *) * end);
}
path[bd.nad->elems[i].depth] = &bd.nad->elems[i];
len = bd.nad->elems[i].depth + 1;
next = buf;
for(j = 1; j < len; j++)
{
strncpy(next, bd.nad->cdata + path[j]->iname, path[j]->lname);
next = next + path[j]->lname;
*next = '.';
next++;
}
next--;
*next = '\0';
elem = xhash_get(c->hash, buf);
if(elem == NULL)
{
elem = pmalloco(xhash_pool(c->hash), sizeof(struct config_elem_st));
xhash_put(c->hash, pstrdup(xhash_pool(c->hash), buf), elem);
}
elem->values = realloc((void *) elem->values, sizeof(char *) * (elem->nvalues + 1));
if(NAD_CDATA_L(bd.nad, i) > 0)
elem->values[elem->nvalues] = pstrdupx(xhash_pool(c->hash), NAD_CDATA(bd.nad, i), NAD_CDATA_L(bd.nad, i));
else
elem->values[elem->nvalues] = "1";
elem->attrs = realloc((void *) elem->attrs, sizeof(char **) * (elem->nvalues + 1));
elem->attrs[elem->nvalues] = NULL;
for(attr = bd.nad->elems[i].attr, j = 0; attr >= 0; attr = bd.nad->attrs[attr].next, j++);
elem->attrs[elem->nvalues] = pmalloc(xhash_pool(c->hash), sizeof(char *) * (j * 2 + 2));
if(j > 0)
{
j = 0;
attr = bd.nad->elems[i].attr;
while(attr >= 0)
{
elem->attrs[elem->nvalues][j] = pstrdupx(xhash_pool(c->hash), NAD_ANAME(bd.nad, attr), NAD_ANAME_L(bd.nad, attr));
elem->attrs[elem->nvalues][j + 1] = pstrdupx(xhash_pool(c->hash), NAD_AVAL(bd.nad, attr), NAD_AVAL_L(bd.nad, attr));
j += 2;
attr = bd.nad->attrs[attr].next;
}
}
elem->attrs[elem->nvalues][j] = NULL;
elem->attrs[elem->nvalues][j + 1] = NULL;
elem->nvalues++;
}
if(path != NULL)
free(path);
if(c->nad != NULL)
nad_free(c->nad);
c->nad = bd.nad;
return 0;
}
config_elem_t config_get(config_t c, char *key)
{
return xhash_get(c->hash, key);
}
char *config_get_one(config_t c, char *key, int num)
{
config_elem_t elem = xhash_get(c->hash, key);
if(elem == NULL)
return NULL;
if(num >= elem->nvalues)
return NULL;
return elem->values[num];
}
int config_count(config_t c, char *key)
{
config_elem_t elem = xhash_get(c->hash, key);
if(elem == NULL)
return 0;
return elem->nvalues;
}
char *config_get_attr(config_t c, char *key, int num, char *attr)
{
config_elem_t elem = xhash_get(c->hash, key);
if(num >= elem->nvalues || elem->attrs == NULL || elem->attrs[num] == NULL)
return NULL;
return j_attr((const char **) elem->attrs[num], attr);
}
static void _config_reaper(xht h, const char *key, void *val, void *arg)
{
config_elem_t elem = (config_elem_t) val;
free(elem->values);
free(elem->attrs);
}
void config_free(config_t c)
{
xhash_walk(c->hash, _config_reaper, NULL);
xhash_free(c->hash);
nad_free(c->nad);
nad_cache_free(c->nads);
free(c);
}