nuclear@0: /* nuclear@0: This file is part of the s-ray renderer . nuclear@0: Copyright (C) 2009 John Tsiombikas nuclear@0: nuclear@0: This program is free software: you can redistribute it and/or modify nuclear@0: it under the terms of the GNU General Public License as published by nuclear@0: the Free Software Foundation, either version 3 of the License, or nuclear@0: (at your option) any later version. nuclear@0: nuclear@0: This program is distributed in the hope that it will be useful, nuclear@0: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@0: GNU General Public License for more details. nuclear@0: nuclear@0: You should have received a copy of the GNU General Public License nuclear@0: along with this program. If not, see . nuclear@0: */ nuclear@0: /* TODO list nuclear@0: * - fix cdata handling. make a nodes's cdata into a number of ordered children nuclear@0: */ nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "xmltree.h" nuclear@0: nuclear@0: #ifdef _MSC_VER nuclear@0: typedef int ssize_t; nuclear@0: #endif nuclear@0: nuclear@0: struct parse_data { nuclear@0: XML_Parser parser; nuclear@0: struct xml_node *cur_node; nuclear@0: nuclear@0: char *buf; nuclear@0: int buf_size; nuclear@0: }; nuclear@0: nuclear@0: static void start(void *udata, const char *name, const char **atts); nuclear@0: static void end(void *udata, const char *name); nuclear@0: static void cdata(void *udata, const char *text, int len); nuclear@0: static void finish_cdata(struct parse_data *pdata); nuclear@0: static void abort_parsing(XML_Parser p); nuclear@0: static int write_rec(struct xml_node *x, FILE *fp, int lvl); nuclear@0: nuclear@0: #define IND_SPACES 4 nuclear@0: static void indent(FILE *fp, int lvl); nuclear@0: nuclear@0: struct xml_attr *xml_create_attr(const char *name, const char *val) nuclear@0: { nuclear@0: struct xml_attr *attr; nuclear@0: nuclear@0: if(!(attr = malloc(sizeof *attr))) { nuclear@0: return 0; nuclear@0: } nuclear@0: memset(attr, 0, sizeof *attr); nuclear@0: nuclear@0: if(name) { nuclear@0: if(!(attr->name = malloc(strlen(name) + 1))) { nuclear@0: free(attr); nuclear@0: return 0; nuclear@0: } nuclear@0: strcpy(attr->name, name); nuclear@0: } nuclear@0: nuclear@0: if(val) { nuclear@0: if(!(attr->str = malloc(strlen(val) + 1))) { nuclear@0: free(attr->name); nuclear@0: free(attr); nuclear@0: return 0; nuclear@0: } nuclear@0: strcpy(attr->str, val); nuclear@0: nuclear@0: if(isdigit(val[0]) || ((val[0] == '+' || val[0] == '-') && isdigit(val[1]))) { nuclear@0: int i; nuclear@0: attr->type = strchr(val, '.') ? ATYPE_FLT : ATYPE_INT; nuclear@0: attr->ival = atoi(val); nuclear@0: attr->fval = atof(val); nuclear@0: nuclear@0: attr->vval[0] = attr->vval[1] = attr->vval[2] = attr->vval[3] = 1.0; nuclear@0: nuclear@0: for(i=0; i<4; i++) { nuclear@0: if(!*val) break; nuclear@0: attr->vval[i] = atof(val); nuclear@0: nuclear@0: while(*val && !isspace(*val)) val++; nuclear@0: while(*val && isspace(*val)) val++; nuclear@0: } nuclear@0: nuclear@0: if(i > 1) { nuclear@0: attr->type = ATYPE_VEC; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return attr; nuclear@0: } nuclear@0: nuclear@0: void xml_free_attr(struct xml_attr *attr) nuclear@0: { nuclear@0: free(attr->name); nuclear@0: free(attr->str); nuclear@0: free(attr); nuclear@0: } nuclear@0: nuclear@0: void xml_free_attr_list(struct xml_attr *alist) nuclear@0: { nuclear@0: while(alist) { nuclear@0: struct xml_attr *tmp = alist; nuclear@0: alist = alist->next; nuclear@0: nuclear@0: xml_free_attr(tmp); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: struct xml_attr *xml_get_attr(struct xml_node *node, const char *attr_name) nuclear@0: { nuclear@0: struct xml_attr *attr; nuclear@0: nuclear@0: attr = node->attr; nuclear@0: while(attr) { nuclear@0: if(strcmp(attr->name, attr_name) == 0) { nuclear@0: return attr; nuclear@0: } nuclear@0: attr = attr->next; nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: struct xml_node *xml_create_tree(void) nuclear@0: { nuclear@0: struct xml_node *x; nuclear@0: nuclear@0: if(!(x = malloc(sizeof *x))) { nuclear@0: return 0; nuclear@0: } nuclear@0: memset(x, 0, sizeof *x); nuclear@0: nuclear@0: return x; nuclear@0: } nuclear@0: nuclear@0: void xml_free_tree(struct xml_node *x) nuclear@0: { nuclear@0: while(x->chld) { nuclear@0: void *tmp = x->chld; nuclear@0: nuclear@0: x->chld = x->chld->next; nuclear@0: xml_free_tree(tmp); nuclear@0: } nuclear@0: nuclear@0: while(x->attr) { nuclear@0: struct xml_attr *tmp = x->attr; nuclear@0: x->attr = x->attr->next; nuclear@0: nuclear@0: free(tmp->name); nuclear@0: free(tmp->str); nuclear@0: free(tmp); nuclear@0: } nuclear@0: nuclear@0: free(x->cdata); nuclear@0: free(x->name); nuclear@0: free(x); nuclear@0: } nuclear@0: nuclear@0: struct xml_node *xml_read_tree(const char *fname) nuclear@0: { nuclear@0: FILE *fp; nuclear@0: struct xml_node *xml; nuclear@0: nuclear@0: if(!(fp = fopen(fname, "rb"))) { nuclear@0: return 0; nuclear@0: } nuclear@0: xml = xml_read_tree_file(fp); nuclear@0: fclose(fp); nuclear@0: return xml; nuclear@0: } nuclear@0: nuclear@0: struct xml_node *xml_read_tree_file(FILE *fp) nuclear@0: { nuclear@0: struct parse_data pdata; nuclear@0: struct xml_node node, *tree; nuclear@0: XML_Parser p; nuclear@0: ssize_t rdsz; nuclear@0: void *buf; nuclear@0: nuclear@0: memset(&node, 0, sizeof node); nuclear@0: nuclear@0: if(!(p = XML_ParserCreate(0))) { nuclear@0: return 0; nuclear@0: } nuclear@0: XML_SetElementHandler(p, start, end); nuclear@0: XML_SetCharacterDataHandler(p, cdata); nuclear@0: XML_SetUserData(p, &pdata); nuclear@0: nuclear@0: memset(&pdata, 0, sizeof pdata); nuclear@0: pdata.parser = p; nuclear@0: pdata.cur_node = &node; nuclear@0: nuclear@0: do { nuclear@0: if(!(buf = XML_GetBuffer(p, 4096))) { nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: if((rdsz = fread(buf, 1, 4096, fp)) == -1) { nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: if(!XML_ParseBuffer(p, rdsz, rdsz < 4096)) { nuclear@0: fprintf(stderr, "XML parsing error: %d: %s\n", (int)XML_GetCurrentLineNumber(p), nuclear@0: XML_ErrorString(XML_GetErrorCode(p))); nuclear@0: break; nuclear@0: } nuclear@0: } while(rdsz == 4096); nuclear@0: nuclear@0: tree = node.chld; nuclear@0: if(tree) { nuclear@0: assert(tree->next == 0); nuclear@0: } nuclear@0: nuclear@0: if(pdata.cur_node != &node) { /* aborted */ nuclear@0: xml_free_tree(tree); nuclear@0: tree = 0; nuclear@0: } nuclear@0: nuclear@0: if(pdata.buf) { nuclear@0: free(pdata.buf); nuclear@0: } nuclear@0: nuclear@0: XML_ParserFree(p); nuclear@0: return tree; nuclear@0: } nuclear@0: nuclear@0: static void start(void *udata, const char *name, const char **atts) nuclear@0: { nuclear@0: struct xml_node *node = 0; nuclear@0: struct parse_data *pdata = udata; nuclear@0: nuclear@0: finish_cdata(pdata); nuclear@0: nuclear@0: if(!(node = malloc(sizeof *node))) { nuclear@0: goto err; nuclear@0: } nuclear@0: memset(node, 0, sizeof *node); nuclear@0: nuclear@0: if(!(node->name = malloc(strlen(name) + 1))) { nuclear@0: goto err; nuclear@0: } nuclear@0: strcpy(node->name, name); nuclear@0: nuclear@0: while(*atts) { nuclear@0: struct xml_attr *attr; nuclear@0: nuclear@0: if(!(attr = xml_create_attr(atts[0], atts[1]))) { nuclear@0: goto err; nuclear@0: } nuclear@0: attr->next = node->attr; nuclear@0: node->attr = attr; nuclear@0: nuclear@0: atts += 2; nuclear@0: } nuclear@0: nuclear@0: xml_add_child(pdata->cur_node, node); nuclear@0: pdata->cur_node = node; nuclear@0: return; nuclear@0: nuclear@0: err: nuclear@0: if(node) { nuclear@0: free(node->name); nuclear@0: xml_free_attr_list(node->attr); nuclear@0: } nuclear@0: free(node); nuclear@0: abort_parsing(pdata->parser); nuclear@0: } nuclear@0: nuclear@0: static void end(void *udata, const char *name) nuclear@0: { nuclear@0: struct parse_data *pdata = udata; nuclear@0: nuclear@0: finish_cdata(pdata); nuclear@0: nuclear@0: pdata->cur_node = pdata->cur_node->up; nuclear@0: } nuclear@0: nuclear@0: static void cdata(void *udata, const char *text, int len) nuclear@0: { nuclear@0: char *tmp; nuclear@0: struct parse_data *pdata = udata; nuclear@0: nuclear@0: if(!(tmp = realloc(pdata->buf, pdata->buf_size + len))) { nuclear@0: abort_parsing(pdata->parser); nuclear@0: return; nuclear@0: } nuclear@0: memcpy(tmp + pdata->buf_size, text, len); nuclear@0: pdata->buf = tmp; nuclear@0: pdata->buf_size += len; nuclear@0: } nuclear@0: nuclear@0: static void finish_cdata(struct parse_data *pdata) nuclear@0: { nuclear@0: char *tmp; nuclear@0: nuclear@0: if(!pdata->buf) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if(!(tmp = realloc(pdata->buf, pdata->buf_size + 1))) { nuclear@0: abort_parsing(pdata->parser); nuclear@0: return; nuclear@0: } nuclear@0: tmp[pdata->buf_size] = 0; nuclear@0: pdata->cur_node->cdata = tmp; nuclear@0: nuclear@0: pdata->buf = 0; nuclear@0: pdata->buf_size = 0; nuclear@0: } nuclear@0: nuclear@0: static void abort_parsing(XML_Parser p) nuclear@0: { nuclear@0: perror("abort XML parsing"); nuclear@0: nuclear@0: XML_SetElementHandler(p, 0, 0); nuclear@0: XML_SetCharacterDataHandler(p, 0); nuclear@0: XML_StopParser(p, 0); nuclear@0: } nuclear@0: nuclear@0: int xml_write_tree(struct xml_node *x, const char *fname) nuclear@0: { nuclear@0: FILE *fp; nuclear@0: int res; nuclear@0: nuclear@0: if(!(fp = fopen(fname, "wb"))) { nuclear@0: return -1; nuclear@0: } nuclear@0: res = xml_write_tree_file(x, fp); nuclear@0: fclose(fp); nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: int xml_write_tree_file(struct xml_node *x, FILE *fp) nuclear@0: { nuclear@0: return write_rec(x, fp, 0); nuclear@0: } nuclear@0: nuclear@0: static int write_rec(struct xml_node *x, FILE *fp, int lvl) nuclear@0: { nuclear@0: struct xml_node *c; nuclear@0: struct xml_attr *attr; nuclear@0: nuclear@0: indent(fp, lvl); nuclear@0: fputc('<', fp); nuclear@0: fputs(x->name, fp); nuclear@0: nuclear@0: attr = x->attr; nuclear@0: while(attr) { nuclear@0: switch(attr->type) { nuclear@0: case ATYPE_INT: nuclear@0: fprintf(fp, " %s=\"%d\"", attr->name, attr->ival); nuclear@0: break; nuclear@0: nuclear@0: case ATYPE_FLT: nuclear@0: fprintf(fp, " %s=\"%.4f\"", attr->name, attr->fval); nuclear@0: break; nuclear@0: nuclear@0: case ATYPE_VEC: nuclear@0: fprintf(fp, " %s=\"%.4f %.4f %.4f %.4f\"", attr->name, attr->vval[0], nuclear@0: attr->vval[1], attr->vval[2], attr->vval[3]); nuclear@0: break; nuclear@0: nuclear@0: case ATYPE_STR: nuclear@0: default: nuclear@0: fprintf(fp, " %s=\"%s\"", attr->name, attr->str); nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: attr = attr->next; nuclear@0: } nuclear@0: nuclear@0: if(!x->chld && !x->cdata) { nuclear@0: fputc('/', fp); nuclear@0: } nuclear@0: fprintf(fp, ">\n"); nuclear@0: nuclear@0: if(x->cdata) { nuclear@0: indent(fp, lvl + 1); nuclear@0: fprintf(fp, "%s\n", x->cdata); nuclear@0: } nuclear@0: nuclear@0: c = x->chld; nuclear@0: while(c) { nuclear@0: write_rec(c, fp, lvl + 1); nuclear@0: c = c->next; nuclear@0: } nuclear@0: nuclear@0: if(x->chld || x->cdata) { nuclear@0: indent(fp, lvl); nuclear@0: fprintf(fp, "\n", x->name); nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void xml_add_child(struct xml_node *x, struct xml_node *chld) nuclear@0: { nuclear@0: chld->next = 0; nuclear@0: nuclear@0: if(x->chld) { nuclear@0: x->chld_tail->next = chld; nuclear@0: x->chld_tail = chld; nuclear@0: } else { nuclear@0: x->chld = x->chld_tail = chld; nuclear@0: } nuclear@0: nuclear@0: chld->up = x; nuclear@0: x->chld_count++; nuclear@0: } nuclear@0: nuclear@0: void xml_remove_subtree(struct xml_node *sub) nuclear@0: { nuclear@0: struct xml_node *x; nuclear@0: nuclear@0: if(sub->up) { nuclear@0: x = sub->up->chld; nuclear@0: if(x == sub) { nuclear@0: sub->up->chld = sub->next; nuclear@0: } else { nuclear@0: while(x) { nuclear@0: if(x->next == sub) { nuclear@0: x->next = x->next->next; nuclear@0: break; nuclear@0: } nuclear@0: x = x->next; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: sub->up->chld_count--; nuclear@0: sub->up = 0; nuclear@0: } nuclear@0: nuclear@0: static void indent(FILE *fp, int lvl) nuclear@0: { nuclear@0: int i, ind = lvl * IND_SPACES; nuclear@0: nuclear@0: for(i=0; i