libidav/xml.c

Sun, 10 Nov 2024 09:12:30 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 10 Nov 2024 09:12:30 +0100
changeset 75
73c8a3780c72
parent 49
2f71f4ee247a
permissions
-rw-r--r--

toolkit update, small UI adjustments

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2018 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 <cx/utils.h>
#include <cx/printf.h>

#include "xml.h"

static DavXmlNodeType convert_type(xmlElementType type) {
    DavXmlNodeType ct;
    switch(type) {
        default: ct = DAV_XML_NONE; break;
        case XML_ELEMENT_NODE: ct = DAV_XML_ELEMENT; break;
        case XML_TEXT_NODE: ct = DAV_XML_TEXT;
    }
    return ct;
}

typedef struct {
    xmlNode    *node;
    DavXmlNode *parent;
} ConvXmlElm;

DavXmlNode* dav_convert_xml(DavSession *sn, xmlNode *node) {
    if(!node) {
        return NULL;
    }
    DavXmlNodeType newnt = convert_type(node->type);
    if(newnt == DAV_XML_NONE) {
        return NULL;
    }
    
    const CxAllocator *a = sn->mp->allocator;
    
    ConvXmlElm ce;
    ce.node = node;
    ce.parent = NULL;
    CxList *stack = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(ConvXmlElm));
    if(!stack) {
        return NULL;
    }
    cxListInsert(stack, 0, &ce);
    
    DavXmlNode *ret = NULL;
    
    while(cxListSize(stack) > 0) {
        ConvXmlElm *c = cxListAt(stack, 0);
        xmlNode *n = c->node;
        DavXmlNode *c_parent = c->parent;
        DavXmlNode *prev = NULL;
        cxListRemove(stack, 0);
        while(n) {
            DavXmlNode *newxn = cxCalloc(a, 1, sizeof(DavXmlNode));
            if(!ret) {
                ret = newxn;
            }
            newxn->type = convert_type(n->type);
            newxn->parent = c_parent;
            if(c_parent && !c_parent->children) {
                c_parent->children = newxn;
            }
            newxn->prev = prev;
            if(prev) {
                prev->next = newxn;
            }
            
            if(newxn->type == DAV_XML_ELEMENT) {
                newxn->name = dav_session_strdup(sn, (char*)n->name);
                if(n->ns && n->ns->href) {
                    newxn->namespace = dav_session_strdup(sn, (char*)n->ns->href);
                }
                
                xmlAttr *attr = n->properties;
                DavXmlAttr *newattr = NULL;
                DavXmlAttr *newattr_last = NULL;
                while(attr) {
                    DavXmlAttr *na = cxCalloc(a, 1, sizeof(DavXmlAttr));
                    na->name = dav_session_strdup(sn, (char*)attr->name);
                    if(attr->children && attr->children->type == XML_TEXT_NODE) {
                        na->value = dav_session_strdup(sn, (char*)attr->children->content);
                    }
                    if(!newattr) {
                        newattr = na;
                    } else {
                        newattr_last->next = na;
                    }
                    newattr_last = na;
                    
                    attr = attr->next;
                }
                newxn->attributes = newattr;
                
                if(n->children) {
                    ConvXmlElm convc;
                    convc.node = n->children;
                    convc.parent = newxn;
                    cxListInsert(stack, 0, &convc);
                }
            } else if(newxn->type == DAV_XML_TEXT) {
                cxmutstr content = cx_strdup_a(a, cx_str((char*)n->content));
                newxn->content = content.ptr;
                newxn->contentlength = content.length;
            }
            
            prev = newxn;
            n = n->next;
        }
    }
    
    return ret;
}

void dav_print_xml(DavXmlNode *node) {
    if(node->type == DAV_XML_ELEMENT) {
        printf("<%s", node->name);
        DavXmlAttr *attr = node->attributes;
        while(attr) {
            printf(" %s=\"%s\"", attr->name, attr->value);
            attr = attr->next;
        }
        putchar('>');
        
        DavXmlNode *child = node->children;
        if(child) {
            dav_print_xml(child);
        }
        
        printf("</%s>", node->name);
    } else {
        fwrite(node->content, 1, node->contentlength, stdout);
        fflush(stdout);
    }
    if(node->next) {
        dav_print_xml(node->next);
    }
}

