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