diff --git a/Makefile b/Makefile index 5e01160..eb39518 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CFLAGS = -I include -fPIC -g LDFLAGS = -shared LIBS = -OBJS = src/core.o src/file.o src/unicode.o src/util.o src/array.o src/xpointer.o +OBJS = src/core.o src/file.o src/unicode.o src/util.o src/array.o src/xpointer.o src/xinclude.o SO = .so diff --git a/example.c b/example.c index c182f00..2c397a1 100644 --- a/example.c +++ b/example.c @@ -49,8 +49,9 @@ int main(int argc, char** argv) { xl_node_t* n; for(i = 1; i < argc; i++) { - xemil_t* h = xl_open_file(argv[i]); - h->new_text = 1; + xemil_t* h = xl_open_file(argv[i]); + h->new_text = 1; + h->do_xinclude = 1; if(h != NULL) { printf("%s:\n", argv[i]); if(xl_parse(h)) { diff --git a/include/xemil.h b/include/xemil.h index 36f4b90..9dfe7cb 100644 --- a/include/xemil.h +++ b/include/xemil.h @@ -58,6 +58,8 @@ struct xemil { void* drv_opaque; void* drv_arg; int new_text; + int do_xinclude; + char* path; xl_node_t* pre; xl_node_t* root; }; @@ -66,6 +68,7 @@ struct xemil { extern "C" { #endif +/* clang-format off */ #define XL_MERGE(target, source) \ { \ xl_node_t** target##_old = target; \ @@ -109,19 +112,19 @@ extern "C" { xl_node_t* scanned_node = r[j]; #define XL_LIST_END \ - if(nodes != NULL) { \ - XL_MERGE(new, nodes); \ + if(nodes != NULL) { \ + XL_MERGE(new, nodes); \ \ - free(nodes); \ - } \ - } \ - free(r); \ - r = new; \ + free(nodes); \ + } \ + } \ + free(r); \ + r = new; \ \ - s = i + 1; \ + s = i + 1; \ \ - if(old == 0) break; \ - } \ + if(old == 0) break; \ + } \ } \ \ if(r[0] == NULL) { \ @@ -130,12 +133,15 @@ extern "C" { } \ \ return r; +/* clang-format on */ /* core.c */ XLDECL xemil_t* xl_open(xl_driver_t* driver, void* arg); XLDECL int xl_parse(xemil_t* handle); XLDECL void xl_close(xemil_t* handle); XLDECL char* xl_get_attribute(xl_node_t* node, const char* key); +XLDECL void xl_free(xl_node_t* node); +XLDECL void xl_replace(xl_node_t* node, xl_node_t* new); XLDECL xl_node_t** xl_get_nodes(xl_node_t* node, const char* name); /* NULL-terminated */ XLDECL xl_node_t** xl_get_path(xl_node_t* node, const char* path); /* NULL-terminated */ @@ -165,6 +171,9 @@ XLDECL void xl_array_free(int** array); /* xpointer.c */ xl_node_t** xl_xpointer(xl_node_t* node, const char* path); +/* xinclude.c */ +void xl_xinclude_scan(xemil_t* handle, xl_node_t* node); + #ifdef __cplusplus } #endif diff --git a/src/core.c b/src/core.c index e0f2afc..c2ffc4f 100644 --- a/src/core.c +++ b/src/core.c @@ -10,7 +10,10 @@ xemil_t* xl_open(xl_driver_t* driver, void* arg) { handle->pre = NULL; handle->root = NULL; - handle->new_text = 0; + handle->path = NULL; + + handle->new_text = 0; + handle->do_xinclude = 0; if(!handle->driver->open(handle)) { free(handle); @@ -560,6 +563,10 @@ int xl_parse(xemil_t* handle) { CLEANUP; + if(handle->do_xinclude) { + xl_xinclude_scan(handle, handle->root); + } + return 1; } @@ -589,8 +596,82 @@ static void recursive_free(xl_node_t* node) { free(node); } +void xl_free(xl_node_t* node) { + xl_node_t* parent = node->parent; + xl_node_t* child; + + child = parent->first_child; + if(child != NULL && child == node) { + parent->first_child = child->next; + if(child->next != NULL) child->next->prev = NULL; + child = child->next; + } + + while(child != NULL) { + xl_node_t* next = child->next; + + if(child == node) { + if(child->prev != NULL) child->prev->next = child->next; + if(child->next != NULL) child->next->prev = child->prev; + } + + child = next; + } + + recursive_free(node); +} + +void xl_replace(xl_node_t* node, xl_node_t* new) { + xl_node_t* parent = node->parent; + xl_node_t* child; + + if(new->parent != NULL&& new->parent->first_child == new) { + new->parent->first_child = new->next; + if(new->next->prev != NULL) new->next->prev = NULL; + } + if(new->parent != NULL) { + child = new->parent->first_child; + + while(child != NULL) { + if(child == new) { + if(child->prev != NULL) child->prev->next = child->next; + if(child->next != NULL) child->next->prev = child->prev; + } + + child = child->next; + } + } + + child = parent->first_child; + if(child != NULL && child == node) { + parent->first_child = new; + + new->parent = parent; + new->prev = NULL; + new->next = child->next; + + child = child->next; + } + + while(child != NULL) { + if(child == node) { + new->parent = parent; + new->next = child->next; + new->prev = child->prev; + + if(child->prev != NULL) child->prev->next = new; + if(child->next != NULL) child->next->prev = new; + } + + child = child->next; + } + + recursive_free(node); +} + void xl_close(xemil_t* handle) { if(handle->root != NULL) recursive_free(handle->root); + if(handle->path != NULL) free(handle->path); handle->driver->close(handle); free(handle); diff --git a/src/file.c b/src/file.c index 45e1d38..c0a5462 100644 --- a/src/file.c +++ b/src/file.c @@ -1,8 +1,19 @@ #include static int xl_driver_file_open(xemil_t* handle) { + char* s; + if((handle->drv_opaque = fopen(handle->drv_arg, "rb")) == NULL) return 0; + handle->path = xl_util_strdup(handle->drv_arg); + + if((s = strrchr(handle->path, '/')) == NULL) { + free(handle->path); + handle->path = xl_util_strdup("./"); + } else { + s[1] = 0; + } + return 1; } diff --git a/src/xinclude.c b/src/xinclude.c new file mode 100644 index 0000000..e556b36 --- /dev/null +++ b/src/xinclude.c @@ -0,0 +1,51 @@ +#include + +void xl_xinclude_scan(xemil_t* handle, xl_node_t* node) { + xl_node_t* child; + + if(node->type == XL_NODE_NODE && node->name != NULL && strcmp(node->name, "xi:include") == 0) { + char* href = xl_get_attribute(node, "href"); + char* parse = xl_get_attribute(node, "parse"); + char* xpointer = xl_get_attribute(node, "xpointer"); + + if(href != NULL) { + xemil_t* new; + + if(handle->path == NULL) { + href = xl_util_strdup(href); + } else { + href = xl_util_strvacat(handle->path, href, NULL); + } + + if(parse == NULL) parse = "xml"; + if(xpointer == NULL) xpointer = "element(/)"; + + if((new = xl_open_file(href)) != NULL) { + new->do_xinclude = 1; + new->new_text = handle->new_text; + if(xl_parse(new) && new->root != NULL) { + xl_node_t** nodes; + if((nodes = xl_xpointer(new->root, xpointer)) != NULL) { + xl_replace(node, nodes[0]); + + if(nodes[0] == new->root) new->root = NULL; + + free(nodes); + } + } + } + + if(new != NULL) xl_close(new); + + free(href); + } + } + + child = node->first_child; + + while(child != NULL) { + xl_xinclude_scan(handle, child); + + child = child->next; + } +}