New easy xml interfacemaster
@@ -2,7 +2,7 @@ | |||
PROJECT(xml) | |||
SET(VERSION_MAJOR "0") | |||
SET(VERSION_MINOR "1") | |||
SET(VERSION_PATCH "2") | |||
SET(VERSION_PATCH "3") | |||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR) | |||
@@ -22,6 +22,7 @@ | |||
*/ | |||
#include <ctype.h> | |||
#include <malloc.h> | |||
#include <stdarg.h> | |||
#include <stdbool.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
@@ -37,7 +38,7 @@ | |||
* UTF-8 text | |||
*/ | |||
struct xml_string { | |||
uint8_t* buffer; | |||
uint8_t const* buffer; | |||
size_t length; | |||
}; | |||
@@ -59,7 +60,11 @@ struct xml_node { | |||
* An xml_document simply contains the root node and the underlying buffer | |||
*/ | |||
struct xml_document { | |||
struct xml_string buffer; | |||
struct { | |||
uint8_t* buffer; | |||
size_t length; | |||
} buffer; | |||
struct xml_node* root; | |||
}; | |||
@@ -134,6 +139,20 @@ static _Bool xml_string_equals(struct xml_string* a, struct xml_string* b) { | |||
/** | |||
* [PRIVATE] | |||
*/ | |||
static uint8_t* xml_string_clone(struct xml_string* s) { | |||
uint8_t* clone = calloc(s->length + 1, sizeof(uint8_t)); | |||
xml_string_copy(s, clone, s->length); | |||
clone[s->length] = 0; | |||
return clone; | |||
} | |||
/** | |||
* [PRIVATE] | |||
* | |||
@@ -715,6 +734,96 @@ struct xml_node* xml_node_child(struct xml_node* node, size_t child) { | |||
/** | |||
* [PUBLIC API] | |||
*/ | |||
struct xml_node* xml_easy_child(struct xml_node* node, uint8_t const* child_name, ...) { | |||
/* Find childrens, 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 = child_name, | |||
.length = strlen(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 { | |||
return 0; | |||
} | |||
} | |||
} | |||
/* No child with that name found | |||
*/ | |||
if (!next) { | |||
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] | |||
*/ | |||
@@ -107,6 +107,31 @@ struct xml_node* xml_node_child(struct xml_node* node, size_t child); | |||
/** | |||
* @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 | |||
*/ | |||
@@ -141,6 +141,58 @@ static void test_xml_parse_document_1() { | |||
/** | |||
* 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); | |||
} | |||
/** | |||
@@ -149,6 +201,7 @@ static void test_xml_parse_document_1() { | |||
int main(int argc, char** argv) { | |||
test_xml_parse_document_0(); | |||
test_xml_parse_document_1(); | |||
test_xml_parse_document_2(); | |||
fprintf(stdout, "All tests passed :-)\n"); | |||
exit(EXIT_SUCCESS); | |||