void dav_print_node(void *stream, cx_write_func writef, CxMap *nsmap, DavXmlNode *node) {
    while(node) {
        if(node->type == DAV_XML_ELEMENT) {
            char *tagend = node->children ? ">" : " />";          
            char *prefix = NULL;
            char *prefix_fr = NULL;
            if(node->namespace) {
                prefix = cxMapGet(nsmap, cx_hash_key_str(node->namespace));
                if(!prefix) {
                    cxmutstr newpre = cx_asprintf("x%zu", cxMapSize(nsmap)+1);
                    // TODO: fix namespace declaration
                    //ucx_map_cstr_put(nsmap, node->namespace, newpre.ptr);
                    prefix = newpre.ptr;
                    prefix_fr = prefix;
                    cx_fprintf(
                            stream,
                            writef,
                            "<%s:%s xmlns:%s=\"%s\"",
                            prefix,
                            node->name,
                            prefix,
                            node->namespace);
                } else {
                    cx_fprintf(stream, writef, "<%s:%s", prefix, node->name);
                }
            } else {
                cx_fprintf(stream, writef, "<%s", node->name);
            }
            
            DavXmlAttr *attr = node->attributes;
            while(attr) {
                cx_fprintf(stream, writef, " %s=\"%s\"", attr->name, attr->value);
                attr = attr->next;
            }
            writef(tagend, 1, strlen(tagend), stream); // end xml tag
            
            if(node->children) {
                dav_print_node(stream, writef, nsmap, node->children);
                if(prefix) {
                    cx_fprintf(stream, writef, "</%s:%s>", prefix, node->name);
                } else {
                    cx_fprintf(stream, writef, "</%s>", node->name);
                }
            }
            
            if(prefix_fr) {
                free(prefix_fr);
            }
        } else if(node->type == DAV_XML_TEXT) {
            writef(node->content, 1, node->contentlength, stream);
        }
        
        node = node->next;
    }
}

/* ------------------------- public API ------------------------- */

char* dav_xml_getstring(DavXmlNode *node) {
    if(node && node->type == DAV_XML_TEXT) {
        return node->content;
    } else {
        return NULL;
    }
}

DavBool dav_xml_isstring(DavXmlNode *node) {
    if(node && node->type == DAV_XML_TEXT && !node->next) {
        return TRUE;
    } else {
        return FALSE;
    }
}

DavXmlNode* dav_xml_nextelm(DavXmlNode *node) {
    node = node->next;
    while(node) {
        if(node->type == DAV_XML_ELEMENT) {
            return node;
        }
        node = node->next;
    }
    return NULL;
}

DavXmlNode* dav_text_node(DavSession *sn, const char *text) {
    const CxAllocator *a = sn->mp->allocator; 
    DavXmlNode *newxn = cxCalloc(a, 1, sizeof(DavXmlNode));
    newxn->type = DAV_XML_TEXT;
    cxmutstr content = cx_strdup_a(a, cx_str(text));
    newxn->content = content.ptr;
    newxn->contentlength = content.length;
    return newxn;
}

DavXmlNode* dav_text_element(DavSession *sn, const char *ns, const char *name, const char *text) {
    const CxAllocator *a = sn->mp->allocator; 
    DavXmlNode *newelm = cxCalloc(a, 1, sizeof(DavXmlNode));
    newelm->type = DAV_XML_ELEMENT;
    newelm->namespace = cx_strdup_a(a, cx_str(ns)).ptr;
    newelm->name = cx_strdup_a(a, cx_str(name)).ptr;
    newelm->children = dav_text_node(sn, text);
    return newelm;
}

static void dav_free_xml_node_a(const CxAllocator *a, DavXmlNode *node) {
    if(node->name) cxFree(a, node->name);
    if(node->namespace) cxFree(a, node->namespace);
    if(node->content) cxFree(a, node->content);
    DavXmlAttr *attr = node->attributes;
    while(attr) {
        if(attr->name) cxFree(a, attr->name);
        if(attr->value) cxFree(a, attr->value);
        attr = attr->next;
    }
    DavXmlNode *children = node->children;
    while(children) {
        DavXmlNode *next_ch = children->next;
        dav_free_xml_node_a(a, children);
        children = next_ch;
    }
    cxFree(a, node);
}

void dav_free_xml_node_sn(DavSession *sn, DavXmlNode *node) {
    dav_free_xml_node_a(sn->mp->allocator, node);
}

