3 * Copyright 2004-2006 Aaron Voisine <aaron@voisine.org>
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 * Modified for Palacios by Jack Lange <jarusl@cs.northwestern.edu>
31 #include <palacios/vmm_xml.h>
32 #include <palacios/vmm_sprintf.h>
34 #include <palacios/vmm.h>
36 #define V3_XML_BUFSIZE 1024 // size of internal memory buffers
38 // Flags for struct v3_xml
39 #define V3_XML_NAMEM 0x80 // name is malloced
40 #define V3_XML_TXTM 0x40 // txt is malloced
41 #define V3_XML_DUP 0x20 // attribute name and value are strduped
44 static char * V3_XML_NIL[] = { NULL }; // empty, null terminated array of strings
47 #define V3_XML_WS "\t\r\n " // whitespace
48 #define V3_XML_ERRL 128 // maximum error string length
50 struct v3_xml_root { // additional data for the root tag
51 struct v3_xml xml; // is a super-struct built on top of v3_xml struct
52 struct v3_xml * cur; // current xml tree insertion point
53 char *str_ptr; // original xml string
54 char *tmp_start; // start of work area
55 char *tmp_end; // end of work area
56 short standalone; // non-zero if <?xml standalone="yes"?>
59 static char * empty_attrib_list[] = { NULL }; // empty, null terminated array of strings
63 static void * tmp_realloc(void * old_ptr, size_t old_size, size_t new_size) {
64 void * new_buf = NULL;
66 new_buf = V3_Malloc(new_size);
68 if (new_buf == NULL) {
69 PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in tmp_realloc in xml\n");
73 memset(new_buf, 0, new_size);
75 memcpy(new_buf, old_ptr, old_size);
81 // set an error string and return root
82 static void v3_xml_err(struct v3_xml_root * root, char * xml_str, const char * err, const char *arg) {
86 for (tmp = root->tmp_start; tmp < xml_str; tmp++) {
92 PrintError(VM_NONE, VCORE_NONE, "XML Error: [error near line %d]: %s (%s)", line, err ,arg ? arg : "");
95 v3_xml_free(&(root->xml));
102 // returns the first child tag with the given name or NULL if not found
103 struct v3_xml * v3_xml_child(struct v3_xml * xml, const char * name) {
104 struct v3_xml * child = NULL;
110 while ((child) && (strcasecmp(name, child->name) != 0)) {
111 child = child->sibling;
117 // returns the Nth tag with the same name in the same subsection or NULL if not
119 struct v3_xml * v3_xml_idx(struct v3_xml * xml, int idx) {
120 for (; xml && idx; idx--) {
127 // returns the value of the requested tag attribute or NULL if not found
128 const char * v3_xml_attr(struct v3_xml * xml, const char * attr) {
131 if ((!xml) || (!xml->attr)) {
135 while ((xml->attr[i]) && (strcasecmp(attr, xml->attr[i]) != 0)) {
139 if (xml->attr[i] != NULL) {
140 return xml->attr[i + 1]; // found attribute
143 return NULL; // found default
149 // sets a flag for the given tag and returns the tag
150 static struct v3_xml * v3_xml_set_flag(struct v3_xml * xml, short flag)
162 // Recursively decodes entity and character references and normalizes new lines
163 // ent is a null terminated array of alternating entity names and values. set t
164 // to '&' for general entity decoding, '%' for parameter entity decoding, 'c'
165 // for cdata sections, ' ' for attribute normalization, or '*' for non-cdata
166 // attribute normalization. Returns s, or if the decoded string is longer than
167 // s, returns a malloced string that must be freed.
168 static char * v3_xml_decode(char * s, char t) {
173 // normalize line endings
179 memmove(s, (s + 1), strlen(s));
198 if ((t != 'c') && (strncmp(s, "&#", 2) == 0)) { // character reference
200 c = strtox(s + 3, &e); // base 16
202 c = strtoi(s + 2, &e); // base 10
205 if ((!c) || (*e != ';')) {
206 // not a character ref
213 memmove(s, strchr(s, ';') + 1, strlen(strchr(s, ';')));
214 } else if ( ( (t == ' ') || (t == '*')) &&
218 // no decoding needed
224 // normalize spaces for non-cdata attributes
225 for (s = r; *s; s++) {
226 if ((l = strspn(s, " "))) {
227 memmove(s, s + l, strlen(s + l) + 1);
230 while ((*s) && (*s != ' ')) {
235 if ((--s >= r) && (*s == ' ')) {
236 // trim any trailing space
245 // called when parser finds character content between open and closing tag
246 static void v3_xml_char_content(struct v3_xml_root * root, char * s, size_t len, char t) {
247 struct v3_xml * xml = root->cur;
251 if ((xml == NULL) || (xml->name == NULL) || (len == 0)) {
256 s[len] = '\0'; // null terminate text (calling functions anticipate this)
257 len = strlen(s = v3_xml_decode(s, t)) + 1;
259 if (xml->txt[0] == '\0') { // empty string
260 // initial character content
264 // allocate our own memory and make a copy
265 if (xml->flags & V3_XML_TXTM) {
266 xml->txt = (tmp_realloc(xml->txt, strlen(xml->txt), (l = strlen(xml->txt)) + len));
270 tmp = V3_Malloc((l = strlen(xml->txt)) + len);
273 PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml char content\n");
277 strcpy(tmp, xml->txt);
281 strcpy(xml->txt + l, s); // add new char content
284 V3_Free(s); // free s if it was malloced by v3_xml_decode()
289 v3_xml_set_flag(xml, V3_XML_TXTM);
293 // called when parser finds closing tag
294 static int v3_xml_close_tag(struct v3_xml_root * root, char * name, char * s) {
295 if ( (root->cur == NULL) ||
296 (root->cur->name == NULL) ||
297 (strcasecmp(name, root->cur->name))) {
298 v3_xml_err(root, s, "unexpected closing tag", name);
302 root->cur = root->cur->parent;
307 // frees a tag attribute list
308 static void v3_xml_free_attr(char **attr) {
312 if ((attr == NULL) || (attr == empty_attrib_list)) {
318 // find end of attribute list
322 m = attr[i + 1]; // list of which names and values are malloced
333 // returns a new empty v3_xml structure with the given root tag name
334 static struct v3_xml * v3_xml_new(const char * name) {
336 struct v3_xml_root * root = (struct v3_xml_root *)V3_Malloc(sizeof(struct v3_xml_root));
339 PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml_new\n");
343 memset(root, 0, sizeof(struct v3_xml_root));
345 root->xml.name = (char *)name;
346 root->cur = &root->xml;
353 // inserts an existing tag into an v3_xml structure
354 struct v3_xml * v3_xml_insert(struct v3_xml * xml, struct v3_xml * dest, size_t off) {
355 struct v3_xml * cur, * prev, * head;
364 if ((head = dest->child)) {
365 // already have sub tags
367 if (head->off <= off) {
371 ((cur->ordered) && (cur->ordered->off <= off));
374 xml->ordered = cur->ordered;
383 for (cur = head, prev = NULL;
384 ((cur) && (strcasecmp(cur->name, xml->name) != 0));
385 prev = cur, cur = cur->sibling);
388 if (cur && cur->off <= off) {
391 while (cur->next && cur->next->off <= off) {
395 xml->next = cur->next;
398 // first tag of this type
402 prev->sibling = cur->sibling;
405 xml->next = cur; // old first tag is now next
407 // new sibling insert point
408 for (cur = head, prev = NULL;
409 ((cur) && (cur->off <= off));
410 prev = cur, cur = cur->sibling);
427 // Adds a child tag. off is the offset of the child tag relative to the start
428 // of the parent tag's character content. Returns the child tag.
429 static struct v3_xml * v3_xml_add_child(struct v3_xml * xml, const char * name, size_t off) {
430 struct v3_xml * child;
436 child = (struct v3_xml *)V3_Malloc(sizeof(struct v3_xml));
439 PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml_add_child\n");
443 memset(child, 0, sizeof(struct v3_xml));
445 child->name = (char *)name;
446 child->attr = empty_attrib_list;
449 return v3_xml_insert(child, xml, off);
453 // called when parser finds start of new tag
454 static void v3_xml_open_tag(struct v3_xml_root * root, char * name, char ** attr) {
455 struct v3_xml * xml = root->cur;
458 xml = v3_xml_add_child(xml, name, strlen(xml->txt));
465 root->cur = xml; // update tag insertion point
474 // parse the given xml string and return an v3_xml structure
475 static struct v3_xml * parse_str(char * buf, size_t len) {
476 struct v3_xml_root * root = (struct v3_xml_root *)v3_xml_new(NULL);
490 v3_xml_err(root, NULL, "Empty XML String", 0);
494 root->tmp_start = buf;
495 root->tmp_end = buf + len; // record start and end of work area
497 last_char = buf[len - 1]; // save end char
498 buf[len - 1] = '\0'; // turn end char into null terminator
500 while ((*buf) && (*buf != '<')) {
506 v3_xml_err(root, buf, "root tag missing", 0);
511 attr = (char **)empty_attrib_list;
512 tag_ptr = ++buf; // skip first '<'
514 if (isalpha(*buf) || (*buf == '_') || (*buf == ':') || (*buf < '\0')) {
517 if (root->cur == NULL) {
518 v3_xml_err(root, tag_ptr, "markup outside of root element", 0);
522 buf += strcspn(buf, V3_XML_WS "/>");
524 while (isspace(*buf)) {
525 // null terminate tag name,
526 // this writes '\0' to spaces after first tag
532 // attributes are name value pairs,
533 // 2nd to last entry is null (end of list)
534 // last entry points to a string map marking whether values have been malloced...
535 // loop through attributes until hitting the closing bracket
537 (*buf) && (*buf != '/') && (*buf != '>');
539 // buf is incremented later on
541 int attr_cnt = (attr_idx / 2) + 1;
542 int val_idx = attr_idx + 1;
543 int term_idx = attr_idx + 2;
544 int last_idx = attr_idx + 3;
546 // attr = allocated space
547 // attr[val_idx] = mem for list of maloced vals
549 attr = tmp_realloc(attr,
550 (((attr_cnt - 1) * (2 * sizeof(char *))) +
551 (2 * sizeof(char *))),
552 ((attr_cnt * (2 * sizeof(char *))) +
553 (2 * sizeof(char *))));
556 PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml parse string\n");
560 attr[last_idx] = tmp_realloc(attr[last_idx - 2],
564 if (!attr[last_idx]) {
565 PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml parse string\n");
570 attr = V3_Malloc(4 * sizeof(char *));
572 PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml parse string\n");
575 attr[last_idx] = V3_Malloc(2);
576 if (!attr[last_idx]) {
577 PrintError(VM_NONE, VCORE_NONE, "Cannot alloocate in xml parse string\n");
582 attr[attr_idx] = buf; // set attribute name
583 attr[val_idx] = ""; // temporary attribute value
584 attr[term_idx] = NULL; // null terminate list
585 strcpy(attr[last_idx] + attr_cnt, " "); // value is not malloc'd, offset into the stringmap
587 buf += strcspn(buf, V3_XML_WS "=/>");
589 if ((*buf == '=') || isspace(*buf)) {
591 *(buf++) = '\0'; // null terminate tag attribute name
593 // eat whitespace (and more multiple '=' ?)
594 buf += strspn(buf, V3_XML_WS "=");
598 if ((quote_char == '"') || (quote_char == '\'')) { // attribute value
599 attr[val_idx] = ++buf;
601 while ((*buf) && (*buf != quote_char)) {
606 // null terminate attribute val
609 char err_buf[2] = {quote_char,0};
611 v3_xml_free_attr(attr);
612 v3_xml_err(root, tag_ptr, "missing quote char", err_buf);
616 attr[val_idx] = v3_xml_decode(attr[val_idx], ' ');
620 while (isspace(*buf)) {
629 if ( ((*buf) && (*buf != '>')) ||
630 ((!*buf) && (last_char != '>'))) {
633 v3_xml_free_attr(attr);
635 v3_xml_err(root, tag_ptr, "missing >", 0);
638 v3_xml_open_tag(root, tag_ptr, attr);
639 v3_xml_close_tag(root, tag_ptr, buf);
640 } else if (((quote_char = *buf) == '>') ||
641 ((!*buf) && (last_char == '>'))) {
643 *buf = '\0'; // temporarily null terminate tag name
644 v3_xml_open_tag(root, tag_ptr, attr);
648 v3_xml_free_attr(attr);
650 v3_xml_err(root, tag_ptr, "missing >", 0);
653 } else if (*buf == '/') {
656 buf += strcspn(tag_ptr = buf + 1, V3_XML_WS ">") + 1;
659 if ((*buf == '\0') && (last_char != '>')) {
660 v3_xml_err(root, tag_ptr, "missing >", 0);
664 *buf = '\0'; // temporarily null terminate tag name
666 if (v3_xml_close_tag(root, tag_ptr, buf) == -1) {
672 buf += strspn(buf, V3_XML_WS);
674 } else if (strncmp(buf, "!--", 3) == 0) {
676 if ( ((buf = strstr(buf + 3, "--")) == 0) ||
677 ((*(buf += 2) != '>') && (*buf)) ||
678 ((!*buf) && (last_char != '>'))) {
679 v3_xml_err(root, tag_ptr, "unclosed <!--", 0);
682 } else if (! strncmp(buf, "![CDATA[", 8)) {
684 if ((buf = strstr(buf, "]]>"))) {
685 v3_xml_char_content(root, tag_ptr + 8, (buf += 2) - tag_ptr - 10, 'c');
687 v3_xml_err(root, tag_ptr, "unclosed <![CDATA[", 0);
691 v3_xml_err(root, tag_ptr, "unexpected <",0);
695 if (! buf || ! *buf) {
702 /* Eat leading whitespace */
703 while (*buf && isspace(*buf)) {
707 if (*buf && (*buf != '<')) {
708 // tag character content
709 while (*buf && (*buf != '<')) {
714 v3_xml_char_content(root, tag_ptr, buf - tag_ptr, '&');
718 } else if (*buf == '\0') {
723 if (root->cur == NULL) {
725 } else if (root->cur->name == NULL) {
726 v3_xml_err(root, tag_ptr, "root tag missing",0);
729 v3_xml_err(root, tag_ptr, "unclosed tag", root->cur->name);
735 struct v3_xml * v3_xml_parse(char * buf) {
737 char * xml_buf = NULL;
743 str_len = strlen(buf);
744 xml_buf = (char *)V3_Malloc(str_len + 1);
747 PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml parse\n");
751 strcpy(xml_buf, buf);
753 return parse_str(xml_buf, str_len);
758 // free the memory allocated for the v3_xml structure
759 void v3_xml_free(struct v3_xml * xml) {
760 struct v3_xml_root * root = (struct v3_xml_root *)xml;
766 v3_xml_free(xml->child);
767 v3_xml_free(xml->ordered);
769 if (xml->parent == NULL) {
770 // free root tag allocations
771 V3_Free(root->str_ptr); // malloced xml data
774 v3_xml_free_attr(xml->attr); // tag attributes
778 if ((xml->flags & V3_XML_TXTM)) {
783 if ((xml->flags & V3_XML_NAMEM)) {
795 /* Adding XML data */
800 // sets the character content for the given tag and returns the tag
801 struct v3_xml * v3_xml_set_txt(struct v3_xml * xml, const char *txt) {
806 if (xml->flags & V3_XML_TXTM) {
807 // existing txt was malloced
811 xml->flags &= ~V3_XML_TXTM;
812 xml->txt = (char *)txt;
816 // Sets the given tag attribute or adds a new attribute if not found. A value
817 // of NULL will remove the specified attribute. Returns the tag given.
818 struct v3_xml * v3_xml_set_attr(struct v3_xml * xml, const char * name, const char * value) {
826 while (xml->attr[l] && strcmp(xml->attr[l], name)) {
830 if (! xml->attr[l]) {
831 // not found, add as new attribute
838 if (xml->attr == V3_XML_NIL) {
840 xml->attr = V3_Malloc(4 * sizeof(char *));
843 PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml set attr\n");
847 // empty list of malloced names/vals
848 xml->attr[1] = strdup("");
851 PrintError(VM_NONE, VCORE_NONE, "Cannot strdup in xml set attr\n");
856 xml->attr = tmp_realloc(xml->attr, l * sizeof(char *), (l + 4) * sizeof(char *));
859 PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml set attr\n");
864 // set attribute name
865 xml->attr[l] = (char *)name;
867 // null terminate attribute list
868 xml->attr[l + 2] = NULL;
870 xml->attr[l + 3] = tmp_realloc(xml->attr[l + 1],
871 strlen(xml->attr[l + 1]),
872 (c = strlen(xml->attr[l + 1])) + 2);
875 if (!xml->attr[l + 3]) {
876 PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml set attr\n");
880 // set name/value as not malloced
881 strcpy(xml->attr[l + 3] + c, " ");
883 if (xml->flags & V3_XML_DUP) {
884 xml->attr[l + 3][c] = V3_XML_NAMEM;
886 } else if (xml->flags & V3_XML_DUP) {
888 V3_Free((char *)name);
892 // find end of attribute list
893 for (c = l; xml->attr[c]; c += 2);
895 if (xml->attr[c + 1][l / 2] & V3_XML_TXTM) {
897 V3_Free(xml->attr[l + 1]);
900 if (xml->flags & V3_XML_DUP) {
901 xml->attr[c + 1][l / 2] |= V3_XML_TXTM;
903 xml->attr[c + 1][l / 2] &= ~V3_XML_TXTM;
908 // set attribute value
909 xml->attr[l + 1] = (char *)value;
913 if (xml->attr[c + 1][l / 2] & V3_XML_NAMEM) {
914 V3_Free(xml->attr[l]);
917 memmove(xml->attr + l, xml->attr + l + 2, (c - l + 2) * sizeof(char*));
919 xml->attr = tmp_realloc(xml->attr, c * sizeof(char *), (c + 2) * sizeof(char *));
921 // fix list of which name/vals are malloced
922 memmove(xml->attr[c + 1] + (l / 2), xml->attr[c + 1] + (l / 2) + 1,
926 // clear strdup() flag
927 xml->flags &= ~V3_XML_DUP;
932 // removes a tag along with its subtags without freeing its memory
933 struct v3_xml * v3_xml_cut(struct v3_xml * xml) {
942 // patch sibling list
943 xml->next->sibling = xml->sibling;
950 // find head of subtag list
951 cur = xml->parent->child;
955 xml->parent->child = xml->ordered;
959 while (cur->ordered != xml) {
963 // patch ordered list
964 cur->ordered = cur->ordered->ordered;
966 // go back to head of subtag list
967 cur = xml->parent->child;
969 if (strcmp(cur->name, xml->name)) {
970 // not in first sibling list
972 while (strcmp(cur->sibling->name, xml->name)) {
976 if (cur->sibling == xml) {
977 // first of a sibling list
978 cur->sibling = (xml->next) ? xml->next
979 : cur->sibling->sibling;
981 // not first of a sibling list
986 while (cur->next && cur->next != xml) {
992 cur->next = cur->next->next;
996 xml->ordered = xml->sibling = xml->next = NULL;
1003 /* ************************** */
1004 /* *** XML ENCODING *** */
1005 /* ************************** */
1007 // Encodes ampersand sequences appending the results to *dst, reallocating *dst
1008 // if length excedes max. a is non-zero for attribute encoding. Returns *dst
1009 static char *ampencode(const char *s, size_t len, char **dst, size_t *dlen,
1010 size_t * max, short a)
1014 for (e = s + len; s != e; s++) {
1015 while (*dlen + 10 > *max) {
1016 *dst = tmp_realloc(*dst, *max, *max + V3_XML_BUFSIZE);
1017 *max += V3_XML_BUFSIZE;
1021 case '\0': return *dst;
1022 case '&': *dlen += sprintf(*dst + *dlen, "&"); break;
1023 case '<': *dlen += sprintf(*dst + *dlen, "<"); break;
1024 case '>': *dlen += sprintf(*dst + *dlen, ">"); break;
1025 case '"': *dlen += sprintf(*dst + *dlen, (a) ? """ : "\""); break;
1026 case '\n': *dlen += sprintf(*dst + *dlen, (a) ? "
" : "\n"); break;
1027 case '\t': *dlen += sprintf(*dst + *dlen, (a) ? "	" : "\t"); break;
1028 case '\r': *dlen += sprintf(*dst + *dlen, "
"); break;
1029 default: (*dst)[(*dlen)++] = *s;
1037 // Recursively converts each tag to xml appending it to *s. Reallocates *s if
1038 // its length excedes max. start is the location of the previous tag in the
1039 // parent tag's character content. Returns *s.
1040 static char *toxml_r(struct v3_xml * xml, char **s, size_t *len, size_t *max,
1043 char *txt = (xml->parent) ? xml->parent->txt : "";
1046 // parent character content up to this tag
1047 *s = ampencode(txt + start, xml->off - start, s, len, max, 0);
1049 while (*len + strlen(xml->name) + 4 > *max) {
1051 *s = tmp_realloc(*s, *max, *max + V3_XML_BUFSIZE);
1052 *max += V3_XML_BUFSIZE;
1056 *len += sprintf(*s + *len, "<%s", xml->name); // open tag
1057 for (i = 0; xml->attr[i]; i += 2) { // tag attributes
1058 if (v3_xml_attr(xml, xml->attr[i]) != xml->attr[i + 1]) continue;
1059 while (*len + strlen(xml->attr[i]) + 7 > *max) {
1061 *s = tmp_realloc(*s, *max, *max + V3_XML_BUFSIZE);
1062 *max += V3_XML_BUFSIZE;
1065 *len += sprintf(*s + *len, " %s=\"", xml->attr[i]);
1066 ampencode(xml->attr[i + 1], -1, s, len, max, 1);
1067 *len += sprintf(*s + *len, "\"");
1071 *len += sprintf(*s + *len, ">");
1073 *s = (xml->child) ? toxml_r(xml->child, s, len, max, 0) //child
1074 : ampencode(xml->txt, -1, s, len, max, 0); //data
1076 while (*len + strlen(xml->name) + 4 > *max) {
1078 *s = tmp_realloc(*s, *max, *max + V3_XML_BUFSIZE);
1079 *max += V3_XML_BUFSIZE;
1082 *len += sprintf(*s + *len, "</%s>", xml->name); // close tag
1084 while (off < xml->off && txt[off]) off++; // make sure off is within bounds
1085 return (xml->ordered) ? toxml_r(xml->ordered, s, len, max, off)
1086 : ampencode(txt + off, -1, s, len, max, 0);
1089 // Converts an xml structure back to xml. Returns a string of xml data that
1091 char * v3_xml_tostr(struct v3_xml * xml) {
1092 struct v3_xml * p = (xml) ? xml->parent : NULL;
1093 struct v3_xml * o = (xml) ? xml->ordered : NULL;
1094 struct v3_xml_root * root = (struct v3_xml_root *)xml;
1095 size_t len = 0, max = V3_XML_BUFSIZE;
1096 char *s = V3_Malloc(max);
1099 PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml tostrr\n");
1105 if (! xml || ! xml->name) return tmp_realloc(s, max, len + 1);
1106 while (root->xml.parent) root = (struct v3_xml_root *)root->xml.parent; // root tag
1109 xml->parent = xml->ordered = NULL;
1110 s = toxml_r(xml, &s, &len, &max, 0);
1115 return tmp_realloc(s, max, len + 1);