# HG changeset patch # User John Tsiombikas # Date 1397195806 -10800 # Node ID 740fec9866b1fb60d1b09501104426373d0a7fbe treestore initial commit diff -r 000000000000 -r 740fec9866b1 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Fri Apr 11 08:56:46 2014 +0300 @@ -0,0 +1,5 @@ +\.o$ +\.d$ +\.swp$ +\.so\. +\.a$ diff -r 000000000000 -r 740fec9866b1 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Fri Apr 11 08:56:46 2014 +0300 @@ -0,0 +1,89 @@ +PREFIX = /usr/local + +csrc = $(wildcard src/*.c) +ccsrc = $(wildcard src/*.cc) +cobj = $(csrc:.c=.o) +ccobj = $(ccsrc:.cc=.o) +obj = $(cobj) $(ccobj) +dep = $(obj:.o=.d) + +cname = treestore +ccname = treestorepp + +capi_major = 0 +capi_minor = 1 +ccapi_major = 0 +ccapi_minor = 1 + +clib_a = lib$(cname).a +cclib_a = lib$(ccname).a + +ifeq ($(shell uname -s), Darwin) + clib_so = lib$(cname).dylib + cclib_so = lib$(ccname).dylib + cshared = -dynamiclib + ccshared = -dynamiclib +else + clib_so = lib$(cname).so.$(capi_major).$(capi_minor) + csoname = lib$(cname).so.$(capi_major) + cdevlink = lib$(cname).so + cclib_so = lib$(ccname).so.$(ccapi_major).$(ccapi_minor) + ccsoname = lib$(ccname).so.$(ccapi_major) + ccdevlink = lib$(ccname).so + + cshared = -shared -Wl,-soname=$(csoname) + ccshared = -shared -Wl,-soname=$(ccsoname) + pic = -fPIC +endif + +dbg = -g +cxx11 = -std=c++11 -DTS_USE_CPP11 + +CFLAGS = -pedantic -Wall $(dbg) $(opt) $(pic) +CXXFLAGS = $(cxx11) $(CFLAGS) + +.PHONY: all +all: $(clib_so) $(clib_a) $(cclib_so) $(cclib_a) + +$(clib_a): $(cobj) + $(AR) rcs $@ $(cobj) + +$(clib_so): $(cobj) + $(CC) -o $@ $(cshared) $(cobj) $(LDFLAGS) + +$(cclib_a): $(ccobj) + $(AR) rcs $@ $(ccobj) + +$(cclib_so): $(ccobj) + $(CXX) -o $@ $(ccshared) $(ccobj) $(LDFLAGS) + +-include $(dep) + +%.d: %.c + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +%.d: %.cc + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +.PHONY: clean +clean: + rm -f $(obj) $(clib_so) $(cclib_so) $(clib_a) $(cclib_a) + +.PHONY: cleandep +cleandep: clean + rm -f $(dep) + + +.PHONY: install +install: all + mkdir -p $(DESTDIR)$(PREFIX)/include $(DESTDIR)$(PREFIX)/lib + cp src/treestore.h src/treestorepp.h $(DESTDIR)$(PREFIX)/include + cp $(clib_a) $(clib_so) $(cclib_a) $(cclib_so) $(DESTDIR)$(PREFIX)/lib + [ -n "$(csoname)" ] && \ + cd $(DESTDIR)$(PREFIX) && \ + rm -f $(csoname) $(cdevlink) $(ccsoname) $(ccdevlink) && \ + ln -s $(clib_so) $(csoname) && \ + ln -s $(cclib_so) $(ccsoname) && \ + ln -s $(csoname) $(cdevlink) && \ + ln -s $(ccsoname) $(ccdevlink) || \ + true diff -r 000000000000 -r 740fec9866b1 src/treestore.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treestore.c Fri Apr 11 08:56:46 2014 +0300 @@ -0,0 +1,245 @@ +#include +#include +#include +#include "treestore.h" + +/* ---- ts_value implementation ---- */ + +int ts_init_value(struct ts_value *tsv) +{ + memset(tsv, 0, sizeof *tsv); + return 0; +} + +void ts_destroy_value(struct ts_value *tsv) +{ + int i; + + free(tsv->str); + free(tsv->vec); + + for(i=0; iarray_size; i++) { + ts_destroy_value(tsv->array + i); + } +} + + +struct ts_value *ts_alloc_value(void) +{ + struct ts_value *v = malloc(sizeof *v); + if(!v || ts_init_value(v) == -1) { + free(v); + return 0; + } + return v; +} + +void ts_free_value(struct ts_value *tsv) +{ + ts_destroy_value(tsv); + free(tsv); +} + + +int ts_copy_value(struct ts_value *dest, struct ts_value *src) +{ + int i; + + if(dest == src) return 0; + + *dest = *src; + + dest->str = 0; + dest->vec = 0; + dest->array = 0; + + if(src->str) { + if(!(dest->str = malloc(strlen(src->str) + 1))) { + goto fail; + } + strcpy(dest->str, src->str); + } + if(src->vec && src->vec_size > 0) { + if(!(dest->vec = malloc(src->vec_size * sizeof *src->vec))) { + goto fail; + } + memcpy(dest->vec, src->vec, src->vec_size * sizeof *src->vec); + } + if(src->array && src->array_size > 0) { + if(!(dest->array = calloc(src->array_size, sizeof *src->array))) { + goto fail; + } + for(i=0; iarray_size; i++) { + if(ts_copy_value(dest->array + i, src->array + i) == -1) { + goto fail; + } + } + } + return 0; + +fail: + free(dest->str); + free(dest->vec); + if(dest->array) { + for(i=0; iarray_size; i++) { + ts_destroy_value(dest->array + i); + } + free(dest->array); + } + return -1; +} + + +int ts_set_value(struct ts_value *tsv, const char *str) +{ + if(tsv->str) { + ts_destroy_value(tsv); + if(ts_init_value(tsv) == -1) { + return -1; + } + } + + if(!(tsv->str = malloc(strlen(str) + 1))) { + return -1; + } + strcpy(tsv->str, str); + return 0; +} + +int ts_set_valueiv(struct ts_value *tsv, int count, ...) +{ + int res; + va_list ap; + va_start(ap, count); + res = ts_set_valueiv_va(tsv, count, ap); + va_end(ap); + return res; +} + +#define MAKE_NUMSTR_FUNC(typestr, fmt) \ + static char *make_##typestr##str(int x) \ + { \ + static char scrap[128]; \ + char *str; \ + int sz = snprintf(scrap, sizeof scrap, fmt, x); \ + if(!(str = malloc(sz + 1))) return 0; \ + sprintf(str, fmt, x); \ + return str; \ + } + +MAKE_NUMSTR_FUNC(int, "%d") +MAKE_NUMSTR_FUNC(float, "%d") + +#define ARGS_ARE_INT ((enum ts_value_type)42) + +int ts_set_valueiv_va(struct ts_value *tsv, int count, va_list ap) +{ + if(count < 1) return -1; + if(count == 1) { + int num = va_arg(ap, int); + if(!(tsv->str = make_intstr(tsv->inum))) { + return -1; + } + + tsv->type = TS_NUMBER; + tsv->inum = num; + tsv->fnum = (float)num; + return 0; + } + + /* otherwise it's an array, let ts_set_valuefv_va handle it */ + /* XXX: va_arg will need to be called with int instead of float. set a special + * value to the type field before calling this, to signify that. + */ + tsv->type = ARGS_ARE_INT; + return ts_set_valuefv_va(tsv, count, ap); +} + +int ts_set_valuei(struct ts_value *tsv, int inum) +{ + return ts_set_valueiv(tsv, 1, inum); +} + + +int ts_set_valuefv(struct ts_value *tsv, int count, ...) +{ + int res; + va_list ap; + va_start(ap, count); + res = ts_set_valuefv_va(tsv, count, ap); + va_end(ap); + return res; +} + +int ts_set_valuefv_va(struct ts_value *tsv, int count, va_list ap) +{ + int i; + + if(count < 1) return -1; + if(count == 1) { + int num = va_arg(ap, int); + if(!(tsv->str = make_floatstr(tsv->inum))) { + return -1; + } + + tsv->type = TS_NUMBER; + tsv->inum = num; + tsv->fnum = (float)num; + return 0; + } + + /* otherwise it's an array, we need to create the ts_value array, and + * the simplified vector + */ + if(!(tsv->vec = malloc(count * sizeof *tsv->vec))) { + return -1; + } + tsv->vec_size = count; + + for(i=0; itype == ARGS_ARE_INT) { /* only when called by ts_set_valueiv_va */ + tsv->vec[i] = (float)va_arg(ap, int); + } else { + tsv->vec[i] = va_arg(ap, double); + } + } + + if(!(tsv->array = malloc(count * sizeof *tsv->array))) { + free(tsv->vec); + } + tsv->array_size = count; + + for(i=0; iarray + i); + if(tsv->type == ARGS_ARE_INT) { /* only when called by ts_set_valueiv_va */ + ts_set_valuei(tsv->array + i, (int)tsv->vec[i]); + } else { + ts_set_valuef(tsv->array + i, tsv->vec[i]); + } + } + + tsv->type = TS_VECTOR; + return 0; +} + +int ts_set_valuef(struct ts_value *tsv, int fnum) +{ + return ts_set_valuefv(tsv, 1, fnum); +} + + +int ts_set_valuev(struct ts_value *tsv, int count, ...) +{ + int res; + va_list ap; + va_start(ap, count); + res = ts_set_valuev_va(tsv, count, ap); + va_end(ap); + return res; +} + +int ts_set_valuev_va(struct ts_value *tsv, int count, va_list ap) +{ + if(count <= 1) return -1; + return -1; /* TODO */ +} diff -r 000000000000 -r 740fec9866b1 src/treestore.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treestore.h Fri Apr 11 08:56:46 2014 +0300 @@ -0,0 +1,88 @@ +#ifndef TREESTORE_H_ +#define TREESTORE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum ts_value_type { TS_UNKNOWN, TS_NUMBER, TS_VECTOR, TS_ARRAY }; + +/** treestore node attribute value */ +struct ts_value { + enum ts_value_type type; + + char *str; /**< all values have a string representation */ + int inum; /**< numeric values (TS_INT/TS_FLOAT) will have this set */ + float fnum; /**< numeric values (TS_INT/TS_FLOAT) will have this set */ + + /** vector values (arrays containing ONLY numbers) will have this set */ + float *vec; /**< elements of the vector */ + int vec_size; /**< size of the vector (in elements), same as array_size */ + + /** array values (including vectors) will have this set */ + struct ts_value *array; /**< elements of the array */ + int array_size; /**< size of the array (in elements) */ +}; + +int ts_init_value(struct ts_value *tsv); +void ts_destroy_value(struct ts_value *tsv); + +struct ts_value *ts_alloc_value(void); /**< also calls ts_init_value */ +void ts_free_value(struct ts_value *tsv); /**< also calls ts_destroy_value */ + +/** perform a deep-copy of a ts_value */ +int ts_copy_value(struct ts_value *dest, struct ts_value *src); + +/** ts_set_value will try to parse the string and initialize the value type fields */ +int ts_set_value(struct ts_value *tsv, const char *str); + +/** set a ts_value from a list of integers */ +int ts_set_valueiv(struct ts_value *tsv, int count, ...); +int ts_set_valueiv_va(struct ts_value *tsv, int count, va_list ap); +int ts_set_valuei(struct ts_value *tsv, int inum); /**< equiv: ts_set_valueiv(val, 1, inum) */ + +/** set a ts_value from a list of floats */ +int ts_set_valuefv(struct ts_value *tsv, int count, ...); +int ts_set_valuefv_va(struct ts_value *tsv, int count, va_list ap); +int ts_set_valuef(struct ts_value *tsv, int fnum); /**< equiv: ts_set_valuefv(val, 1, fnum) */ + +/** set a ts_value from a list of ts_value pointers. they are deep-copied as per ts_copy_value */ +int ts_set_valuev(struct ts_value *tsv, int count, ...); +int ts_set_valuev_va(struct ts_value *tsv, int count, va_list ap); + + +/** treestore node attribute */ +struct ts_attr { + char *name; + + struct ts_value val; + + struct ts_attr *next; +}; + + +/** treestore node */ +struct ts_node { + char *name; + + int attr_count; + struct ts_attr *attr_list, *attr_tail; + + int child_count; + struct ts_node *child_list, *child_tail; + struct ts_node *parent; + + struct ts_node *next; /* next sibling */ +}; + +struct ts_node *ts_create_node(void); +void ts_free_node(struct ts_node *n); +void ts_free_tree(struct ts_node *tree); + +#ifdef __cplusplus +} +#endif + +#endif /* TREESTORE_H_ */ diff -r 000000000000 -r 740fec9866b1 src/treestorepp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treestorepp.h Fri Apr 11 08:56:46 2014 +0300 @@ -0,0 +1,44 @@ +#ifndef TREESTOREPP_H_ +#define TREESTOREPP_H_ + +#include "treestore.h" + +/// wraps a C ts_value in a convenient class +class TSValue { +private: + ts_value *ctsv; + +public: + TSValue(); + ~TSValue(); + + TSValue(const TSValue &tsv); + TSValue &operator =(const TSValue &tsv); + +#ifdef TS_USE_CPP11 + TSValue(const TSValue &&tsv); + TSValue &operator =(const TSValue &&tsv); +#endif + + bool set(const char *str); + bool set_int(int inum); + bool set_int(int count, ...); + bool set_float(float fnum); + bool set_float(int count, ...); + bool set_array(int count, const TSValue &v0, ...); + + const char *get() const; + + int get_int() const; + int *get_intv() const; + + float get_float() const; + float *get_floatv() const; + + const TSValue *get_array() const; + int get_array_size() const; + + int get_vec_size() const; //< equiv: get_array_size */ +}; + +#endif // TREESTOREPP_H_ diff -r 000000000000 -r 740fec9866b1 src/xmltree.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xmltree.c Fri Apr 11 08:56:46 2014 +0300 @@ -0,0 +1,448 @@ +/* +This file is part of the s-ray renderer . +Copyright (C) 2009 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +/* TODO list + * - fix cdata handling. make a nodes's cdata into a number of ordered children + */ +#include +#include +#include +#include +#include +#include +#include "xmltree.h" + +#ifdef _MSC_VER +typedef int ssize_t; +#endif + +struct parse_data { + XML_Parser parser; + struct xml_node *cur_node; + + char *buf; + int buf_size; +}; + +static void start(void *udata, const char *name, const char **atts); +static void end(void *udata, const char *name); +static void cdata(void *udata, const char *text, int len); +static void finish_cdata(struct parse_data *pdata); +static void abort_parsing(XML_Parser p); +static int write_rec(struct xml_node *x, FILE *fp, int lvl); + +#define IND_SPACES 4 +static void indent(FILE *fp, int lvl); + +struct xml_attr *xml_create_attr(const char *name, const char *val) +{ + struct xml_attr *attr; + + if(!(attr = malloc(sizeof *attr))) { + return 0; + } + memset(attr, 0, sizeof *attr); + + if(name) { + if(!(attr->name = malloc(strlen(name) + 1))) { + free(attr); + return 0; + } + strcpy(attr->name, name); + } + + if(val) { + if(!(attr->str = malloc(strlen(val) + 1))) { + free(attr->name); + free(attr); + return 0; + } + strcpy(attr->str, val); + + if(isdigit(val[0]) || ((val[0] == '+' || val[0] == '-') && isdigit(val[1]))) { + int i; + attr->type = strchr(val, '.') ? ATYPE_FLT : ATYPE_INT; + attr->ival = atoi(val); + attr->fval = atof(val); + + attr->vval[0] = attr->vval[1] = attr->vval[2] = attr->vval[3] = 1.0; + + for(i=0; i<4; i++) { + if(!*val) break; + attr->vval[i] = atof(val); + + while(*val && !isspace(*val)) val++; + while(*val && isspace(*val)) val++; + } + + if(i > 1) { + attr->type = ATYPE_VEC; + } + } + } + + return attr; +} + +void xml_free_attr(struct xml_attr *attr) +{ + free(attr->name); + free(attr->str); + free(attr); +} + +void xml_free_attr_list(struct xml_attr *alist) +{ + while(alist) { + struct xml_attr *tmp = alist; + alist = alist->next; + + xml_free_attr(tmp); + } +} + +struct xml_attr *xml_get_attr(struct xml_node *node, const char *attr_name) +{ + struct xml_attr *attr; + + attr = node->attr; + while(attr) { + if(strcmp(attr->name, attr_name) == 0) { + return attr; + } + attr = attr->next; + } + return 0; +} + +struct xml_node *xml_create_tree(void) +{ + struct xml_node *x; + + if(!(x = malloc(sizeof *x))) { + return 0; + } + memset(x, 0, sizeof *x); + + return x; +} + +void xml_free_tree(struct xml_node *x) +{ + while(x->chld) { + void *tmp = x->chld; + + x->chld = x->chld->next; + xml_free_tree(tmp); + } + + while(x->attr) { + struct xml_attr *tmp = x->attr; + x->attr = x->attr->next; + + free(tmp->name); + free(tmp->str); + free(tmp); + } + + free(x->cdata); + free(x->name); + free(x); +} + +struct xml_node *xml_read_tree(const char *fname) +{ + FILE *fp; + struct xml_node *xml; + + if(!(fp = fopen(fname, "rb"))) { + return 0; + } + xml = xml_read_tree_file(fp); + fclose(fp); + return xml; +} + +struct xml_node *xml_read_tree_file(FILE *fp) +{ + struct parse_data pdata; + struct xml_node node, *tree; + XML_Parser p; + ssize_t rdsz; + void *buf; + + memset(&node, 0, sizeof node); + + if(!(p = XML_ParserCreate(0))) { + return 0; + } + XML_SetElementHandler(p, start, end); + XML_SetCharacterDataHandler(p, cdata); + XML_SetUserData(p, &pdata); + + memset(&pdata, 0, sizeof pdata); + pdata.parser = p; + pdata.cur_node = &node; + + do { + if(!(buf = XML_GetBuffer(p, 4096))) { + break; + } + + if((rdsz = fread(buf, 1, 4096, fp)) == -1) { + break; + } + + if(!XML_ParseBuffer(p, rdsz, rdsz < 4096)) { + fprintf(stderr, "XML parsing error: %d: %s\n", (int)XML_GetCurrentLineNumber(p), + XML_ErrorString(XML_GetErrorCode(p))); + break; + } + } while(rdsz == 4096); + + tree = node.chld; + if(tree) { + assert(tree->next == 0); + } + + if(pdata.cur_node != &node) { /* aborted */ + xml_free_tree(tree); + tree = 0; + } + + if(pdata.buf) { + free(pdata.buf); + } + + XML_ParserFree(p); + return tree; +} + +static void start(void *udata, const char *name, const char **atts) +{ + struct xml_node *node = 0; + struct parse_data *pdata = udata; + + finish_cdata(pdata); + + if(!(node = malloc(sizeof *node))) { + goto err; + } + memset(node, 0, sizeof *node); + + if(!(node->name = malloc(strlen(name) + 1))) { + goto err; + } + strcpy(node->name, name); + + while(*atts) { + struct xml_attr *attr; + + if(!(attr = xml_create_attr(atts[0], atts[1]))) { + goto err; + } + attr->next = node->attr; + node->attr = attr; + + atts += 2; + } + + xml_add_child(pdata->cur_node, node); + pdata->cur_node = node; + return; + +err: + if(node) { + free(node->name); + xml_free_attr_list(node->attr); + } + free(node); + abort_parsing(pdata->parser); +} + +static void end(void *udata, const char *name) +{ + struct parse_data *pdata = udata; + + finish_cdata(pdata); + + pdata->cur_node = pdata->cur_node->up; +} + +static void cdata(void *udata, const char *text, int len) +{ + char *tmp; + struct parse_data *pdata = udata; + + if(!(tmp = realloc(pdata->buf, pdata->buf_size + len))) { + abort_parsing(pdata->parser); + return; + } + memcpy(tmp + pdata->buf_size, text, len); + pdata->buf = tmp; + pdata->buf_size += len; +} + +static void finish_cdata(struct parse_data *pdata) +{ + char *tmp; + + if(!pdata->buf) { + return; + } + + if(!(tmp = realloc(pdata->buf, pdata->buf_size + 1))) { + abort_parsing(pdata->parser); + return; + } + tmp[pdata->buf_size] = 0; + pdata->cur_node->cdata = tmp; + + pdata->buf = 0; + pdata->buf_size = 0; +} + +static void abort_parsing(XML_Parser p) +{ + perror("abort XML parsing"); + + XML_SetElementHandler(p, 0, 0); + XML_SetCharacterDataHandler(p, 0); + XML_StopParser(p, 0); +} + +int xml_write_tree(struct xml_node *x, const char *fname) +{ + FILE *fp; + int res; + + if(!(fp = fopen(fname, "wb"))) { + return -1; + } + res = xml_write_tree_file(x, fp); + fclose(fp); + return res; +} + +int xml_write_tree_file(struct xml_node *x, FILE *fp) +{ + return write_rec(x, fp, 0); +} + +static int write_rec(struct xml_node *x, FILE *fp, int lvl) +{ + struct xml_node *c; + struct xml_attr *attr; + + indent(fp, lvl); + fputc('<', fp); + fputs(x->name, fp); + + attr = x->attr; + while(attr) { + switch(attr->type) { + case ATYPE_INT: + fprintf(fp, " %s=\"%d\"", attr->name, attr->ival); + break; + + case ATYPE_FLT: + fprintf(fp, " %s=\"%.4f\"", attr->name, attr->fval); + break; + + case ATYPE_VEC: + fprintf(fp, " %s=\"%.4f %.4f %.4f %.4f\"", attr->name, attr->vval[0], + attr->vval[1], attr->vval[2], attr->vval[3]); + break; + + case ATYPE_STR: + default: + fprintf(fp, " %s=\"%s\"", attr->name, attr->str); + break; + } + + attr = attr->next; + } + + if(!x->chld && !x->cdata) { + fputc('/', fp); + } + fprintf(fp, ">\n"); + + if(x->cdata) { + indent(fp, lvl + 1); + fprintf(fp, "%s\n", x->cdata); + } + + c = x->chld; + while(c) { + write_rec(c, fp, lvl + 1); + c = c->next; + } + + if(x->chld || x->cdata) { + indent(fp, lvl); + fprintf(fp, "\n", x->name); + } + return 0; +} + +void xml_add_child(struct xml_node *x, struct xml_node *chld) +{ + chld->next = 0; + + if(x->chld) { + x->chld_tail->next = chld; + x->chld_tail = chld; + } else { + x->chld = x->chld_tail = chld; + } + + chld->up = x; + x->chld_count++; +} + +void xml_remove_subtree(struct xml_node *sub) +{ + struct xml_node *x; + + if(sub->up) { + x = sub->up->chld; + if(x == sub) { + sub->up->chld = sub->next; + } else { + while(x) { + if(x->next == sub) { + x->next = x->next->next; + break; + } + x = x->next; + } + } + } + + sub->up->chld_count--; + sub->up = 0; +} + +static void indent(FILE *fp, int lvl) +{ + int i, ind = lvl * IND_SPACES; + + for(i=0; i + +enum { + ATYPE_STR, + ATYPE_INT, + ATYPE_FLT, + ATYPE_VEC +}; + +struct xml_attr { + char *name; + char *str; + + int type; + int ival; + float fval; + float vval[4]; + + struct xml_attr *next; +}; + +struct xml_node { + char *name; + char *cdata; + struct xml_attr *attr; /* attribute list */ + + int chld_count; + + struct xml_node *up; /* parent pointer */ + struct xml_node *chld, *chld_tail; /* children list */ + struct xml_node *next; /* next sibling */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +struct xml_attr *xml_create_attr(const char *name, const char *val); +void xml_free_attr(struct xml_attr *attr); +void xml_free_attr_list(struct xml_attr *alist); + +struct xml_attr *xml_get_attr(struct xml_node *node, const char *attr_name); + +struct xml_node *xml_create_tree(void); +void xml_free_tree(struct xml_node *x); + +struct xml_node *xml_read_tree(const char *fname); +struct xml_node *xml_read_tree_file(FILE *fp); + +int xml_write_tree(struct xml_node *x, const char *fname); +int xml_write_tree_file(struct xml_node *x, FILE *fp); + +void xml_add_child(struct xml_node *x, struct xml_node *chld); +void xml_remove_subtree(struct xml_node *sub); + +#ifdef __cplusplus +} +#endif + +#endif /* COMMON_XMLTREE_H_ */