void dav_free_xml_node(DavXmlNode *node) {
    dav_free_xml_node_a(cxDefaultAllocator, node);
}

DavXmlAttr* dav_copy_xml_attr(DavXmlAttr *attr) {
    if(!attr) {
        return NULL;
    }
    DavXmlAttr *newattr = NULL;
    DavXmlAttr *prev = NULL;
    while(attr) {
        DavXmlAttr *n = calloc(1, sizeof(DavXmlAttr));
        n->name = strdup(attr->name);
        n->value = strdup(attr->value);
        if(prev) {
            prev->next = n;
        } else {
            newattr = n;
        }
        prev = n;
        attr = attr->next;
    }
    return newattr;
}

DavXmlNode* dav_copy_node(DavXmlNode *node) {
    DavXmlNode *ret = NULL;
    DavXmlNode *prev = NULL;
    while(node) {
        DavXmlNode *copy = calloc(1, sizeof(DavXmlNode));
        copy->type = node->type;
        if(node->type == DAV_XML_ELEMENT) {
            copy->namespace = strdup(node->namespace);
            copy->name = strdup(node->name);
            copy->children = dav_copy_node(node->children);
            copy->attributes = dav_copy_xml_attr(node->attributes);
        } else {
            copy->contentlength = node->contentlength;
            copy->content = malloc(node->contentlength+1);
            memcpy(copy->content, node->content, node->contentlength);
            copy->content[copy->contentlength] = 0;
        }
        if(!ret) {
            ret = copy;
        }
        if(prev) {
            prev->next = copy;
            copy->prev = prev;
        }
        prev = copy;
        node = node->next;
    }
    return ret;
}


DavXmlNode* dav_xml_createnode(const char *ns, const char *name) {
    DavXmlNode *node = calloc(1, sizeof(DavXmlNode));
    node->type = DAV_XML_ELEMENT;
    node->namespace = strdup(ns);
    node->name = strdup(name);
    return node;
}

DavXmlNode* dav_xml_createnode_with_text(const char *ns, const char *name, const char *text) {
    DavXmlNode *node = calloc(1, sizeof(DavXmlNode));
    node->type = DAV_XML_ELEMENT;
    node->namespace = strdup(ns);
    node->name = strdup(name);
    
    DavXmlNode *textnode = dav_xml_createtextnode(text);
    node->children = textnode;
    
    return node;
}

DavXmlNode* dav_xml_createtextnode(const char *text) {
    DavXmlNode *node = calloc(1, sizeof(DavXmlNode));
    node->type = DAV_XML_TEXT;
    cxmutstr content = cx_strdup(cx_str((char*)text));
    node->content = content.ptr;
    node->contentlength = content.length;
    return node;
}

void dav_xml_add_child(DavXmlNode *node, DavXmlNode *child) {
    DavXmlNode *last_child = NULL;
    DavXmlNode *c = node->children;
    while(c) {
        last_child = c;
        c = c->next;
    }
    if(last_child) {
        last_child->next = child;
        child->prev = last_child;
    } else {
        node->children = child;
    }
}

void dav_xml_add_attr(DavXmlNode *node, const char *name, const char *value) {
    DavXmlAttr *attr = calloc(1, sizeof(DavXmlAttr));
    attr->name = strdup(name);
    attr->value = strdup(value);
    
    if(node->attributes) { 
        DavXmlAttr *end = node->attributes;
        DavXmlAttr* last = end;
        while(end) {
            last = end;
            end = end->next;
        }
        last->next = attr;
    } else {
        node->attributes = attr;
    }
}

char* dav_xml_get_attr(DavXmlNode *node, const char *name) {
    DavXmlAttr *attr = node->attributes;
    while(attr) {
        if(!strcmp(attr->name, name)) {
            return attr->value;
        }
        
        attr = attr->next;
    }
    return NULL;
}

DavXmlNode* dav_parse_xml(DavSession *sn, const char *str, size_t len) {
    xmlDoc *doc = xmlReadMemory(str, len, NULL, NULL, 0);
    if(!doc) {
        return NULL;
    }
    xmlNode *xml_root = xmlDocGetRootElement(doc);
    if(!xml_root) {
        xmlFreeDoc(doc);
        return NULL;
    }
    DavXmlNode *x = dav_convert_xml(sn, xml_root);
    xmlFreeDoc(doc);
    return x;
}

mercurial