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.

223 rivejä
7.8 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 <iostream>
  24. #include <cstdlib>
  25. #include <cstdio>
  26. #include <xml.h>
  27. /**
  28. * Will halt the program iff assertion fails
  29. */
  30. static void _assert_that(bool condition, const char* message,
  31. const char* func, const char* file, int line) {
  32. if (!condition) {
  33. std::cerr << "Assertion failed: " << message << ", in " << func << " ("
  34. << file << ":" << line << ")\n";
  35. exit(EXIT_FAILURE);
  36. }
  37. }
  38. #define assert_that(condition, message) \
  39. _assert_that(condition, message, __func__, __FILE__, __LINE__)
  40. /**
  41. * @return true iff xml string equals the c string
  42. */
  43. static bool string_equals(struct xml_string* a, const char* b) {
  44. size_t a_length = xml_string_length(a);
  45. size_t b_length = strlen(b);
  46. uint8_t* a_buffer = new uint8_t[((a_length + 1) * sizeof(uint8_t))];
  47. xml_string_copy(a, a_buffer, a_length);
  48. a_buffer[a_length] = 0;
  49. if (a_length != b_length) {
  50. std::cerr << "string_equals: " << a_buffer << "#" << a_length << " <> "
  51. << b << "#" << b_length << "\n";
  52. delete[] a_buffer;
  53. return false;
  54. }
  55. size_t i = 0; for (; i < a_length; ++i) {
  56. if (a_buffer[i] != b[i]) {
  57. std::cerr << "string_equals: " << a_buffer << " <> " << b << "\n";
  58. delete[] a_buffer;
  59. return false;
  60. }
  61. }
  62. delete[] a_buffer;
  63. return true;
  64. }
  65. /**
  66. * Converts a static character array to an uint8_t data source
  67. */
  68. #define SOURCE(source, content) \
  69. uint8_t* source = new uint8_t[strlen(content)]; \
  70. { \
  71. const char* content_string = content; \
  72. memcpy(source, content_string, strlen(content) + 1); \
  73. }
  74. /**
  75. * Tries to parse a simple document containing only one tag
  76. */
  77. static void test_xml_parse_document_0() {
  78. SOURCE(source, "<Hello>World</Hello>");
  79. // uint8_t* source = malloc((1 + strlen("<Hello>World</Hello>")) *
  80. // sizeof(uint8_t));
  81. // {
  82. // const char* content_string = "<Hello>World</Hello>";
  83. // memcpy(source, content_string, strlen("<Hello>World</Hello>") + 1);
  84. // }
  85. struct xml_document* document = xml_parse_document(source,
  86. strlen((const char *)source));
  87. assert_that(document, "Could not parse document");
  88. struct xml_node* root = xml_document_root(document);
  89. assert_that(string_equals(xml_node_name(root), "Hello"),
  90. "root node name must be `Hello'");
  91. assert_that(string_equals(xml_node_content(root), "World"),
  92. "root node content must be `World'");
  93. xml_document_free(document, true);
  94. }
  95. /**
  96. * Tries to parse a document containing multiple tags
  97. */
  98. static void test_xml_parse_document_1() {
  99. SOURCE(source, ""
  100. "<Parent>\n"
  101. "\t<Child>\n"
  102. "\t\tFirst content\n"
  103. "\t</Child>\n"
  104. "\t<Child>\n"
  105. "\t\tSecond content\n"
  106. "\t</Child>\n"
  107. "</Parent>\n"
  108. );
  109. struct xml_document* document = xml_parse_document(source,
  110. strlen((const char *)source));
  111. assert_that(document, "Could not parse document");
  112. struct xml_node* root = xml_document_root(document);
  113. assert_that(string_equals(xml_node_name(root), "Parent"),
  114. "root node name must be `Parent'");
  115. assert_that(2 == xml_node_children(root), "root must have two children");
  116. struct xml_node* first_child = xml_node_child(root, 0);
  117. struct xml_node* second_child = xml_node_child(root, 1);
  118. assert_that(first_child && second_child,
  119. "Failed retrieving the children of root");
  120. struct xml_node* third_child = xml_node_child(root, 2);
  121. assert_that(!third_child, "root has a third child where non should be");
  122. assert_that(string_equals(xml_node_name(first_child), "Child"),
  123. "first_child node name must be `Child'");
  124. assert_that(string_equals(xml_node_content(first_child), "First content"),
  125. "first_child node content must be `First content'");
  126. assert_that(string_equals(xml_node_name(second_child), "Child"),
  127. "second_child node name must be `Child'");
  128. assert_that(string_equals(xml_node_content(second_child), "Second content"),
  129. "second_child node content must be `tSecond content'");
  130. xml_document_free(document, true);
  131. }
  132. /**
  133. * Tests the eas functionality
  134. */
  135. static void test_xml_parse_document_2() {
  136. SOURCE(source, ""
  137. "<Parent>\n"
  138. "\t<Child>\n"
  139. "\t\tFirst content\n"
  140. "\t</Child>\n"
  141. "\t<This><Is>\n"
  142. "<A><Test>Content A</Test></A>\n"
  143. "<B><Test>Content B</Test></B>\n"
  144. "\t</Is></This>\n"
  145. "\t<Child>\n"
  146. "\t\tSecond content\n"
  147. "\t</Child>\n"
  148. "</Parent>\n"
  149. );
  150. struct xml_document* document = xml_parse_document(source,
  151. strlen((const char *)source));
  152. assert_that(document, "Could not parse document");
  153. struct xml_node* root = xml_document_root(document);
  154. assert_that(string_equals(xml_node_name(root), "Parent"),
  155. "root node name must be `Parent'");
  156. assert_that(3 == xml_node_children(root),
  157. "root must have two children");
  158. struct xml_node* test_a = xml_easy_child(root, (uint8_t *)"This",
  159. (uint8_t *)"Is", (uint8_t *)"A", (uint8_t *)"Test", 0);
  160. assert_that(test_a, "Cannot find Parent/This/Is/A/Test");
  161. assert_that(string_equals(xml_node_content(test_a), "Content A"),
  162. "Content of Parent/This/Is/A/Test must be `Content A'");
  163. struct xml_node* test_b = xml_easy_child(root, (uint8_t *)"This",
  164. (uint8_t *)"Is", (uint8_t *)"B", (uint8_t *)"Test", 0);
  165. assert_that(test_b, "Cannot find Parent/This/Is/B/Test");
  166. assert_that(string_equals(xml_node_content(test_b), "Content B"),
  167. "Content of Parent/This/Is/B/Test must be `Content B'");
  168. struct xml_node* test_c = xml_easy_child(root, (uint8_t *)"This",
  169. (uint8_t *)"Is", (uint8_t *)"C", (uint8_t *)"Test", 0);
  170. assert_that(!test_c,
  171. "Must not find Parent/This/Is/C/Test because no such path exists");
  172. struct xml_node* must_be_null = xml_easy_child(root, (uint8_t *)"Child");
  173. assert_that(!must_be_null,
  174. "Parent/Child cannot be a valid expression, because there are two children "
  175. "named `Child' in `Parent'");
  176. uint8_t* name_is = xml_easy_name(xml_easy_child(root, (uint8_t *)"This",
  177. (uint8_t *)"Is", 0));
  178. assert_that(!strcmp((const char*)name_is, "Is"),
  179. "Name of Parent/This/Is must be `Is'");
  180. delete[] name_is;
  181. uint8_t* content_a = xml_easy_content(test_a);
  182. assert_that(!strcmp((const char*)content_a, "Content A"),
  183. "Content of Parent/This/Is/A/Test must be `Content A'");
  184. delete[] content_a;
  185. xml_document_free(document, true);
  186. }
  187. /**
  188. * Tests the xml_open_document functionality
  189. */
  190. static void test_xml_parse_document_3() {
  191. #define FILE_NAME "test.xml"
  192. FILE* handle = fopen(FILE_NAME, "rb");
  193. assert_that(handle, "Cannot open " FILE_NAME);
  194. struct xml_document* document = xml_open_document(handle);
  195. assert_that(document, "Cannot parse " FILE_NAME);
  196. struct xml_node* element = xml_easy_child(xml_document_root(document),
  197. (uint8_t *)"Element", (uint8_t *)"With", 0);
  198. assert_that(element, "Cannot find Document/Element/With");
  199. assert_that(string_equals(xml_node_content(element), "Child"),
  200. "Content of Document/Element/With must be `Child'");
  201. xml_document_free(document, true);
  202. #undef FILE_NAME
  203. }
  204. int main(int argc, char **argv) {
  205. test_xml_parse_document_0();
  206. test_xml_parse_document_1();
  207. test_xml_parse_document_2();
  208. test_xml_parse_document_3();
  209. std::cout << "All tests passed :-)\n";
  210. exit(EXIT_SUCCESS);
  211. }