libidav/xml.c

changeset 1
b5bb7b3cd597
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/xml.c	Mon Jan 22 17:27:47 2024 +0100
@@ -0,0 +1,438 @@
+/*
+ * 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(stack->size > 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%d", (int)nsmap->size+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