Tue, 14 Jan 2020 22:05:34 +0100
add xml tree iterator
/* * 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 "xml.h" typedef struct StackElm { WSXmlNode *node; // list of nodes WSXmlNode *parent; // if not NULL, call endcb after node->next is NULL 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->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) { // 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; // 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 of the current node are processed if(xmlnode->next) { // reuse current StackElm cur->node = xmlnode->next; 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) 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) { if(cur->parent) { if(endcb(cur->parent, 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; }