Squashed commit of the following: commitmaster7300b93a1f
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:51:19 2012 +0100 Easy development... commit66b764840d
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:50:11 2012 +0100 Easy development... commit62e4801612
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:47:26 2012 +0100 Easy development... commitd3b67262ff
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:47:08 2012 +0100 Easy development... commit65661e2ac0
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:45:05 2012 +0100 Easy development... commit3ae765fd82
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:44:58 2012 +0100 Easy development... commitc6d1a3bb2c
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:44:36 2012 +0100 Easy development... commitdeb9846236
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:44:13 2012 +0100 Easy development... commit53cb08c3fc
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:43:41 2012 +0100 Easy development... commitce3766de25
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:40:47 2012 +0100 Easy development... commit9df056a6a0
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:37:59 2012 +0100 Easy interface... commitc6b1797f0a
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:37:49 2012 +0100 Easy interface... commitb72f20c42d
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:37:32 2012 +0100 Easy interface... commit9d9e3994a3
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:36:52 2012 +0100 Easy interface... commitfe43225e7d
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:36:18 2012 +0100 Easy interface... commit9881f5516d
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:35:55 2012 +0100 Easy interface... commit5a0b4197be
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:35:23 2012 +0100 Easy interface... commite4bce00912
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:34:47 2012 +0100 Easy interface... commit0872374b18
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:33:56 2012 +0100 Easy interface... commitfa075c61c6
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:31:58 2012 +0100 Easy interface... commit221f9cd044
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:30:12 2012 +0100 Easy interface... commitec2b858a82
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:29:32 2012 +0100 Easy interface... commit4159c73181
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:29:17 2012 +0100 Easy interface... commite040c00436
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:28:35 2012 +0100 Easy interface... commita34b05154c
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:27:39 2012 +0100 Easy interface... commitf9b46f112b
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:26:37 2012 +0100 Easy interface... commitd049b2f822
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:26:24 2012 +0100 Easy interface... commit8ff31ce1f3
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:25:39 2012 +0100 Easy interface... commit004a580c6b
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:25:12 2012 +0100 Easy interface... commit29d1070b98
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:23:57 2012 +0100 Easy interface... commit158f5db904
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:23:40 2012 +0100 Easy interface... commit9f761ff51b
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:21:27 2012 +0100 Easy interface... commit795c6464ca
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:19:08 2012 +0100 Easy interface... commit64f60cddad
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:18:32 2012 +0100 Easy interface... commitd0c595ea84
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:18:23 2012 +0100 Easy interface... commit4a593ce313
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:17:59 2012 +0100 Easy interface... commita1552478a0
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:15:54 2012 +0100 Easy interface... commitac2184dee3
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:14:57 2012 +0100 Easy interface... commit393cbe4bf0
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:14:31 2012 +0100 Easy interface... commita56995ac8a
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:13:55 2012 +0100 Easy interface... commit921d7d6740
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:07:57 2012 +0100 Easy interface... commitfe7de39d75
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 14:04:36 2012 +0100 Easy interface... commite6e158dd43
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 13:59:29 2012 +0100 Easy interface... commit9bb88f95d4
Author: ooxi <85fcd0ef4ec8@f977375cdcd6.anonbox.net> Date: Thu Nov 1 13:59:01 2012 +0100 Easy interface...
@@ -2,7 +2,7 @@ | |||||
PROJECT(xml) | PROJECT(xml) | ||||
SET(VERSION_MAJOR "0") | SET(VERSION_MAJOR "0") | ||||
SET(VERSION_MINOR "1") | SET(VERSION_MINOR "1") | ||||
SET(VERSION_PATCH "2") | |||||
SET(VERSION_PATCH "3") | |||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR) | CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR) | ||||
@@ -22,6 +22,7 @@ | |||||
*/ | */ | ||||
#include <ctype.h> | #include <ctype.h> | ||||
#include <malloc.h> | #include <malloc.h> | ||||
#include <stdarg.h> | |||||
#include <stdbool.h> | #include <stdbool.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
@@ -37,7 +38,7 @@ | |||||
* UTF-8 text | * UTF-8 text | ||||
*/ | */ | ||||
struct xml_string { | struct xml_string { | ||||
uint8_t* buffer; | |||||
uint8_t const* buffer; | |||||
size_t length; | size_t length; | ||||
}; | }; | ||||
@@ -59,7 +60,11 @@ struct xml_node { | |||||
* An xml_document simply contains the root node and the underlying buffer | * An xml_document simply contains the root node and the underlying buffer | ||||
*/ | */ | ||||
struct xml_document { | struct xml_document { | ||||
struct xml_string buffer; | |||||
struct { | |||||
uint8_t* buffer; | |||||
size_t length; | |||||
} buffer; | |||||
struct xml_node* root; | 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] | * [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] | * [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 | * @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) { | int main(int argc, char** argv) { | ||||
test_xml_parse_document_0(); | test_xml_parse_document_0(); | ||||
test_xml_parse_document_1(); | test_xml_parse_document_1(); | ||||
test_xml_parse_document_2(); | |||||
fprintf(stdout, "All tests passed :-)\n"); | fprintf(stdout, "All tests passed :-)\n"); | ||||
exit(EXIT_SUCCESS); | exit(EXIT_SUCCESS); | ||||