src/server/webdav/xml.c

Tue, 14 Jan 2020 22:05:34 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 14 Jan 2020 22:05:34 +0100
branch
webdav
changeset 223
bbaec8415c10
parent 211
2160585200ac
child 224
0de1ec82628e
permissions
-rw-r--r--

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;
}

mercurial