Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Cleanup and sanity-checking of before/after null-check and copy+paste errors (Coverit...
[palacios.git] / palacios / src / palacios / vmm_xml.c
1 /* ezxml.c
2  *
3  * Copyright 2004-2006 Aaron Voisine <aaron@voisine.org>
4  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
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.
23  */
24
25 /* 
26  * Modified for Palacios by Jack Lange <jarusl@cs.northwestern.edu> 
27  */
28
29
30
31 #include <palacios/vmm_xml.h>
32 #include <palacios/vmm_sprintf.h>
33 #include <stdarg.h>
34 #include <palacios/vmm.h>
35
36 #define V3_XML_BUFSIZE 1024 // size of internal memory buffers
37
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
42 //
43
44 static char * V3_XML_NIL[] = { NULL }; // empty, null terminated array of strings
45
46
47 #define V3_XML_WS   "\t\r\n "  // whitespace
48 #define V3_XML_ERRL 128        // maximum error string length
49
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"?>
57     char err[V3_XML_ERRL]; // error string
58 };
59
60 static char * empty_attrib_list[] = { NULL }; // empty, null terminated array of strings
61
62
63
64 static void * tmp_realloc(void * old_ptr, size_t old_size, size_t new_size) {
65     void * new_buf = NULL; 
66
67     new_buf = V3_Malloc(new_size);
68     
69     if (new_buf == NULL) {
70         PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in tmp_realloc in xml\n");
71         return NULL;
72     }
73
74     memset(new_buf, 0, new_size);
75
76     memcpy(new_buf, old_ptr, old_size);
77     V3_Free(old_ptr);
78
79     return new_buf;
80 }
81
82 // set an error string and return root
83 static void v3_xml_err(struct v3_xml_root * root, char * xml_str, const char * err, ...) {
84     va_list ap;
85     int line = 1;
86     char * tmp;
87     char fmt[V3_XML_ERRL];
88     
89     for (tmp = root->tmp_start; tmp < xml_str; tmp++) {
90         if (*tmp == '\n') {
91             line++;
92         }
93     }
94
95     snprintf(fmt, V3_XML_ERRL, "[error near line %d]: %s", line, err);
96
97     va_start(ap, err);
98     vsnprintf(root->err, V3_XML_ERRL, fmt, ap);
99     va_end(ap);
100
101     PrintError(VM_NONE, VCORE_NONE, "XML Error: %s\n", root->err);
102
103     // free memory
104     v3_xml_free(&(root->xml));
105
106     return;
107 }
108
109
110
111 // returns the first child tag with the given name or NULL if not found
112 struct v3_xml * v3_xml_child(struct v3_xml * xml, const char * name) {
113     struct v3_xml * child = NULL;
114
115     if (xml != NULL) {
116         child = xml->child;
117     }
118
119     while ((child) && (strcasecmp(name, child->name) != 0)) {
120         child = child->sibling;
121     }
122
123     return child;
124 }
125
126 // returns the Nth tag with the same name in the same subsection or NULL if not
127 // found
128 struct v3_xml * v3_xml_idx(struct v3_xml * xml, int idx) {
129     for (; xml && idx; idx--) {
130         xml = xml->next;
131     }
132
133     return xml;
134 }
135
136 // returns the value of the requested tag attribute or NULL if not found
137 const char * v3_xml_attr(struct v3_xml * xml, const char * attr) {
138     int i = 0;
139
140     if ((!xml) || (!xml->attr)) {
141         return NULL;
142     }
143
144     while ((xml->attr[i]) && (strcasecmp(attr, xml->attr[i]) != 0)) {
145         i += 2;
146     }
147
148     if (xml->attr[i] != NULL) {
149         return xml->attr[i + 1]; // found attribute
150     }
151
152     return NULL; // found default
153 }
154
155 // same as v3_xml_get but takes an already initialized va_list
156 static struct v3_xml * v3_xml_vget(struct v3_xml * xml, va_list ap) {
157     char * name = va_arg(ap, char *);
158     int idx = -1;
159
160     if ((name != NULL) && (*name != 0)) {
161         idx = va_arg(ap, int);    
162         xml = v3_xml_child(xml, name);
163     }
164     return (idx < 0) ? xml : v3_xml_vget(v3_xml_idx(xml, idx), ap);
165 }
166
167 // Traverses the xml tree to retrieve a specific subtag. Takes a variable
168 // length list of tag names and indexes. The argument list must be terminated
169 // by either an index of -1 or an empty string tag name. Example: 
170 // title = v3_xml_get(library, "shelf", 0, "book", 2, "title", -1);
171 // This retrieves the title of the 3rd book on the 1st shelf of library.
172 // Returns NULL if not found.
173 struct v3_xml * v3_xml_get(struct v3_xml * xml, ...) {
174     va_list ap;
175     struct v3_xml * r;
176
177     va_start(ap, xml);
178     r = v3_xml_vget(xml, ap);
179     va_end(ap);
180     return r;
181 }
182
183
184 // sets a flag for the given tag and returns the tag
185 static struct v3_xml * v3_xml_set_flag(struct v3_xml * xml, short flag)
186 {
187     if (xml) {
188         xml->flags |= flag;
189     }
190     return xml;
191 }
192
193
194
195
196
197 // Recursively decodes entity and character references and normalizes new lines
198 // ent is a null terminated array of alternating entity names and values. set t
199 // to '&' for general entity decoding, '%' for parameter entity decoding, 'c'
200 // for cdata sections, ' ' for attribute normalization, or '*' for non-cdata
201 // attribute normalization. Returns s, or if the decoded string is longer than
202 // s, returns a malloced string that must be freed.
203 static char * v3_xml_decode(char * s, char t) {
204     char * e;
205     char * r = s;
206     long c, l;
207
208     // normalize line endings
209     for (; *s; s++) { 
210         while (*s == '\r') {
211             *(s++) = '\n';
212
213             if (*s == '\n') {
214                 memmove(s, (s + 1), strlen(s));
215             }
216         }
217     }
218     
219     for (s = r; ; ) {
220
221         while ( (*s) && 
222                 (*s != '&') && 
223                 ( (*s != '%') || 
224                   (t != '%')) && 
225                 (!isspace(*s))) {
226             s++;
227         }
228
229         if (*s == '\0') {
230             break;
231         }
232
233         if ((t != 'c') && (strncmp(s, "&#", 2) == 0)) { // character reference
234             if (s[2] == 'x') {
235                 c = strtox(s + 3, &e); // base 16
236             } else {
237                 c = strtoi(s + 2, &e); // base 10
238             }
239
240             if ((!c) || (*e != ';')) { 
241                 // not a character ref
242                 s++; 
243                 continue;
244             }
245
246             *(s++) = c; 
247
248             memmove(s, strchr(s, ';') + 1, strlen(strchr(s, ';')));
249         } else if ( ( (t == ' ') || (t == '*')) && 
250                     (isspace(*s))) {
251             *(s++) = ' ';
252         } else {
253             // no decoding needed
254             s++;
255         }
256     }
257
258     if (t == '*') { 
259         // normalize spaces for non-cdata attributes
260         for (s = r; *s; s++) {
261             if ((l = strspn(s, " "))) {
262                 memmove(s, s + l, strlen(s + l) + 1);
263             }
264
265             while ((*s) && (*s != ' ')) {
266                 s++;
267             }
268         }
269
270         if ((--s >= r) && (*s == ' ')) {
271             // trim any trailing space
272             *s = '\0'; 
273         }
274     }
275     return r;
276 }
277
278
279
280 // called when parser finds character content between open and closing tag
281 static void v3_xml_char_content(struct v3_xml_root * root, char * s, size_t len, char t) {
282     struct v3_xml * xml = root->cur;
283     char * m = s;
284     size_t l = 0;
285
286     if ((xml == NULL) || (xml->name == NULL) || (len == 0)) {
287         // sanity check
288         return;
289     }
290
291     s[len] = '\0'; // null terminate text (calling functions anticipate this)
292     len = strlen(s = v3_xml_decode(s, t)) + 1;
293
294     if (xml->txt[0] == '\0') { // empty string
295         // initial character content
296         xml->txt = s;
297     } else { 
298
299         // allocate our own memory and make a copy
300         if (xml->flags & V3_XML_TXTM) {
301             xml->txt = (tmp_realloc(xml->txt, strlen(xml->txt), (l = strlen(xml->txt)) + len));
302         } else {
303             char * tmp = NULL;
304
305             tmp = V3_Malloc((l = strlen(xml->txt)) + len);
306
307             if (!tmp) {
308                 PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml char content\n");
309                 return ;
310             }
311
312             strcpy(tmp, xml->txt);
313             xml->txt = tmp;
314         }
315
316         strcpy(xml->txt + l, s); // add new char content
317         
318         if (s != m) {
319             V3_Free(s); // free s if it was malloced by v3_xml_decode()
320         }
321     }
322
323     if (xml->txt != m) {
324         v3_xml_set_flag(xml, V3_XML_TXTM);
325     }
326 }
327
328 // called when parser finds closing tag
329 static int v3_xml_close_tag(struct v3_xml_root * root, char * name, char * s) {
330     if ( (root->cur == NULL) || 
331          (root->cur->name == NULL) || 
332          (strcasecmp(name, root->cur->name))) {
333         v3_xml_err(root, s, "unexpected closing tag </%s>", name);
334         return -1;
335     }
336
337     root->cur = root->cur->parent;
338     return 0;
339 }
340
341
342 // frees a tag attribute list
343 static void v3_xml_free_attr(char **attr) {
344     int i = 0;
345     char * m;
346     
347     if ((attr == NULL) || (attr == empty_attrib_list)) {
348         // nothing to free
349         return; 
350     }
351
352     while (attr[i]) {
353         // find end of attribute list
354         i += 2;
355     }
356
357     m = attr[i + 1]; // list of which names and values are malloced
358
359     V3_Free(m);
360     V3_Free(attr);
361 }
362
363
364
365
366
367
368 // returns a new empty v3_xml structure with the given root tag name
369 static struct v3_xml * v3_xml_new(const char * name) {
370
371     struct v3_xml_root * root = (struct v3_xml_root *)V3_Malloc(sizeof(struct v3_xml_root));
372
373     if (!root) {
374         PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml_new\n");
375         return NULL;
376     }
377
378     memset(root, 0, sizeof(struct v3_xml_root));
379
380     root->xml.name = (char *)name;
381     root->cur = &root->xml;
382     root->xml.txt = "";
383     memset(root->err, 0, V3_XML_ERRL);
384
385
386     return &root->xml;
387 }
388
389 // inserts an existing tag into an v3_xml structure
390 struct v3_xml * v3_xml_insert(struct v3_xml * xml, struct v3_xml * dest, size_t off) {
391     struct v3_xml * cur, * prev, * head;
392
393     xml->next = NULL;
394     xml->sibling = NULL;
395     xml->ordered = NULL;
396     
397     xml->off = off;
398     xml->parent = dest;
399
400     if ((head = dest->child)) { 
401         // already have sub tags
402
403         if (head->off <= off) {
404             // not first subtag
405
406             for (cur = head; 
407                  ((cur->ordered) && (cur->ordered->off <= off));
408                  cur = cur->ordered);
409
410             xml->ordered = cur->ordered;
411             cur->ordered = xml;
412         } else { 
413             // first subtag
414             xml->ordered = head;
415             dest->child = xml;
416         }
417
418         // find tag type
419         for (cur = head, prev = NULL; 
420              ((cur) && (strcasecmp(cur->name, xml->name) != 0));
421              prev = cur, cur = cur->sibling); 
422
423
424         if (cur && cur->off <= off) { 
425             // not first of type
426             
427             while (cur->next && cur->next->off <= off) {
428                 cur = cur->next;
429             }
430
431             xml->next = cur->next;
432             cur->next = xml;
433         } else { 
434             // first tag of this type
435             
436             if (prev && cur) {
437                 // remove old first
438                 prev->sibling = cur->sibling;
439             }
440
441             xml->next = cur; // old first tag is now next
442
443             // new sibling insert point
444             for (cur = head, prev = NULL; 
445                  ((cur) && (cur->off <= off));
446                  prev = cur, cur = cur->sibling);
447             
448             xml->sibling = cur;
449
450             if (prev) {
451                 prev->sibling = xml;
452             }
453         }
454     } else {
455         // only sub tag
456         dest->child = xml;
457     }
458
459     return xml;
460 }
461
462
463 // Adds a child tag. off is the offset of the child tag relative to the start
464 // of the parent tag's character content. Returns the child tag.
465 static struct v3_xml * v3_xml_add_child(struct v3_xml * xml, const char * name, size_t off) {
466     struct v3_xml * child;
467
468     if (xml == NULL) {
469         return NULL;
470     }
471
472     child = (struct v3_xml *)V3_Malloc(sizeof(struct v3_xml));
473
474     if (!child) {
475         PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml_add_child\n");
476         return NULL;
477     }
478
479     memset(child, 0, sizeof(struct v3_xml));
480
481     child->name = (char *)name;
482     child->attr = empty_attrib_list;
483     child->txt = "";
484
485     return v3_xml_insert(child, xml, off);
486 }
487
488
489 // called when parser finds start of new tag
490 static void v3_xml_open_tag(struct v3_xml_root * root, char * name, char ** attr) {
491     struct v3_xml * xml = root->cur;
492     
493     if (xml->name) {
494         xml = v3_xml_add_child(xml, name, strlen(xml->txt));
495     } else {
496         // first open tag
497         xml->name = name; 
498     }
499
500     xml->attr = attr;
501     root->cur = xml; // update tag insertion point
502 }
503
504
505
506
507
508
509
510 // parse the given xml string and return an v3_xml structure
511 static struct v3_xml * parse_str(char * buf, size_t len) {
512     struct v3_xml_root * root = (struct v3_xml_root *)v3_xml_new(NULL);
513     char quote_char;
514     char last_char; 
515     char * tag_ptr;
516     char ** attr; 
517     int attr_idx;
518
519     if (!buf) {
520         return NULL;
521     }
522
523     root->str_ptr = buf;
524
525     if (len == 0) {
526         v3_xml_err(root, NULL, "Empty XML String\n");
527         return NULL;
528     }
529
530     root->tmp_start = buf;
531     root->tmp_end = buf + len; // record start and end of work area
532     
533     last_char = buf[len - 1]; // save end char
534     buf[len - 1] = '\0'; // turn end char into null terminator
535
536     while ((*buf) && (*buf != '<')) {
537         // find first tag
538         buf++; 
539     }
540
541     if (*buf == '\0') {
542         v3_xml_err(root, buf, "root tag missing");
543         return NULL;
544     }
545
546     for (; ; ) {
547         attr = (char **)empty_attrib_list;
548         tag_ptr = ++buf; // skip first '<'
549         
550         if (isalpha(*buf) || (*buf == '_') || (*buf == ':') || (*buf < '\0')) {
551             // new tag
552
553             if (root->cur == NULL) {
554                 v3_xml_err(root, tag_ptr, "markup outside of root element");
555                 return NULL;
556             }
557
558             buf += strcspn(buf, V3_XML_WS "/>");
559
560             while (isspace(*buf)) {
561                 // null terminate tag name, 
562                 // this writes '\0' to spaces after first tag 
563                 *(buf++) = '\0';
564             }
565
566         
567
568             // attributes are name value pairs, 
569             //     2nd to last entry is null  (end of list)
570             //     last entry points to a string map marking whether values have been malloced...
571             // loop through attributes until hitting the closing bracket
572             for (attr_idx = 0; 
573                  (*buf) && (*buf != '/') && (*buf != '>'); 
574                  attr_idx += 2) {
575                 // buf is incremented later on
576                 // new attrib
577                 int attr_cnt = (attr_idx / 2) + 1;
578                 int val_idx = attr_idx + 1;
579                 int term_idx = attr_idx + 2;
580                 int last_idx = attr_idx + 3;
581
582                 // attr = allocated space
583                 // attr[val_idx] = mem for list of maloced vals
584                 if (attr_cnt > 1) {
585                     attr = tmp_realloc(attr,
586                                        (((attr_cnt - 1) * (2 * sizeof(char *))) + 
587                                         (2 * sizeof(char *))), 
588                                        ((attr_cnt * (2 * sizeof(char *))) + 
589                                         (2 * sizeof(char *))));
590
591                     if (!attr) {
592                         PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml parse string\n");
593                         return NULL;
594                     }
595
596                     attr[last_idx] = tmp_realloc(attr[last_idx - 2], 
597                                                  attr_cnt,
598                                                  (attr_cnt + 1)); 
599
600                     if (!attr[last_idx]) {
601                         PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml parse string\n");
602                         return NULL;
603                     }
604
605                 } else {
606                     attr = V3_Malloc(4 * sizeof(char *)); 
607                     if (!attr) {
608                         PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml parse string\n");
609                         return NULL;
610                     }
611                     attr[last_idx] = V3_Malloc(2);
612                     if (!attr[last_idx]) {
613                         PrintError(VM_NONE, VCORE_NONE, "Cannot alloocate in xml parse string\n");
614                         return NULL;
615                     }
616                 }
617
618                 attr[attr_idx] = buf; // set attribute name
619                 attr[val_idx] = ""; // temporary attribute value
620                 attr[term_idx] = NULL; // null terminate list
621                 strcpy(attr[last_idx] + attr_cnt, " "); // value is not malloc'd, offset into the stringmap
622
623                 buf += strcspn(buf, V3_XML_WS "=/>");
624
625                 if ((*buf == '=') || isspace(*buf)) {
626
627                     *(buf++) = '\0'; // null terminate tag attribute name
628
629                     // eat whitespace (and more multiple '=' ?)
630                     buf += strspn(buf, V3_XML_WS "=");
631
632                     quote_char = *buf;
633
634                     if ((quote_char == '"') || (quote_char == '\'')) { // attribute value
635                         attr[val_idx] = ++buf;
636
637                         while ((*buf) && (*buf != quote_char)) {
638                             buf++;
639                         }
640
641                         if (*buf) {
642                             // null terminate attribute val
643                             *(buf++) = '\0';
644                         } else {
645                             v3_xml_free_attr(attr);
646                             v3_xml_err(root, tag_ptr, "missing %c", quote_char);
647                             return NULL;
648                         }
649
650                         attr[val_idx] = v3_xml_decode(attr[val_idx], ' ');
651                     }
652                 }
653
654                 while (isspace(*buf)) {
655                     buf++;
656                 }
657             }
658
659             if (*buf == '/') { 
660                 // self closing tag
661                 *(buf++) = '\0';
662                 
663                 if ( ((*buf) && (*buf != '>')) || 
664                      ((!*buf) && (last_char != '>'))) {
665
666                     if (attr_idx > 0) {
667                         v3_xml_free_attr(attr);
668                     }
669                     v3_xml_err(root, tag_ptr, "missing >");
670                     return NULL;
671                 }
672                 v3_xml_open_tag(root, tag_ptr, attr);
673                 v3_xml_close_tag(root, tag_ptr, buf);
674             } else if (((quote_char = *buf) == '>') || 
675                        ((!*buf) && (last_char == '>'))) {
676                 // open tag
677                 *buf = '\0'; // temporarily null terminate tag name
678                 v3_xml_open_tag(root, tag_ptr, attr);
679                 *buf = quote_char;
680             } else {
681                 if (attr_idx > 0) {
682                     v3_xml_free_attr(attr);
683                 }
684                 v3_xml_err(root, tag_ptr, "missing >"); 
685                 return NULL;
686             }
687         } else if (*buf == '/') { 
688             // close tag
689             
690             buf += strcspn(tag_ptr = buf + 1, V3_XML_WS ">") + 1;
691             
692             quote_char = *buf;
693             if ((*buf == '\0') && (last_char != '>')) {
694                 v3_xml_err(root, tag_ptr, "missing >");
695                 return NULL;
696             }
697
698             *buf = '\0'; // temporarily null terminate tag name
699             
700             if (v3_xml_close_tag(root, tag_ptr, buf) == -1) {
701                 return NULL;
702             }
703
704             *buf = quote_char;
705             if (isspace(*buf)) {
706                 buf += strspn(buf, V3_XML_WS);
707             }
708         } else if (strncmp(buf, "!--", 3) == 0) {
709             // xml comment
710             if ( ((buf = strstr(buf + 3, "--")) == 0) || 
711                  ((*(buf += 2) != '>') && (*buf)) ||
712                  ((!*buf) && (last_char != '>'))) {
713                 v3_xml_err(root, tag_ptr, "unclosed <!--");
714                 return NULL;
715             }
716         } else if (! strncmp(buf, "![CDATA[", 8)) { 
717             // cdata
718             if ((buf = strstr(buf, "]]>"))) {
719                 v3_xml_char_content(root, tag_ptr + 8, (buf += 2) - tag_ptr - 10, 'c');
720             } else {
721                 v3_xml_err(root, tag_ptr, "unclosed <![CDATA[");
722                 return NULL;
723             }
724         } else {
725             v3_xml_err(root, tag_ptr, "unexpected <");
726             return NULL;
727         }
728
729         if (! buf || ! *buf) {
730             break;
731         }
732
733         *buf = '\0';
734         tag_ptr = ++buf;
735
736         /* Eat leading whitespace */
737         while (*buf && isspace(*buf)) {
738             buf++;
739         }
740
741         if (*buf && (*buf != '<')) { 
742             // tag character content
743             while (*buf && (*buf != '<')) {
744                 buf++;
745             }
746
747             if (*buf) { 
748                 v3_xml_char_content(root, tag_ptr, buf - tag_ptr, '&');
749             } else {
750                 break;
751             }
752         } else if (*buf == '\0') {
753             break;
754         }
755     }
756
757     if (root->cur == NULL) {
758         return &root->xml;
759     } else if (root->cur->name == NULL) {
760         v3_xml_err(root, tag_ptr, "root tag missing");
761         return NULL;
762     } else {
763         v3_xml_err(root, tag_ptr, "unclosed tag <%s>", root->cur->name);
764         return NULL;
765     }
766 }
767
768
769 struct v3_xml * v3_xml_parse(char * buf) {
770     int str_len = 0;
771     char * xml_buf = NULL;
772
773     if (!buf) {
774         return NULL;
775     }
776
777     str_len = strlen(buf);
778     xml_buf = (char *)V3_Malloc(str_len + 1);
779
780     if (!xml_buf) {
781         PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml parse\n");
782         return NULL;
783     }
784
785     strcpy(xml_buf, buf);
786
787     return parse_str(xml_buf, str_len);
788 }
789
790
791
792 // free the memory allocated for the v3_xml structure
793 void v3_xml_free(struct v3_xml * xml) {
794     struct v3_xml_root * root = (struct v3_xml_root *)xml;
795
796     if (xml == NULL) {
797         return;
798     }
799
800     v3_xml_free(xml->child);
801     v3_xml_free(xml->ordered);
802
803     if (xml->parent == NULL) { 
804         // free root tag allocations
805         V3_Free(root->str_ptr); // malloced xml data
806     }
807
808     v3_xml_free_attr(xml->attr); // tag attributes
809
810
811
812     if ((xml->flags & V3_XML_TXTM)) {
813         // character content
814         V3_Free(xml->txt); 
815     }
816
817     if ((xml->flags & V3_XML_NAMEM)) {
818         // tag name
819         V3_Free(xml->name);
820     }
821
822     V3_Free(xml);
823 }
824
825
826
827
828
829 /* Adding XML data */
830
831
832
833
834 // sets the character content for the given tag and returns the tag
835 struct v3_xml *  v3_xml_set_txt(struct v3_xml * xml, const char *txt) {
836     if (! xml) {
837         return NULL;
838     }
839
840     if (xml->flags & V3_XML_TXTM) {
841         // existing txt was malloced
842         V3_Free(xml->txt); 
843     }
844
845     xml->flags &= ~V3_XML_TXTM;
846     xml->txt = (char *)txt;
847     return xml;
848 }
849
850 // Sets the given tag attribute or adds a new attribute if not found. A value
851 // of NULL will remove the specified attribute. Returns the tag given.
852 struct v3_xml * v3_xml_set_attr(struct v3_xml * xml, const char * name, const char * value) {
853     int l = 0;
854     int c;
855
856     if (! xml) {
857         return NULL;
858     }
859
860     while (xml->attr[l] && strcmp(xml->attr[l], name)) {
861         l += 2;
862     }
863
864     if (! xml->attr[l]) { 
865         // not found, add as new attribute
866         
867         if (! value) {
868             // nothing to do
869             return xml;
870         }
871        
872         if (xml->attr == V3_XML_NIL) { 
873             // first attribute
874             xml->attr = V3_Malloc(4 * sizeof(char *));
875
876             if (!xml->attr) {
877                 PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml set attr\n");
878                 return NULL;
879             }
880
881             // empty list of malloced names/vals
882             xml->attr[1] = strdup(""); 
883
884             if (!xml->attr[1]) {
885                 PrintError(VM_NONE, VCORE_NONE, "Cannot strdup in xml set attr\n");
886                 return NULL;
887             }
888
889         } else {
890             xml->attr = tmp_realloc(xml->attr, l * sizeof(char *), (l + 4) * sizeof(char *));
891
892             if (!xml->attr) {
893                 PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml set attr\n");
894                 return NULL;
895             }
896         }
897
898         // set attribute name
899         xml->attr[l] = (char *)name; 
900
901         // null terminate attribute list
902         xml->attr[l + 2] = NULL; 
903
904         xml->attr[l + 3] = tmp_realloc(xml->attr[l + 1],
905                                        strlen(xml->attr[l + 1]),
906                                        (c = strlen(xml->attr[l + 1])) + 2);
907
908
909         if (!xml->attr[l + 3]) {
910             PrintError(VM_NONE, VCORE_NONE, "Cannot reallocate in xml set attr\n");
911             return NULL;
912         }
913
914         // set name/value as not malloced
915         strcpy(xml->attr[l + 3] + c, " "); 
916
917         if (xml->flags & V3_XML_DUP) {
918             xml->attr[l + 3][c] = V3_XML_NAMEM;
919         }
920     } else if (xml->flags & V3_XML_DUP) {
921         // name was strduped
922         V3_Free((char *)name); 
923     }
924
925
926     // find end of attribute list
927     for (c = l; xml->attr[c]; c += 2); 
928
929     if (xml->attr[c + 1][l / 2] & V3_XML_TXTM) {
930         //old val
931         V3_Free(xml->attr[l + 1]); 
932     }
933
934     if (xml->flags & V3_XML_DUP) {
935         xml->attr[c + 1][l / 2] |= V3_XML_TXTM;
936     } else {
937         xml->attr[c + 1][l / 2] &= ~V3_XML_TXTM;
938     }
939
940
941     if (value) {
942         // set attribute value
943         xml->attr[l + 1] = (char *)value; 
944     } else { 
945         // remove attribute
946         
947         if (xml->attr[c + 1][l / 2] & V3_XML_NAMEM) {
948             V3_Free(xml->attr[l]);
949         }
950
951         memmove(xml->attr + l, xml->attr + l + 2, (c - l + 2) * sizeof(char*));
952
953         xml->attr = tmp_realloc(xml->attr, c * sizeof(char *), (c + 2) * sizeof(char *));
954
955         // fix list of which name/vals are malloced
956         memmove(xml->attr[c + 1] + (l / 2), xml->attr[c + 1] + (l / 2) + 1,
957                 (c / 2) - (l / 2)); 
958     }
959
960     // clear strdup() flag
961     xml->flags &= ~V3_XML_DUP; 
962
963     return xml;
964 }
965
966 // removes a tag along with its subtags without freeing its memory
967 struct v3_xml * v3_xml_cut(struct v3_xml * xml) {
968     struct v3_xml * cur;
969
970     if (! xml) {
971         // nothing to do
972         return NULL; 
973     }
974
975     if (xml->next) {
976         // patch sibling list
977         xml->next->sibling = xml->sibling; 
978     }
979
980
981     if (xml->parent) { 
982         // not root tag
983
984         // find head of subtag list
985         cur = xml->parent->child; 
986
987         if (cur == xml) {
988             // first subtag
989             xml->parent->child = xml->ordered; 
990         } else { 
991         // not first subtag
992
993             while (cur->ordered != xml) {
994                 cur = cur->ordered;
995             }
996
997             // patch ordered list
998             cur->ordered = cur->ordered->ordered; 
999
1000             // go back to head of subtag list
1001             cur = xml->parent->child; 
1002
1003             if (strcmp(cur->name, xml->name)) {
1004                 // not in first sibling list
1005
1006                 while (strcmp(cur->sibling->name, xml->name)) {
1007                     cur = cur->sibling;
1008                 }
1009
1010                 if (cur->sibling == xml) { 
1011                     // first of a sibling list
1012                     cur->sibling = (xml->next) ? xml->next
1013                                                : cur->sibling->sibling;
1014                 } else {
1015                     // not first of a sibling list
1016                     cur = cur->sibling;
1017                 }
1018             }
1019
1020             while (cur->next && cur->next != xml) {
1021                 cur = cur->next;
1022             }
1023
1024             if (cur->next) {
1025                 // patch next list
1026                 cur->next = cur->next->next; 
1027             }
1028         } 
1029    }
1030     xml->ordered = xml->sibling = xml->next = NULL;
1031     return xml;
1032 }
1033
1034
1035
1036
1037 /* ************************** */
1038 /* *** XML ENCODING       *** */
1039 /* ************************** */
1040
1041 // Encodes ampersand sequences appending the results to *dst, reallocating *dst
1042 // if length excedes max. a is non-zero for attribute encoding. Returns *dst
1043 static char *ampencode(const char *s, size_t len, char **dst, size_t *dlen,
1044                       size_t * max, short a)
1045 {
1046     const char * e;
1047     
1048     for (e = s + len; s != e; s++) {
1049         while (*dlen + 10 > *max) {
1050             *dst = tmp_realloc(*dst, *max, *max + V3_XML_BUFSIZE);
1051             *max += V3_XML_BUFSIZE;
1052         }
1053
1054         switch (*s) {
1055         case '\0': return *dst;
1056         case '&': *dlen += sprintf(*dst + *dlen, "&amp;"); break;
1057         case '<': *dlen += sprintf(*dst + *dlen, "&lt;"); break;
1058         case '>': *dlen += sprintf(*dst + *dlen, "&gt;"); break;
1059         case '"': *dlen += sprintf(*dst + *dlen, (a) ? "&quot;" : "\""); break;
1060         case '\n': *dlen += sprintf(*dst + *dlen, (a) ? "&#xA;" : "\n"); break;
1061         case '\t': *dlen += sprintf(*dst + *dlen, (a) ? "&#x9;" : "\t"); break;
1062         case '\r': *dlen += sprintf(*dst + *dlen, "&#xD;"); break;
1063         default: (*dst)[(*dlen)++] = *s;
1064         }
1065     }
1066     return *dst;
1067 }
1068
1069
1070
1071 // Recursively converts each tag to xml appending it to *s. Reallocates *s if
1072 // its length excedes max. start is the location of the previous tag in the
1073 // parent tag's character content. Returns *s.
1074 static char *toxml_r(struct v3_xml * xml, char **s, size_t *len, size_t *max,
1075                     size_t start) {
1076     int i;
1077     char *txt = (xml->parent) ? xml->parent->txt : "";
1078     size_t off = 0;
1079
1080     // parent character content up to this tag
1081     *s = ampencode(txt + start, xml->off - start, s, len, max, 0);
1082
1083     while (*len + strlen(xml->name) + 4 > *max) {
1084         // reallocate s
1085         *s = tmp_realloc(*s, *max, *max + V3_XML_BUFSIZE);
1086         *max += V3_XML_BUFSIZE;
1087     }
1088
1089
1090     *len += sprintf(*s + *len, "<%s", xml->name); // open tag
1091     for (i = 0; xml->attr[i]; i += 2) { // tag attributes
1092         if (v3_xml_attr(xml, xml->attr[i]) != xml->attr[i + 1]) continue;
1093         while (*len + strlen(xml->attr[i]) + 7 > *max) {
1094             // reallocate s
1095             *s = tmp_realloc(*s, *max, *max + V3_XML_BUFSIZE);
1096             *max += V3_XML_BUFSIZE;
1097         }
1098
1099         *len += sprintf(*s + *len, " %s=\"", xml->attr[i]);
1100         ampencode(xml->attr[i + 1], -1, s, len, max, 1);
1101         *len += sprintf(*s + *len, "\"");
1102     }
1103
1104   
1105     *len += sprintf(*s + *len, ">");
1106
1107     *s = (xml->child) ? toxml_r(xml->child, s, len, max, 0) //child
1108                       : ampencode(xml->txt, -1, s, len, max, 0);  //data
1109     
1110     while (*len + strlen(xml->name) + 4 > *max) {
1111         // reallocate s
1112         *s = tmp_realloc(*s, *max, *max + V3_XML_BUFSIZE);
1113         *max += V3_XML_BUFSIZE;
1114     }
1115
1116     *len += sprintf(*s + *len, "</%s>", xml->name); // close tag
1117
1118     while (off < xml->off && txt[off]) off++; // make sure off is within bounds
1119     return (xml->ordered) ? toxml_r(xml->ordered, s, len, max, off)
1120                           : ampencode(txt + off, -1, s, len, max, 0);
1121 }
1122
1123 // Converts an xml structure back to xml. Returns a string of xml data that
1124 // must be freed.
1125 char * v3_xml_tostr(struct v3_xml * xml) {
1126     struct v3_xml * p = (xml) ? xml->parent : NULL;
1127     struct v3_xml * o = (xml) ? xml->ordered : NULL;
1128     struct v3_xml_root * root = (struct v3_xml_root *)xml;
1129     size_t len = 0, max = V3_XML_BUFSIZE;
1130     char *s = V3_Malloc(max);
1131
1132     if (!s) {
1133         PrintError(VM_NONE, VCORE_NONE, "Cannot allocate in xml tostrr\n");
1134         return NULL;
1135     }
1136
1137     strcpy(s, "");
1138
1139     if (! xml || ! xml->name) return tmp_realloc(s, max, len + 1);
1140     while (root->xml.parent) root = (struct v3_xml_root *)root->xml.parent; // root tag
1141
1142
1143     xml->parent = xml->ordered = NULL;
1144     s = toxml_r(xml, &s, &len, &max, 0);
1145     xml->parent = p;
1146     xml->ordered = o;
1147
1148
1149     return tmp_realloc(s, max, len + 1);
1150 }