Simple XML subset parser comparable to glib's Markup parser, but without any dependencies in one self contained file. Forked from https://github.com/ooxi/xml.c
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1130 rivejä
21 KiB

  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. #include "xml.h"
  24. #ifdef XML_PARSER_VERBOSE
  25. #include <alloca.h>
  26. #endif
  27. #include <ctype.h>
  28. #ifndef __MACH__
  29. #include <malloc.h>
  30. #endif
  31. #include <stdarg.h>
  32. #include <stdbool.h>
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. /*
  36. * public domain strtok_r() by Charlie Gordon
  37. *
  38. * from comp.lang.c 9/14/2007
  39. *
  40. * http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684
  41. *
  42. * (Declaration that it's public domain):
  43. * http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c
  44. */
  45. static char* xml_strtok_r(char *str, const char *delim, char **nextp) {
  46. char *ret;
  47. if (str == NULL) {
  48. str = *nextp;
  49. }
  50. str += strspn(str, delim);
  51. if (*str == '\0') {
  52. return NULL;
  53. }
  54. ret = str;
  55. str += strcspn(str, delim);
  56. if (*str) {
  57. *str++ = '\0';
  58. }
  59. *nextp = str;
  60. return ret;
  61. }
  62. /**
  63. * [OPAQUE API]
  64. *
  65. * UTF-8 text
  66. */
  67. struct xml_string {
  68. uint8_t const* buffer;
  69. size_t length;
  70. };
  71. /**
  72. * [OPAQUE API]
  73. *
  74. * An xml_attribute may contain text content.
  75. */
  76. struct xml_attribute {
  77. struct xml_string* name;
  78. struct xml_string* content;
  79. };
  80. /**
  81. * [OPAQUE API]
  82. *
  83. * An xml_node will always contain a tag name, a 0-terminated list of attributes
  84. * and a 0-terminated list of children. Moreover it may contain text content.
  85. */
  86. struct xml_node {
  87. struct xml_string* name;
  88. struct xml_string* content;
  89. struct xml_attribute** attributes;
  90. struct xml_node** children;
  91. };
  92. /**
  93. * [OPAQUE API]
  94. *
  95. * An xml_document simply contains the root node and the underlying buffer
  96. */
  97. struct xml_document {
  98. struct {
  99. uint8_t* buffer;
  100. size_t length;
  101. } buffer;
  102. struct xml_node* root;
  103. };
  104. /**
  105. * [PRIVATE]
  106. *
  107. * Parser context
  108. */
  109. struct xml_parser {
  110. uint8_t* buffer;
  111. size_t position;
  112. size_t length;
  113. };
  114. /**
  115. * [PRIVATE]
  116. *
  117. * Character offsets
  118. */
  119. enum xml_parser_offset {
  120. NO_CHARACTER = -1,
  121. CURRENT_CHARACTER = 0,
  122. NEXT_CHARACTER = 1,
  123. };
  124. /**
  125. * [PRIVATE]
  126. *
  127. * @return Number of attributes in 0-terminated array
  128. */
  129. static size_t get_zero_terminated_array_attributes(struct xml_attribute** attributes) {
  130. size_t elements = 0;
  131. while (attributes[elements]) {
  132. ++elements;
  133. }
  134. return elements;
  135. }
  136. /**
  137. * [PRIVATE]
  138. *
  139. * @return Number of nodes in 0-terminated array
  140. */
  141. static size_t get_zero_terminated_array_nodes(struct xml_node** nodes) {
  142. size_t elements = 0;
  143. while (nodes[elements]) {
  144. ++elements;
  145. }
  146. return elements;
  147. }
  148. /**
  149. * [PRIVATE]
  150. *
  151. * @warning No UTF conversions will be attempted
  152. *
  153. * @return true iff a == b
  154. */
  155. static _Bool xml_string_equals(struct xml_string* a, struct xml_string* b) {
  156. if (a->length != b->length) {
  157. return false;
  158. }
  159. size_t i = 0; for (; i < a->length; ++i) {
  160. if (a->buffer[i] != b->buffer[i]) {
  161. return false;
  162. }
  163. }
  164. return true;
  165. }
  166. /**
  167. * [PRIVATE]
  168. */
  169. static uint8_t* xml_string_clone(struct xml_string* s) {
  170. if (!s) {
  171. return 0;
  172. }
  173. uint8_t* clone = calloc(s->length + 1, sizeof(uint8_t));
  174. xml_string_copy(s, clone, s->length);
  175. clone[s->length] = 0;
  176. return clone;
  177. }
  178. /**
  179. * [PRIVATE]
  180. *
  181. * Frees the resources allocated by the string
  182. *
  183. * @warning `buffer` must _not_ be freed, since it is a reference to the
  184. * document's buffer
  185. */
  186. static void xml_string_free(struct xml_string* string) {
  187. free(string);
  188. }
  189. /**
  190. * [PRIVATE]
  191. *
  192. * Frees the resources allocated by the attribute
  193. */
  194. static void xml_attribute_free(struct xml_attribute* attribute) {
  195. if(attribute->name) {
  196. xml_string_free(attribute->name);
  197. }
  198. if(attribute->content) {
  199. xml_string_free(attribute->content);
  200. }
  201. free(attribute);
  202. }
  203. /**
  204. * [PRIVATE]
  205. *
  206. * Frees the resources allocated by the node
  207. */
  208. static void xml_node_free(struct xml_node* node) {
  209. xml_string_free(node->name);
  210. if (node->content) {
  211. xml_string_free(node->content);
  212. }
  213. struct xml_attribute** at = node->attributes;
  214. while(*at) {
  215. xml_attribute_free(*at);
  216. ++at;
  217. }
  218. free(node->attributes);
  219. struct xml_node** it = node->children;
  220. while (*it) {
  221. xml_node_free(*it);
  222. ++it;
  223. }
  224. free(node->children);
  225. free(node);
  226. }
  227. /**
  228. * [PRIVATE]
  229. *
  230. * Echos the parsers call stack for debugging purposes
  231. */
  232. #ifdef XML_PARSER_VERBOSE
  233. static void xml_parser_info(struct xml_parser* parser, char const* message) {
  234. fprintf(stdout, "xml_parser_info %s\n", message);
  235. }
  236. #else
  237. #define xml_parser_info(parser, message) {}
  238. #endif
  239. /**
  240. * [PRIVATE]
  241. *
  242. * Echos an error regarding the parser's source to the console
  243. */
  244. static void xml_parser_error(struct xml_parser* parser, enum xml_parser_offset offset, char const* message) {
  245. int row = 0;
  246. int column = 0;
  247. #define min(X,Y) ((X) < (Y) ? (X) : (Y))
  248. #define max(X,Y) ((X) > (Y) ? (X) : (Y))
  249. size_t character = max(0, min(parser->length, parser->position + offset));
  250. #undef min
  251. #undef max
  252. size_t position = 0; for (; position < character; ++position) {
  253. column++;
  254. if ('\n' == parser->buffer[position]) {
  255. row++;
  256. column = 0;
  257. }
  258. }
  259. if (NO_CHARACTER != offset) {
  260. fprintf(stderr, "xml_parser_error at %i:%i (is %c): %s\n",
  261. row + 1, column, parser->buffer[character], message
  262. );
  263. } else {
  264. fprintf(stderr, "xml_parser_error at %i:%i: %s\n",
  265. row + 1, column, message
  266. );
  267. }
  268. }
  269. /**
  270. * [PRIVATE]
  271. *
  272. * Returns the n-th not-whitespace byte in parser and 0 if such a byte does not
  273. * exist
  274. */
  275. static uint8_t xml_parser_peek(struct xml_parser* parser, size_t n) {
  276. size_t position = parser->position;
  277. while (position < parser->length) {
  278. if (!isspace(parser->buffer[position])) {
  279. if (n == 0) {
  280. return parser->buffer[position];
  281. } else {
  282. --n;
  283. }
  284. }
  285. position++;
  286. }
  287. return 0;
  288. }
  289. /**
  290. * [PRIVATE]
  291. *
  292. * Moves the parser's position n bytes. If the new position would be out of
  293. * bounds, it will be converted to the bounds itself
  294. */
  295. static void xml_parser_consume(struct xml_parser* parser, size_t n) {
  296. /* Debug information
  297. */
  298. #ifdef XML_PARSER_VERBOSE
  299. #define min(X,Y) ((X) < (Y) ? (X) : (Y))
  300. char* consumed = alloca((n + 1) * sizeof(char));
  301. memcpy(consumed, &parser->buffer[parser->position], min(n, parser->length - parser->position));
  302. consumed[n] = 0;
  303. #undef min
  304. size_t message_buffer_length = 512;
  305. char* message_buffer = alloca(512 * sizeof(char));
  306. snprintf(message_buffer, message_buffer_length, "Consuming %li bytes \"%s\"", (long)n, consumed);
  307. message_buffer[message_buffer_length - 1] = 0;
  308. xml_parser_info(parser, message_buffer);
  309. #endif
  310. /* Move the position forward
  311. */
  312. parser->position += n;
  313. /* Don't go too far
  314. *
  315. * @warning Valid because parser->length must be greater than 0
  316. */
  317. if (parser->position >= parser->length) {
  318. parser->position = parser->length - 1;
  319. }
  320. }
  321. /**
  322. * [PRIVATE]
  323. *
  324. * Skips to the next non-whitespace character
  325. */
  326. static void xml_skip_whitespace(struct xml_parser* parser) {
  327. xml_parser_info(parser, "whitespace");
  328. while (isspace(parser->buffer[parser->position])) {
  329. if (parser->position + 1 >= parser->length) {
  330. return;
  331. } else {
  332. parser->position++;
  333. }
  334. }
  335. }
  336. /**
  337. * [PRIVATE]
  338. *
  339. * Finds and creates all attributes on the given node.
  340. *
  341. * @author Blake Felt
  342. * @see https://github.com/Molorius
  343. */
  344. static struct xml_attribute** xml_find_attributes(struct xml_parser* parser, struct xml_string* tag_open) {
  345. xml_parser_info(parser, "find_attributes");
  346. char* tmp;
  347. char* rest = NULL;
  348. char* token;
  349. char* str_name;
  350. char* str_content;
  351. const unsigned char* start_name;
  352. const unsigned char* start_content;
  353. size_t old_elements;
  354. size_t new_elements;
  355. struct xml_attribute* new_attribute;
  356. struct xml_attribute** attributes;
  357. int position;
  358. attributes = calloc(1, sizeof(struct xml_attribute*));
  359. attributes[0] = 0;
  360. tmp = (char*) xml_string_clone(tag_open);
  361. token = xml_strtok_r(tmp, " ", &rest); // skip the first value
  362. if(token == NULL) {
  363. goto cleanup;
  364. }
  365. tag_open->length = strlen(token);
  366. for(token=xml_strtok_r(NULL," ", &rest); token!=NULL; token=xml_strtok_r(NULL," ", &rest)) {
  367. str_name = malloc(strlen(token)+1);
  368. str_content = malloc(strlen(token)+1);
  369. // %s=\"%s\" wasn't working for some reason, ugly hack to make it work
  370. if(sscanf(token, "%[^=]=\"%[^\"]", str_name, str_content) != 2) {
  371. if(sscanf(token, "%[^=]=\'%[^\']", str_name, str_content) != 2) {
  372. free(str_name);
  373. free(str_content);
  374. continue;
  375. }
  376. }
  377. position = token-tmp;
  378. start_name = &tag_open->buffer[position];
  379. start_content = &tag_open->buffer[position + strlen(str_name) + 2];
  380. new_attribute = malloc(sizeof(struct xml_attribute));
  381. new_attribute->name = malloc(sizeof(struct xml_string));
  382. new_attribute->name->buffer = (unsigned char*)start_name;
  383. new_attribute->name->length = strlen(str_name);
  384. new_attribute->content = malloc(sizeof(struct xml_string));
  385. new_attribute->content->buffer = (unsigned char*)start_content;
  386. new_attribute->content->length = strlen(str_content);
  387. old_elements = get_zero_terminated_array_attributes(attributes);
  388. new_elements = old_elements + 1;
  389. attributes = realloc(attributes, (new_elements+1)*sizeof(struct xml_attributes*));
  390. attributes[new_elements-1] = new_attribute;
  391. attributes[new_elements] = 0;
  392. free(str_name);
  393. free(str_content);
  394. }
  395. cleanup:
  396. free(tmp);
  397. return attributes;
  398. }
  399. /**
  400. * [PRIVATE]
  401. *
  402. * Parses the name out of the an XML tag's ending
  403. *
  404. * ---( Example )---
  405. * tag_name>
  406. * ---
  407. */
  408. static struct xml_string* xml_parse_tag_end(struct xml_parser* parser) {
  409. xml_parser_info(parser, "tag_end");
  410. size_t start = parser->position;
  411. size_t length = 0;
  412. /* Parse until `>' or a whitespace is reached
  413. */
  414. while (start + length < parser->length) {
  415. uint8_t current = xml_parser_peek(parser, CURRENT_CHARACTER);
  416. if (('>' == current) || isspace(current)) {
  417. break;
  418. } else {
  419. xml_parser_consume(parser, 1);
  420. length++;
  421. }
  422. }
  423. /* Consume `>'
  424. */
  425. if ('>' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
  426. xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_end::expected tag end");
  427. return 0;
  428. }
  429. xml_parser_consume(parser, 1);
  430. /* Return parsed tag name
  431. */
  432. struct xml_string* name = malloc(sizeof(struct xml_string));
  433. name->buffer = &parser->buffer[start];
  434. name->length = length;
  435. return name;
  436. }
  437. /**
  438. * [PRIVATE]
  439. *
  440. * Parses an opening XML tag without attributes
  441. *
  442. * ---( Example )---
  443. * <tag_name>
  444. * ---
  445. */
  446. static struct xml_string* xml_parse_tag_open(struct xml_parser* parser) {
  447. xml_parser_info(parser, "tag_open");
  448. xml_skip_whitespace(parser);
  449. /* Consume `<'
  450. */
  451. if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
  452. xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_open::expected opening tag");
  453. return 0;
  454. }
  455. xml_parser_consume(parser, 1);
  456. /* Consume tag name
  457. */
  458. return xml_parse_tag_end(parser);
  459. }
  460. /**
  461. * [PRIVATE]
  462. *
  463. * Parses an closing XML tag without attributes
  464. *
  465. * ---( Example )---
  466. * </tag_name>
  467. * ---
  468. */
  469. static struct xml_string* xml_parse_tag_close(struct xml_parser* parser) {
  470. xml_parser_info(parser, "tag_close");
  471. xml_skip_whitespace(parser);
  472. /* Consume `</'
  473. */
  474. if ( ('<' != xml_parser_peek(parser, CURRENT_CHARACTER))
  475. || ('/' != xml_parser_peek(parser, NEXT_CHARACTER))) {
  476. if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
  477. xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_close::expected closing tag `<'");
  478. }
  479. if ('/' != xml_parser_peek(parser, NEXT_CHARACTER)) {
  480. xml_parser_error(parser, NEXT_CHARACTER, "xml_parse_tag_close::expected closing tag `/'");
  481. }
  482. return 0;
  483. }
  484. xml_parser_consume(parser, 2);
  485. /* Consume tag name
  486. */
  487. return xml_parse_tag_end(parser);
  488. }
  489. /**
  490. * [PRIVATE]
  491. *
  492. * Parses a tag's content
  493. *
  494. * ---( Example )---
  495. * this is
  496. * a
  497. * tag {} content
  498. * ---
  499. *
  500. * @warning CDATA etc. is _not_ and will never be supported
  501. */
  502. static struct xml_string* xml_parse_content(struct xml_parser* parser) {
  503. xml_parser_info(parser, "content");
  504. /* Whitespace will be ignored
  505. */
  506. xml_skip_whitespace(parser);
  507. size_t start = parser->position;
  508. size_t length = 0;
  509. /* Consume until `<' is reached
  510. */
  511. while (start + length < parser->length) {
  512. uint8_t current = xml_parser_peek(parser, CURRENT_CHARACTER);
  513. if ('<' == current) {
  514. break;
  515. } else {
  516. xml_parser_consume(parser, 1);
  517. length++;
  518. }
  519. }
  520. /* Next character must be an `<' or we have reached end of file
  521. */
  522. if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
  523. xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_content::expected <");
  524. return 0;
  525. }
  526. /* Ignore tailing whitespace
  527. */
  528. while ((length > 0) && isspace(parser->buffer[start + length - 1])) {
  529. length--;
  530. }
  531. /* Return text
  532. */
  533. struct xml_string* content = malloc(sizeof(struct xml_string));
  534. content->buffer = &parser->buffer[start];
  535. content->length = length;
  536. return content;
  537. }
  538. /**
  539. * [PRIVATE]
  540. *
  541. * Parses an XML fragment node
  542. *
  543. * ---( Example without children )---
  544. * <Node>Text</Node>
  545. * ---
  546. *
  547. * ---( Example with children )---
  548. * <Parent>
  549. * <Child>Text</Child>
  550. * <Child>Text</Child>
  551. * <Test>Content</Test>
  552. * </Parent>
  553. * ---
  554. */
  555. static struct xml_node* xml_parse_node(struct xml_parser* parser) {
  556. xml_parser_info(parser, "node");
  557. /* Setup variables
  558. */
  559. struct xml_string* tag_open = 0;
  560. struct xml_string* tag_close = 0;
  561. struct xml_string* content = 0;
  562. size_t original_length;
  563. struct xml_attribute** attributes;
  564. struct xml_node** children = calloc(1, sizeof(struct xml_node*));
  565. children[0] = 0;
  566. /* Parse open tag
  567. */
  568. tag_open = xml_parse_tag_open(parser);
  569. if (!tag_open) {
  570. xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag_open");
  571. goto exit_failure;
  572. }
  573. original_length = tag_open->length;
  574. attributes = xml_find_attributes(parser, tag_open);
  575. /* If tag ends with `/' it's self closing, skip content lookup */
  576. if (tag_open->length > 0 && '/' == tag_open->buffer[original_length - 1]) {
  577. /* Drop `/'
  578. */
  579. goto node_creation;
  580. }
  581. /* If the content does not start with '<', a text content is assumed
  582. */
  583. if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
  584. content = xml_parse_content(parser);
  585. if (!content) {
  586. xml_parser_error(parser, 0, "xml_parse_node::content");
  587. goto exit_failure;
  588. }
  589. /* Otherwise children are to be expected
  590. */
  591. } else while ('/' != xml_parser_peek(parser, NEXT_CHARACTER)) {
  592. /* Parse child node
  593. */
  594. struct xml_node* child = xml_parse_node(parser);
  595. if (!child) {
  596. xml_parser_error(parser, NEXT_CHARACTER, "xml_parse_node::child");
  597. goto exit_failure;
  598. }
  599. /* Grow child array :)
  600. */
  601. size_t old_elements = get_zero_terminated_array_nodes(children);
  602. size_t new_elements = old_elements + 1;
  603. children = realloc(children, (new_elements + 1) * sizeof(struct xml_node*));
  604. /* Save child
  605. */
  606. children[new_elements - 1] = child;
  607. children[new_elements] = 0;
  608. }
  609. /* Parse close tag
  610. */
  611. tag_close = xml_parse_tag_close(parser);
  612. if (!tag_close) {
  613. xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag_close");
  614. goto exit_failure;
  615. }
  616. /* Close tag has to match open tag
  617. */
  618. if (!xml_string_equals(tag_open, tag_close)) {
  619. xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag missmatch");
  620. goto exit_failure;
  621. }
  622. /* Return parsed node
  623. */
  624. xml_string_free(tag_close);
  625. node_creation:;
  626. struct xml_node* node = malloc(sizeof(struct xml_node));
  627. node->name = tag_open;
  628. node->content = content;
  629. node->attributes = attributes;
  630. node->children = children;
  631. return node;
  632. /* A failure occured, so free all allocalted resources
  633. */
  634. exit_failure:
  635. if (tag_open) {
  636. xml_string_free(tag_open);
  637. }
  638. if (tag_close) {
  639. xml_string_free(tag_close);
  640. }
  641. if (content) {
  642. xml_string_free(content);
  643. }
  644. struct xml_node** it = children;
  645. while (*it) {
  646. xml_node_free(*it);
  647. ++it;
  648. }
  649. free(children);
  650. return 0;
  651. }
  652. /**
  653. * [PUBLIC API]
  654. */
  655. struct xml_document* xml_parse_document(uint8_t* buffer, size_t length) {
  656. /* Initialize parser
  657. */
  658. struct xml_parser parser = {
  659. .buffer = buffer,
  660. .position = 0,
  661. .length = length
  662. };
  663. /* An empty buffer can never contain a valid document
  664. */
  665. if (!length) {
  666. xml_parser_error(&parser, NO_CHARACTER, "xml_parse_document::length equals zero");
  667. return 0;
  668. }
  669. /* Parse the root node
  670. */
  671. struct xml_node* root = xml_parse_node(&parser);
  672. if (!root) {
  673. xml_parser_error(&parser, NO_CHARACTER, "xml_parse_document::parsing document failed");
  674. return 0;
  675. }
  676. /* Return parsed document
  677. */
  678. struct xml_document* document = malloc(sizeof(struct xml_document));
  679. document->buffer.buffer = buffer;
  680. document->buffer.length = length;
  681. document->root = root;
  682. return document;
  683. }
  684. /**
  685. * [PUBLIC API]
  686. */
  687. struct xml_document* xml_open_document(FILE* source) {
  688. /* Prepare buffer
  689. */
  690. size_t const read_chunk = 1; // TODO 4096;
  691. size_t document_length = 0;
  692. size_t buffer_size = 1; // TODO 4069
  693. uint8_t* buffer = malloc(buffer_size * sizeof(uint8_t));
  694. /* Read hole file into buffer
  695. */
  696. while (!feof(source)) {
  697. /* Reallocate buffer
  698. */
  699. if (buffer_size - document_length < read_chunk) {
  700. buffer = realloc(buffer, buffer_size + 2 * read_chunk);
  701. buffer_size += 2 * read_chunk;
  702. }
  703. size_t read = fread(
  704. &buffer[document_length],
  705. sizeof(uint8_t), read_chunk,
  706. source
  707. );
  708. document_length += read;
  709. }
  710. fclose(source);
  711. /* Try to parse buffer
  712. */
  713. struct xml_document* document = xml_parse_document(buffer, document_length);
  714. if (!document) {
  715. free(buffer);
  716. return 0;
  717. }
  718. return document;
  719. }
  720. /**
  721. * [PUBLIC API]
  722. */
  723. void xml_document_free(struct xml_document* document, bool free_buffer) {
  724. xml_node_free(document->root);
  725. if (free_buffer) {
  726. free(document->buffer.buffer);
  727. }
  728. free(document);
  729. }
  730. /**
  731. * [PUBLIC API]
  732. */
  733. struct xml_node* xml_document_root(struct xml_document* document) {
  734. return document->root;
  735. }
  736. /**
  737. * [PUBLIC API]
  738. */
  739. struct xml_string* xml_node_name(struct xml_node* node) {
  740. return node->name;
  741. }
  742. /**
  743. * [PUBLIC API]
  744. */
  745. struct xml_string* xml_node_content(struct xml_node* node) {
  746. return node->content;
  747. }
  748. /**
  749. * [PUBLIC API]
  750. *
  751. * @warning O(n)
  752. */
  753. size_t xml_node_children(struct xml_node* node) {
  754. return get_zero_terminated_array_nodes(node->children);
  755. }
  756. /**
  757. * [PUBLIC API]
  758. */
  759. struct xml_node* xml_node_child(struct xml_node* node, size_t child) {
  760. if (child >= xml_node_children(node)) {
  761. return 0;
  762. }
  763. return node->children[child];
  764. }
  765. /**
  766. * [PUBLIC API]
  767. */
  768. size_t xml_node_attributes(struct xml_node* node) {
  769. return get_zero_terminated_array_attributes(node->attributes);
  770. }
  771. /**
  772. * [PUBLIC API]
  773. */
  774. struct xml_string* xml_node_attribute_name(struct xml_node* node, size_t attribute) {
  775. if(attribute >= xml_node_attributes(node)) {
  776. return 0;
  777. }
  778. return node->attributes[attribute]->name;
  779. }
  780. /**
  781. * [PUBLIC API]
  782. */
  783. struct xml_string* xml_node_attribute_content(struct xml_node* node, size_t attribute) {
  784. if(attribute >= xml_node_attributes(node)) {
  785. return 0;
  786. }
  787. return node->attributes[attribute]->content;
  788. }
  789. /**
  790. * [PUBLIC API]
  791. */
  792. struct xml_node* xml_easy_child(struct xml_node* node, uint8_t const* child_name, ...) {
  793. /* Find children, one by one
  794. */
  795. struct xml_node* current = node;
  796. va_list arguments;
  797. va_start(arguments, child_name);
  798. /* Descent to current.child
  799. */
  800. while (child_name) {
  801. /* Convert child_name to xml_string for easy comparison
  802. */
  803. struct xml_string cn = {
  804. .buffer = child_name,
  805. .length = strlen(child_name)
  806. };
  807. /* Interate through all children
  808. */
  809. struct xml_node* next = 0;
  810. size_t i = 0; for (; i < xml_node_children(current); ++i) {
  811. struct xml_node* child = xml_node_child(current, i);
  812. if (xml_string_equals(xml_node_name(child), &cn)) {
  813. if (!next) {
  814. next = child;
  815. /* Two children with the same name
  816. */
  817. } else {
  818. va_end(arguments);
  819. return 0;
  820. }
  821. }
  822. }
  823. /* No child with that name found
  824. */
  825. if (!next) {
  826. va_end(arguments);
  827. return 0;
  828. }
  829. current = next;
  830. /* Find name of next child
  831. */
  832. child_name = va_arg(arguments, uint8_t const*);
  833. }
  834. va_end(arguments);
  835. /* Return current element
  836. */
  837. return current;
  838. }
  839. /**
  840. * [PUBLIC API]
  841. */
  842. uint8_t* xml_easy_name(struct xml_node* node) {
  843. if (!node) {
  844. return 0;
  845. }
  846. return xml_string_clone(xml_node_name(node));
  847. }
  848. /**
  849. * [PUBLIC API]
  850. */
  851. uint8_t* xml_easy_content(struct xml_node* node) {
  852. if (!node) {
  853. return 0;
  854. }
  855. return xml_string_clone(xml_node_content(node));
  856. }
  857. /**
  858. * [PUBLIC API]
  859. */
  860. size_t xml_string_length(struct xml_string* string) {
  861. if (!string) {
  862. return 0;
  863. }
  864. return string->length;
  865. }
  866. /**
  867. * [PUBLIC API]
  868. */
  869. void xml_string_copy(struct xml_string* string, uint8_t* buffer, size_t length) {
  870. if (!string) {
  871. return;
  872. }
  873. #define min(X,Y) ((X) < (Y) ? (X) : (Y))
  874. length = min(length, string->length);
  875. #undef min
  876. memcpy(buffer, string->buffer, length);
  877. }