Fri, 17 Jan 2020 22:23:30 +0100
implement multistatus writer
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2019 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ucx/string.h> #include <ucx/map.h> #include "../util/util.h" #include "xml.h" /* ------------------------ utils ------------------------ */ /* * generates a string key for an xml namespace * format: prefix '\0' href */ static sstr_t namespace_key(UcxAllocator *a, WSNamespace *ns) { sstr_t key = sstrcat_a(a, 3, ns->prefix ? sstr((char*)ns->prefix) : S("\0"), S("\0"), sstr((char*)ns->href)); return key; } /* ------------------------ wsxml_iterator ------------------------ */ typedef struct StackElm { WSXmlNode *node; // list of nodes //WSXmlNode *parent; // if not NULL, call endcb after node->next is NULL int endonly; struct StackElm *next; } StackElm; #define STACK_PUSH(stack, elm) if(stack) { elm->next = stack; } stack = elm; int wsxml_iterator( pool_handle_t *pool, WSXmlNode *node, wsxml_func begincb, wsxml_func endcb, void *udata) { if(!node) { return 0; } StackElm *stack = pool_malloc(pool, sizeof(StackElm)); if(!stack) { return 1; // OOM } stack->next = NULL; stack->node = node; stack->endonly = 0; //stack->parent = NULL; int ret = 0; int br = 0; while(stack) { StackElm *cur = stack; WSXmlNode *xmlnode = cur->node; // get top stack element stack = cur->next; // and remove it cur->next = NULL; while(xmlnode && !cur->endonly) { // element begin callback if(begincb(xmlnode, udata)) { br = 1; break; // I don't like break with labels - is this wrong? } if(xmlnode->children) { // put the children on the stack // the next stack iteration will process the children StackElm *newelm = pool_malloc(pool, sizeof(StackElm)); if(!newelm) { ret = 1; br = 1; break; } newelm->next = NULL; newelm->node = xmlnode->children; // setting the parent will make sure endcb will be called // for the current xmlnode after all children are processed //newelm->parent = xmlnode; newelm->endonly = 0; // if xmlnode->next is not NULL, there are still nodes at // this level, therefore we have to put these also on the // stack // this way, the remaining nodes are processed after all // children and the end tag are processed if(xmlnode->next) { StackElm *nextelm = pool_malloc(pool, sizeof(StackElm)); if(!nextelm) { ret = 1; br = 1; break; } nextelm->node = xmlnode->next; nextelm->next = NULL; nextelm->endonly = 0; STACK_PUSH(stack, nextelm); } // we have to put the end tag of the current element // on the stack to ensure endcb is called for the current // element, after all children are processed // reuse cur cur->node = xmlnode; cur->endonly = 1; STACK_PUSH(stack, cur); cur = NULL; // now we can put the children on the stack STACK_PUSH(stack, newelm); // break, because we don't want to process xmlnode->next now break; } else { // no children means, the end callback can be called directly // after the begin callback (no intermediate nodes) cur->node = NULL; if(endcb(xmlnode, udata)) { br = 1; break; } } // continue with next node at this level xmlnode = xmlnode->next; } if(br) { break; // break because of an error } if(cur && cur->node) { //xmlNode *endNode = cur->parent ? cur->parent : cur->node; xmlNode *endNode = cur->node; if(endcb(endNode, udata)) { break; } pool_free(pool, cur); } } // free all remaining elements StackElm *elm = stack; while(elm) { StackElm *next = elm->next; pool_free(pool, elm); elm = next; } return ret; } /* ------------------------ wsxml_get_namespaces ------------------------ */ typedef struct WSNsCollector { UcxAllocator *a; UcxMap *nsmap; WebdavNSList *def; int error; } WSNsCollector; static int nslist_node_begin(xmlNode *node, void *userdata) { WSNsCollector *col = userdata; // namespace required for all elements if(node->type == XML_ELEMENT_NODE && node->ns) { // we create a list of unique prefix-href namespaces by putting // all namespaces in a map sstr_t nskey = namespace_key(col->a, node->ns); if(!nskey.ptr) { col->error = 1; return 1; } if(ucx_map_sstr_put(col->nsmap, nskey, node->ns)) { col->error = 1; return 1; } // collect all namespace definitions for removing these namespaces // from col->nsmap later WSNamespace *def = node->nsDef; while(def) { WebdavNSList *newdef = col->a->malloc( col->a->pool, sizeof(WebdavNSList)); if(!newdef) { col->error = 1; return 1; } newdef->namespace = def; newdef->prev = NULL; newdef->next = NULL; // prepend newdef to the list if(col->def) { newdef->next = col->def; col->def->prev = newdef; } col->def = newdef; // continue with next namespace definition def = def->next; } } return 0; } static int nslist_node_end(xmlNode *node, void *userdata) { return 0; } WebdavNSList* wsxml_get_required_namespaces( pool_handle_t *pool, WSXmlNode *node, int *error) { if(error) *error = 0; UcxAllocator a = util_pool_allocator(pool); UcxMap *nsmap = ucx_map_new_a(&a, 16); if(!nsmap) { if(error) *error = 1; return NULL; } WSNsCollector col; col.a = &a; col.nsmap = nsmap; col.def = NULL; // iterate over all xml elements // this will fill the hashmap with all namespaces // all namespace definitions are added to col.def WebdavNSList *list = NULL; WebdavNSList *end = NULL; if(wsxml_iterator(pool, node, nslist_node_begin, nslist_node_end, &col)) { if(error) *error = 1; } else { // remove all namespace definitions from the map // what we get is a map that contains all missing namespace definitions WebdavNSList *def = col.def; while(def) { sstr_t nskey = namespace_key(&a, def->namespace); if(!nskey.ptr) { if(error) *error = 1; break; } ucx_map_sstr_remove(nsmap, nskey); def = def->next; } // convert nsmap to a list UcxMapIterator i = ucx_map_iterator(nsmap); WSNamespace *ns; UCX_MAP_FOREACH(key, ns, i) { WebdavNSList *newelm = pool_malloc(pool, sizeof(WebdavNSList)); if(!newelm) { if(error) *error = 1; list = NULL; break; } newelm->namespace = ns; newelm->next = NULL; newelm->prev = end; // NULL or the end of list if(end) { end->next = newelm; // append new element } else { list = newelm; // start new list } end = newelm; } } ucx_map_free(nsmap); return list; }