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
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

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