1 /**
2 * Copyright (c) 2012 ooxi/xml.c
3 * https://github.com/ooxi/xml.c
4 *
5 * This software is provided 'as-is', without any express or implied warranty.
6 * In no event will the authors be held liable for any damages arising from the
7 * use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software in a
15 * product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24 #include "config.h"
25
26 #ifdef XML_PARSER_VERBOSE
27 #include <alloca.h>
28 #endif
29
30 #include <ctype.h>
31 #include <stdarg.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35
36 #include "common.h"
37 #include "xml.h"
38
39
40 /*
41 * public domain strtok_r() by Charlie Gordon
42 *
43 * from comp.lang.c 9/14/2007
44 *
45 * http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684
46 *
47 * (Declaration that it's public domain):
48 * http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c
49 */
xml_strtok_r(char * str,const char * delim,char ** nextp)50 static char* xml_strtok_r(char *str, const char *delim, char **nextp) {
51 char *ret;
52
53 if (str == NULL) {
54 str = *nextp;
55 }
56
57 str += strspn(str, delim);
58
59 if (*str == '\0') {
60 return NULL;
61 }
62
63 ret = str;
64
65 str += strcspn(str, delim);
66
67 if (*str) {
68 *str++ = '\0';
69 }
70
71 *nextp = str;
72
73 return ret;
74 }
75
76
77
78
79
80
81 /**
82 * [OPAQUE API]
83 *
84 * UTF-8 text
85 */
86 struct xml_string {
87 uint8_t const* buffer;
88 size_t length;
89 };
90
91 /**
92 * [OPAQUE API]
93 *
94 * An xml_attribute may contain text content.
95 */
96 struct xml_attribute {
97 struct xml_string* name;
98 struct xml_string* content;
99 };
100
101 /**
102 * [OPAQUE API]
103 *
104 * An xml_node will always contain a tag name, a 0-terminated list of attributes
105 * and a 0-terminated list of children. Moreover it may contain text content.
106 */
107 struct xml_node {
108 struct xml_string* name;
109 struct xml_string* content;
110 struct xml_attribute** attributes;
111 struct xml_node** children;
112 };
113
114 /**
115 * [OPAQUE API]
116 *
117 * An xml_document simply contains the root node and the underlying buffer
118 */
119 struct xml_document {
120 struct {
121 uint8_t* buffer;
122 size_t length;
123 } buffer;
124
125 struct xml_node* root;
126 };
127
128
129
130
131
132 /**
133 * [PRIVATE]
134 *
135 * Parser context
136 */
137 struct xml_parser {
138 uint8_t* buffer;
139 size_t position;
140 size_t length;
141 };
142
143 /**
144 * [PRIVATE]
145 *
146 * Character offsets
147 */
148 enum xml_parser_offset {
149 NO_CHARACTER = -1,
150 CURRENT_CHARACTER = 0,
151 NEXT_CHARACTER = 1,
152 };
153
154
155
156
157
158 /**
159 * [PRIVATE]
160 *
161 * @return Number of attributes in 0-terminated array
162 */
get_zero_terminated_array_attributes(struct xml_attribute ** attributes)163 static size_t get_zero_terminated_array_attributes(struct xml_attribute** attributes) {
164 size_t elements = 0;
165
166 while (attributes[elements]) {
167 ++elements;
168 }
169
170 return elements;
171 }
172
173
174
175 /**
176 * [PRIVATE]
177 *
178 * @return Number of nodes in 0-terminated array
179 */
get_zero_terminated_array_nodes(struct xml_node ** nodes)180 static size_t get_zero_terminated_array_nodes(struct xml_node** nodes) {
181 size_t elements = 0;
182
183 while (nodes[elements]) {
184 ++elements;
185 }
186
187 return elements;
188 }
189
190
191
192 /**
193 * [PRIVATE]
194 *
195 * @warning No UTF conversions will be attempted
196 *
197 * @return true iff a == b
198 */
xml_string_equals(struct xml_string * a,struct xml_string * b)199 static _Bool xml_string_equals(struct xml_string* a, struct xml_string* b) {
200
201 size_t i = 0;
202 if (a->length != b->length) {
203 return false;
204 }
205
206 for (; i < a->length; ++i) {
207 if (a->buffer[i] != b->buffer[i]) {
208 return false;
209 }
210 }
211
212 return true;
213 }
214
215
216
217 /**
218 * [PRIVATE]
219 */
xml_string_clone(struct xml_string * s)220 static uint8_t* xml_string_clone(struct xml_string* s) {
221 uint8_t* clone;
222 if (!s) {
223 return 0;
224 }
225
226 clone = ms3_ccalloc(s->length + 1, sizeof(uint8_t));
227
228 xml_string_copy(s, clone, s->length);
229 clone[s->length] = 0;
230
231 return clone;
232 }
233
234
235
236 /**
237 * [PRIVATE]
238 *
239 * Frees the resources allocated by the string
240 *
241 * @warning `buffer` must _not_ be freed, since it is a reference to the
242 * document's buffer
243 */
xml_string_free(struct xml_string * string)244 static void xml_string_free(struct xml_string* string) {
245 ms3_cfree(string);
246 }
247
248
249
250 /**
251 * [PRIVATE]
252 *
253 * Frees the resources allocated by the attribute
254 */
xml_attribute_free(struct xml_attribute * attribute)255 static void xml_attribute_free(struct xml_attribute* attribute) {
256 if(attribute->name) {
257 xml_string_free(attribute->name);
258 }
259 if(attribute->content) {
260 xml_string_free(attribute->content);
261 }
262 ms3_cfree(attribute);
263 }
264
265 /**
266 * [PRIVATE]
267 *
268 * Frees the resources allocated by the node
269 */
xml_node_free(struct xml_node * node)270 static void xml_node_free(struct xml_node* node) {
271 struct xml_attribute** at;
272 struct xml_node** it;
273
274 xml_string_free(node->name);
275
276 if (node->content) {
277 xml_string_free(node->content);
278 }
279
280 at = node->attributes;
281 while(*at) {
282 xml_attribute_free(*at);
283 ++at;
284 }
285 ms3_cfree(node->attributes);
286
287 it = node->children;
288 while (*it) {
289 xml_node_free(*it);
290 ++it;
291 }
292 ms3_cfree(node->children);
293
294 ms3_cfree(node);
295 }
296
297
298
299 /**
300 * [PRIVATE]
301 *
302 * Echos the parsers call stack for debugging purposes
303 */
304 #ifdef XML_PARSER_VERBOSE
xml_parser_info(struct xml_parser * parser,char const * message)305 static void xml_parser_info(struct xml_parser* parser, char const* message) {
306 fprintf(stdout, "xml_parser_info %s\n", message);
307 }
308 #else
309 #define xml_parser_info(parser, message) {}
310 #endif
311
312
313
314 /**
315 * [PRIVATE]
316 *
317 * Echos an error regarding the parser's source to the console
318 */
319
320 #define tmp_min(X,Y) ((X) < (Y) ? (X) : (Y))
321
322
xml_parser_error(struct xml_parser * parser,enum xml_parser_offset offset,char const * message)323 static void xml_parser_error(struct xml_parser* parser, enum xml_parser_offset offset, char const* message) {
324 int row = 0;
325 int column = 0;
326
327 size_t character = tmp_min(parser->length, parser->position + offset);
328 size_t position = 0; for (; position < character; ++position) {
329 column++;
330
331 if ('\n' == parser->buffer[position]) {
332 row++;
333 column = 0;
334 }
335 }
336
337 if (NO_CHARACTER != offset) {
338 fprintf(stderr, "xml_parser_error at %i:%i (is %c): %s\n",
339 row + 1, column, parser->buffer[character], message
340 );
341 } else {
342 fprintf(stderr, "xml_parser_error at %i:%i: %s\n",
343 row + 1, column, message
344 );
345 }
346 }
347
348
349
350 /**
351 * [PRIVATE]
352 *
353 * Returns the n-th not-whitespace byte in parser and 0 if such a byte does not
354 * exist
355 */
xml_parser_peek(struct xml_parser * parser,size_t n)356 static uint8_t xml_parser_peek(struct xml_parser* parser, size_t n) {
357 size_t position = parser->position;
358
359 while (position < parser->length) {
360 if (!isspace(parser->buffer[position])) {
361 if (n == 0) {
362 return parser->buffer[position];
363 } else {
364 --n;
365 }
366 }
367
368 position++;
369 }
370
371 return 0;
372 }
373
374
375
376 /**
377 * [PRIVATE]
378 *
379 * Moves the parser's position n bytes. If the new position would be out of
380 * bounds, it will be converted to the bounds itself
381 */
xml_parser_consume(struct xml_parser * parser,size_t n)382 static void xml_parser_consume(struct xml_parser* parser, size_t n) {
383
384 /* Debug information
385 */
386 #ifdef XML_PARSER_VERBOSE
387 #define min(X,Y) ((X) < (Y) ? (X) : (Y))
388 char* consumed = alloca((n + 1) * sizeof(char));
389 memcpy(consumed, &parser->buffer[parser->position], min(n, parser->length - parser->position));
390 consumed[n] = 0;
391 #undef min
392
393 size_t message_buffer_length = 512;
394 char* message_buffer = alloca(512 * sizeof(char));
395 snprintf(message_buffer, message_buffer_length, "Consuming %li bytes \"%s\"", (long)n, consumed);
396 message_buffer[message_buffer_length - 1] = 0;
397
398 xml_parser_info(parser, message_buffer);
399 #endif
400
401
402 /* Move the position forward
403 */
404 parser->position += n;
405
406 /* Don't go too far
407 *
408 * @warning Valid because parser->length must be greater than 0
409 */
410 if (parser->position >= parser->length) {
411 parser->position = parser->length - 1;
412 }
413 }
414
415
416
417 /**
418 * [PRIVATE]
419 *
420 * Skips to the next non-whitespace character
421 */
xml_skip_whitespace(struct xml_parser * parser)422 static void xml_skip_whitespace(struct xml_parser* parser) {
423 xml_parser_info(parser, "whitespace");
424
425 while (isspace(parser->buffer[parser->position])) {
426 if (parser->position + 1 >= parser->length) {
427 return;
428 } else {
429 parser->position++;
430 }
431 }
432 }
433
434
435
436 /**
437 * [PRIVATE]
438 *
439 * Finds and creates all attributes on the given node.
440 *
441 * @author Blake Felt
442 * @see https://github.com/Molorius
443 */
xml_find_attributes(struct xml_parser * parser,struct xml_string * tag_open)444 static struct xml_attribute** xml_find_attributes(struct xml_parser* parser, struct xml_string* tag_open) {
445 char* tmp;
446 char* rest = NULL;
447 char* token;
448 char* str_name;
449 char* str_content;
450 const unsigned char* start_name;
451 const unsigned char* start_content;
452 size_t old_elements;
453 size_t new_elements;
454 struct xml_attribute* new_attribute;
455 struct xml_attribute** attributes;
456 long position;
457
458 (void) parser; // clang for some reason thinks this isn't used
459 xml_parser_info(parser, "find_attributes");
460 attributes = ms3_ccalloc(1, sizeof(struct xml_attribute*));
461 attributes[0] = 0;
462
463 tmp = (char*) xml_string_clone(tag_open);
464
465 token = xml_strtok_r(tmp, " ", &rest); // skip the first value
466 if(token == NULL) {
467 goto cleanup;
468 }
469 tag_open->length = strlen(token);
470
471 for(token=xml_strtok_r(NULL," ", &rest); token!=NULL; token=xml_strtok_r(NULL," ", &rest)) {
472 str_name = ms3_cmalloc(strlen(token)+1);
473 str_content = ms3_cmalloc(strlen(token)+1);
474 // %s=\"%s\" wasn't working for some reason, ugly hack to make it work
475 if(sscanf(token, "%[^=]=\"%[^\"]", str_name, str_content) != 2) {
476 if(sscanf(token, "%[^=]=\'%[^\']", str_name, str_content) != 2) {
477 ms3_cfree(str_name);
478 ms3_cfree(str_content);
479 continue;
480 }
481 }
482 position = token-tmp;
483 start_name = &tag_open->buffer[position];
484 start_content = &tag_open->buffer[position + strlen(str_name) + 2];
485
486 new_attribute = ms3_cmalloc(sizeof(struct xml_attribute));
487 new_attribute->name = ms3_cmalloc(sizeof(struct xml_string));
488 new_attribute->name->buffer = (unsigned char*)start_name;
489 new_attribute->name->length = strlen(str_name);
490 new_attribute->content = ms3_cmalloc(sizeof(struct xml_string));
491 new_attribute->content->buffer = (unsigned char*)start_content;
492 new_attribute->content->length = strlen(str_content);
493
494 old_elements = get_zero_terminated_array_attributes(attributes);
495 new_elements = old_elements + 1;
496 attributes = ms3_crealloc(attributes, (new_elements+1)*sizeof(struct xml_attributes*));
497
498 attributes[new_elements-1] = new_attribute;
499 attributes[new_elements] = 0;
500
501
502 ms3_cfree(str_name);
503 ms3_cfree(str_content);
504 }
505
506 cleanup:
507 ms3_cfree(tmp);
508 return attributes;
509 }
510
511
512
513 /**
514 * [PRIVATE]
515 *
516 * Parses the name out of the an XML tag's ending
517 *
518 * ---( Example )---
519 * tag_name>
520 * ---
521 */
xml_parse_tag_end(struct xml_parser * parser)522 static struct xml_string* xml_parse_tag_end(struct xml_parser* parser) {
523 size_t start;
524 size_t length = 0;
525 struct xml_string* name;
526
527 xml_parser_info(parser, "tag_end");
528 start = parser->position;
529
530 /* Parse until `>' or a whitespace is reached
531 */
532 while (start + length < parser->length) {
533 uint8_t current = xml_parser_peek(parser, CURRENT_CHARACTER);
534
535 if (('>' == current) || isspace(current)) {
536 break;
537 } else {
538 xml_parser_consume(parser, 1);
539 length++;
540 }
541 }
542
543 /* Consume `>'
544 */
545 if ('>' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
546 xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_end::expected tag end");
547 return 0;
548 }
549 xml_parser_consume(parser, 1);
550
551 /* Return parsed tag name
552 */
553 name = ms3_cmalloc(sizeof(struct xml_string));
554 name->buffer = &parser->buffer[start];
555 name->length = length;
556 return name;
557 }
558
559 /**
560 * [PRIVATE]
561 *
562 * Parses an opening XML tag without attributes
563 *
564 * ---( Example )---
565 * <tag_name>
566 * ---
567 */
xml_parse_tag_open(struct xml_parser * parser)568 static struct xml_string* xml_parse_tag_open(struct xml_parser* parser) {
569 xml_parser_info(parser, "tag_open");
570 xml_skip_whitespace(parser);
571
572 /* Consume `<'
573 */
574 if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
575 xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_open::expected opening tag");
576 return 0;
577 }
578 xml_parser_consume(parser, 1);
579
580 /* Consume tag name
581 */
582 return xml_parse_tag_end(parser);
583 }
584
585
586
587 /**
588 * [PRIVATE]
589 *
590 * Parses an closing XML tag without attributes
591 *
592 * ---( Example )---
593 * </tag_name>
594 * ---
595 */
xml_parse_tag_close(struct xml_parser * parser)596 static struct xml_string* xml_parse_tag_close(struct xml_parser* parser) {
597 xml_parser_info(parser, "tag_close");
598 xml_skip_whitespace(parser);
599
600 /* Consume `</'
601 */
602 if ( ('<' != xml_parser_peek(parser, CURRENT_CHARACTER))
603 || ('/' != xml_parser_peek(parser, NEXT_CHARACTER))) {
604
605 if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
606 xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_close::expected closing tag `<'");
607 }
608 if ('/' != xml_parser_peek(parser, NEXT_CHARACTER)) {
609 xml_parser_error(parser, NEXT_CHARACTER, "xml_parse_tag_close::expected closing tag `/'");
610 }
611
612 return 0;
613 }
614 xml_parser_consume(parser, 2);
615
616 /* Consume tag name
617 */
618 return xml_parse_tag_end(parser);
619 }
620
621
622
623 /**
624 * [PRIVATE]
625 *
626 * Parses a tag's content
627 *
628 * ---( Example )---
629 * this is
630 * a
631 * tag {} content
632 * ---
633 *
634 * @warning CDATA etc. is _not_ and will never be supported
635 */
xml_parse_content(struct xml_parser * parser)636 static struct xml_string* xml_parse_content(struct xml_parser* parser) {
637 size_t start;
638 size_t length = 0;
639 struct xml_string* content;
640
641 xml_parser_info(parser, "content");
642
643 /* Whitespace will be ignored
644 */
645 xml_skip_whitespace(parser);
646
647 start = parser->position;
648
649 /* Consume until `<' is reached
650 */
651 while (start + length < parser->length) {
652 uint8_t current = xml_parser_peek(parser, CURRENT_CHARACTER);
653
654 if ('<' == current) {
655 break;
656 } else {
657 xml_parser_consume(parser, 1);
658 length++;
659 }
660 }
661
662 /* Next character must be an `<' or we have reached end of file
663 */
664 if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
665 xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_content::expected <");
666 return 0;
667 }
668
669 /* Ignore tailing whitespace
670 */
671 while ((length > 0) && isspace(parser->buffer[start + length - 1])) {
672 length--;
673 }
674
675 /* Return text
676 */
677 content = ms3_cmalloc(sizeof(struct xml_string));
678 content->buffer = &parser->buffer[start];
679 content->length = length;
680 return content;
681 }
682
683
684
685 /**
686 * [PRIVATE]
687 *
688 * Parses an XML fragment node
689 *
690 * ---( Example without children )---
691 * <Node>Text</Node>
692 * ---
693 *
694 * ---( Example with children )---
695 * <Parent>
696 * <Child>Text</Child>
697 * <Child>Text</Child>
698 * <Test>Content</Test>
699 * </Parent>
700 * ---
701 */
xml_parse_node(struct xml_parser * parser)702 static struct xml_node* xml_parse_node(struct xml_parser* parser) {
703 /* Setup variables
704 */
705 struct xml_string* tag_open = 0;
706 struct xml_string* tag_close = 0;
707 struct xml_string* content = 0;
708 struct xml_node* node;
709 struct xml_node** it;
710 size_t original_length;
711 struct xml_attribute** attributes;
712 struct xml_node** children = ms3_ccalloc(1, sizeof(struct xml_node*));
713 children[0] = 0;
714
715 xml_parser_info(parser, "node");
716
717 /* Parse open tag
718 */
719 tag_open = xml_parse_tag_open(parser);
720 if (!tag_open) {
721 xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag_open");
722 goto exit_failure;
723 }
724
725 original_length = tag_open->length;
726 attributes = xml_find_attributes(parser, tag_open);
727
728 /* If tag ends with `/' it's self closing, skip content lookup */
729 if (tag_open->length > 0 && '/' == tag_open->buffer[original_length - 1]) {
730 /* Drop `/'
731 */
732 goto node_creation;
733 }
734
735 /* If the content does not start with '<', a text content is assumed
736 */
737 if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
738 content = xml_parse_content(parser);
739
740 if (!content) {
741 xml_parser_error(parser, 0, "xml_parse_node::content");
742 goto exit_failure;
743 }
744
745
746 /* Otherwise children are to be expected
747 */
748 } else while ('/' != xml_parser_peek(parser, NEXT_CHARACTER)) {
749
750 /* Parse child node
751 */
752 struct xml_node* child = xml_parse_node(parser);
753 size_t old_elements, new_elements;
754
755 if (!child) {
756 xml_parser_error(parser, NEXT_CHARACTER, "xml_parse_node::child");
757 goto exit_failure;
758 }
759
760 /* Grow child array :)
761 */
762 old_elements = get_zero_terminated_array_nodes(children);
763 new_elements = old_elements + 1;
764 children = ms3_crealloc(children, (new_elements + 1) * sizeof(struct xml_node*));
765
766 /* Save child
767 */
768 children[new_elements - 1] = child;
769 children[new_elements] = 0;
770 }
771
772
773 /* Parse close tag
774 */
775 tag_close = xml_parse_tag_close(parser);
776 if (!tag_close) {
777 xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag_close");
778 goto exit_failure;
779 }
780
781
782 /* Close tag has to match open tag
783 */
784 if (!xml_string_equals(tag_open, tag_close)) {
785 xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag mismatch");
786 goto exit_failure;
787 }
788
789
790 /* Return parsed node
791 */
792 xml_string_free(tag_close);
793
794 node_creation:;
795 node = ms3_cmalloc(sizeof(struct xml_node));
796 node->name = tag_open;
797 node->content = content;
798 node->attributes = attributes;
799 node->children = children;
800 return node;
801
802
803 /* A failure occured, so free all allocalted resources
804 */
805 exit_failure:
806 if (tag_open) {
807 xml_string_free(tag_open);
808 }
809 if (tag_close) {
810 xml_string_free(tag_close);
811 }
812 if (content) {
813 xml_string_free(content);
814 }
815
816 it = children;
817 while (*it) {
818 xml_node_free(*it);
819 ++it;
820 }
821 ms3_cfree(children);
822
823 return 0;
824 }
825
826
827 /**
828 * [PRIVATE]
829 * Skips XML headers in <? text ?> format
830 */
xml_parse_skip_meta(struct xml_parser * parser)831 static void xml_parse_skip_meta(struct xml_parser* parser) {
832 if ('<' == xml_parser_peek(parser, CURRENT_CHARACTER) &&
833 '?' == xml_parser_peek(parser, NEXT_CHARACTER)) {
834 size_t pos = parser->position;
835 while (pos < parser->length) {
836 if ('?' == parser->buffer[pos] &&
837 '>' == parser->buffer[pos + 1]) {
838 parser->position = pos + 2;
839 return;
840 }
841 pos++;
842 }
843 }
844 }
845
846 /**
847 * [PUBLIC API]
848 */
xml_parse_document(uint8_t * buffer,size_t length)849 struct xml_document* xml_parse_document(uint8_t* buffer, size_t length) {
850
851 /* Initialize parser
852 */
853 struct xml_parser parser = {
854 .buffer = buffer,
855 .position = 0,
856 .length = length
857 };
858 struct xml_node* root;
859 struct xml_document* document;
860
861 /* An empty buffer can never contain a valid document
862 */
863 if (!length) {
864 xml_parser_error(&parser, NO_CHARACTER, "xml_parse_document::length equals zero");
865 return 0;
866 }
867
868 /* Parse the root node
869 */
870 xml_parse_skip_meta(&parser);
871 root = xml_parse_node(&parser);
872 if (!root) {
873 xml_parser_error(&parser, NO_CHARACTER, "xml_parse_document::parsing document failed");
874 return 0;
875 }
876
877 /* Return parsed document
878 */
879 document = ms3_cmalloc(sizeof(struct xml_document));
880 document->buffer.buffer = buffer;
881 document->buffer.length = length;
882 document->root = root;
883
884 return document;
885 }
886
887
888
889 /**
890 * [PUBLIC API]
891 */
xml_open_document(FILE * source)892 struct xml_document* xml_open_document(FILE* source) {
893
894 /* Prepare buffer
895 */
896 size_t const read_chunk = 1; // TODO 4096;
897
898 size_t document_length = 0;
899 size_t buffer_size = 1; // TODO 4069
900 struct xml_document* document;
901 uint8_t* buffer = ms3_cmalloc(buffer_size * sizeof(uint8_t));
902
903
904 /* Read hole file into buffer
905 */
906 while (!feof(source)) {
907 size_t read;
908 /* Reallocate buffer
909 */
910 if (buffer_size - document_length < read_chunk) {
911 buffer = ms3_crealloc(buffer, buffer_size + 2 * read_chunk);
912 buffer_size += 2 * read_chunk;
913 }
914
915 read = fread(&buffer[document_length],
916 sizeof(uint8_t), read_chunk,
917 source
918 );
919
920 document_length += read;
921 }
922 fclose(source);
923
924 /* Try to parse buffer
925 */
926 document = xml_parse_document(buffer, document_length);
927
928 if (!document) {
929 ms3_cfree(buffer);
930 return 0;
931 }
932 return document;
933 }
934
935
936
937 /**
938 * [PUBLIC API]
939 */
xml_document_free(struct xml_document * document,bool free_buffer)940 void xml_document_free(struct xml_document* document, bool free_buffer) {
941 xml_node_free(document->root);
942
943 if (free_buffer) {
944 ms3_cfree(document->buffer.buffer);
945 }
946 ms3_cfree(document);
947 }
948
949
950
951 /**
952 * [PUBLIC API]
953 */
xml_document_root(struct xml_document * document)954 struct xml_node* xml_document_root(struct xml_document* document) {
955 return document->root;
956 }
957
958
959
960 /**
961 * [PUBLIC API]
962 */
xml_node_name(struct xml_node * node)963 struct xml_string* xml_node_name(struct xml_node* node) {
964 return node->name;
965 }
966
xml_node_name_cmp(struct xml_node * node,const char * name)967 int xml_node_name_cmp(struct xml_node* node, const char *name) {
968 return strncmp((char*)node->name->buffer, name, node->name->length);
969 }
970
971 /**
972 * [PUBLIC API]
973 */
xml_node_content(struct xml_node * node)974 struct xml_string* xml_node_content(struct xml_node* node) {
975 return node->content;
976 }
977
978
979
980 /**
981 * [PUBLIC API]
982 *
983 * @warning O(n)
984 */
xml_node_children(struct xml_node * node)985 size_t xml_node_children(struct xml_node* node) {
986 return get_zero_terminated_array_nodes(node->children);
987 }
988
989
990
991 /**
992 * [PUBLIC API]
993 */
xml_node_child(struct xml_node * node,size_t child)994 struct xml_node* xml_node_child(struct xml_node* node, size_t child) {
995 if (child >= xml_node_children(node)) {
996 return 0;
997 }
998
999 return node->children[child];
1000 }
1001
1002
1003
1004 /**
1005 * [PUBLIC API]
1006 */
xml_node_attributes(struct xml_node * node)1007 size_t xml_node_attributes(struct xml_node* node) {
1008 return get_zero_terminated_array_attributes(node->attributes);
1009 }
1010
1011
1012
1013 /**
1014 * [PUBLIC API]
1015 */
xml_node_attribute_name(struct xml_node * node,size_t attribute)1016 struct xml_string* xml_node_attribute_name(struct xml_node* node, size_t attribute) {
1017 if(attribute >= xml_node_attributes(node)) {
1018 return 0;
1019 }
1020
1021 return node->attributes[attribute]->name;
1022 }
1023
1024
1025
1026 /**
1027 * [PUBLIC API]
1028 */
xml_node_attribute_content(struct xml_node * node,size_t attribute)1029 struct xml_string* xml_node_attribute_content(struct xml_node* node, size_t attribute) {
1030 if(attribute >= xml_node_attributes(node)) {
1031 return 0;
1032 }
1033
1034 return node->attributes[attribute]->content;
1035 }
1036
1037
1038
1039 /**
1040 * [PUBLIC API]
1041 */
xml_easy_child(struct xml_node * node,uint8_t const * child_name,...)1042 struct xml_node* xml_easy_child(struct xml_node* node, uint8_t const* child_name, ...) {
1043
1044 /* Find children, one by one
1045 */
1046 struct xml_node* current = node;
1047
1048 va_list arguments;
1049 va_start(arguments, child_name);
1050
1051
1052 /* Descent to current.child
1053 */
1054 while (child_name) {
1055
1056 /* Convert child_name to xml_string for easy comparison
1057 */
1058 struct xml_string cn = {
1059 .buffer = child_name,
1060 .length = strlen((const char*)child_name)
1061 };
1062
1063 /* Interate through all children
1064 */
1065 struct xml_node* next = 0;
1066
1067 size_t i = 0; for (; i < xml_node_children(current); ++i) {
1068 struct xml_node* child = xml_node_child(current, i);
1069
1070 if (xml_string_equals(xml_node_name(child), &cn)) {
1071 if (!next) {
1072 next = child;
1073
1074 /* Two children with the same name
1075 */
1076 } else {
1077 va_end(arguments);
1078 return 0;
1079 }
1080 }
1081 }
1082
1083 /* No child with that name found
1084 */
1085 if (!next) {
1086 va_end(arguments);
1087 return 0;
1088 }
1089 current = next;
1090
1091 /* Find name of next child
1092 */
1093 child_name = va_arg(arguments, uint8_t const*);
1094 }
1095 va_end(arguments);
1096
1097
1098 /* Return current element
1099 */
1100 return current;
1101 }
1102
1103
1104
1105 /**
1106 * [PUBLIC API]
1107 */
xml_easy_name(struct xml_node * node)1108 uint8_t* xml_easy_name(struct xml_node* node) {
1109 if (!node) {
1110 return 0;
1111 }
1112
1113 return xml_string_clone(xml_node_name(node));
1114 }
1115
1116
1117
1118 /**
1119 * [PUBLIC API]
1120 */
xml_easy_content(struct xml_node * node)1121 uint8_t* xml_easy_content(struct xml_node* node) {
1122 if (!node) {
1123 return 0;
1124 }
1125
1126 return xml_string_clone(xml_node_content(node));
1127 }
1128
1129
1130
1131 /**
1132 * [PUBLIC API]
1133 */
xml_string_length(struct xml_string * string)1134 size_t xml_string_length(struct xml_string* string) {
1135 if (!string) {
1136 return 0;
1137 }
1138 return string->length;
1139 }
1140
1141
1142
1143 /**
1144 * [PUBLIC API]
1145 */
xml_string_copy(struct xml_string * string,uint8_t * buffer,size_t length)1146 void xml_string_copy(struct xml_string* string, uint8_t* buffer, size_t length) {
1147 if (!string) {
1148 return;
1149 }
1150
1151 #define min(X,Y) ((X) < (Y) ? (X) : (Y))
1152 length = min(length, string->length);
1153 #undef min
1154
1155 memcpy(buffer, string->buffer, length);
1156 buffer[length]= '\0';
1157 }
1158