summaryrefslogtreecommitdiff
path: root/libs/xml.c
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-08-23 11:18:44 +0200
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-08-23 11:18:44 +0200
commit359422c97cce93bbb27051f9df3efb45bd0b9052 (patch)
tree2e352bb852a25390d40d45e199f835d218ad497f /libs/xml.c
parent8ea59863c5d13e68e080cf7612047ea4c655292c (diff)
settings file writing
Diffstat (limited to 'libs/xml.c')
-rw-r--r--libs/xml.c/.github/workflows/ci.yaml24
-rw-r--r--libs/xml.c/.gitignore2
-rw-r--r--libs/xml.c/.mc/rebuild.sh21
-rw-r--r--libs/xml.c/.mc/ubuntu-22.04.yaml8
-rw-r--r--libs/xml.c/CMakeLists.txt51
-rw-r--r--libs/xml.c/LICENSE21
-rw-r--r--libs/xml.c/README.md126
-rw-r--r--libs/xml.c/src/xml.c1131
-rw-r--r--libs/xml.c/src/xml.h196
-rw-r--r--libs/xml.c/test/CMakeLists.txt128
-rw-r--r--libs/xml.c/test/example.c96
-rw-r--r--libs/xml.c/test/test-attributes.xml1
-rw-r--r--libs/xml.c/test/test-huitre39.c181
-rw-r--r--libs/xml.c/test/test-xml-c.c265
-rw-r--r--libs/xml.c/test/test-xml-cpp.cpp222
-rw-r--r--libs/xml.c/test/test.xml7
16 files changed, 2480 insertions, 0 deletions
diff --git a/libs/xml.c/.github/workflows/ci.yaml b/libs/xml.c/.github/workflows/ci.yaml
new file mode 100644
index 0000000..2f9a4a9
--- /dev/null
+++ b/libs/xml.c/.github/workflows/ci.yaml
@@ -0,0 +1,24 @@
+name: CI
+
+# Controls when the action will run. Triggers the workflow on push or pull
+# request events but only for the main branch
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches: [ master ]
+ schedule:
+ - cron: '0 0 15 * *'
+
+
+jobs:
+ ci:
+ runs-on: ubuntu-20.04
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Build and Test
+ run: npx --package mini-cross@0.15.2 mc --no-tty ubuntu-22.04 .mc/rebuild.sh
+
diff --git a/libs/xml.c/.gitignore b/libs/xml.c/.gitignore
new file mode 100644
index 0000000..bdc5af0
--- /dev/null
+++ b/libs/xml.c/.gitignore
@@ -0,0 +1,2 @@
+*~
+build
diff --git a/libs/xml.c/.mc/rebuild.sh b/libs/xml.c/.mc/rebuild.sh
new file mode 100644
index 0000000..67443c3
--- /dev/null
+++ b/libs/xml.c/.mc/rebuild.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -e
+
+
+# @see https://stackoverflow.com/a/1482133
+DIRECTORY_OF_SCRIPT=`dirname "$(readlink -f "$0")"`
+
+SOURCE_DIRECTORY="${DIRECTORY_OF_SCRIPT}/.."
+BUILD_DIRECTORY="${DIRECTORY_OF_SCRIPT}/../build"
+
+
+if [ -d "${BUILD_DIRECTORY}" ]; then
+ rm -rf "${BUILD_DIRECTORY}"
+fi
+mkdir "${BUILD_DIRECTORY}"
+
+(cd "${BUILD_DIRECTORY}" && cmake -DCMAKE_BUILD_TYPE=Release ..)
+make --directory "${BUILD_DIRECTORY}" --silent
+make --directory "${BUILD_DIRECTORY}" --silent test
+
diff --git a/libs/xml.c/.mc/ubuntu-22.04.yaml b/libs/xml.c/.mc/ubuntu-22.04.yaml
new file mode 100644
index 0000000..762305c
--- /dev/null
+++ b/libs/xml.c/.mc/ubuntu-22.04.yaml
@@ -0,0 +1,8 @@
+---
+base: ubuntu:22.04
+install:
+ - cmake
+ - g++
+ - gcc
+ - valgrind
+---
diff --git a/libs/xml.c/CMakeLists.txt b/libs/xml.c/CMakeLists.txt
new file mode 100644
index 0000000..698dc78
--- /dev/null
+++ b/libs/xml.c/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Project setup
+project(xml C CXX)
+set(VERSION_MAJOR "0")
+set(VERSION_MINOR "2")
+set(VERSION_PATCH "0")
+cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
+
+
+# Define main library target
+add_library(xml STATIC "")
+
+
+# Compiler setup
+target_compile_options(
+ xml
+ PRIVATE
+ -std=c11
+)
+
+
+# Options
+option(XML_PARSER_VERBOSE "Enable to be told everything the xml parser does" OFF)
+
+if(XML_PARSER_VERBOSE)
+ target_compile_definitions(
+ xml
+ PRIVATE
+ XML_PARSER_VERBOSE
+ )
+endif(XML_PARSER_VERBOSE)
+
+
+# Sources
+target_sources(
+ xml
+ PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/src/xml.c"
+)
+
+
+target_include_directories(
+ xml
+ PUBLIC
+ "${CMAKE_CURRENT_LIST_DIR}/src/"
+)
+
+
+# Build unit cases
+enable_testing()
+add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/test")
+
diff --git a/libs/xml.c/LICENSE b/libs/xml.c/LICENSE
new file mode 100644
index 0000000..f6c1ad0
--- /dev/null
+++ b/libs/xml.c/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2012 ooxi/xml.c
+ https://github.com/ooxi/xml.c
+
+This software is provided 'as-is', without any express or implied warranty. In
+no event will the authors be held liable for any damages arising from the use of
+this software.
+
+Permission is granted to anyone to use this software for any purpose, including
+commercial applications, and to alter it and redistribute it freely, subject to
+the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim
+ that you wrote the original software. If you use this software in a product,
+ an acknowledgment in the product documentation would be appreciated but is
+ not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
diff --git a/libs/xml.c/README.md b/libs/xml.c/README.md
new file mode 100644
index 0000000..b255bd2
--- /dev/null
+++ b/libs/xml.c/README.md
@@ -0,0 +1,126 @@
+xml.c
+=====
+
+Similar to the [GLib Markup parser](http://developer.gnome.org/glib/2.34/glib-Simple-XML-Subset-Parser.html),
+which also just parses an xml subset, [xml.c](https://github.com/ooxi/xml.c) is
+a simple, small and self contained xml parser in one file. Ideal for embedding
+into other projects without the need for big external dependencies.
+
+[![Build Status](https://github.com/ooxi/xml.c/actions/workflows/ci.yaml/badge.svg)](https://github.com/ooxi/xml.c/actions)
+
+
+Downloads
+---------
+
+All releases are based on master, so the preferred way of using xml.c is adding
+the repository as [git submodule](http://git-scm.com/book/en/Git-Tools-Submodules).
+
+If you prefer formal releases, check out the [release tags](https://github.com/ooxi/xml.c/tags).
+
+
+Building xml.c
+--------------
+
+Since xml.c uses [CMake](http://www.cmake.org/), building the library is fairly
+easy
+
+ $ git clone https://github.com/ooxi/xml.c.git xml.c
+ $ mkdir xml.c/build; cd xml.c/build
+ $ cmake -DCMAKE_BUILD_TYPE=Release ..
+ $ make && make test
+
+If you need a debug build, specify `CMAKE_BUILD_TYPE` as `Debug` and rebuild.
+
+
+Usage
+-----
+
+This example is also [included in the repository](https://github.com/ooxi/xml.c/blob/master/test/example.c)
+and will be build by default. Most of the code is C boilerplate, the important
+functions are `xml_parse_document`, `xml_document_root`, `xml_node_name`,
+`xml_node_content` and `xml_node_child` / `xml_node_children`.
+
+```c
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <xml.h>
+
+
+
+int main(int argc, char** argv) {
+
+ /* XML source, could be read from disk
+ */
+ uint8_t* source = ""
+ "<Root>"
+ "<Hello>World</Hello>"
+ "<This>"
+ "<Is>:-)</Is>"
+ "<An>:-O</An>"
+ "<Example>:-D</Example>"
+ "</This>"
+ "</Root>"
+ ;
+
+
+ /* Parse the document
+ *
+ * Watch out: Remember not to free the source until you have freed the
+ * document itself. If you have to free the source before, supply a
+ * copy to xml_parse_document which can be freed together with the
+ * document (`free_buffer' argument to `xml_document_free')
+ */
+ struct xml_document* document = xml_parse_document(source, strlen(source));
+
+ /* You _have_ to check the result of `xml_parse_document', if it's 0
+ * then the source could not be parsed. If you think this is a bug in
+ * xml.c, then use a debug build (cmake -DCMAKE_BUILD_TYPE=Debug) which
+ * will verbosely tell you about the parsing process
+ */
+ if (!document) {
+ printf("Could parse document\n");
+ exit(EXIT_FAILURE);
+ }
+ struct xml_node* root = xml_document_root(document);
+
+
+ /* Say Hello World :-)
+ */
+ struct xml_node* root_hello = xml_node_child(root, 0);
+ struct xml_string* hello = xml_node_name(root_hello);
+ struct xml_string* world = xml_node_content(root_hello);
+
+ /* Watch out: `xml_string_copy' will not 0-terminate your buffers! (but
+ * `calloc' will :-)
+ */
+ uint8_t* hello_0 = calloc(xml_string_length(hello) + 1, sizeof(uint8_t));
+ uint8_t* world_0 = calloc(xml_string_length(world) + 1, sizeof(uint8_t));
+ xml_string_copy(hello, hello_0, xml_string_length(hello));
+ xml_string_copy(world, world_0, xml_string_length(world));
+
+ printf("%s %s\n", hello_0, world_0);
+ free(hello_0);
+ free(world_0);
+
+
+ /* Extract amount of Root/This children
+ */
+ struct xml_node* root_this = xml_node_child(root, 1);
+ printf("Root/This has %lu children\n", (unsigned long)xml_node_children(root_this));
+
+
+ /* Remember to free the document or you'll risk a memory leak
+ */
+ xml_document_free(document, false);
+}
+```
+
+Another usage example can be found in the [unit case](https://github.com/ooxi/xml.c/blob/master/test/test-xml.c).
+
+
+License
+-------
+
+[libpng/zlib](https://github.com/ooxi/xml.c/blob/master/LICENSE) (BSD)
+
diff --git a/libs/xml.c/src/xml.c b/libs/xml.c/src/xml.c
new file mode 100644
index 0000000..4c4e6f2
--- /dev/null
+++ b/libs/xml.c/src/xml.c
@@ -0,0 +1,1131 @@
+/**
+ * Copyright (c) 2012 ooxi/xml.c
+ * https://github.com/ooxi/xml.c
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+#define _CRT_SECURE_NO_WARNINGS
+#include "xml.h"
+
+#ifdef XML_PARSER_VERBOSE
+#include <alloca.h>
+#endif
+
+#include <ctype.h>
+
+#ifndef __MACH__
+#include <malloc.h>
+#endif
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+
+
+
+/*
+ * public domain strtok_r() by Charlie Gordon
+ *
+ * from comp.lang.c 9/14/2007
+ *
+ * http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684
+ *
+ * (Declaration that it's public domain):
+ * http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c
+ */
+static char* xml_strtok_r(char *str, const char *delim, char **nextp) {
+ char *ret;
+
+ if (str == NULL) {
+ str = *nextp;
+ }
+
+ str += strspn(str, delim);
+
+ if (*str == '\0') {
+ return NULL;
+ }
+
+ ret = str;
+
+ str += strcspn(str, delim);
+
+ if (*str) {
+ *str++ = '\0';
+ }
+
+ *nextp = str;
+
+ return ret;
+}
+
+
+
+
+
+
+/**
+ * [OPAQUE API]
+ *
+ * UTF-8 text
+ */
+struct xml_string {
+ uint8_t const* buffer;
+ size_t length;
+};
+
+/**
+ * [OPAQUE API]
+ *
+ * An xml_attribute may contain text content.
+ */
+struct xml_attribute {
+ struct xml_string* name;
+ struct xml_string* content;
+};
+
+/**
+ * [OPAQUE API]
+ *
+ * An xml_node will always contain a tag name, a 0-terminated list of attributes
+ * and a 0-terminated list of children. Moreover it may contain text content.
+ */
+struct xml_node {
+ struct xml_string* name;
+ struct xml_string* content;
+ struct xml_attribute** attributes;
+ struct xml_node** children;
+};
+
+/**
+ * [OPAQUE API]
+ *
+ * An xml_document simply contains the root node and the underlying buffer
+ */
+struct xml_document {
+ struct {
+ uint8_t* buffer;
+ size_t length;
+ } buffer;
+
+ struct xml_node* root;
+};
+
+
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Parser context
+ */
+struct xml_parser {
+ uint8_t* buffer;
+ size_t position;
+ size_t length;
+};
+
+/**
+ * [PRIVATE]
+ *
+ * Character offsets
+ */
+enum xml_parser_offset {
+ NO_CHARACTER = -1,
+ CURRENT_CHARACTER = 0,
+ NEXT_CHARACTER = 1,
+};
+
+
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * @return Number of attributes in 0-terminated array
+ */
+static size_t get_zero_terminated_array_attributes(struct xml_attribute** attributes) {
+ size_t elements = 0;
+
+ while (attributes[elements]) {
+ ++elements;
+ }
+
+ return elements;
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * @return Number of nodes in 0-terminated array
+ */
+static size_t get_zero_terminated_array_nodes(struct xml_node** nodes) {
+ size_t elements = 0;
+
+ while (nodes[elements]) {
+ ++elements;
+ }
+
+ return elements;
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * @warning No UTF conversions will be attempted
+ *
+ * @return true iff a == b
+ */
+static _Bool xml_string_equals(struct xml_string* a, struct xml_string* b) {
+
+ if (a->length != b->length) {
+ return false;
+ }
+
+ size_t i = 0; for (; i < a->length; ++i) {
+ if (a->buffer[i] != b->buffer[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+/**
+ * [PRIVATE]
+ */
+static uint8_t* xml_string_clone(struct xml_string* s) {
+ if (!s) {
+ return 0;
+ }
+
+ uint8_t* clone = calloc(s->length + 1, sizeof(uint8_t));
+
+ xml_string_copy(s, clone, s->length);
+ clone[s->length] = 0;
+
+ return clone;
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Frees the resources allocated by the string
+ *
+ * @warning `buffer` must _not_ be freed, since it is a reference to the
+ * document's buffer
+ */
+static void xml_string_free(struct xml_string* string) {
+ free(string);
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Frees the resources allocated by the attribute
+ */
+static void xml_attribute_free(struct xml_attribute* attribute) {
+ if(attribute->name) {
+ xml_string_free(attribute->name);
+ }
+ if(attribute->content) {
+ xml_string_free(attribute->content);
+ }
+ free(attribute);
+}
+
+/**
+ * [PRIVATE]
+ *
+ * Frees the resources allocated by the node
+ */
+static void xml_node_free(struct xml_node* node) {
+ xml_string_free(node->name);
+
+ if (node->content) {
+ xml_string_free(node->content);
+ }
+
+ struct xml_attribute** at = node->attributes;
+ while(*at) {
+ xml_attribute_free(*at);
+ ++at;
+ }
+ free(node->attributes);
+
+ struct xml_node** it = node->children;
+ while (*it) {
+ xml_node_free(*it);
+ ++it;
+ }
+ free(node->children);
+
+ free(node);
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Echos the parsers call stack for debugging purposes
+ */
+#ifdef XML_PARSER_VERBOSE
+static void xml_parser_info(struct xml_parser* parser, char const* message) {
+ fprintf(stdout, "xml_parser_info %s\n", message);
+}
+#else
+#define xml_parser_info(parser, message) {}
+#endif
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Echos an error regarding the parser's source to the console
+ */
+static void xml_parser_error(struct xml_parser* parser, enum xml_parser_offset offset, char const* message) {
+ int row = 0;
+ int column = 0;
+
+ //#define min(X,Y) ((X) < (Y) ? (X) : (Y))
+ //#define max(X,Y) ((X) > (Y) ? (X) : (Y))
+ size_t character = max(0, min(parser->length, parser->position + offset));
+ //#undef min
+ //#undef max
+
+ size_t position = 0; for (; position < character; ++position) {
+ column++;
+
+ if ('\n' == parser->buffer[position]) {
+ row++;
+ column = 0;
+ }
+ }
+
+ if (NO_CHARACTER != offset) {
+ fprintf(stderr, "xml_parser_error at %i:%i (is %c): %s\n",
+ row + 1, column, parser->buffer[character], message
+ );
+ } else {
+ fprintf(stderr, "xml_parser_error at %i:%i: %s\n",
+ row + 1, column, message
+ );
+ }
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Returns the n-th not-whitespace byte in parser and 0 if such a byte does not
+ * exist
+ */
+static uint8_t xml_parser_peek(struct xml_parser* parser, size_t n) {
+ size_t position = parser->position;
+
+ while (position < parser->length) {
+ if (!isspace(parser->buffer[position])) {
+ if (n == 0) {
+ return parser->buffer[position];
+ } else {
+ --n;
+ }
+ }
+
+ position++;
+ }
+
+ return 0;
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Moves the parser's position n bytes. If the new position would be out of
+ * bounds, it will be converted to the bounds itself
+ */
+static void xml_parser_consume(struct xml_parser* parser, size_t n) {
+
+ /* Debug information
+ */
+ #ifdef XML_PARSER_VERBOSE
+ #define min(X,Y) ((X) < (Y) ? (X) : (Y))
+ char* consumed = alloca((n + 1) * sizeof(char));
+ memcpy(consumed, &parser->buffer[parser->position], min(n, parser->length - parser->position));
+ consumed[n] = 0;
+ #undef min
+
+ size_t message_buffer_length = 512;
+ char* message_buffer = alloca(512 * sizeof(char));
+ snprintf(message_buffer, message_buffer_length, "Consuming %li bytes \"%s\"", (long)n, consumed);
+ message_buffer[message_buffer_length - 1] = 0;
+
+ xml_parser_info(parser, message_buffer);
+ #endif
+
+
+ /* Move the position forward
+ */
+ parser->position += n;
+
+ /* Don't go too far
+ *
+ * @warning Valid because parser->length must be greater than 0
+ */
+ if (parser->position >= parser->length) {
+ parser->position = parser->length - 1;
+ }
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Skips to the next non-whitespace character
+ */
+static void xml_skip_whitespace(struct xml_parser* parser) {
+ xml_parser_info(parser, "whitespace");
+
+ while (isspace(parser->buffer[parser->position])) {
+ if (parser->position + 1 >= parser->length) {
+ return;
+ } else {
+ parser->position++;
+ }
+ }
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Finds and creates all attributes on the given node.
+ *
+ * @author Blake Felt
+ * @see https://github.com/Molorius
+ */
+static struct xml_attribute** xml_find_attributes(struct xml_parser* parser, struct xml_string* tag_open) {
+ (void)parser;
+ xml_parser_info(parser, "find_attributes");
+ char* tmp;
+ char* rest = NULL;
+ char* token;
+ char* str_name;
+ char* str_content;
+ const unsigned char* start_name;
+ const unsigned char* start_content;
+ size_t old_elements;
+ size_t new_elements;
+ struct xml_attribute* new_attribute;
+ struct xml_attribute** attributes;
+ int position;
+
+ attributes = calloc(1, sizeof(struct xml_attribute*));
+ attributes[0] = 0;
+
+ tmp = (char*) xml_string_clone(tag_open);
+
+ token = xml_strtok_r(tmp, " ", &rest); // skip the first value
+ if(token == NULL) {
+ goto cleanup;
+ }
+ tag_open->length = strlen(token);
+
+ for(token=xml_strtok_r(NULL," ", &rest); token!=NULL; token=xml_strtok_r(NULL," ", &rest)) {
+ str_name = malloc(strlen(token)+1);
+ str_content = malloc(strlen(token)+1);
+ // %s=\"%s\" wasn't working for some reason, ugly hack to make it work
+ if(sscanf(token, "%[^=]=\"%[^\"]", str_name, str_content) != 2) {
+ if(sscanf(token, "%[^=]=\'%[^\']", str_name, str_content) != 2) {
+ free(str_name);
+ free(str_content);
+ continue;
+ }
+ }
+ position = (int)(token-tmp);
+ start_name = &tag_open->buffer[position];
+ start_content = &tag_open->buffer[position + strlen(str_name) + 2];
+
+ new_attribute = malloc(sizeof(struct xml_attribute));
+ new_attribute->name = malloc(sizeof(struct xml_string));
+ new_attribute->name->buffer = (unsigned char*)start_name;
+ new_attribute->name->length = strlen(str_name);
+ new_attribute->content = malloc(sizeof(struct xml_string));
+ new_attribute->content->buffer = (unsigned char*)start_content;
+ new_attribute->content->length = strlen(str_content);
+
+ old_elements = get_zero_terminated_array_attributes(attributes);
+ new_elements = old_elements + 1;
+ attributes = realloc(attributes, (new_elements+1)*sizeof(struct xml_attribute*));
+
+ attributes[new_elements-1] = new_attribute;
+ attributes[new_elements] = 0;
+
+
+ free(str_name);
+ free(str_content);
+ }
+
+cleanup:
+ free(tmp);
+ return attributes;
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Parses the name out of the an XML tag's ending
+ *
+ * ---( Example )---
+ * tag_name>
+ * ---
+ */
+static struct xml_string* xml_parse_tag_end(struct xml_parser* parser) {
+ xml_parser_info(parser, "tag_end");
+ size_t start = parser->position;
+ size_t length = 0;
+
+ /* Parse until `>' or a whitespace is reached
+ */
+ while (start + length < parser->length) {
+ uint8_t current = xml_parser_peek(parser, CURRENT_CHARACTER);
+
+ if (('>' == current) || isspace(current)) {
+ break;
+ } else {
+ xml_parser_consume(parser, 1);
+ length++;
+ }
+ }
+
+ /* Consume `>'
+ */
+ if ('>' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
+ xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_end::expected tag end");
+ return 0;
+ }
+ xml_parser_consume(parser, 1);
+
+ /* Return parsed tag name
+ */
+ struct xml_string* name = malloc(sizeof(struct xml_string));
+ name->buffer = &parser->buffer[start];
+ name->length = length;
+ return name;
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Parses an opening XML tag without attributes
+ *
+ * ---( Example )---
+ * <tag_name>
+ * ---
+ */
+static struct xml_string* xml_parse_tag_open(struct xml_parser* parser) {
+ xml_parser_info(parser, "tag_open");
+ xml_skip_whitespace(parser);
+
+ /* Consume `<'
+ */
+ if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
+ xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_open::expected opening tag");
+ return 0;
+ }
+ xml_parser_consume(parser, 1);
+
+ /* Consume tag name
+ */
+ return xml_parse_tag_end(parser);
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Parses an closing XML tag without attributes
+ *
+ * ---( Example )---
+ * </tag_name>
+ * ---
+ */
+static struct xml_string* xml_parse_tag_close(struct xml_parser* parser) {
+ xml_parser_info(parser, "tag_close");
+ xml_skip_whitespace(parser);
+
+ /* Consume `</'
+ */
+ if ( ('<' != xml_parser_peek(parser, CURRENT_CHARACTER))
+ || ('/' != xml_parser_peek(parser, NEXT_CHARACTER))) {
+
+ if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
+ xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_close::expected closing tag `<'");
+ }
+ if ('/' != xml_parser_peek(parser, NEXT_CHARACTER)) {
+ xml_parser_error(parser, NEXT_CHARACTER, "xml_parse_tag_close::expected closing tag `/'");
+ }
+
+ return 0;
+ }
+ xml_parser_consume(parser, 2);
+
+ /* Consume tag name
+ */
+ return xml_parse_tag_end(parser);
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Parses a tag's content
+ *
+ * ---( Example )---
+ * this is
+ * a
+ * tag {} content
+ * ---
+ *
+ * @warning CDATA etc. is _not_ and will never be supported
+ */
+static struct xml_string* xml_parse_content(struct xml_parser* parser) {
+ xml_parser_info(parser, "content");
+
+ /* Whitespace will be ignored
+ */
+ xml_skip_whitespace(parser);
+
+ size_t start = parser->position;
+ size_t length = 0;
+
+ /* Consume until `<' is reached
+ */
+ while (start + length < parser->length) {
+ uint8_t current = xml_parser_peek(parser, CURRENT_CHARACTER);
+
+ if ('<' == current) {
+ break;
+ } else {
+ xml_parser_consume(parser, 1);
+ length++;
+ }
+ }
+
+ /* Next character must be an `<' or we have reached end of file
+ */
+ if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
+ xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_content::expected <");
+ return 0;
+ }
+
+ /* Ignore tailing whitespace
+ */
+ while ((length > 0) && isspace(parser->buffer[start + length - 1])) {
+ length--;
+ }
+
+ /* Return text
+ */
+ struct xml_string* content = malloc(sizeof(struct xml_string));
+ content->buffer = &parser->buffer[start];
+ content->length = length;
+ return content;
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Parses an XML fragment node
+ *
+ * ---( Example without children )---
+ * <Node>Text</Node>
+ * ---
+ *
+ * ---( Example with children )---
+ * <Parent>
+ * <Child>Text</Child>
+ * <Child>Text</Child>
+ * <Test>Content</Test>
+ * </Parent>
+ * ---
+ */
+static struct xml_node* xml_parse_node(struct xml_parser* parser) {
+ xml_parser_info(parser, "node");
+
+ /* Setup variables
+ */
+ struct xml_string* tag_open = 0;
+ struct xml_string* tag_close = 0;
+ struct xml_string* content = 0;
+
+ size_t original_length;
+ struct xml_attribute** attributes;
+
+ struct xml_node** children = calloc(1, sizeof(struct xml_node*));
+ children[0] = 0;
+
+
+ /* Parse open tag
+ */
+ tag_open = xml_parse_tag_open(parser);
+ if (!tag_open) {
+ xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag_open");
+ goto exit_failure;
+ }
+
+ original_length = tag_open->length;
+ attributes = xml_find_attributes(parser, tag_open);
+
+ /* If tag ends with `/' it's self closing, skip content lookup */
+ if (tag_open->length > 0 && '/' == tag_open->buffer[original_length - 1]) {
+ /* Drop `/'
+ */
+ goto node_creation;
+ }
+
+ /* If the content does not start with '<', a text content is assumed
+ */
+ if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
+ content = xml_parse_content(parser);
+
+ if (!content) {
+ xml_parser_error(parser, 0, "xml_parse_node::content");
+ goto exit_failure;
+ }
+
+
+ /* Otherwise children are to be expected
+ */
+ } else while ('/' != xml_parser_peek(parser, NEXT_CHARACTER)) {
+
+ /* Parse child node
+ */
+ struct xml_node* child = xml_parse_node(parser);
+ if (!child) {
+ xml_parser_error(parser, NEXT_CHARACTER, "xml_parse_node::child");
+ goto exit_failure;
+ }
+
+ /* Grow child array :)
+ */
+ size_t old_elements = get_zero_terminated_array_nodes(children);
+ size_t new_elements = old_elements + 1;
+ children = realloc(children, (new_elements + 1) * sizeof(struct xml_node*));
+
+ /* Save child
+ */
+ children[new_elements - 1] = child;
+ children[new_elements] = 0;
+ }
+
+
+ /* Parse close tag
+ */
+ tag_close = xml_parse_tag_close(parser);
+ if (!tag_close) {
+ xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag_close");
+ goto exit_failure;
+ }
+
+
+ /* Close tag has to match open tag
+ */
+ if (!xml_string_equals(tag_open, tag_close)) {
+ xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag missmatch");
+ goto exit_failure;
+ }
+
+
+ /* Return parsed node
+ */
+ xml_string_free(tag_close);
+
+node_creation:;
+ struct xml_node* node = malloc(sizeof(struct xml_node));
+ node->name = tag_open;
+ node->content = content;
+ node->attributes = attributes;
+ node->children = children;
+ return node;
+
+
+ /* A failure occured, so free all allocalted resources
+ */
+exit_failure:
+ if (tag_open) {
+ xml_string_free(tag_open);
+ }
+ if (tag_close) {
+ xml_string_free(tag_close);
+ }
+ if (content) {
+ xml_string_free(content);
+ }
+
+ struct xml_node** it = children;
+ while (*it) {
+ xml_node_free(*it);
+ ++it;
+ }
+ free(children);
+
+ return 0;
+}
+
+
+
+
+
+/**
+ * [PUBLIC API]
+ */
+struct xml_document* xml_parse_document(uint8_t* buffer, size_t length) {
+
+ /* Initialize parser
+ */
+ struct xml_parser parser = {
+ .buffer = buffer,
+ .position = 0,
+ .length = length
+ };
+
+ /* An empty buffer can never contain a valid document
+ */
+ if (!length) {
+ xml_parser_error(&parser, NO_CHARACTER, "xml_parse_document::length equals zero");
+ return 0;
+ }
+
+ /* Parse the root node
+ */
+ struct xml_node* root = xml_parse_node(&parser);
+ if (!root) {
+ xml_parser_error(&parser, NO_CHARACTER, "xml_parse_document::parsing document failed");
+ return 0;
+ }
+
+ /* Return parsed document
+ */
+ struct xml_document* document = malloc(sizeof(struct xml_document));
+ document->buffer.buffer = buffer;
+ document->buffer.length = length;
+ document->root = root;
+
+ return document;
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+struct xml_document* xml_open_document(FILE* source) {
+
+ /* Prepare buffer
+ */
+ size_t const read_chunk = 1; // TODO 4096;
+
+ size_t document_length = 0;
+ size_t buffer_size = 1; // TODO 4069
+ uint8_t* buffer = malloc(buffer_size * sizeof(uint8_t));
+
+ /* Read hole file into buffer
+ */
+ while (!feof(source)) {
+
+ /* Reallocate buffer
+ */
+ if (buffer_size - document_length < read_chunk) {
+ buffer = realloc(buffer, buffer_size + 2 * read_chunk);
+ buffer_size += 2 * read_chunk;
+ }
+
+ size_t read = fread(
+ &buffer[document_length],
+ sizeof(uint8_t), read_chunk,
+ source
+ );
+
+ document_length += read;
+ }
+ fclose(source);
+
+ /* Try to parse buffer
+ */
+ struct xml_document* document = xml_parse_document(buffer, document_length);
+
+ if (!document) {
+ free(buffer);
+ return 0;
+ }
+ return document;
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+void xml_document_free(struct xml_document* document, bool free_buffer) {
+ xml_node_free(document->root);
+
+ if (free_buffer) {
+ free(document->buffer.buffer);
+ }
+ free(document);
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+struct xml_node* xml_document_root(struct xml_document* document) {
+ return document->root;
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+struct xml_string* xml_node_name(struct xml_node* node) {
+ return node->name;
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+struct xml_string* xml_node_content(struct xml_node* node) {
+ return node->content;
+}
+
+
+
+/**
+ * [PUBLIC API]
+ *
+ * @warning O(n)
+ */
+size_t xml_node_children(struct xml_node* node) {
+ return get_zero_terminated_array_nodes(node->children);
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+struct xml_node* xml_node_child(struct xml_node* node, size_t child) {
+ if (child >= xml_node_children(node)) {
+ return 0;
+ }
+
+ return node->children[child];
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+size_t xml_node_attributes(struct xml_node* node) {
+ return get_zero_terminated_array_attributes(node->attributes);
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+struct xml_string* xml_node_attribute_name(struct xml_node* node, size_t attribute) {
+ if(attribute >= xml_node_attributes(node)) {
+ return 0;
+ }
+
+ return node->attributes[attribute]->name;
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+struct xml_string* xml_node_attribute_content(struct xml_node* node, size_t attribute) {
+ if(attribute >= xml_node_attributes(node)) {
+ return 0;
+ }
+
+ return node->attributes[attribute]->content;
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+struct xml_node* xml_easy_child(struct xml_node* node, uint8_t const* child_name, ...) {
+
+ /* Find children, one by one
+ */
+ struct xml_node* current = node;
+
+ va_list arguments;
+ va_start(arguments, child_name);
+
+
+ /* Descent to current.child
+ */
+ while (child_name) {
+
+ /* Convert child_name to xml_string for easy comparison
+ */
+ struct xml_string cn = {
+ .buffer = (const uint8_t *)child_name,
+ .length = strlen((const char *)child_name)
+ };
+
+ /* Interate through all children
+ */
+ struct xml_node* next = 0;
+
+ size_t i = 0; for (; i < xml_node_children(current); ++i) {
+ struct xml_node* child = xml_node_child(current, i);
+
+ if (xml_string_equals(xml_node_name(child), &cn)) {
+ if (!next) {
+ next = child;
+
+ /* Two children with the same name
+ */
+ } else {
+ va_end(arguments);
+ return 0;
+ }
+ }
+ }
+
+ /* No child with that name found
+ */
+ if (!next) {
+ va_end(arguments);
+ return 0;
+ }
+ current = next;
+
+ /* Find name of next child
+ */
+ child_name = va_arg(arguments, uint8_t const*);
+ }
+ va_end(arguments);
+
+
+ /* Return current element
+ */
+ return current;
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+uint8_t* xml_easy_name(struct xml_node* node) {
+ if (!node) {
+ return 0;
+ }
+
+ return xml_string_clone(xml_node_name(node));
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+uint8_t* xml_easy_content(struct xml_node* node) {
+ if (!node) {
+ return 0;
+ }
+
+ return xml_string_clone(xml_node_content(node));
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+size_t xml_string_length(struct xml_string* string) {
+ if (!string) {
+ return 0;
+ }
+ return string->length;
+}
+
+
+
+/**
+ * [PUBLIC API]
+ */
+void xml_string_copy(struct xml_string* string, uint8_t* buffer, size_t length) {
+ if (!string) {
+ return;
+ }
+
+ //#define min(X,Y) ((X) < (Y) ? (X) : (Y))
+ length = min(length, string->length);
+ //#undef min
+
+ memcpy(buffer, string->buffer, length);
+}
+
diff --git a/libs/xml.c/src/xml.h b/libs/xml.c/src/xml.h
new file mode 100644
index 0000000..688a4be
--- /dev/null
+++ b/libs/xml.c/src/xml.h
@@ -0,0 +1,196 @@
+/**
+ * Copyright (c) 2012 ooxi/xml.c
+ * https://github.com/ooxi/xml.c
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+#ifndef HEADER_XML
+#define HEADER_XML
+
+
+/**
+ * Includes
+ */
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Opaque structure holding the parsed xml document
+ */
+struct xml_document;
+struct xml_node;
+struct xml_attribute;
+
+/**
+ * Internal character sequence representation
+ */
+struct xml_string;
+
+
+
+/**
+ * Tries to parse the XML fragment in buffer
+ *
+ * @param buffer Chunk to parse
+ * @param length Size of the buffer
+ *
+ * @warning `buffer` will be referenced by the document, you may not free it
+ * until you free the xml_document
+ * @warning You have to call xml_document_free after you finished using the
+ * document
+ *
+ * @return The parsed xml fragment iff parsing was successful, 0 otherwise
+ */
+struct xml_document* xml_parse_document(uint8_t* buffer, size_t length);
+
+
+
+/**
+ * Tries to read an XML document from disk
+ *
+ * @param source File that will be read into an xml document. Will be closed
+ *
+ * @warning You have to call xml_document_free with free_buffer = true after you
+ * finished using the document
+ *
+ * @return The parsed xml fragment iff parsing was successful, 0 otherwise
+ */
+struct xml_document* xml_open_document(FILE* source);
+
+
+
+/**
+ * Frees all resources associated with the document. All xml_node and xml_string
+ * references obtained through the document will be invalidated
+ *
+ * @param document xml_document to free
+ * @param free_buffer iff true the internal buffer supplied via xml_parse_buffer
+ * will be freed with the `free` system call
+ */
+void xml_document_free(struct xml_document* document, bool free_buffer);
+
+
+/**
+ * @return xml_node representing the document root
+ */
+struct xml_node* xml_document_root(struct xml_document* document);
+
+
+
+/**
+ * @return The xml_node's tag name
+ */
+struct xml_string* xml_node_name(struct xml_node* node);
+
+
+
+/**
+ * @return The xml_node's string content (if available, otherwise NULL)
+ */
+struct xml_string* xml_node_content(struct xml_node* node);
+
+
+
+/**
+ * @return Number of child nodes
+ */
+size_t xml_node_children(struct xml_node* node);
+
+
+
+/**
+ * @return The n-th child or 0 if out of range
+ */
+struct xml_node* xml_node_child(struct xml_node* node, size_t child);
+
+
+
+/**
+ * @return Number of attribute nodes
+ */
+size_t xml_node_attributes(struct xml_node* node);
+
+
+
+/**
+ * @return the n-th attribute name or 0 if out of range
+ */
+struct xml_string* xml_node_attribute_name(struct xml_node* node, size_t attribute);
+
+
+
+/**
+ * @return the n-th attribute content or 0 if out of range
+ */
+struct xml_string* xml_node_attribute_content(struct xml_node* node, size_t attribute);
+
+
+
+/**
+ * @return The node described by the path or 0 if child cannot be found
+ * @warning Each element on the way must be unique
+ * @warning Last argument must be 0
+ */
+struct xml_node* xml_easy_child(struct xml_node* node, uint8_t const* child, ...);
+
+
+
+/**
+ * @return 0-terminated copy of node name
+ * @warning User must free the result
+ */
+uint8_t* xml_easy_name(struct xml_node* node);
+
+
+
+/**
+ * @return 0-terminated copy of node content
+ * @warning User must free the result
+ */
+uint8_t* xml_easy_content(struct xml_node* node);
+
+
+
+/**
+ * @return Length of the string
+ */
+size_t xml_string_length(struct xml_string* string);
+
+
+
+/**
+ * Copies the string into the supplied buffer
+ *
+ * @warning String will not be 0-terminated
+ * @warning Will write at most length bytes, even if the string is longer
+ */
+void xml_string_copy(struct xml_string* string, uint8_t* buffer, size_t length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/libs/xml.c/test/CMakeLists.txt b/libs/xml.c/test/CMakeLists.txt
new file mode 100644
index 0000000..09097d0
--- /dev/null
+++ b/libs/xml.c/test/CMakeLists.txt
@@ -0,0 +1,128 @@
+# xml.c / test
+cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
+
+
+
+# Example
+add_executable(
+ "${PROJECT_NAME}-example"
+ "${CMAKE_CURRENT_LIST_DIR}/example.c"
+)
+
+target_compile_options(
+ "${PROJECT_NAME}-example"
+ PRIVATE
+ -std=c11
+)
+
+target_link_libraries(
+ "${PROJECT_NAME}-example"
+ PRIVATE
+ xml
+)
+
+add_test(
+ NAME "${PROJECT_NAME}-example"
+ COMMAND "${PROJECT_NAME}-example"
+)
+
+
+
+# Test cases
+FILE( COPY "${CMAKE_CURRENT_LIST_DIR}/test.xml"
+ DESTINATION "${CMAKE_CURRENT_BINARY_DIR}"
+)
+
+FILE( COPY "${CMAKE_CURRENT_LIST_DIR}/test-attributes.xml"
+ DESTINATION "${CMAKE_CURRENT_BINARY_DIR}"
+)
+
+
+
+# Test (C)
+add_executable(
+ "${PROJECT_NAME}-test-c"
+ "${CMAKE_CURRENT_LIST_DIR}/test-xml-c.c"
+)
+
+target_compile_options(
+ "${PROJECT_NAME}-test-c"
+ PRIVATE
+ -std=c11
+)
+
+target_link_libraries(
+ "${PROJECT_NAME}-test-c"
+ PRIVATE
+ xml
+)
+
+
+add_test(
+ NAME "${PROJECT_NAME}-test-c"
+ COMMAND "${PROJECT_NAME}-test-c"
+)
+
+add_test(
+ NAME "${PROJECT_NAME}-test-c-valgrind"
+ COMMAND valgrind --tool=memcheck --leak-check=full --track-origins=yes -v "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-test-c"
+)
+
+
+
+# Test (C++)
+add_executable(
+ "${PROJECT_NAME}-test-cpp"
+ "${CMAKE_CURRENT_LIST_DIR}/test-xml-cpp.cpp"
+)
+
+target_compile_options(
+ "${PROJECT_NAME}-test-cpp"
+ PRIVATE
+ -std=c++11
+)
+
+target_link_libraries(
+ "${PROJECT_NAME}-test-cpp"
+ PRIVATE
+ xml
+)
+
+
+add_test(
+ NAME "${PROJECT_NAME}-test-cpp"
+ COMMAND "${PROJECT_NAME}-test-cpp"
+)
+
+
+add_test(
+ NAME "${PROJECT_NAME}-test-cpp-valgrind"
+ COMMAND valgrind --tool=memcheck --leak-check=full --track-origins=yes -v "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-test-cpp"
+)
+
+
+
+# Test huitre39
+add_executable(
+ "${PROJECT_NAME}-test-huitre39"
+ "${CMAKE_CURRENT_LIST_DIR}/test-huitre39.c"
+)
+
+target_compile_options(
+ "${PROJECT_NAME}-test-huitre39"
+ PRIVATE
+ -std=c11
+)
+
+target_link_libraries(
+ "${PROJECT_NAME}-test-huitre39"
+ PRIVATE
+ xml
+)
+
+
+add_test(
+ NAME "${PROJECT_NAME}-test-huitre39"
+ COMMAND "${PROJECT_NAME}-test-huitre39"
+)
+
diff --git a/libs/xml.c/test/example.c b/libs/xml.c/test/example.c
new file mode 100644
index 0000000..ebf605c
--- /dev/null
+++ b/libs/xml.c/test/example.c
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2012 ooxi/xml.c
+ * https://github.com/ooxi/xml.c
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <xml.h>
+
+
+
+int main(int argc, char** argv) {
+
+ /* XML source, could be read from disk
+ */
+ uint8_t* source = ""
+ "<Root>"
+ "<Hello>World</Hello>"
+ "<This>"
+ "<Is>:-)</Is>"
+ "<An>:-O</An>"
+ "<Example>:-D</Example>"
+ "</This>"
+ "</Root>"
+ ;
+
+
+ /* Parse the document
+ *
+ * Watch out: Remember not to free the source until you have freed the
+ * document itself. If you have to free the source before, supply a
+ * copy to xml_parse_document which can be freed together with the
+ * document (`free_buffer' argument to `xml_document_free')
+ */
+ struct xml_document* document = xml_parse_document(source, strlen(source));
+
+ /* You _have_ to check the result of `xml_parse_document', if it's 0
+ * then the source could not be parsed. If you think this is a bug in
+ * xml.c, than use a debug build (cmake -DCMAKE_BUILD_TYPE=Debug) which
+ * will verbosely tell you about the parsing process
+ */
+ if (!document) {
+ printf("Could parse document\n");
+ exit(EXIT_FAILURE);
+ }
+ struct xml_node* root = xml_document_root(document);
+
+
+ /* Say Hello World :-)
+ */
+ struct xml_node* root_hello = xml_node_child(root, 0);
+ struct xml_string* hello = xml_node_name(root_hello);
+ struct xml_string* world = xml_node_content(root_hello);
+
+ /* Watch out: `xml_string_copy' will not 0-terminate your buffers! (but
+ * `calloc' will :-)
+ */
+ uint8_t* hello_0 = calloc(xml_string_length(hello) + 1, sizeof(uint8_t));
+ uint8_t* world_0 = calloc(xml_string_length(world) + 1, sizeof(uint8_t));
+ xml_string_copy(hello, hello_0, xml_string_length(hello));
+ xml_string_copy(world, world_0, xml_string_length(world));
+
+ printf("%s %s\n", hello_0, world_0);
+ free(hello_0);
+ free(world_0);
+
+
+ /* Extract amount of Root/This children
+ */
+ struct xml_node* root_this = xml_node_child(root, 1);
+ printf("Root/This has %lu children\n", (unsigned long)xml_node_children(root_this));
+
+
+ /* Remember to free the document or you'll risk a memory leak
+ */
+ xml_document_free(document, false);
+}
+
diff --git a/libs/xml.c/test/test-attributes.xml b/libs/xml.c/test/test-attributes.xml
new file mode 100644
index 0000000..7832c02
--- /dev/null
+++ b/libs/xml.c/test/test-attributes.xml
@@ -0,0 +1 @@
+<Test value="2" value_2="Hello"></Test>
diff --git a/libs/xml.c/test/test-huitre39.c b/libs/xml.c/test/test-huitre39.c
new file mode 100644
index 0000000..43b9ae7
--- /dev/null
+++ b/libs/xml.c/test/test-huitre39.c
@@ -0,0 +1,181 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "./xml.h"
+
+
+
+
+
+/**
+ * Will halt the program iff assertion fails
+ */
+static void _assert_that(_Bool condition, char const* message, char const* func, char const* file, int line) {
+ if (!condition) {
+ fprintf(stderr, "Assertion failed: %s, in %s (%s:%i)\n", message, func, file, line);
+ exit(EXIT_FAILURE);
+ }
+}
+
+#define assert_that(condition, message) \
+ _assert_that(condition, message, __func__, __FILE__, __LINE__)
+
+
+
+
+
+/**
+ * Behaves similar to `getElementsByTagName`, however returns just the first and
+ * not all elements with a given tag name
+ *
+ * @param base Node in tree which should be the start of te recursive search
+ * @param 0-terminated tag name, case sensitive
+ *
+ * @return First node below `base` iff found, otherwise 0
+ * @warning Depth-First search!
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName
+ */
+static struct xml_node* get_node_by_name(struct xml_node* base, uint8_t* name) {
+
+
+ /* Check whether `base` already has the tag name we are searching for
+ */
+ size_t const name_length = strlen(name);
+
+ uint8_t* base_name = xml_easy_name(base);
+ size_t const base_name_length = strlen(base_name);
+
+
+ /* Length of `name` and `base_name` do match, we should now do a real
+ * comparison
+ */
+ if (name_length == base_name_length) {
+ int const rs = memcmp(name, base_name, name_length);
+
+ /* Names match! We have found an element which fullfills our
+ * search criteria
+ */
+ if (!rs) {
+ free(base_name);
+ return base;
+ }
+ }
+
+
+ /* Unfortunately, `base` is not the element we are looking for :-(
+ */
+ free(base_name);
+
+
+ /* Let's take a look at the children of `base`
+ */
+ size_t const number_of_children = xml_node_children(base);
+
+
+ /* No children → No luck with base
+ */
+ if (!number_of_children) {
+ return 0;
+ }
+
+
+ /* Recursivly look through all children
+ */
+ size_t child = 0; for (; child < number_of_children; ++child) {
+ struct xml_node* child_node = xml_node_child(base, child);
+
+ /* Maybe this child does contain the element we are looking for?
+ */
+ struct xml_node* search_result = get_node_by_name(
+ child_node, name
+ );
+
+ /* We are lucky!
+ */
+ if (search_result) {
+ return search_result;
+ }
+ }
+
+
+ /* No luck :-(
+ */
+ return 0;
+}
+
+
+
+
+
+int main(int argc, char** argv) {
+
+ /* XML source, could be read from disk
+ */
+ uint8_t* source = ""
+ "<Root>"
+ "<Hello>World</Hello>"
+
+ "<Functions>"
+ "<Function>"
+ "<as>testas one</as>"
+ "<os>testos</os>"
+ "</Function>"
+
+ "<Function>"
+ "<is>testis</is>"
+ "<us>testus</us>"
+ "<ls>testls</ls>"
+ "</Function>"
+
+ "<Function>"
+ "<mn>testmn</mn>"
+ "<as>testas two</as>"
+ "</Function>"
+ "</Functions>"
+ "</Root>"
+ ;
+
+ struct xml_document* document = xml_parse_document(source, strlen(source));
+
+ if (!document) {
+ printf("Could parse document\n");
+ exit(EXIT_FAILURE);
+ }
+ struct xml_node* root = xml_document_root(document);
+
+
+
+ /* We expect to find Root / Functions / Function#1 / us
+ */
+ struct xml_node* us = get_node_by_name(root, "us");
+ assert_that(us, "Did not find element by tag name `us'");
+
+ uint8_t* us_content = xml_easy_content(us);
+ assert_that(us_content, "`us' should have content");
+ assert_that(!strcmp(us_content, "testus"), "Unexpected content for node `us'");
+ free(us_content);
+
+
+ /* We expect to find Root / Functions / Function#0 / as
+ */
+ struct xml_node* as = get_node_by_name(root, "as");
+ assert_that(as, "Did not find element by tag name `as'");
+
+ uint8_t* as_content = xml_easy_content(as);
+ assert_that(as_content, "`as' should have content");
+ assert_that(!strcmp(as_content, "testas one"), "Unexpected content for first `as' node");
+ free(as_content);
+
+
+ /* We do not expect do find a node with tag name `does_not_exist'
+ */
+ struct xml_node* does_not_exist = get_node_by_name(root, "does_not_exist");
+ assert_that(!does_not_exist, "Found node that should not exist");
+
+
+
+ xml_document_free(document, false);
+}
+
diff --git a/libs/xml.c/test/test-xml-c.c b/libs/xml.c/test/test-xml-c.c
new file mode 100644
index 0000000..ee54bac
--- /dev/null
+++ b/libs/xml.c/test/test-xml-c.c
@@ -0,0 +1,265 @@
+/**
+ * Copyright (c) 2012 ooxi/xml.c
+ * https://github.com/ooxi/xml.c
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+#include <alloca.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <xml.h>
+
+
+
+
+
+/**
+ * Will halt the program iff assertion fails
+ */
+static void _assert_that(_Bool condition, char const* message, char const* func, char const* file, int line) {
+ if (!condition) {
+ fprintf(stderr, "Assertion failed: %s, in %s (%s:%i)\n", message, func, file, line);
+ exit(EXIT_FAILURE);
+ }
+}
+
+#define assert_that(condition, message) \
+ _assert_that(condition, message, __func__, __FILE__, __LINE__)
+
+
+
+/**
+ * @return true iff xml string equals the c string
+ */
+static _Bool string_equals(struct xml_string* a, char const* b) {
+ size_t a_length = xml_string_length(a);
+ size_t b_length = strlen(b);
+
+ uint8_t* a_buffer = alloca((a_length + 1) * sizeof(uint8_t));
+ xml_string_copy(a, a_buffer, a_length);
+ a_buffer[a_length] = 0;
+
+ if (a_length != b_length) {
+ fprintf(stderr, "string_equals: %s#%i <> %s#%i\n", a_buffer, (int)a_length, b, (int)b_length);
+ return false;
+ }
+
+ size_t i = 0; for (; i < a_length; ++i) {
+ if (a_buffer[i] != b[i]) {
+ fprintf(stderr, "string_equals: %s <> %s\n", a_buffer, b);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+/**
+ * Converts a static character array to an uint8_t data source
+ */
+#define SOURCE(source, content) \
+ uint8_t* source = calloc(strlen(content) + 1, sizeof(uint8_t)); \
+ { char const* content_string = content; \
+ memcpy(source, content_string, strlen(content) + 1); \
+ }
+
+
+
+/**
+ * Tries to parse a simple document containing only one tag
+ */
+static void test_xml_parse_document_0() {
+ SOURCE(source, "<Hello>World</Hello>");
+// uint8_t* source = malloc((1 + strlen("<Hello>World</Hello>")) * sizeof(uint8_t));
+// { char const* content_string = "<Hello>World</Hello>";
+// memcpy(source, content_string, strlen("<Hello>World</Hello>") + 1);
+// }
+
+ struct xml_document* document = xml_parse_document(source, strlen(source));
+ assert_that(document, "Could not parse document");
+
+ struct xml_node* root = xml_document_root(document);
+ assert_that(string_equals(xml_node_name(root), "Hello"), "root node name must be `Hello'");
+ assert_that(string_equals(xml_node_content(root), "World"), "root node content must be `World'");
+
+ xml_document_free(document, true);
+}
+
+/**
+ * Tries to parse a document containing multiple tags
+ */
+static void test_xml_parse_document_1() {
+ SOURCE(source, ""
+ "<Parent>\n"
+ "\t<Child>\n"
+ "\t\tFirst content\n"
+ "\t</Child>\n"
+ "\t<Child>\n"
+ "\t\tSecond content\n"
+ "\t</Child>\n"
+ "</Parent>\n"
+ );
+ struct xml_document* document = xml_parse_document(source, strlen(source));
+ assert_that(document, "Could not parse document");
+
+ struct xml_node* root = xml_document_root(document);
+ assert_that(string_equals(xml_node_name(root), "Parent"), "root node name must be `Parent'");
+ assert_that(2 == xml_node_children(root), "root must have two children");
+
+ struct xml_node* first_child = xml_node_child(root, 0);
+ struct xml_node* second_child = xml_node_child(root, 1);
+ assert_that(first_child && second_child, "Failed retrieving the children of root");
+
+ struct xml_node* third_child = xml_node_child(root, 2);
+ assert_that(!third_child, "root has a third child where non should be");
+
+ assert_that(string_equals(xml_node_name(first_child), "Child"), "first_child node name must be `Child'");
+ assert_that(string_equals(xml_node_content(first_child), "First content"), "first_child node content must be `First content'");
+ assert_that(string_equals(xml_node_name(second_child), "Child"), "second_child node name must be `Child'");
+ assert_that(string_equals(xml_node_content(second_child), "Second content"), "second_child node content must be `tSecond content'");
+
+ xml_document_free(document, true);
+}
+
+
+
+/**
+ * Tests the eas functionality
+ */
+static void test_xml_parse_document_2() {
+ SOURCE(source, ""
+ "<Parent>\n"
+ "\t<Child>\n"
+ "\t\tFirst content\n"
+ "\t</Child>\n"
+ "\t<This><Is>\n"
+ "<A><Test>Content A</Test></A>\n"
+ "<B><Test>Content B</Test></B>\n"
+ "\t</Is></This>\n"
+ "\t<Child>\n"
+ "\t\tSecond content\n"
+ "\t</Child>\n"
+ "</Parent>\n"
+ );
+ struct xml_document* document = xml_parse_document(source, strlen(source));
+ assert_that(document, "Could not parse document");
+
+ struct xml_node* root = xml_document_root(document);
+ assert_that(string_equals(xml_node_name(root), "Parent"), "root node name must be `Parent'");
+ assert_that(3 == xml_node_children(root), "root must have two children");
+
+ struct xml_node* test_a = xml_easy_child(root, "This", "Is", "A", "Test", 0);
+ assert_that(test_a, "Cannot find Parent/This/Is/A/Test");
+ assert_that(string_equals(xml_node_content(test_a), "Content A"), "Content of Parent/This/Is/A/Test must be `Content A'");
+
+ struct xml_node* test_b = xml_easy_child(root, "This", "Is", "B", "Test", 0);
+ assert_that(test_b, "Cannot find Parent/This/Is/B/Test");
+ assert_that(string_equals(xml_node_content(test_b), "Content B"), "Content of Parent/This/Is/B/Test must be `Content B'");
+
+ struct xml_node* test_c = xml_easy_child(root, "This", "Is", "C", "Test", 0);
+ assert_that(!test_c, "Must not find Parent/This/Is/C/Test because no such path exists");
+
+ struct xml_node* must_be_null = xml_easy_child(root, "Child");
+ assert_that(!must_be_null, "Parent/Child cannot be a valid expression, because there are two children named `Child' in `Parent'");
+
+ uint8_t* name_is = xml_easy_name(xml_easy_child(root, "This", "Is", 0));
+ assert_that(!strcmp(name_is, "Is"), "Name of Parent/This/Is must be `Is'");
+ free(name_is);
+
+ uint8_t* content_a = xml_easy_content(test_a);
+ assert_that(!strcmp(content_a, "Content A"), "Content of Parent/This/Is/A/Test must be `Content A'");
+ free(content_a);
+
+ xml_document_free(document, true);
+}
+
+
+
+/**
+ * Tests the xml_open_document functionality
+ */
+static void test_xml_parse_document_3() {
+ #define FILE_NAME "test.xml"
+ FILE* handle = fopen(FILE_NAME, "rb");
+ assert_that(handle, "Cannot open " FILE_NAME);
+
+ struct xml_document* document = xml_open_document(handle);
+ assert_that(document, "Cannot parse " FILE_NAME);
+
+ struct xml_node* element = xml_easy_child(
+ xml_document_root(document), "Element", "With", 0
+ );
+ assert_that(element, "Cannot find Document/Element/With");
+ assert_that(string_equals(xml_node_content(element), "Child"), "Content of Document/Element/With must be `Child'");
+
+ xml_document_free(document, true);
+ #undef FILE_NAME
+}
+
+
+
+/**
+ * Test parsing of attributes
+ *
+ * @author Isty001
+ * @see https://github.com/Isty001/
+ */
+static void test_xml_parse_attributes() {
+ #define FILE_NAME "test-attributes.xml"
+ FILE* handle = fopen(FILE_NAME, "rb");
+ assert_that(handle, "Cannot open " FILE_NAME);
+
+ struct xml_document* document = xml_open_document(handle);
+ assert_that(document, "Cannot parse " FILE_NAME);
+
+ struct xml_node* element = xml_easy_child(
+ xml_document_root(document), 0
+ );
+
+ assert_that(element, "Cannot find Document/Element/With");
+ assert_that(2 == xml_node_attributes(element), "Should have 2 attributes");
+
+ assert_that(string_equals(xml_node_attribute_name(element, 0), "value"), "Content of Document/Element/With must be `Child'");
+ assert_that(string_equals(xml_node_attribute_content(element, 0), "2"), "First attribute's content should be \"2\"");
+
+ assert_that(string_equals(xml_node_attribute_name(element, 1), "value_2"), "Content of Document/Element/With must be `Child'");
+ assert_that(string_equals(xml_node_attribute_content(element, 1), "Hello"), "Second attribute's content should be Hello");
+
+ xml_document_free(document, true);
+ #undef FILE_NAME
+}
+
+
+
+/**
+ * Console interface
+ */
+int main(int argc, char** argv) {
+ test_xml_parse_document_0();
+ test_xml_parse_document_1();
+ test_xml_parse_document_2();
+ test_xml_parse_document_3();
+ test_xml_parse_attributes();
+
+ fprintf(stdout, "All tests passed :-)\n");
+ exit(EXIT_SUCCESS);
+}
diff --git a/libs/xml.c/test/test-xml-cpp.cpp b/libs/xml.c/test/test-xml-cpp.cpp
new file mode 100644
index 0000000..513d14b
--- /dev/null
+++ b/libs/xml.c/test/test-xml-cpp.cpp
@@ -0,0 +1,222 @@
+/**
+ * Copyright (c) 2012 ooxi/xml.c
+ * https://github.com/ooxi/xml.c
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include <iostream>
+#include <cstdlib>
+#include <cstdio>
+#include <xml.h>
+
+/**
+ * Will halt the program iff assertion fails
+ */
+static void _assert_that(bool condition, const char* message,
+ const char* func, const char* file, int line) {
+ if (!condition) {
+ std::cerr << "Assertion failed: " << message << ", in " << func << " ("
+ << file << ":" << line << ")\n";
+ exit(EXIT_FAILURE);
+ }
+}
+
+#define assert_that(condition, message) \
+ _assert_that(condition, message, __func__, __FILE__, __LINE__)
+
+/**
+ * @return true iff xml string equals the c string
+ */
+static bool string_equals(struct xml_string* a, const char* b) {
+ size_t a_length = xml_string_length(a);
+ size_t b_length = strlen(b);
+ uint8_t* a_buffer = new uint8_t[((a_length + 1) * sizeof(uint8_t))];
+ xml_string_copy(a, a_buffer, a_length);
+ a_buffer[a_length] = 0;
+ if (a_length != b_length) {
+ std::cerr << "string_equals: " << a_buffer << "#" << a_length << " <> "
+ << b << "#" << b_length << "\n";
+ delete[] a_buffer;
+ return false;
+ }
+ size_t i = 0; for (; i < a_length; ++i) {
+ if (a_buffer[i] != b[i]) {
+ std::cerr << "string_equals: " << a_buffer << " <> " << b << "\n";
+ delete[] a_buffer;
+ return false;
+ }
+ }
+ delete[] a_buffer;
+ return true;
+}
+
+
+/**
+ * Converts a static character array to an uint8_t data source which can be
+ * freed
+ */
+#define SOURCE(source, content) \
+ uint8_t* source = (uint8_t*)calloc(strlen(content) + 1, sizeof(uint8_t)); \
+ memcpy(source, (content), strlen(content) + 1); \
+
+
+/**
+ * Tries to parse a simple document containing only one tag
+ */
+static void test_xml_parse_document_0() {
+ SOURCE(source, "<Hello>World</Hello>");
+ // uint8_t* source = malloc((1 + strlen("<Hello>World</Hello>")) *
+ // sizeof(uint8_t));
+ // {
+ // const char* content_string = "<Hello>World</Hello>";
+ // memcpy(source, content_string, strlen("<Hello>World</Hello>") + 1);
+ // }
+ struct xml_document* document = xml_parse_document(source,
+ strlen((const char *)source));
+ assert_that(document, "Could not parse document");
+ struct xml_node* root = xml_document_root(document);
+ assert_that(string_equals(xml_node_name(root), "Hello"),
+ "root node name must be `Hello'");
+ assert_that(string_equals(xml_node_content(root), "World"),
+ "root node content must be `World'");
+ xml_document_free(document, true);
+}
+
+/**
+ * Tries to parse a document containing multiple tags
+ */
+static void test_xml_parse_document_1() {
+ SOURCE(source, ""
+ "<Parent>\n"
+ "\t<Child>\n"
+ "\t\tFirst content\n"
+ "\t</Child>\n"
+ "\t<Child>\n"
+ "\t\tSecond content\n"
+ "\t</Child>\n"
+ "</Parent>\n"
+ );
+ struct xml_document* document = xml_parse_document(source,
+ strlen((const char *)source));
+ assert_that(document, "Could not parse document");
+ struct xml_node* root = xml_document_root(document);
+ assert_that(string_equals(xml_node_name(root), "Parent"),
+ "root node name must be `Parent'");
+ assert_that(2 == xml_node_children(root), "root must have two children");
+ struct xml_node* first_child = xml_node_child(root, 0);
+ struct xml_node* second_child = xml_node_child(root, 1);
+ assert_that(first_child && second_child,
+ "Failed retrieving the children of root");
+ struct xml_node* third_child = xml_node_child(root, 2);
+ assert_that(!third_child, "root has a third child where non should be");
+ assert_that(string_equals(xml_node_name(first_child), "Child"),
+ "first_child node name must be `Child'");
+ assert_that(string_equals(xml_node_content(first_child), "First content"),
+ "first_child node content must be `First content'");
+ assert_that(string_equals(xml_node_name(second_child), "Child"),
+ "second_child node name must be `Child'");
+ assert_that(string_equals(xml_node_content(second_child), "Second content"),
+ "second_child node content must be `tSecond content'");
+ xml_document_free(document, true);
+}
+
+/**
+ * Tests the eas functionality
+ */
+static void test_xml_parse_document_2() {
+ SOURCE(source, ""
+ "<Parent>\n"
+ "\t<Child>\n"
+ "\t\tFirst content\n"
+ "\t</Child>\n"
+ "\t<This><Is>\n"
+ "<A><Test>Content A</Test></A>\n"
+ "<B><Test>Content B</Test></B>\n"
+ "\t</Is></This>\n"
+ "\t<Child>\n"
+ "\t\tSecond content\n"
+ "\t</Child>\n"
+ "</Parent>\n"
+ );
+ struct xml_document* document = xml_parse_document(source,
+ strlen((const char *)source));
+ assert_that(document, "Could not parse document");
+ struct xml_node* root = xml_document_root(document);
+ assert_that(string_equals(xml_node_name(root), "Parent"),
+ "root node name must be `Parent'");
+ assert_that(3 == xml_node_children(root),
+ "root must have two children");
+ struct xml_node* test_a = xml_easy_child(root, (uint8_t *)"This",
+ (uint8_t *)"Is", (uint8_t *)"A", (uint8_t *)"Test", 0);
+ assert_that(test_a, "Cannot find Parent/This/Is/A/Test");
+ assert_that(string_equals(xml_node_content(test_a), "Content A"),
+ "Content of Parent/This/Is/A/Test must be `Content A'");
+ struct xml_node* test_b = xml_easy_child(root, (uint8_t *)"This",
+ (uint8_t *)"Is", (uint8_t *)"B", (uint8_t *)"Test", 0);
+ assert_that(test_b, "Cannot find Parent/This/Is/B/Test");
+ assert_that(string_equals(xml_node_content(test_b), "Content B"),
+ "Content of Parent/This/Is/B/Test must be `Content B'");
+ struct xml_node* test_c = xml_easy_child(root, (uint8_t *)"This",
+ (uint8_t *)"Is", (uint8_t *)"C", (uint8_t *)"Test", 0);
+ assert_that(!test_c,
+ "Must not find Parent/This/Is/C/Test because no such path exists");
+ struct xml_node* must_be_null = xml_easy_child(root, (uint8_t *)"Child");
+ assert_that(!must_be_null,
+ "Parent/Child cannot be a valid expression, because there are two children "
+ "named `Child' in `Parent'");
+ uint8_t* name_is = xml_easy_name(xml_easy_child(root, (uint8_t *)"This",
+ (uint8_t *)"Is", 0));
+ assert_that(!strcmp((const char*)name_is, "Is"),
+ "Name of Parent/This/Is must be `Is'");
+ free(name_is);
+ uint8_t* content_a = xml_easy_content(test_a);
+ assert_that(!strcmp((const char*)content_a, "Content A"),
+ "Content of Parent/This/Is/A/Test must be `Content A'");
+ free(content_a);
+ xml_document_free(document, true);
+}
+
+/**
+ * Tests the xml_open_document functionality
+ */
+static void test_xml_parse_document_3() {
+ #define FILE_NAME "test.xml"
+ FILE* handle = fopen(FILE_NAME, "rb");
+ assert_that(handle, "Cannot open " FILE_NAME);
+ struct xml_document* document = xml_open_document(handle);
+ assert_that(document, "Cannot parse " FILE_NAME);
+ struct xml_node* element = xml_easy_child(xml_document_root(document),
+ (uint8_t *)"Element", (uint8_t *)"With", 0);
+ assert_that(element, "Cannot find Document/Element/With");
+ assert_that(string_equals(xml_node_content(element), "Child"),
+ "Content of Document/Element/With must be `Child'");
+ xml_document_free(document, true);
+ #undef FILE_NAME
+}
+
+int main(int argc, char **argv) {
+ test_xml_parse_document_0();
+ test_xml_parse_document_1();
+ test_xml_parse_document_2();
+ test_xml_parse_document_3();
+ std::cout << "All tests passed :-)\n";
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/xml.c/test/test.xml b/libs/xml.c/test/test.xml
new file mode 100644
index 0000000..a0e1ab8
--- /dev/null
+++ b/libs/xml.c/test/test.xml
@@ -0,0 +1,7 @@
+<Document>
+ <Prefix></Prefix>
+ <Element>
+ <With>Child</With>
+ </Element>
+</Document>
+