#define V3_XML_DUP 0x20 // attribute name and value are strduped
//
+static char * V3_XML_NIL[] = { NULL }; // empty, null terminated array of strings
+
#define V3_XML_WS "\t\r\n " // whitespace
#define V3_XML_ERRL 128 // maximum error string length
char *str_ptr; // original xml string
char *tmp_start; // start of work area
char *tmp_end; // end of work area
- char **ent; // general entities (ampersand sequences)
- char ***attr; // default attributes
short standalone; // non-zero if <?xml standalone="yes"?>
- char err[V3_XML_ERRL]; // error string
};
static char * empty_attrib_list[] = { NULL }; // empty, null terminated array of strings
-static void * tmp_realloc(void * old_ptr, uint_t old_size, uint_t new_size) {
- void * new_buf = V3_Malloc(new_size);
+static void * tmp_realloc(void * old_ptr, size_t old_size, size_t new_size) {
+ void * new_buf = NULL;
+
+ new_buf = V3_Malloc(new_size);
+
if (new_buf == NULL) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in tmp_realloc in xml\n");
return NULL;
}
+ memset(new_buf, 0, new_size);
+
memcpy(new_buf, old_ptr, old_size);
V3_Free(old_ptr);
return new_buf;
}
-
-
-
-
// set an error string and return root
-static void v3_xml_err(struct v3_xml_root * root, char * xml_str, const char * err, ...) {
- va_list ap;
+static void v3_xml_err(struct v3_xml_root * root, char * xml_str, const char * err, const char *arg) {
int line = 1;
char * tmp;
- char fmt[V3_XML_ERRL];
for (tmp = root->tmp_start; tmp < xml_str; tmp++) {
if (*tmp == '\n') {
}
}
- snprintf(fmt, V3_XML_ERRL, "[error near line %d]: %s", line, err);
-
- va_start(ap, err);
- vsnprintf(root->err, V3_XML_ERRL, fmt, ap);
- va_end(ap);
-
- PrintError("XML Error: %s\n", root->err);
-
+ PrintError(VM_NONE, VCORE_NONE, "XML Error: [error near line %d]: %s (%s)", line, err ,arg ? arg : "");
+
// free memory
v3_xml_free(&(root->xml));
if (xml != NULL) {
child = xml->child;
- }
+ }
- if (child != NULL) {
- while (strcmp(name, child->name) != 0) {
- child = child->sibling;
- }
+ while ((child) && (strcasecmp(name, child->name) != 0)) {
+ child = child->sibling;
}
return child;
// returns the value of the requested tag attribute or NULL if not found
const char * v3_xml_attr(struct v3_xml * xml, const char * attr) {
int i = 0;
- int j = 1;
- struct v3_xml_root * root = (struct v3_xml_root *)xml;
if ((!xml) || (!xml->attr)) {
return NULL;
}
- while ((xml->attr[i]) && (strcmp(attr, xml->attr[i]) != 0)) {
+ while ((xml->attr[i]) && (strcasecmp(attr, xml->attr[i]) != 0)) {
i += 2;
}
return xml->attr[i + 1]; // found attribute
}
- while (root->xml.parent != NULL) {
- root = (struct v3_xml_root *)root->xml.parent; // root tag
- }
-
- for (i = 0;
- ( (root->attr[i] != NULL) &&
- (strcmp(xml->name, root->attr[i][0]) != 0) );
- i++);
-
- if (! root->attr[i]) {
- return NULL; // no matching default attributes
- }
-
- while ((root->attr[i][j] != NULL) && (strcmp(attr, root->attr[i][j]) != 0)) {
- j += 3;
- }
-
- return (root->attr[i][j] != NULL) ? root->attr[i][j + 1] : NULL; // found default
+ return NULL; // found default
}
-// same as v3_xml_get but takes an already initialized va_list
-static struct v3_xml * v3_xml_vget(struct v3_xml * xml, va_list ap) {
- char * name = va_arg(ap, char *);
- int idx = -1;
- if ((name != NULL) && (*name != 0)) {
- idx = va_arg(ap, int);
- xml = v3_xml_child(xml, name);
- }
- return (idx < 0) ? xml : v3_xml_vget(v3_xml_idx(xml, idx), ap);
-}
-
-// Traverses the xml tree to retrieve a specific subtag. Takes a variable
-// length list of tag names and indexes. The argument list must be terminated
-// by either an index of -1 or an empty string tag name. Example:
-// title = v3_xml_get(library, "shelf", 0, "book", 2, "title", -1);
-// This retrieves the title of the 3rd book on the 1st shelf of library.
-// Returns NULL if not found.
-struct v3_xml * v3_xml_get(struct v3_xml * xml, ...) {
- va_list ap;
- struct v3_xml * r;
-
- va_start(ap, xml);
- r = v3_xml_vget(xml, ap);
- va_end(ap);
- return r;
-}
// sets a flag for the given tag and returns the tag
static struct v3_xml * v3_xml_set_flag(struct v3_xml * xml, short flag)
{
- if (xml) xml->flags |= flag;
+ if (xml) {
+ xml->flags |= flag;
+ }
return xml;
}
// for cdata sections, ' ' for attribute normalization, or '*' for non-cdata
// attribute normalization. Returns s, or if the decoded string is longer than
// s, returns a malloced string that must be freed.
-static char * v3_xml_decode(char * s, char ** ent, char t) {
+static char * v3_xml_decode(char * s, char t) {
char * e;
char * r = s;
- char * m = s;
- long b, c, d, l;
+ long c, l;
// normalize line endings
for (; *s; s++) {
*(s++) = c;
memmove(s, strchr(s, ';') + 1, strlen(strchr(s, ';')));
- } else if ( ( (*s == '&') &&
- ((t == '&') || (t == ' ') || (t == '*'))) ||
- ( (*s == '%') && (t == '%'))) {
- // entity reference`
-
- for ( (b = 0);
- (ent[b]) && (strncmp(s + 1, ent[b], strlen(ent[b])) != 0);
- (b += 2)); // find entity in entity list
-
- if (ent[b++]) { // found a match
- if (((c = strlen(ent[b])) - 1) > ((e = strchr(s, ';')) - s)) {
- l = (d = (s - r)) + c + strlen(e); // new length
- r = ((r == m) ? strcpy(V3_Malloc(l), r) : tmp_realloc(r, strlen(r), l));
- e = strchr((s = r + d), ';'); // fix up pointers
- }
-
- memmove(s + c, e + 1, strlen(e)); // shift rest of string
- strncpy(s, ent[b], c); // copy in replacement text
- } else {
- // not a known entity
- s++;
- }
} else if ( ( (t == ' ') || (t == '*')) &&
(isspace(*s))) {
*(s++) = ' ';
}
s[len] = '\0'; // null terminate text (calling functions anticipate this)
- len = strlen(s = v3_xml_decode(s, root->ent, t)) + 1;
+ len = strlen(s = v3_xml_decode(s, t)) + 1;
- if (! *(xml->txt)) {
+ if (xml->txt[0] == '\0') { // empty string
// initial character content
xml->txt = s;
} else {
+
// allocate our own memory and make a copy
- xml->txt = (xml->flags & V3_XML_TXTM) ?
- (tmp_realloc(xml->txt, strlen(xml->txt), (l = strlen(xml->txt)) + len)) :
- (strcpy(V3_Malloc((l = strlen(xml->txt)) + len), xml->txt));
+ if (xml->flags & V3_XML_TXTM) {
+ xml->txt = (tmp_realloc(xml->txt, strlen(xml->txt), (l = strlen(xml->txt)) + len));
+ } else {
+ char * tmp = NULL;
+
+ tmp = V3_Malloc((l = strlen(xml->txt)) + len);
+
+ if (!tmp) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml char content\n");
+ return ;
+ }
+
+ strcpy(tmp, xml->txt);
+ xml->txt = tmp;
+ }
strcpy(xml->txt + l, s); // add new char content
static int v3_xml_close_tag(struct v3_xml_root * root, char * name, char * s) {
if ( (root->cur == NULL) ||
(root->cur->name == NULL) ||
- (strcmp(name, root->cur->name))) {
- v3_xml_err(root, s, "unexpected closing tag </%s>", name);
+ (strcasecmp(name, root->cur->name))) {
+ v3_xml_err(root, s, "unexpected closing tag", name);
return -1;
}
return 0;
}
-// checks for circular entity references, returns non-zero if no circular
-// references are found, zero otherwise
-static int v3_xml_ent_ok(char * name, char * s, char ** ent) {
- int i;
-
- for (; ; s++) {
- while ((*s != '\0') && (*s != '&')) {
- // find next entity reference
- s++;
- }
-
- if (*s == '\0') {
- return 1;
- }
-
- if (strncmp(s + 1, name, strlen(name)) == 0) {
- // circular ref.
- return 0;
- }
-
- for (i = 0; (ent[i]) && (strncmp(ent[i], s + 1, strlen(ent[i]))); i += 2);
-
- if ((ent[i] != NULL) && (v3_xml_ent_ok(name, ent[i + 1], ent) == 0)) {
- return 0;
- }
- }
-}
-
-
// frees a tag attribute list
static void v3_xml_free_attr(char **attr) {
m = attr[i + 1]; // list of which names and values are malloced
- for (i = 0; m[i]; i++) {
- if (m[i] & V3_XML_NAMEM) {
- V3_Free(attr[i * 2]);
- }
-
- if (m[i] & V3_XML_TXTM) {
- V3_Free(attr[(i * 2) + 1]);
- }
- }
-
V3_Free(m);
V3_Free(attr);
}
// returns a new empty v3_xml structure with the given root tag name
static struct v3_xml * v3_xml_new(const char * name) {
- static char * ent[] = { "lt;", "<", "gt;", ">", "quot;", """,
- "apos;", "'", "amp;", "&", NULL };
struct v3_xml_root * root = (struct v3_xml_root *)V3_Malloc(sizeof(struct v3_xml_root));
+
+ if (!root) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml_new\n");
+ return NULL;
+ }
+
memset(root, 0, sizeof(struct v3_xml_root));
root->xml.name = (char *)name;
root->cur = &root->xml;
root->xml.txt = "";
- memset(root->err, 0, V3_XML_ERRL);
- root->ent = V3_Malloc(sizeof(ent));
- memcpy(root->ent, ent, sizeof(ent));
-
- root->xml.attr = empty_attrib_list;
- root->attr = (char ***)(empty_attrib_list);
return &root->xml;
}
// inserts an existing tag into an v3_xml structure
-static struct v3_xml * v3_xml_insert(struct v3_xml * xml, struct v3_xml * dest, size_t off) {
+struct v3_xml * v3_xml_insert(struct v3_xml * xml, struct v3_xml * dest, size_t off) {
struct v3_xml * cur, * prev, * head;
xml->next = NULL;
// find tag type
for (cur = head, prev = NULL;
- ((cur) && (strcmp(cur->name, xml->name) != 0));
+ ((cur) && (strcasecmp(cur->name, xml->name) != 0));
prev = cur, cur = cur->sibling);
}
child = (struct v3_xml *)V3_Malloc(sizeof(struct v3_xml));
+
+ if (!child) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml_add_child\n");
+ return NULL;
+ }
+
memset(child, 0, sizeof(struct v3_xml));
child->name = (char *)name;
char last_char;
char * tag_ptr;
char ** attr;
- char ** tmp_attr = NULL; // initialize a to avoid compile warning
int attr_idx;
- int i, j;
+
+ if (!buf) {
+ return NULL;
+ }
root->str_ptr = buf;
if (len == 0) {
- v3_xml_err(root, NULL, "Empty XML String\n");
+ v3_xml_err(root, NULL, "Empty XML String", 0);
return NULL;
}
}
if (*buf == '\0') {
- v3_xml_err(root, buf, "root tag missing");
+ v3_xml_err(root, buf, "root tag missing", 0);
return NULL;
}
// new tag
if (root->cur == NULL) {
- v3_xml_err(root, tag_ptr, "markup outside of root element");
+ v3_xml_err(root, tag_ptr, "markup outside of root element", 0);
return NULL;
}
*(buf++) = '\0';
}
- // check if attribute follows tag
- if ((*buf) && (*buf != '/') && (*buf != '>')) {
- // there is an attribute
- // find attributes for correct tag
- for ((i = 0);
- ((tmp_attr = root->attr[i]) &&
- (strcmp(tmp_attr[0], tag_ptr) != 0));
- (i++)) ;
-
- // 'tmp_attr' now points to the attribute list associated with 'tag_ptr'
- }
+
// attributes are name value pairs,
// 2nd to last entry is null (end of list)
(2 * sizeof(char *))),
((attr_cnt * (2 * sizeof(char *))) +
(2 * sizeof(char *))));
-
+
+ if (!attr) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml parse string\n");
+ return NULL;
+ }
+
attr[last_idx] = tmp_realloc(attr[last_idx - 2],
attr_cnt,
(attr_cnt + 1));
+
+ if (!attr[last_idx]) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml parse string\n");
+ return NULL;
+ }
+
} else {
attr = V3_Malloc(4 * sizeof(char *));
+ if (!attr) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml parse string\n");
+ return NULL;
+ }
attr[last_idx] = V3_Malloc(2);
+ if (!attr[last_idx]) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot alloocate in xml parse string\n");
+ return NULL;
+ }
}
-
attr[attr_idx] = buf; // set attribute name
attr[val_idx] = ""; // temporary attribute value
buf += strcspn(buf, V3_XML_WS "=/>");
if ((*buf == '=') || isspace(*buf)) {
-
+
*(buf++) = '\0'; // null terminate tag attribute name
-
+
// eat whitespace (and more multiple '=' ?)
buf += strspn(buf, V3_XML_WS "=");
// null terminate attribute val
*(buf++) = '\0';
} else {
+ char err_buf[2] = {quote_char,0};
+
v3_xml_free_attr(attr);
- v3_xml_err(root, tag_ptr, "missing %c", quote_char);
+ v3_xml_err(root, tag_ptr, "missing quote char", err_buf);
return NULL;
}
- for (j = 1;
- ( (tmp_attr) && (tmp_attr[j]) &&
- (strcmp(tmp_attr[j], attr[attr_idx]) != 0));
- j += 3);
-
- attr[val_idx] = v3_xml_decode(attr[val_idx], root->ent,
- ((tmp_attr && tmp_attr[j]) ?
- *tmp_attr[j + 2] :
- ' '));
-
- if ( (attr[val_idx] < tag_ptr) ||
- (attr[val_idx] > buf) ) {
- attr[last_idx][attr_cnt - 1] = V3_XML_TXTM; // value malloced
- }
+ attr[val_idx] = v3_xml_decode(attr[val_idx], ' ');
}
}
if (attr_idx > 0) {
v3_xml_free_attr(attr);
}
- v3_xml_err(root, tag_ptr, "missing >");
+ v3_xml_err(root, tag_ptr, "missing >", 0);
return NULL;
}
v3_xml_open_tag(root, tag_ptr, attr);
if (attr_idx > 0) {
v3_xml_free_attr(attr);
}
- v3_xml_err(root, tag_ptr, "missing >");
+ v3_xml_err(root, tag_ptr, "missing >", 0);
return NULL;
}
} else if (*buf == '/') {
quote_char = *buf;
if ((*buf == '\0') && (last_char != '>')) {
- v3_xml_err(root, tag_ptr, "missing >");
+ v3_xml_err(root, tag_ptr, "missing >", 0);
return NULL;
}
if ( ((buf = strstr(buf + 3, "--")) == 0) ||
((*(buf += 2) != '>') && (*buf)) ||
((!*buf) && (last_char != '>'))) {
- v3_xml_err(root, tag_ptr, "unclosed <!--");
+ v3_xml_err(root, tag_ptr, "unclosed <!--", 0);
return NULL;
}
} else if (! strncmp(buf, "![CDATA[", 8)) {
if ((buf = strstr(buf, "]]>"))) {
v3_xml_char_content(root, tag_ptr + 8, (buf += 2) - tag_ptr - 10, 'c');
} else {
- v3_xml_err(root, tag_ptr, "unclosed <![CDATA[");
+ v3_xml_err(root, tag_ptr, "unclosed <![CDATA[", 0);
return NULL;
}
} else {
- v3_xml_err(root, tag_ptr, "unexpected <");
+ v3_xml_err(root, tag_ptr, "unexpected <",0);
return NULL;
}
*buf = '\0';
tag_ptr = ++buf;
+ /* Eat leading whitespace */
+ while (*buf && isspace(*buf)) {
+ buf++;
+ }
+
if (*buf && (*buf != '<')) {
// tag character content
while (*buf && (*buf != '<')) {
if (root->cur == NULL) {
return &root->xml;
} else if (root->cur->name == NULL) {
- v3_xml_err(root, tag_ptr, "root tag missing");
+ v3_xml_err(root, tag_ptr, "root tag missing",0);
return NULL;
} else {
- v3_xml_err(root, tag_ptr, "unclosed tag <%s>", root->cur->name);
+ v3_xml_err(root, tag_ptr, "unclosed tag", root->cur->name);
return NULL;
}
}
str_len = strlen(buf);
xml_buf = (char *)V3_Malloc(str_len + 1);
+
+ if (!xml_buf) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml parse\n");
+ return NULL;
+ }
+
strcpy(xml_buf, buf);
return parse_str(xml_buf, str_len);
// free the memory allocated for the v3_xml structure
void v3_xml_free(struct v3_xml * xml) {
struct v3_xml_root * root = (struct v3_xml_root *)xml;
- int i, j;
- char **a, *s;
if (xml == NULL) {
return;
if (xml->parent == NULL) {
// free root tag allocations
+ V3_Free(root->str_ptr); // malloced xml data
+ }
+
+ v3_xml_free_attr(xml->attr); // tag attributes
+
+
+
+ if ((xml->flags & V3_XML_TXTM)) {
+ // character content
+ V3_Free(xml->txt);
+ }
+
+ if ((xml->flags & V3_XML_NAMEM)) {
+ // tag name
+ V3_Free(xml->name);
+ }
+
+ V3_Free(xml);
+}
+
+
+
+
+
+/* Adding XML data */
+
+
+
+
+// sets the character content for the given tag and returns the tag
+struct v3_xml * v3_xml_set_txt(struct v3_xml * xml, const char *txt) {
+ if (! xml) {
+ return NULL;
+ }
+
+ if (xml->flags & V3_XML_TXTM) {
+ // existing txt was malloced
+ V3_Free(xml->txt);
+ }
+
+ xml->flags &= ~V3_XML_TXTM;
+ xml->txt = (char *)txt;
+ return xml;
+}
+
+// Sets the given tag attribute or adds a new attribute if not found. A value
+// of NULL will remove the specified attribute. Returns the tag given.
+struct v3_xml * v3_xml_set_attr(struct v3_xml * xml, const char * name, const char * value) {
+ int l = 0;
+ int c;
+
+ if (! xml) {
+ return NULL;
+ }
+
+ while (xml->attr[l] && strcmp(xml->attr[l], name)) {
+ l += 2;
+ }
+
+ if (! xml->attr[l]) {
+ // not found, add as new attribute
- for (i = 10; root->ent[i]; i += 2) {
- // 0 - 9 are default entites (<>&"')
- if ((s = root->ent[i + 1]) < root->tmp_start || s > root->tmp_end) {
- V3_Free(s);
+ if (! value) {
+ // nothing to do
+ return xml;
+ }
+
+ if (xml->attr == V3_XML_NIL) {
+ // first attribute
+ xml->attr = V3_Malloc(4 * sizeof(char *));
+
+ if (!xml->attr) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml set attr\n");
+ return NULL;
+ }
+
+ // empty list of malloced names/vals
+ xml->attr[1] = strdup("");
+
+ if (!xml->attr[1]) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot strdup in xml set attr\n");
+ return NULL;
+ }
+
+ } else {
+ xml->attr = tmp_realloc(xml->attr, l * sizeof(char *), (l + 4) * sizeof(char *));
+
+ if (!xml->attr) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml set attr\n");
+ return NULL;
}
}
- V3_Free(root->ent); // free list of general entities
+ // set attribute name
+ xml->attr[l] = (char *)name;
+
+ // null terminate attribute list
+ xml->attr[l + 2] = NULL;
+
+ xml->attr[l + 3] = tmp_realloc(xml->attr[l + 1],
+ strlen(xml->attr[l + 1]),
+ (c = strlen(xml->attr[l + 1])) + 2);
+
+
+ if (!xml->attr[l + 3]) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml set attr\n");
+ return NULL;
+ }
+
+ // set name/value as not malloced
+ strcpy(xml->attr[l + 3] + c, " ");
+
+ if (xml->flags & V3_XML_DUP) {
+ xml->attr[l + 3][c] = V3_XML_NAMEM;
+ }
+ } else if (xml->flags & V3_XML_DUP) {
+ // name was strduped
+ V3_Free((char *)name);
+ }
+
+
+ // find end of attribute list
+ for (c = l; xml->attr[c]; c += 2);
+
+ if (xml->attr[c + 1][l / 2] & V3_XML_TXTM) {
+ //old val
+ V3_Free(xml->attr[l + 1]);
+ }
+
+ if (xml->flags & V3_XML_DUP) {
+ xml->attr[c + 1][l / 2] |= V3_XML_TXTM;
+ } else {
+ xml->attr[c + 1][l / 2] &= ~V3_XML_TXTM;
+ }
+
+
+ if (value) {
+ // set attribute value
+ xml->attr[l + 1] = (char *)value;
+ } else {
+ // remove attribute
+
+ if (xml->attr[c + 1][l / 2] & V3_XML_NAMEM) {
+ V3_Free(xml->attr[l]);
+ }
+
+ memmove(xml->attr + l, xml->attr + l + 2, (c - l + 2) * sizeof(char*));
+
+ xml->attr = tmp_realloc(xml->attr, c * sizeof(char *), (c + 2) * sizeof(char *));
+
+ // fix list of which name/vals are malloced
+ memmove(xml->attr[c + 1] + (l / 2), xml->attr[c + 1] + (l / 2) + 1,
+ (c / 2) - (l / 2));
+ }
+
+ // clear strdup() flag
+ xml->flags &= ~V3_XML_DUP;
+
+ return xml;
+}
+
+// removes a tag along with its subtags without freeing its memory
+struct v3_xml * v3_xml_cut(struct v3_xml * xml) {
+ struct v3_xml * cur;
+
+ if (! xml) {
+ // nothing to do
+ return NULL;
+ }
+
+ if (xml->next) {
+ // patch sibling list
+ xml->next->sibling = xml->sibling;
+ }
- for (i = 0; (a = root->attr[i]); i++) {
- for (j = 1; a[j++]; j += 2) {
- // free malloced attribute values
- if (a[j] && (a[j] < root->tmp_start || a[j] > root->tmp_end)) {
- V3_Free(a[j]);
+
+ if (xml->parent) {
+ // not root tag
+
+ // find head of subtag list
+ cur = xml->parent->child;
+
+ if (cur == xml) {
+ // first subtag
+ xml->parent->child = xml->ordered;
+ } else {
+ // not first subtag
+
+ while (cur->ordered != xml) {
+ cur = cur->ordered;
+ }
+
+ // patch ordered list
+ cur->ordered = cur->ordered->ordered;
+
+ // go back to head of subtag list
+ cur = xml->parent->child;
+
+ if (strcmp(cur->name, xml->name)) {
+ // not in first sibling list
+
+ while (strcmp(cur->sibling->name, xml->name)) {
+ cur = cur->sibling;
+ }
+
+ if (cur->sibling == xml) {
+ // first of a sibling list
+ cur->sibling = (xml->next) ? xml->next
+ : cur->sibling->sibling;
+ } else {
+ // not first of a sibling list
+ cur = cur->sibling;
}
+ }
+
+ while (cur->next && cur->next != xml) {
+ cur = cur->next;
}
- V3_Free(a);
- }
- if (root->attr[0]) {
- // free default attribute list
- V3_Free(root->attr);
+ if (cur->next) {
+ // patch next list
+ cur->next = cur->next->next;
+ }
+ }
+ }
+ xml->ordered = xml->sibling = xml->next = NULL;
+ return xml;
+}
+
+
+
+
+/* ************************** */
+/* *** XML ENCODING *** */
+/* ************************** */
+
+// Encodes ampersand sequences appending the results to *dst, reallocating *dst
+// if length excedes max. a is non-zero for attribute encoding. Returns *dst
+static char *ampencode(const char *s, size_t len, char **dst, size_t *dlen,
+ size_t * max, short a)
+{
+ const char * e;
+
+ for (e = s + len; s != e; s++) {
+ while (*dlen + 10 > *max) {
+ *dst = tmp_realloc(*dst, *max, *max + V3_XML_BUFSIZE);
+ *max += V3_XML_BUFSIZE;
}
- V3_Free(root->str_ptr); // malloced xml data
+ switch (*s) {
+ case '\0': return *dst;
+ case '&': *dlen += sprintf(*dst + *dlen, "&"); break;
+ case '<': *dlen += sprintf(*dst + *dlen, "<"); break;
+ case '>': *dlen += sprintf(*dst + *dlen, ">"); break;
+ case '"': *dlen += sprintf(*dst + *dlen, (a) ? """ : "\""); break;
+ case '\n': *dlen += sprintf(*dst + *dlen, (a) ? "
" : "\n"); break;
+ case '\t': *dlen += sprintf(*dst + *dlen, (a) ? "	" : "\t"); break;
+ case '\r': *dlen += sprintf(*dst + *dlen, "
"); break;
+ default: (*dst)[(*dlen)++] = *s;
+ }
}
+ return *dst;
+}
- v3_xml_free_attr(xml->attr); // tag attributes
- if ((xml->flags & V3_XML_TXTM)) {
- // character content
- V3_Free(xml->txt);
+
+// Recursively converts each tag to xml appending it to *s. Reallocates *s if
+// its length excedes max. start is the location of the previous tag in the
+// parent tag's character content. Returns *s.
+static char *toxml_r(struct v3_xml * xml, char **s, size_t *len, size_t *max,
+ size_t start) {
+ int i;
+ char *txt = (xml->parent) ? xml->parent->txt : "";
+ size_t off = 0;
+
+ // parent character content up to this tag
+ *s = ampencode(txt + start, xml->off - start, s, len, max, 0);
+
+ while (*len + strlen(xml->name) + 4 > *max) {
+ // reallocate s
+ *s = tmp_realloc(*s, *max, *max + V3_XML_BUFSIZE);
+ *max += V3_XML_BUFSIZE;
}
- if ((xml->flags & V3_XML_NAMEM)) {
- // tag name
- V3_Free(xml->name);
+
+ *len += sprintf(*s + *len, "<%s", xml->name); // open tag
+ for (i = 0; xml->attr[i]; i += 2) { // tag attributes
+ if (v3_xml_attr(xml, xml->attr[i]) != xml->attr[i + 1]) continue;
+ while (*len + strlen(xml->attr[i]) + 7 > *max) {
+ // reallocate s
+ *s = tmp_realloc(*s, *max, *max + V3_XML_BUFSIZE);
+ *max += V3_XML_BUFSIZE;
+ }
+
+ *len += sprintf(*s + *len, " %s=\"", xml->attr[i]);
+ ampencode(xml->attr[i + 1], -1, s, len, max, 1);
+ *len += sprintf(*s + *len, "\"");
}
- V3_Free(xml);
+
+ *len += sprintf(*s + *len, ">");
+
+ *s = (xml->child) ? toxml_r(xml->child, s, len, max, 0) //child
+ : ampencode(xml->txt, -1, s, len, max, 0); //data
+
+ while (*len + strlen(xml->name) + 4 > *max) {
+ // reallocate s
+ *s = tmp_realloc(*s, *max, *max + V3_XML_BUFSIZE);
+ *max += V3_XML_BUFSIZE;
+ }
+
+ *len += sprintf(*s + *len, "</%s>", xml->name); // close tag
+
+ while (off < xml->off && txt[off]) off++; // make sure off is within bounds
+ return (xml->ordered) ? toxml_r(xml->ordered, s, len, max, off)
+ : ampencode(txt + off, -1, s, len, max, 0);
}
+// Converts an xml structure back to xml. Returns a string of xml data that
+// must be freed.
+char * v3_xml_tostr(struct v3_xml * xml) {
+ struct v3_xml * p = (xml) ? xml->parent : NULL;
+ struct v3_xml * o = (xml) ? xml->ordered : NULL;
+ struct v3_xml_root * root = (struct v3_xml_root *)xml;
+ size_t len = 0, max = V3_XML_BUFSIZE;
+ char *s = V3_Malloc(max);
+
+ if (!s) {
+ PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml tostrr\n");
+ return NULL;
+ }
+
+ strcpy(s, "");
+
+ if (! xml || ! xml->name) return tmp_realloc(s, max, len + 1);
+ while (root->xml.parent) root = (struct v3_xml_root *)root->xml.parent; // root tag
+
+
+ xml->parent = xml->ordered = NULL;
+ s = toxml_r(xml, &s, &len, &max, 0);
+ xml->parent = p;
+ xml->ordered = o;
+
+
+ return tmp_realloc(s, max, len + 1);
+}