Browse Source

Merge pull request #2 from ooxi/easy-interface

New easy xml interface
master
ooxi 11 years ago
parent
commit
4a42ec0bc3
4 changed files with 190 additions and 3 deletions
  1. +1
    -1
      CMakeLists.txt
  2. +111
    -2
      src/xml.c
  3. +25
    -0
      src/xml.h
  4. +53
    -0
      test/test-xml.c

+ 1
- 1
CMakeLists.txt View File

@@ -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)


+ 111
- 2
src/xml.c View File

@@ -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]
*/


+ 25
- 0
src/xml.h View File

@@ -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
*/


+ 53
- 0
test/test-xml.c View File

@@ -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);


Loading…
Cancel
Save