|
Packit |
8fb591 |
/**
|
|
Packit |
8fb591 |
* @file xml.c
|
|
Packit |
8fb591 |
* @author Radek Krejci <rkrejci@cesnet.cz>
|
|
Packit |
8fb591 |
* @brief XML parser implementation for libyang
|
|
Packit |
8fb591 |
*
|
|
Packit |
8fb591 |
* Copyright (c) 2015 CESNET, z.s.p.o.
|
|
Packit |
8fb591 |
*
|
|
Packit |
8fb591 |
* This source code is licensed under BSD 3-Clause License (the "License").
|
|
Packit |
8fb591 |
* You may not use this file except in compliance with the License.
|
|
Packit |
8fb591 |
* You may obtain a copy of the License at
|
|
Packit |
8fb591 |
*
|
|
Packit |
8fb591 |
* https://opensource.org/licenses/BSD-3-Clause
|
|
Packit |
8fb591 |
*/
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
#include <assert.h>
|
|
Packit |
8fb591 |
#include <errno.h>
|
|
Packit |
8fb591 |
#include <ctype.h>
|
|
Packit |
8fb591 |
#include <stdint.h>
|
|
Packit |
8fb591 |
#include <stdio.h>
|
|
Packit |
8fb591 |
#include <stdlib.h>
|
|
Packit |
8fb591 |
#include <string.h>
|
|
Packit |
8fb591 |
#include <unistd.h>
|
|
Packit |
8fb591 |
#include <pthread.h>
|
|
Packit |
8fb591 |
#include <sys/stat.h>
|
|
Packit |
8fb591 |
#include <sys/mman.h>
|
|
Packit |
8fb591 |
#include <sys/syscall.h>
|
|
Packit |
8fb591 |
#include <fcntl.h>
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
#include "common.h"
|
|
Packit |
8fb591 |
#include "hash_table.h"
|
|
Packit |
8fb591 |
#include "printer.h"
|
|
Packit |
8fb591 |
#include "parser.h"
|
|
Packit |
8fb591 |
#include "tree_schema.h"
|
|
Packit |
8fb591 |
#include "xml_internal.h"
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
#define ign_xmlws(p) \
|
|
Packit |
8fb591 |
while (is_xmlws(*p)) { \
|
|
Packit |
8fb591 |
p++; \
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
static struct lyxml_attr *lyxml_dup_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API const struct lyxml_ns *
|
|
Packit |
8fb591 |
lyxml_get_ns(const struct lyxml_elem *elem, const char *prefix)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_attr *attr;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!elem) {
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
for (attr = elem->attr; attr; attr = attr->next) {
|
|
Packit |
8fb591 |
if (attr->type != LYXML_ATTR_NS) {
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (!attr->name) {
|
|
Packit |
8fb591 |
if (!prefix) {
|
|
Packit |
8fb591 |
/* default namespace found */
|
|
Packit |
8fb591 |
if (!attr->value) {
|
|
Packit |
8fb591 |
/* empty default namespace -> no default namespace */
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
return (struct lyxml_ns *)attr;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if (prefix && !strcmp(attr->name, prefix)) {
|
|
Packit |
8fb591 |
/* prefix found */
|
|
Packit |
8fb591 |
return (struct lyxml_ns *)attr;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* go recursively */
|
|
Packit |
8fb591 |
return lyxml_get_ns(elem->parent, prefix);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
static void
|
|
Packit |
8fb591 |
lyxml_correct_attr_ns(struct ly_ctx *ctx, struct lyxml_attr *attr, struct lyxml_elem *attr_parent, int copy_ns)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
const struct lyxml_ns *tmp_ns;
|
|
Packit |
8fb591 |
struct lyxml_elem *ns_root, *attr_root;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if ((attr->type != LYXML_ATTR_NS) && attr->ns) {
|
|
Packit |
8fb591 |
/* find the root of attr */
|
|
Packit |
8fb591 |
for (attr_root = attr_parent; attr_root->parent; attr_root = attr_root->parent);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* find the root of attr NS */
|
|
Packit |
8fb591 |
for (ns_root = attr->ns->parent; ns_root->parent; ns_root = ns_root->parent);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* attr NS is defined outside attr parent subtree */
|
|
Packit |
8fb591 |
if (ns_root != attr_root) {
|
|
Packit |
8fb591 |
if (copy_ns) {
|
|
Packit |
8fb591 |
tmp_ns = attr->ns;
|
|
Packit |
8fb591 |
/* we may have already copied the NS over? */
|
|
Packit |
8fb591 |
attr->ns = lyxml_get_ns(attr_parent, tmp_ns->prefix);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* we haven't copied it over, copy it now */
|
|
Packit |
8fb591 |
if (!attr->ns) {
|
|
Packit |
8fb591 |
attr->ns = (struct lyxml_ns *)lyxml_dup_attr(ctx, attr_parent, (struct lyxml_attr *)tmp_ns);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
attr->ns = NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
static struct lyxml_attr *
|
|
Packit |
8fb591 |
lyxml_dup_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_attr *result, *a;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!attr || !parent) {
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (attr->type == LYXML_ATTR_NS) {
|
|
Packit |
8fb591 |
/* this is correct, despite that all attributes seems like a standard
|
|
Packit |
8fb591 |
* attributes (struct lyxml_attr), some of them can be namespace
|
|
Packit |
8fb591 |
* definitions (and in that case they are struct lyxml_ns).
|
|
Packit |
8fb591 |
*/
|
|
Packit |
8fb591 |
result = (struct lyxml_attr *)calloc(1, sizeof (struct lyxml_ns));
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
result = calloc(1, sizeof (struct lyxml_attr));
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!result, LOGMEM(ctx), NULL);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
result->value = lydict_insert(ctx, attr->value, 0);
|
|
Packit |
8fb591 |
result->name = lydict_insert(ctx, attr->name, 0);
|
|
Packit |
8fb591 |
result->type = attr->type;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* set namespace in case of standard attributes */
|
|
Packit |
8fb591 |
if (result->type == LYXML_ATTR_STD && attr->ns) {
|
|
Packit |
8fb591 |
result->ns = attr->ns;
|
|
Packit |
8fb591 |
lyxml_correct_attr_ns(ctx, result, parent, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* set parent pointer in case of namespace attribute */
|
|
Packit |
8fb591 |
if (result->type == LYXML_ATTR_NS) {
|
|
Packit |
8fb591 |
((struct lyxml_ns *)result)->parent = parent;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* put attribute into the parent's attributes list */
|
|
Packit |
8fb591 |
if (parent->attr) {
|
|
Packit |
8fb591 |
/* go to the end of the list */
|
|
Packit |
8fb591 |
for (a = parent->attr; a->next; a = a->next);
|
|
Packit |
8fb591 |
/* and append new attribute */
|
|
Packit |
8fb591 |
a->next = result;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* add the first attribute in the list */
|
|
Packit |
8fb591 |
parent->attr = result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
void
|
|
Packit |
8fb591 |
lyxml_correct_elem_ns(struct ly_ctx *ctx, struct lyxml_elem *elem, int copy_ns, int correct_attrs)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
const struct lyxml_ns *tmp_ns;
|
|
Packit |
8fb591 |
struct lyxml_elem *elem_root, *ns_root, *tmp, *iter;
|
|
Packit |
8fb591 |
struct lyxml_attr *attr;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* find the root of elem */
|
|
Packit |
8fb591 |
for (elem_root = elem; elem_root->parent; elem_root = elem_root->parent);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
LY_TREE_DFS_BEGIN(elem, tmp, iter) {
|
|
Packit |
8fb591 |
if (iter->ns) {
|
|
Packit |
8fb591 |
/* find the root of elem NS */
|
|
Packit |
8fb591 |
for (ns_root = iter->ns->parent; ns_root; ns_root = ns_root->parent);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* elem NS is defined outside elem subtree */
|
|
Packit |
8fb591 |
if (ns_root != elem_root) {
|
|
Packit |
8fb591 |
if (copy_ns) {
|
|
Packit |
8fb591 |
tmp_ns = iter->ns;
|
|
Packit |
8fb591 |
/* we may have already copied the NS over? */
|
|
Packit |
8fb591 |
iter->ns = lyxml_get_ns(iter, tmp_ns->prefix);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* we haven't copied it over, copy it now */
|
|
Packit |
8fb591 |
if (!iter->ns) {
|
|
Packit |
8fb591 |
iter->ns = (struct lyxml_ns *)lyxml_dup_attr(ctx, iter, (struct lyxml_attr *)tmp_ns);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
iter->ns = NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (correct_attrs) {
|
|
Packit |
8fb591 |
LY_TREE_FOR(iter->attr, attr) {
|
|
Packit |
8fb591 |
lyxml_correct_attr_ns(ctx, attr, elem_root, copy_ns);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
LY_TREE_DFS_END(elem, tmp, iter);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
struct lyxml_elem *
|
|
Packit |
8fb591 |
lyxml_dup_elem(struct ly_ctx *ctx, struct lyxml_elem *elem, struct lyxml_elem *parent, int recursive)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_elem *result, *child;
|
|
Packit |
8fb591 |
struct lyxml_attr *attr;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!elem) {
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
result = calloc(1, sizeof *result);
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!result, LOGMEM(ctx), NULL);
|
|
Packit |
8fb591 |
result->content = lydict_insert(ctx, elem->content, 0);
|
|
Packit |
8fb591 |
result->name = lydict_insert(ctx, elem->name, 0);
|
|
Packit |
8fb591 |
result->flags = elem->flags;
|
|
Packit |
8fb591 |
result->prev = result;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (parent) {
|
|
Packit |
8fb591 |
lyxml_add_child(ctx, parent, result);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* keep old namespace for now */
|
|
Packit |
8fb591 |
result->ns = elem->ns;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* duplicate attributes */
|
|
Packit |
8fb591 |
for (attr = elem->attr; attr; attr = attr->next) {
|
|
Packit |
8fb591 |
lyxml_dup_attr(ctx, result, attr);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* correct namespaces */
|
|
Packit |
8fb591 |
lyxml_correct_elem_ns(ctx, result, 1, 0);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!recursive) {
|
|
Packit |
8fb591 |
return result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* duplicate children */
|
|
Packit |
8fb591 |
LY_TREE_FOR(elem->child, child) {
|
|
Packit |
8fb591 |
lyxml_dup_elem(ctx, child, result, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return result;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API struct lyxml_elem *
|
|
Packit |
8fb591 |
lyxml_dup(struct ly_ctx *ctx, struct lyxml_elem *root)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
return lyxml_dup_elem(ctx, root, NULL, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
void
|
|
Packit |
8fb591 |
lyxml_unlink_elem(struct ly_ctx *ctx, struct lyxml_elem *elem, int copy_ns)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_elem *parent, *first;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!elem) {
|
|
Packit |
8fb591 |
return;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* store pointers to important nodes */
|
|
Packit |
8fb591 |
parent = elem->parent;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* unlink from parent */
|
|
Packit |
8fb591 |
if (parent) {
|
|
Packit |
8fb591 |
if (parent->child == elem) {
|
|
Packit |
8fb591 |
/* we unlink the first child */
|
|
Packit |
8fb591 |
/* update the parent's link */
|
|
Packit |
8fb591 |
parent->child = elem->next;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
/* forget about the parent */
|
|
Packit |
8fb591 |
elem->parent = NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (copy_ns < 2) {
|
|
Packit |
8fb591 |
lyxml_correct_elem_ns(ctx, elem, copy_ns, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* unlink from siblings */
|
|
Packit |
8fb591 |
if (elem->prev == elem) {
|
|
Packit |
8fb591 |
/* there are no more siblings */
|
|
Packit |
8fb591 |
return;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (elem->next) {
|
|
Packit |
8fb591 |
elem->next->prev = elem->prev;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* unlinking the last element */
|
|
Packit |
8fb591 |
if (parent) {
|
|
Packit |
8fb591 |
first = parent->child;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
first = elem;
|
|
Packit |
8fb591 |
while (first->prev->next) {
|
|
Packit |
8fb591 |
first = first->prev;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
first->prev = elem->prev;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (elem->prev->next) {
|
|
Packit |
8fb591 |
elem->prev->next = elem->next;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* clean up the unlinked element */
|
|
Packit |
8fb591 |
elem->next = NULL;
|
|
Packit |
8fb591 |
elem->prev = elem;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API void
|
|
Packit |
8fb591 |
lyxml_unlink(struct ly_ctx *ctx, struct lyxml_elem *elem)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
if (!elem) {
|
|
Packit |
8fb591 |
return;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
lyxml_unlink_elem(ctx, elem, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
void
|
|
Packit |
8fb591 |
lyxml_free_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_attr *aiter, *aprev;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!attr) {
|
|
Packit |
8fb591 |
return;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (parent) {
|
|
Packit |
8fb591 |
/* unlink attribute from the parent's list of attributes */
|
|
Packit |
8fb591 |
aprev = NULL;
|
|
Packit |
8fb591 |
for (aiter = parent->attr; aiter; aiter = aiter->next) {
|
|
Packit |
8fb591 |
if (aiter == attr) {
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
aprev = aiter;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (!aiter) {
|
|
Packit |
8fb591 |
/* attribute to remove not found */
|
|
Packit |
8fb591 |
return;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!aprev) {
|
|
Packit |
8fb591 |
/* attribute is first in parent's list of attributes */
|
|
Packit |
8fb591 |
parent->attr = attr->next;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* reconnect previous attribute to the next */
|
|
Packit |
8fb591 |
aprev->next = attr->next;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
lydict_remove(ctx, attr->name);
|
|
Packit |
8fb591 |
lydict_remove(ctx, attr->value);
|
|
Packit |
8fb591 |
free(attr);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
void
|
|
Packit |
8fb591 |
lyxml_free_attrs(struct ly_ctx *ctx, struct lyxml_elem *elem)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_attr *a, *next;
|
|
Packit |
8fb591 |
if (!elem || !elem->attr) {
|
|
Packit |
8fb591 |
return;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
a = elem->attr;
|
|
Packit |
8fb591 |
do {
|
|
Packit |
8fb591 |
next = a->next;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
lydict_remove(ctx, a->name);
|
|
Packit |
8fb591 |
lydict_remove(ctx, a->value);
|
|
Packit |
8fb591 |
free(a);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
a = next;
|
|
Packit |
8fb591 |
} while (a);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
static void
|
|
Packit |
8fb591 |
lyxml_free_elem(struct ly_ctx *ctx, struct lyxml_elem *elem)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_elem *e, *next;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!elem) {
|
|
Packit |
8fb591 |
return;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
lyxml_free_attrs(ctx, elem);
|
|
Packit |
8fb591 |
LY_TREE_FOR_SAFE(elem->child, next, e) {
|
|
Packit |
8fb591 |
lyxml_free_elem(ctx, e);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
lydict_remove(ctx, elem->name);
|
|
Packit |
8fb591 |
lydict_remove(ctx, elem->content);
|
|
Packit |
8fb591 |
free(elem);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API void
|
|
Packit |
8fb591 |
lyxml_free(struct ly_ctx *ctx, struct lyxml_elem *elem)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
if (!elem) {
|
|
Packit |
8fb591 |
return;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
lyxml_unlink_elem(ctx, elem, 2);
|
|
Packit |
8fb591 |
lyxml_free_elem(ctx, elem);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API void
|
|
Packit |
8fb591 |
lyxml_free_withsiblings(struct ly_ctx *ctx, struct lyxml_elem *elem)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_elem *iter, *aux;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!elem) {
|
|
Packit |
8fb591 |
return;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* optimization - avoid freeing (unlinking) the last node of the siblings list */
|
|
Packit |
8fb591 |
/* so, first, free the node's predecessors to the beginning of the list ... */
|
|
Packit |
8fb591 |
for(iter = elem->prev; iter->next; iter = aux) {
|
|
Packit |
8fb591 |
aux = iter->prev;
|
|
Packit |
8fb591 |
lyxml_free(ctx, iter);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
/* ... then, the node is the first in the siblings list, so free them all */
|
|
Packit |
8fb591 |
LY_TREE_FOR_SAFE(elem, aux, iter) {
|
|
Packit |
8fb591 |
lyxml_free(ctx, iter);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API const char *
|
|
Packit |
8fb591 |
lyxml_get_attr(const struct lyxml_elem *elem, const char *name, const char *ns)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_attr *a;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
assert(elem);
|
|
Packit |
8fb591 |
assert(name);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
for (a = elem->attr; a; a = a->next) {
|
|
Packit |
8fb591 |
if (a->type != LYXML_ATTR_STD) {
|
|
Packit |
8fb591 |
continue;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!strcmp(name, a->name)) {
|
|
Packit |
8fb591 |
if ((!ns && !a->ns) || (ns && a->ns && !strcmp(ns, a->ns->value))) {
|
|
Packit |
8fb591 |
return a->value;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
int
|
|
Packit |
8fb591 |
lyxml_add_child(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_elem *elem)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_elem *e;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
assert(parent);
|
|
Packit |
8fb591 |
assert(elem);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* (re)link element to parent */
|
|
Packit |
8fb591 |
if (elem->parent) {
|
|
Packit |
8fb591 |
lyxml_unlink_elem(ctx, elem, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
elem->parent = parent;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* link parent to element */
|
|
Packit |
8fb591 |
if (parent->child) {
|
|
Packit |
8fb591 |
e = parent->child;
|
|
Packit |
8fb591 |
elem->prev = e->prev;
|
|
Packit |
8fb591 |
elem->next = NULL;
|
|
Packit |
8fb591 |
elem->prev->next = elem;
|
|
Packit |
8fb591 |
e->prev = elem;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
parent->child = elem;
|
|
Packit |
8fb591 |
elem->prev = elem;
|
|
Packit |
8fb591 |
elem->next = NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return EXIT_SUCCESS;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
int
|
|
Packit |
8fb591 |
lyxml_getutf8(struct ly_ctx *ctx, const char *buf, unsigned int *read)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
int c, aux;
|
|
Packit |
8fb591 |
int i;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
c = buf[0];
|
|
Packit |
8fb591 |
*read = 0;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* buf is NULL terminated string, so 0 means EOF */
|
|
Packit |
8fb591 |
if (!c) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_EOF, LY_VLOG_NONE, NULL);
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
*read = 1;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* process character byte(s) */
|
|
Packit |
8fb591 |
if ((c & 0xf8) == 0xf0) {
|
|
Packit |
8fb591 |
/* four bytes character */
|
|
Packit |
8fb591 |
*read = 4;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
c &= 0x07;
|
|
Packit |
8fb591 |
for (i = 1; i <= 3; i++) {
|
|
Packit |
8fb591 |
aux = buf[i];
|
|
Packit |
8fb591 |
if ((aux & 0xc0) != 0x80) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
c = (c << 6) | (aux & 0x3f);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (c < 0x1000 || c > 0x10ffff) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if ((c & 0xf0) == 0xe0) {
|
|
Packit |
8fb591 |
/* three bytes character */
|
|
Packit |
8fb591 |
*read = 3;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
c &= 0x0f;
|
|
Packit |
8fb591 |
for (i = 1; i <= 2; i++) {
|
|
Packit |
8fb591 |
aux = buf[i];
|
|
Packit |
8fb591 |
if ((aux & 0xc0) != 0x80) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
c = (c << 6) | (aux & 0x3f);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if ((c & 0xe0) == 0xc0) {
|
|
Packit |
8fb591 |
/* two bytes character */
|
|
Packit |
8fb591 |
*read = 2;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
aux = buf[1];
|
|
Packit |
8fb591 |
if ((aux & 0xc0) != 0x80) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c = ((c & 0x1f) << 6) | (aux & 0x3f);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (c < 0x80) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if (!(c & 0x80)) {
|
|
Packit |
8fb591 |
/* one byte character */
|
|
Packit |
8fb591 |
if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
|
|
Packit |
8fb591 |
/* invalid character */
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* invalid character */
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return c;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* logs directly */
|
|
Packit |
8fb591 |
static int
|
|
Packit |
8fb591 |
parse_ignore(struct ly_ctx *ctx, const char *data, const char *endstr, unsigned int *len)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
unsigned int slen;
|
|
Packit |
8fb591 |
const char *c = data;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
slen = strlen(endstr);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
while (*c && strncmp(c, endstr, slen)) {
|
|
Packit |
8fb591 |
c++;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (!*c) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_MISS, LY_VLOG_NONE, NULL, "closing sequence", endstr);
|
|
Packit |
8fb591 |
return EXIT_FAILURE;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c += slen;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
*len = c - data;
|
|
Packit |
8fb591 |
return EXIT_SUCCESS;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* logs directly, fails when return == NULL and *len == 0 */
|
|
Packit |
8fb591 |
static char *
|
|
Packit |
8fb591 |
parse_text(struct ly_ctx *ctx, const char *data, char delim, unsigned int *len)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
#define BUFSIZE 1024
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
char buf[BUFSIZE];
|
|
Packit |
8fb591 |
char *result = NULL, *aux;
|
|
Packit |
8fb591 |
unsigned int r;
|
|
Packit |
8fb591 |
int o, size = 0;
|
|
Packit |
8fb591 |
int cdsect = 0;
|
|
Packit |
8fb591 |
int32_t n;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
for (*len = o = 0; cdsect || data[*len] != delim; o++) {
|
|
Packit |
8fb591 |
if (!data[*len] || (!cdsect && !strncmp(&data[*len], "]]>", 3))) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "element content, \"]]>\" found");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
loop:
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (o > BUFSIZE - 4) {
|
|
Packit |
8fb591 |
/* add buffer into the result */
|
|
Packit |
8fb591 |
if (result) {
|
|
Packit |
8fb591 |
size = size + o;
|
|
Packit |
8fb591 |
result = ly_realloc(result, size + 1);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
size = o;
|
|
Packit |
8fb591 |
result = malloc((size + 1) * sizeof *result);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!result, LOGMEM(ctx), NULL);
|
|
Packit |
8fb591 |
memcpy(&result[size - o], buf, o);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* write again into the beginning of the buffer */
|
|
Packit |
8fb591 |
o = 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (cdsect || !strncmp(&data[*len], "
|
|
Packit |
8fb591 |
/* CDSect */
|
|
Packit |
8fb591 |
if (!cdsect) {
|
|
Packit |
8fb591 |
cdsect = 1;
|
|
Packit |
8fb591 |
*len += 9;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (data[*len] && !strncmp(&data[*len], "]]>", 3)) {
|
|
Packit |
8fb591 |
*len += 3;
|
|
Packit |
8fb591 |
cdsect = 0;
|
|
Packit |
8fb591 |
o--; /* we don't write any data in this iteration */
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
buf[o] = data[*len];
|
|
Packit |
8fb591 |
(*len)++;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if (data[*len] == '&') {
|
|
Packit |
8fb591 |
(*len)++;
|
|
Packit |
8fb591 |
if (data[*len] != '#') {
|
|
Packit |
8fb591 |
/* entity reference - only predefined refs are supported */
|
|
Packit |
8fb591 |
if (!strncmp(&data[*len], "lt;", 3)) {
|
|
Packit |
8fb591 |
buf[o] = '<';
|
|
Packit |
8fb591 |
*len += 3;
|
|
Packit |
8fb591 |
} else if (!strncmp(&data[*len], "gt;", 3)) {
|
|
Packit |
8fb591 |
buf[o] = '>';
|
|
Packit |
8fb591 |
*len += 3;
|
|
Packit |
8fb591 |
} else if (!strncmp(&data[*len], "amp;", 4)) {
|
|
Packit |
8fb591 |
buf[o] = '&';
|
|
Packit |
8fb591 |
*len += 4;
|
|
Packit |
8fb591 |
} else if (!strncmp(&data[*len], "apos;", 5)) {
|
|
Packit |
8fb591 |
buf[o] = '\'';
|
|
Packit |
8fb591 |
*len += 5;
|
|
Packit |
8fb591 |
} else if (!strncmp(&data[*len], "quot;", 5)) {
|
|
Packit |
8fb591 |
buf[o] = '\"';
|
|
Packit |
8fb591 |
*len += 5;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "entity reference (only predefined references are supported)");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* character reference */
|
|
Packit |
8fb591 |
(*len)++;
|
|
Packit |
8fb591 |
if (isdigit(data[*len])) {
|
|
Packit |
8fb591 |
for (n = 0; isdigit(data[*len]); (*len)++) {
|
|
Packit |
8fb591 |
n = (10 * n) + (data[*len] - '0');
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (data[*len] != ';') {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "character reference, missing semicolon");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if (data[(*len)++] == 'x' && isxdigit(data[*len])) {
|
|
Packit |
8fb591 |
for (n = 0; isxdigit(data[*len]); (*len)++) {
|
|
Packit |
8fb591 |
if (isdigit(data[*len])) {
|
|
Packit |
8fb591 |
r = (data[*len] - '0');
|
|
Packit |
8fb591 |
} else if (data[*len] > 'F') {
|
|
Packit |
8fb591 |
r = 10 + (data[*len] - 'a');
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
r = 10 + (data[*len] - 'A');
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
n = (16 * n) + r;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "character reference");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
r = pututf8(ctx, &buf[o], n);
|
|
Packit |
8fb591 |
if (!r) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "character reference value");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
o += r - 1; /* o is ++ in for loop */
|
|
Packit |
8fb591 |
(*len)++;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
r = copyutf8(ctx, &buf[o], &data[*len]);
|
|
Packit |
8fb591 |
if (!r) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
o += r - 1; /* o is ++ in for loop */
|
|
Packit |
8fb591 |
(*len) = (*len) + r;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (delim == '<' && !strncmp(&data[*len], "
|
|
Packit |
8fb591 |
/* ignore loop's end condition on beginning of CDSect */
|
|
Packit |
8fb591 |
goto loop;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
#undef BUFSIZE
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (o) {
|
|
Packit |
8fb591 |
if (result) {
|
|
Packit |
8fb591 |
size = size + o;
|
|
Packit |
8fb591 |
aux = realloc(result, size + 1);
|
|
Packit |
8fb591 |
result = aux;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
size = o;
|
|
Packit |
8fb591 |
result = malloc((size + 1) * sizeof *result);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!result, LOGMEM(ctx), NULL);
|
|
Packit |
8fb591 |
memcpy(&result[size - o], buf, o);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (result) {
|
|
Packit |
8fb591 |
result[size] = '\0';
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
size = 0;
|
|
Packit |
8fb591 |
result = strdup("");
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!result, LOGMEM(ctx), NULL)
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return result;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
error:
|
|
Packit |
8fb591 |
*len = 0;
|
|
Packit |
8fb591 |
free(result);
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* logs directly */
|
|
Packit |
8fb591 |
static struct lyxml_attr *
|
|
Packit |
8fb591 |
parse_attr(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
const char *c = data, *start, *delim;
|
|
Packit |
8fb591 |
char *prefix = NULL, xml_flag, *str;
|
|
Packit |
8fb591 |
int uc;
|
|
Packit |
8fb591 |
struct lyxml_attr *attr = NULL, *a;
|
|
Packit |
8fb591 |
unsigned int size;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* check if it is attribute or namespace */
|
|
Packit |
8fb591 |
if (!strncmp(c, "xmlns", 5)) {
|
|
Packit |
8fb591 |
/* namespace */
|
|
Packit |
8fb591 |
attr = calloc(1, sizeof (struct lyxml_ns));
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!attr, LOGMEM(ctx), NULL);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
attr->type = LYXML_ATTR_NS;
|
|
Packit |
8fb591 |
((struct lyxml_ns *)attr)->parent = parent;
|
|
Packit |
8fb591 |
c += 5;
|
|
Packit |
8fb591 |
if (*c != ':') {
|
|
Packit |
8fb591 |
/* default namespace, prefix will be empty */
|
|
Packit |
8fb591 |
goto equal;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c++; /* go after ':' to the prefix value */
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* attribute */
|
|
Packit |
8fb591 |
attr = calloc(1, sizeof *attr);
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!attr, LOGMEM(ctx), NULL);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
attr->type = LYXML_ATTR_STD;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* process name part of the attribute */
|
|
Packit |
8fb591 |
start = c;
|
|
Packit |
8fb591 |
uc = lyxml_getutf8(ctx, c, &size);
|
|
Packit |
8fb591 |
if (!is_xmlnamestartchar(uc)) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "NameStartChar of the attribute");
|
|
Packit |
8fb591 |
free(attr);
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
xml_flag = 4;
|
|
Packit |
8fb591 |
if (*c == 'x') {
|
|
Packit |
8fb591 |
xml_flag = 1;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c += size;
|
|
Packit |
8fb591 |
uc = lyxml_getutf8(ctx, c, &size);
|
|
Packit |
8fb591 |
while (is_xmlnamechar(uc)) {
|
|
Packit |
8fb591 |
if (attr->type == LYXML_ATTR_STD) {
|
|
Packit |
8fb591 |
if ((*c == ':') && (xml_flag != 3)) {
|
|
Packit |
8fb591 |
/* attribute in a namespace (but disregard the special "xml" namespace) */
|
|
Packit |
8fb591 |
start = c + 1;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* look for the prefix in namespaces */
|
|
Packit |
8fb591 |
prefix = malloc((c - data + 1) * sizeof *prefix);
|
|
Packit |
8fb591 |
LY_CHECK_ERR_GOTO(!prefix, LOGMEM(ctx), error);
|
|
Packit |
8fb591 |
memcpy(prefix, data, c - data);
|
|
Packit |
8fb591 |
prefix[c - data] = '\0';
|
|
Packit |
8fb591 |
attr->ns = lyxml_get_ns(parent, prefix);
|
|
Packit |
8fb591 |
} else if (((*c == 'm') && (xml_flag == 1)) ||
|
|
Packit |
8fb591 |
((*c == 'l') && (xml_flag == 2))) {
|
|
Packit |
8fb591 |
++xml_flag;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
xml_flag = 4;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c += size;
|
|
Packit |
8fb591 |
uc = lyxml_getutf8(ctx, c, &size);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* store the name */
|
|
Packit |
8fb591 |
size = c - start;
|
|
Packit |
8fb591 |
attr->name = lydict_insert(ctx, start, size);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
equal:
|
|
Packit |
8fb591 |
/* check Eq mark that can be surrounded by whitespaces */
|
|
Packit |
8fb591 |
ign_xmlws(c);
|
|
Packit |
8fb591 |
if (*c != '=') {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "attribute definition, \"=\" expected");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c++;
|
|
Packit |
8fb591 |
ign_xmlws(c);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* process value part of the attribute */
|
|
Packit |
8fb591 |
if (!*c || (*c != '"' && *c != '\'')) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "attribute value, \" or \' expected");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
delim = c;
|
|
Packit |
8fb591 |
str = parse_text(ctx, ++c, *delim, &size);
|
|
Packit |
8fb591 |
if (!str && !size) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
attr->value = lydict_insert_zc(ctx, str);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
*len = c + size + 1 - data; /* +1 is delimiter size */
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* put attribute into the parent's attributes list */
|
|
Packit |
8fb591 |
if (parent->attr) {
|
|
Packit |
8fb591 |
/* go to the end of the list */
|
|
Packit |
8fb591 |
for (a = parent->attr; a->next; a = a->next);
|
|
Packit |
8fb591 |
/* and append new attribute */
|
|
Packit |
8fb591 |
a->next = attr;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* add the first attribute in the list */
|
|
Packit |
8fb591 |
parent->attr = attr;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
free(prefix);
|
|
Packit |
8fb591 |
return attr;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
error:
|
|
Packit |
8fb591 |
lyxml_free_attr(ctx, NULL, attr);
|
|
Packit |
8fb591 |
free(prefix);
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* logs directly */
|
|
Packit |
8fb591 |
struct lyxml_elem *
|
|
Packit |
8fb591 |
lyxml_parse_elem(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent, int options)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
const char *c = data, *start, *e;
|
|
Packit |
8fb591 |
const char *lws; /* leading white space for handling mixed content */
|
|
Packit |
8fb591 |
int uc;
|
|
Packit |
8fb591 |
char *str;
|
|
Packit |
8fb591 |
char *prefix = NULL;
|
|
Packit |
8fb591 |
unsigned int prefix_len = 0;
|
|
Packit |
8fb591 |
struct lyxml_elem *elem = NULL, *child;
|
|
Packit |
8fb591 |
struct lyxml_attr *attr;
|
|
Packit |
8fb591 |
unsigned int size;
|
|
Packit |
8fb591 |
int nons_flag = 0, closed_flag = 0;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
*len = 0;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (*c != '<') {
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* locate element name */
|
|
Packit |
8fb591 |
c++;
|
|
Packit |
8fb591 |
e = c;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
uc = lyxml_getutf8(ctx, e, &size);
|
|
Packit |
8fb591 |
if (!is_xmlnamestartchar(uc)) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "NameStartChar of the element");
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
e += size;
|
|
Packit |
8fb591 |
uc = lyxml_getutf8(ctx, e, &size);
|
|
Packit |
8fb591 |
while (is_xmlnamechar(uc)) {
|
|
Packit |
8fb591 |
if (*e == ':') {
|
|
Packit |
8fb591 |
if (prefix_len) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_NONE, NULL, "element name, multiple colons found");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
/* element in a namespace */
|
|
Packit |
8fb591 |
start = e + 1;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* look for the prefix in namespaces */
|
|
Packit |
8fb591 |
prefix_len = e - c;
|
|
Packit |
8fb591 |
LY_CHECK_ERR_GOTO(prefix, LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, e), error);
|
|
Packit |
8fb591 |
prefix = malloc((prefix_len + 1) * sizeof *prefix);
|
|
Packit |
8fb591 |
LY_CHECK_ERR_GOTO(!prefix, LOGMEM(ctx), error);
|
|
Packit |
8fb591 |
memcpy(prefix, c, prefix_len);
|
|
Packit |
8fb591 |
prefix[prefix_len] = '\0';
|
|
Packit |
8fb591 |
c = start;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
e += size;
|
|
Packit |
8fb591 |
uc = lyxml_getutf8(ctx, e, &size);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (!*e) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_EOF, LY_VLOG_NONE, NULL);
|
|
Packit |
8fb591 |
free(prefix);
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* allocate element structure */
|
|
Packit |
8fb591 |
elem = calloc(1, sizeof *elem);
|
|
Packit |
8fb591 |
LY_CHECK_ERR_RETURN(!elem, free(prefix); LOGMEM(ctx), NULL);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
elem->next = NULL;
|
|
Packit |
8fb591 |
elem->prev = elem;
|
|
Packit |
8fb591 |
if (parent) {
|
|
Packit |
8fb591 |
lyxml_add_child(ctx, parent, elem);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* store the name into the element structure */
|
|
Packit |
8fb591 |
elem->name = lydict_insert(ctx, c, e - c);
|
|
Packit |
8fb591 |
c = e;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
process:
|
|
Packit |
8fb591 |
ign_xmlws(c);
|
|
Packit |
8fb591 |
if (!strncmp("/>", c, 2)) {
|
|
Packit |
8fb591 |
/* we are done, it was EmptyElemTag */
|
|
Packit |
8fb591 |
c += 2;
|
|
Packit |
8fb591 |
elem->content = lydict_insert(ctx, "", 0);
|
|
Packit |
8fb591 |
closed_flag = 1;
|
|
Packit |
8fb591 |
} else if (*c == '>') {
|
|
Packit |
8fb591 |
/* process element content */
|
|
Packit |
8fb591 |
c++;
|
|
Packit |
8fb591 |
lws = NULL;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
while (*c) {
|
|
Packit |
8fb591 |
if (!strncmp(c, "</", 2)) {
|
|
Packit |
8fb591 |
if (lws && !elem->child) {
|
|
Packit |
8fb591 |
/* leading white spaces were actually content */
|
|
Packit |
8fb591 |
goto store_content;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* Etag */
|
|
Packit |
8fb591 |
c += 2;
|
|
Packit |
8fb591 |
/* get name and check it */
|
|
Packit |
8fb591 |
e = c;
|
|
Packit |
8fb591 |
uc = lyxml_getutf8(ctx, e, &size);
|
|
Packit |
8fb591 |
if (!is_xmlnamestartchar(uc)) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_XML, elem, "NameStartChar of the element");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
e += size;
|
|
Packit |
8fb591 |
uc = lyxml_getutf8(ctx, e, &size);
|
|
Packit |
8fb591 |
while (is_xmlnamechar(uc)) {
|
|
Packit |
8fb591 |
if (*e == ':') {
|
|
Packit |
8fb591 |
/* element in a namespace */
|
|
Packit |
8fb591 |
start = e + 1;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* look for the prefix in namespaces */
|
|
Packit |
8fb591 |
if (!prefix || memcmp(prefix, c, e - c)) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_SPEC, LY_VLOG_XML, elem,
|
|
Packit |
8fb591 |
"Invalid (different namespaces) opening (%s) and closing element tags.", elem->name);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c = start;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
e += size;
|
|
Packit |
8fb591 |
uc = lyxml_getutf8(ctx, e, &size);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (!*e) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_EOF, LY_VLOG_NONE, NULL);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* check that it corresponds to opening tag */
|
|
Packit |
8fb591 |
size = e - c;
|
|
Packit |
8fb591 |
str = malloc((size + 1) * sizeof *str);
|
|
Packit |
8fb591 |
LY_CHECK_ERR_GOTO(!str, LOGMEM(ctx), error);
|
|
Packit |
8fb591 |
memcpy(str, c, e - c);
|
|
Packit |
8fb591 |
str[e - c] = '\0';
|
|
Packit |
8fb591 |
if (size != strlen(elem->name) || memcmp(str, elem->name, size)) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_SPEC, LY_VLOG_XML, elem,
|
|
Packit |
8fb591 |
"Invalid (mixed names) opening (%s) and closing (%s) element tags.", elem->name, str);
|
|
Packit |
8fb591 |
free(str);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
free(str);
|
|
Packit |
8fb591 |
c = e;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
ign_xmlws(c);
|
|
Packit |
8fb591 |
if (*c != '>') {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_SPEC, LY_VLOG_XML, elem, "Data after closing element tag \"%s\".", elem->name);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c++;
|
|
Packit |
8fb591 |
if (!(elem->flags & LYXML_ELEM_MIXED) && !elem->content) {
|
|
Packit |
8fb591 |
/* there was no content, but we don't want NULL (only if mixed content) */
|
|
Packit |
8fb591 |
elem->content = lydict_insert(ctx, "", 0);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
closed_flag = 1;
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
} else if (!strncmp(c, "
|
|
Packit |
8fb591 |
if (lws) {
|
|
Packit |
8fb591 |
/* leading white spaces were only formatting */
|
|
Packit |
8fb591 |
lws = NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
/* PI - ignore it */
|
|
Packit |
8fb591 |
c += 2;
|
|
Packit |
8fb591 |
if (parse_ignore(ctx, c, "?>", &size)) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c += size;
|
|
Packit |
8fb591 |
} else if (!strncmp(c, "
|
|
Packit |
8fb591 |
if (lws) {
|
|
Packit |
8fb591 |
/* leading white spaces were only formatting */
|
|
Packit |
8fb591 |
lws = NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
/* Comment - ignore it */
|
|
Packit |
8fb591 |
c += 4;
|
|
Packit |
8fb591 |
if (parse_ignore(ctx, c, "-->", &size)) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c += size;
|
|
Packit |
8fb591 |
} else if (!strncmp(c, "
|
|
Packit |
8fb591 |
/* CDSect */
|
|
Packit |
8fb591 |
goto store_content;
|
|
Packit |
8fb591 |
} else if (*c == '<') {
|
|
Packit |
8fb591 |
if (lws) {
|
|
Packit |
8fb591 |
if (elem->flags & LYXML_ELEM_MIXED) {
|
|
Packit |
8fb591 |
/* we have a mixed content */
|
|
Packit |
8fb591 |
goto store_content;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* leading white spaces were only formatting */
|
|
Packit |
8fb591 |
lws = NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (elem->content) {
|
|
Packit |
8fb591 |
/* we have a mixed content */
|
|
Packit |
8fb591 |
if (options & LYXML_PARSE_NOMIXEDCONTENT) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_XML, elem, "XML element with mixed content");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
child = calloc(1, sizeof *child);
|
|
Packit |
8fb591 |
LY_CHECK_ERR_GOTO(!child, LOGMEM(ctx), error);
|
|
Packit |
8fb591 |
child->content = elem->content;
|
|
Packit |
8fb591 |
elem->content = NULL;
|
|
Packit |
8fb591 |
lyxml_add_child(ctx, elem, child);
|
|
Packit |
8fb591 |
elem->flags |= LYXML_ELEM_MIXED;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
child = lyxml_parse_elem(ctx, c, &size, elem, options);
|
|
Packit |
8fb591 |
if (!child) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c += size; /* move after processed child element */
|
|
Packit |
8fb591 |
} else if (is_xmlws(*c)) {
|
|
Packit |
8fb591 |
lws = c;
|
|
Packit |
8fb591 |
ign_xmlws(c);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
store_content:
|
|
Packit |
8fb591 |
/* store text content */
|
|
Packit |
8fb591 |
if (lws) {
|
|
Packit |
8fb591 |
/* process content including the leading white spaces */
|
|
Packit |
8fb591 |
c = lws;
|
|
Packit |
8fb591 |
lws = NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
str = parse_text(ctx, c, '<', &size);
|
|
Packit |
8fb591 |
if (!str && !size) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
elem->content = lydict_insert_zc(ctx, str);
|
|
Packit |
8fb591 |
c += size; /* move after processed text content */
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (elem->child) {
|
|
Packit |
8fb591 |
/* we have a mixed content */
|
|
Packit |
8fb591 |
if (options & LYXML_PARSE_NOMIXEDCONTENT) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_XML, elem, "XML element with mixed content");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
child = calloc(1, sizeof *child);
|
|
Packit |
8fb591 |
LY_CHECK_ERR_GOTO(!child, LOGMEM(ctx), error);
|
|
Packit |
8fb591 |
child->content = elem->content;
|
|
Packit |
8fb591 |
elem->content = NULL;
|
|
Packit |
8fb591 |
lyxml_add_child(ctx, elem, child);
|
|
Packit |
8fb591 |
elem->flags |= LYXML_ELEM_MIXED;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* process attribute */
|
|
Packit |
8fb591 |
attr = parse_attr(ctx, c, &size, elem);
|
|
Packit |
8fb591 |
if (!attr) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c += size; /* move after processed attribute */
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* check namespace */
|
|
Packit |
8fb591 |
if (attr->type == LYXML_ATTR_NS) {
|
|
Packit |
8fb591 |
if ((!prefix || !prefix[0]) && !attr->name) {
|
|
Packit |
8fb591 |
if (attr->value) {
|
|
Packit |
8fb591 |
/* default prefix */
|
|
Packit |
8fb591 |
elem->ns = (struct lyxml_ns *)attr;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
/* xmlns="" -> no namespace */
|
|
Packit |
8fb591 |
nons_flag = 1;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if (prefix && prefix[0] && attr->name && !strncmp(attr->name, prefix, prefix_len + 1)) {
|
|
Packit |
8fb591 |
/* matching namespace with prefix */
|
|
Packit |
8fb591 |
elem->ns = (struct lyxml_ns *)attr;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* go back to finish element processing */
|
|
Packit |
8fb591 |
goto process;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
*len = c - data;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!closed_flag) {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_MISS, LY_VLOG_XML, elem, "closing element tag", elem->name);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!elem->ns && !nons_flag && parent) {
|
|
Packit |
8fb591 |
elem->ns = lyxml_get_ns(parent, prefix_len ? prefix : NULL);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
free(prefix);
|
|
Packit |
8fb591 |
return elem;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
error:
|
|
Packit |
8fb591 |
lyxml_free(ctx, elem);
|
|
Packit |
8fb591 |
free(prefix);
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* logs directly */
|
|
Packit |
8fb591 |
API struct lyxml_elem *
|
|
Packit |
8fb591 |
lyxml_parse_mem(struct ly_ctx *ctx, const char *data, int options)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
const char *c = data;
|
|
Packit |
8fb591 |
unsigned int len;
|
|
Packit |
8fb591 |
struct lyxml_elem *root, *first = NULL, *next;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!ctx) {
|
|
Packit |
8fb591 |
LOGARG;
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
repeat:
|
|
Packit |
8fb591 |
/* process document */
|
|
Packit |
8fb591 |
while (1) {
|
|
Packit |
8fb591 |
if (!*c) {
|
|
Packit |
8fb591 |
/* eof */
|
|
Packit |
8fb591 |
return first;
|
|
Packit |
8fb591 |
} else if (is_xmlws(*c)) {
|
|
Packit |
8fb591 |
/* skip whitespaces */
|
|
Packit |
8fb591 |
ign_xmlws(c);
|
|
Packit |
8fb591 |
} else if (!strncmp(c, "
|
|
Packit |
8fb591 |
/* XMLDecl or PI - ignore it */
|
|
Packit |
8fb591 |
c += 2;
|
|
Packit |
8fb591 |
if (parse_ignore(ctx, c, "?>", &len)) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c += len;
|
|
Packit |
8fb591 |
} else if (!strncmp(c, "
|
|
Packit |
8fb591 |
/* Comment - ignore it */
|
|
Packit |
8fb591 |
c += 2;
|
|
Packit |
8fb591 |
if (parse_ignore(ctx, c, "-->", &len)) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c += len;
|
|
Packit |
8fb591 |
} else if (!strncmp(c, "
|
|
Packit |
8fb591 |
/* DOCTYPE */
|
|
Packit |
8fb591 |
/* TODO - standalone ignore counting < and > */
|
|
Packit |
8fb591 |
LOGERR(ctx, LY_EINVAL, "DOCTYPE not supported in XML documents.");
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
} else if (*c == '<') {
|
|
Packit |
8fb591 |
/* element - process it in next loop to strictly follow XML
|
|
Packit |
8fb591 |
* format
|
|
Packit |
8fb591 |
*/
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, c);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
root = lyxml_parse_elem(ctx, c, &len, NULL, options);
|
|
Packit |
8fb591 |
if (!root) {
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
} else if (!first) {
|
|
Packit |
8fb591 |
first = root;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
first->prev->next = root;
|
|
Packit |
8fb591 |
root->prev = first->prev;
|
|
Packit |
8fb591 |
first->prev = root;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
c += len;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* ignore the rest of document where can be comments, PIs and whitespaces,
|
|
Packit |
8fb591 |
* note that we are not detecting syntax errors in these parts
|
|
Packit |
8fb591 |
*/
|
|
Packit |
8fb591 |
ign_xmlws(c);
|
|
Packit |
8fb591 |
if (*c) {
|
|
Packit |
8fb591 |
if (options & LYXML_PARSE_MULTIROOT) {
|
|
Packit |
8fb591 |
goto repeat;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
LOGWRN(ctx, "There are some not parsed data:\n%s", c);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return first;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
error:
|
|
Packit |
8fb591 |
LY_TREE_FOR_SAFE(first, next, root) {
|
|
Packit |
8fb591 |
lyxml_free(ctx, root);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API struct lyxml_elem *
|
|
Packit |
8fb591 |
lyxml_parse_path(struct ly_ctx *ctx, const char *filename, int options)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyxml_elem *elem = NULL;
|
|
Packit |
8fb591 |
size_t length;
|
|
Packit |
8fb591 |
int fd;
|
|
Packit |
8fb591 |
char *addr;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!filename || !ctx) {
|
|
Packit |
8fb591 |
LOGARG;
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
fd = open(filename, O_RDONLY);
|
|
Packit |
8fb591 |
if (fd == -1) {
|
|
Packit |
8fb591 |
LOGERR(ctx, LY_EINVAL,"Opening file \"%s\" failed.", filename);
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (lyp_mmap(ctx, fd, 0, &length, (void **)&addr)) {
|
|
Packit |
8fb591 |
LOGERR(ctx, LY_ESYS, "Mapping file descriptor into memory failed (%s()).", __func__);
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
} else if (!addr) {
|
|
Packit |
8fb591 |
/* empty XML file */
|
|
Packit |
8fb591 |
goto error;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
elem = lyxml_parse_mem(ctx, addr, options);
|
|
Packit |
8fb591 |
lyp_munmap(addr, length);
|
|
Packit |
8fb591 |
close(fd);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return elem;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
error:
|
|
Packit |
8fb591 |
if (fd != -1) {
|
|
Packit |
8fb591 |
close(fd);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return NULL;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
int
|
|
Packit |
8fb591 |
lyxml_dump_text(struct lyout *out, const char *text, LYXML_DATA_TYPE type)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
unsigned int i, n;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!text) {
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
for (i = n = 0; text[i]; i++) {
|
|
Packit |
8fb591 |
switch (text[i]) {
|
|
Packit |
8fb591 |
case '&':
|
|
Packit |
8fb591 |
n += ly_print(out, "&");
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
case '<':
|
|
Packit |
8fb591 |
n += ly_print(out, "<");
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
case '>':
|
|
Packit |
8fb591 |
/* not needed, just for readability */
|
|
Packit |
8fb591 |
n += ly_print(out, ">");
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
case '"':
|
|
Packit |
8fb591 |
if (type == LYXML_DATA_ATTR) {
|
|
Packit |
8fb591 |
n += ly_print(out, """);
|
|
Packit |
8fb591 |
break;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
/* falls through */
|
|
Packit |
8fb591 |
default:
|
|
Packit |
8fb591 |
ly_write(out, &text[i], 1);
|
|
Packit |
8fb591 |
n++;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return n;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
static int
|
|
Packit |
8fb591 |
dump_elem(struct lyout *out, const struct lyxml_elem *e, int level, int options, int last_elem)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
int size = 0;
|
|
Packit |
8fb591 |
struct lyxml_attr *a;
|
|
Packit |
8fb591 |
struct lyxml_elem *child;
|
|
Packit |
8fb591 |
const char *delim, *delim_outer;
|
|
Packit |
8fb591 |
int indent;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!e->name) {
|
|
Packit |
8fb591 |
/* mixed content */
|
|
Packit |
8fb591 |
if (e->content) {
|
|
Packit |
8fb591 |
return lyxml_dump_text(out, e->content, LYXML_DATA_ELEM);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
delim = delim_outer = (options & LYXML_PRINT_FORMAT) ? "\n" : "";
|
|
Packit |
8fb591 |
indent = 2 * level;
|
|
Packit |
8fb591 |
if ((e->flags & LYXML_ELEM_MIXED) || (e->parent && (e->parent->flags & LYXML_ELEM_MIXED))) {
|
|
Packit |
8fb591 |
delim = "";
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (e->parent && (e->parent->flags & LYXML_ELEM_MIXED)) {
|
|
Packit |
8fb591 |
delim_outer = "";
|
|
Packit |
8fb591 |
indent = 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
if (last_elem && (options & LYXML_PRINT_NO_LAST_NEWLINE)) {
|
|
Packit |
8fb591 |
delim_outer = "";
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!(options & (LYXML_PRINT_OPEN | LYXML_PRINT_CLOSE | LYXML_PRINT_ATTRS)) || (options & LYXML_PRINT_OPEN)) {
|
|
Packit |
8fb591 |
/* opening tag */
|
|
Packit |
8fb591 |
if (e->ns && e->ns->prefix) {
|
|
Packit |
8fb591 |
size += ly_print(out, "%*s<%s:%s", indent, "", e->ns->prefix, e->name);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
size += ly_print(out, "%*s<%s", indent, "", e->name);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if (options & LYXML_PRINT_CLOSE) {
|
|
Packit |
8fb591 |
indent = 0;
|
|
Packit |
8fb591 |
goto close;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* attributes */
|
|
Packit |
8fb591 |
for (a = e->attr; a; a = a->next) {
|
|
Packit |
8fb591 |
if (a->type == LYXML_ATTR_NS) {
|
|
Packit |
8fb591 |
if (a->name) {
|
|
Packit |
8fb591 |
size += ly_print(out, " xmlns:%s=\"%s\"", a->name, a->value ? a->value : "");
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
size += ly_print(out, " xmlns=\"%s\"", a->value ? a->value : "");
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
} else if (a->ns && a->ns->prefix) {
|
|
Packit |
8fb591 |
size += ly_print(out, " %s:%s=\"%s\"", a->ns->prefix, a->name, a->value);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
size += ly_print(out, " %s=\"%s\"", a->name, a->value);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* apply options */
|
|
Packit |
8fb591 |
if ((options & LYXML_PRINT_CLOSE) && (options & LYXML_PRINT_OPEN)) {
|
|
Packit |
8fb591 |
size += ly_print(out, "/>%s", delim);
|
|
Packit |
8fb591 |
return size;
|
|
Packit |
8fb591 |
} else if (options & LYXML_PRINT_OPEN) {
|
|
Packit |
8fb591 |
ly_print(out, ">");
|
|
Packit |
8fb591 |
return ++size;
|
|
Packit |
8fb591 |
} else if (options & LYXML_PRINT_ATTRS) {
|
|
Packit |
8fb591 |
return size;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!e->child && (!e->content || !e->content[0])) {
|
|
Packit |
8fb591 |
size += ly_print(out, "/>%s", delim);
|
|
Packit |
8fb591 |
return size;
|
|
Packit |
8fb591 |
} else if (e->content && e->content[0]) {
|
|
Packit |
8fb591 |
ly_print(out, ">");
|
|
Packit |
8fb591 |
size++;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
size += lyxml_dump_text(out, e->content, LYXML_DATA_ELEM);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (e->ns && e->ns->prefix) {
|
|
Packit |
8fb591 |
size += ly_print(out, "</%s:%s>%s", e->ns->prefix, e->name, delim);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
size += ly_print(out, "</%s>%s", e->name, delim);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
return size;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
size += ly_print(out, ">%s", delim);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
/* go recursively */
|
|
Packit |
8fb591 |
LY_TREE_FOR(e->child, child) {
|
|
Packit |
8fb591 |
if (options & LYXML_PRINT_FORMAT) {
|
|
Packit |
8fb591 |
size += dump_elem(out, child, level + 1, LYXML_PRINT_FORMAT, 0);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
size += dump_elem(out, child, level, 0, 0);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
close:
|
|
Packit |
8fb591 |
/* closing tag */
|
|
Packit |
8fb591 |
if (e->ns && e->ns->prefix) {
|
|
Packit |
8fb591 |
size += ly_print(out, "%*s</%s:%s>%s", indent, "", e->ns->prefix, e->name, delim_outer);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
size += ly_print(out, "%*s</%s>%s", indent, "", e->name, delim_outer);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return size;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
static int
|
|
Packit |
8fb591 |
dump_siblings(struct lyout *out, const struct lyxml_elem *e, int options)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
const struct lyxml_elem *start, *iter, *next;
|
|
Packit |
8fb591 |
int ret = 0;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (e->parent) {
|
|
Packit |
8fb591 |
start = e->parent->child;
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
start = e;
|
|
Packit |
8fb591 |
while(start->prev && start->prev->next) {
|
|
Packit |
8fb591 |
start = start->prev;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
LY_TREE_FOR_SAFE(start, next, iter) {
|
|
Packit |
8fb591 |
ret += dump_elem(out, iter, 0, options, (next ? 0 : 1));
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
return ret;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API int
|
|
Packit |
8fb591 |
lyxml_print_file(FILE *stream, const struct lyxml_elem *elem, int options)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyout out;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!stream || !elem) {
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
memset(&out, 0, sizeof out);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
out.type = LYOUT_STREAM;
|
|
Packit |
8fb591 |
out.method.f = stream;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (options & LYXML_PRINT_SIBLINGS) {
|
|
Packit |
8fb591 |
return dump_siblings(&out, elem, options);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
return dump_elem(&out, elem, 0, options, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API int
|
|
Packit |
8fb591 |
lyxml_print_fd(int fd, const struct lyxml_elem *elem, int options)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyout out;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (fd < 0 || !elem) {
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
memset(&out, 0, sizeof out);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
out.type = LYOUT_FD;
|
|
Packit |
8fb591 |
out.method.fd = fd;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (options & LYXML_PRINT_SIBLINGS) {
|
|
Packit |
8fb591 |
return dump_siblings(&out, elem, options);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
return dump_elem(&out, elem, 0, options, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API int
|
|
Packit |
8fb591 |
lyxml_print_mem(char **strp, const struct lyxml_elem *elem, int options)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyout out;
|
|
Packit |
8fb591 |
int r;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!strp || !elem) {
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
memset(&out, 0, sizeof out);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
out.type = LYOUT_MEMORY;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (options & LYXML_PRINT_SIBLINGS) {
|
|
Packit |
8fb591 |
r = dump_siblings(&out, elem, options);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
r = dump_elem(&out, elem, 0, options, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
*strp = out.method.mem.buf;
|
|
Packit |
8fb591 |
return r;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
API int
|
|
Packit |
8fb591 |
lyxml_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lyxml_elem *elem, int options)
|
|
Packit |
8fb591 |
{
|
|
Packit |
8fb591 |
struct lyout out;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (!writeclb || !elem) {
|
|
Packit |
8fb591 |
return 0;
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
memset(&out, 0, sizeof out);
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
out.type = LYOUT_CALLBACK;
|
|
Packit |
8fb591 |
out.method.clb.f = writeclb;
|
|
Packit |
8fb591 |
out.method.clb.arg = arg;
|
|
Packit |
8fb591 |
|
|
Packit |
8fb591 |
if (options & LYXML_PRINT_SIBLINGS) {
|
|
Packit |
8fb591 |
return dump_siblings(&out, elem, options);
|
|
Packit |
8fb591 |
} else {
|
|
Packit |
8fb591 |
return dump_elem(&out, elem, 0, options, 1);
|
|
Packit |
8fb591 |
}
|
|
Packit |
8fb591 |
}
|