#define _GNU_SOURCE
#include "xml.h"
#include <assert.h>
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "compat.h"
#include "in_internal.h"
#include "ly_common.h"
#include "out_internal.h"
#include "tree.h"
#include "tree_schema_internal.h"
#define move_input(c, s) \
ly_in_skip(c->in, s); \
LY_CHECK_ERR_RET(!c->in->current[0], LOGVAL(c->ctx, NULL, LY_VCODE_EOF), LY_EVALID)
#define ign_xmlws(c) \
while (is_xmlws(*(c)->in->current)) { \
if (*(c)->in->current == '\n') { \
LY_IN_NEW_LINE((c)->in); \
} \
ly_in_skip(c->in, 1); \
}
static LY_ERR lyxml_next_attr_content(struct lyxml_ctx *xmlctx, const char **value, uint32_t *value_len, ly_bool *ws_only,
ly_bool *dynamic);
LY_ERR
skip_section(struct lyxml_ctx *xmlctx, const char *delim, uint32_t delim_len, const char *sectname)
{
uint32_t i;
register const char *input, *a, *b;
uint64_t parsed = 0, newlines = 0;
for (input = xmlctx->in->current; *input; ++input, ++parsed) {
if (*input != *delim) {
if (*input == '\n') {
++newlines;
}
continue;
}
a = input;
b = delim;
for (i = 0; i < delim_len; ++i) {
if (*a++ != *b++) {
break;
}
}
if (i == delim_len) {
xmlctx->in->line += newlines;
ly_in_skip(xmlctx->in, parsed + delim_len);
return LY_SUCCESS;
}
}
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_NTERM, sectname);
return LY_EVALID;
}
static LY_ERR
lyxml_parse_identifier(struct lyxml_ctx *xmlctx, const char **start, const char **end)
{
const char *s, *in;
uint32_t c;
uint32_t parsed;
LY_ERR rc;
in = s = xmlctx->in->current;
LY_CHECK_ERR_RET(ly_getutf8(&in, &c, &parsed),
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_INCHAR, in[0]),
LY_EVALID);
LY_CHECK_ERR_RET(!is_xmlqnamestartchar(c),
LOGVAL(xmlctx->ctx, NULL, LYVE_SYNTAX, "Identifier \"%s\" starts with an invalid character.", in - parsed),
LY_EVALID);
do {
ly_in_skip(xmlctx->in, parsed);
rc = ly_getutf8(&in, &c, &parsed);
LY_CHECK_ERR_RET(rc, LOGVAL(xmlctx->ctx, NULL, LY_VCODE_INCHAR, in[0]), LY_EVALID);
} while (is_xmlqnamechar(c));
*start = s;
*end = xmlctx->in->current;
return LY_SUCCESS;
}
LY_ERR
lyxml_ns_add(struct lyxml_ctx *xmlctx, const char *prefix, uint32_t prefix_len, char *uri)
{
LY_ERR rc = LY_SUCCESS;
struct lyxml_ns *ns;
uint32_t i;
if (xmlctx->ns.count) {
i = xmlctx->ns.count;
do {
--i;
ns = xmlctx->ns.objs[i];
if (ns->depth < xmlctx->elements.count) {
break;
} else if (prefix && ns->prefix && !ly_strncmp(ns->prefix, prefix, prefix_len)) {
if (!strcmp(ns->uri, uri)) {
goto cleanup;
}
LOGVAL(xmlctx->ctx, NULL, LYVE_SYNTAX, "Duplicate XML NS prefix \"%s\" used for namespaces \"%s\" and \"%s\".",
ns->prefix, ns->uri, uri);
rc = LY_EVALID;
goto cleanup;
} else if (!prefix && !ns->prefix) {
if (!strcmp(ns->uri, uri)) {
goto cleanup;
}
LOGVAL(xmlctx->ctx, NULL, LYVE_SYNTAX, "Duplicate default XML namespaces \"%s\" and \"%s\".", ns->uri, uri);
rc = LY_EVALID;
goto cleanup;
}
} while (i);
}
ns = malloc(sizeof *ns);
LY_CHECK_ERR_GOTO(!ns, LOGMEM(xmlctx->ctx); rc = LY_EMEM, cleanup);
ns->depth = xmlctx->elements.count;
ns->uri = uri;
if (prefix) {
ns->prefix = strndup(prefix, prefix_len);
LY_CHECK_ERR_GOTO(!ns->prefix, LOGMEM(xmlctx->ctx); free(ns); rc = LY_EMEM, cleanup);
} else {
ns->prefix = NULL;
}
rc = ly_set_add(&xmlctx->ns, ns, 1, NULL);
LY_CHECK_ERR_GOTO(rc, free(ns->prefix); free(ns), cleanup);
uri = NULL;
cleanup:
free(uri);
return rc;
}
void
lyxml_ns_rm(struct lyxml_ctx *xmlctx)
{
struct lyxml_ns *ns;
uint32_t u;
if (!xmlctx->ns.count) {
return;
}
u = xmlctx->ns.count;
do {
--u;
ns = (struct lyxml_ns *)xmlctx->ns.objs[u];
if (ns->depth != xmlctx->elements.count + 1) {
break;
}
free(ns->prefix);
free(ns->uri);
free(ns);
--xmlctx->ns.count;
} while (u);
if (!xmlctx->ns.count) {
ly_set_erase(&xmlctx->ns, NULL);
}
}
const struct lyxml_ns *
lyxml_ns_get(const struct ly_set *ns_set, const char *prefix, uint32_t prefix_len)
{
struct lyxml_ns *ns;
uint32_t u;
if (!ns_set->count) {
return NULL;
}
u = ns_set->count;
do {
--u;
ns = (struct lyxml_ns *)ns_set->objs[u];
if (prefix && prefix_len) {
if (ns->prefix && !ly_strncmp(ns->prefix, prefix, prefix_len)) {
return ns;
}
} else if (!ns->prefix) {
return ns;
}
} while (u);
return NULL;
}
static LY_ERR
lyxml_skip_until_end_or_after_otag(struct lyxml_ctx *xmlctx)
{
const struct ly_ctx *ctx = xmlctx->ctx;
const char *endtag, *sectname;
uint32_t endtag_len;
while (1) {
ign_xmlws(xmlctx);
if (xmlctx->in->current[0] == '\0') {
if (xmlctx->elements.count) {
LOGVAL(ctx, NULL, LY_VCODE_EOF);
return LY_EVALID;
}
return LY_SUCCESS;
} else if (xmlctx->in->current[0] != '<') {
LOGVAL(ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current), xmlctx->in->current,
"element tag start ('<')");
return LY_EVALID;
}
move_input(xmlctx, 1);
if (xmlctx->in->current[0] == '!') {
move_input(xmlctx, 1);
if (!strncmp(xmlctx->in->current, "--", 2)) {
move_input(xmlctx, 2);
sectname = "Comment";
endtag = "-->";
endtag_len = ly_strlen_const("-->");
} else if (!strncmp(xmlctx->in->current, "DOCTYPE", ly_strlen_const("DOCTYPE"))) {
LOGVAL(ctx, NULL, LY_VCODE_NSUPP, "Document Type Declaration");
return LY_EVALID;
} else {
LOGVAL(ctx, NULL, LYVE_SYNTAX, "Unknown XML section \"%.20s\".", &xmlctx->in->current[-2]);
return LY_EVALID;
}
LY_CHECK_RET(skip_section(xmlctx, endtag, endtag_len, sectname));
} else if (xmlctx->in->current[0] == '?') {
LY_CHECK_RET(skip_section(xmlctx, "?>", 2, "Declaration"));
} else {
break;
}
}
return LY_SUCCESS;
}
static LY_ERR
lyxml_parse_qname(struct lyxml_ctx *xmlctx, const char **prefix, uint32_t *prefix_len, const char **name, uint32_t *name_len)
{
const char *start, *end;
*prefix = NULL;
*prefix_len = 0;
LY_CHECK_RET(lyxml_parse_identifier(xmlctx, &start, &end));
if (end[0] == ':') {
if (end - start > UINT32_MAX) {
LOGVAL(xmlctx->ctx, NULL, LYVE_SYNTAX, "XML qualified name prefix too long.");
return LY_EINVAL;
}
*prefix = start;
*prefix_len = end - start;
move_input(xmlctx, 1);
LY_CHECK_RET(lyxml_parse_identifier(xmlctx, &start, &end));
}
if (end - start > UINT32_MAX) {
LOGVAL(xmlctx->ctx, NULL, LYVE_SYNTAX, "XML qualified name too long.");
return LY_EINVAL;
}
*name = start;
*name_len = end - start;
return LY_SUCCESS;
}
static LY_ERR
lyxml_parse_value_use_buf(const struct ly_ctx *ctx, const char **in, uint32_t *offset, uint32_t need_space, char **buf,
uint32_t *len, uint32_t *size)
{
#define BUFSIZE 24
#define BUFSIZE_STEP 128
uint64_t req_size;
if (!*buf) {
*buf = malloc(BUFSIZE);
LY_CHECK_ERR_RET(!*buf, LOGMEM(ctx), LY_EMEM);
*size = BUFSIZE;
}
req_size = (uint64_t)*len + (uint64_t)*offset + (uint64_t)need_space;
if (req_size > UINT32_MAX) {
LOGVAL(ctx, NULL, LYVE_SYNTAX, "XML value too long.");
return LY_EINVAL;
}
while (req_size >= *size) {
*buf = ly_realloc(*buf, *size + BUFSIZE_STEP);
LY_CHECK_ERR_RET(!*buf, LOGMEM(ctx), LY_EMEM);
*size += BUFSIZE_STEP;
}
if (*offset) {
memcpy(&(*buf)[*len], *in, *offset);
*len += *offset;
*in += *offset;
*offset = 0;
}
return LY_SUCCESS;
#undef BUFSIZE
#undef BUFSIZE_STEP
}
static LY_ERR
lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, uint32_t *length, ly_bool *ws_only, ly_bool *dynamic)
{
#define ADD_CHECK_OVERFLOW(var, num, err_label) \
if (var + num < var) { \
LOGVAL(ctx, NULL, LYVE_SYNTAX, "XML value too long."); \
goto err_label; \
} \
var += num;
const struct ly_ctx *ctx = xmlctx->ctx;
const char *in = xmlctx->in->current, *start, *in_aux, *p;
char *buf = NULL;
uint32_t offset;
uint32_t len;
uint32_t size = 0;
uint32_t n, u;
ly_bool ws = 1;
assert(xmlctx);
start = in;
offset = len = 0;
while (in[offset]) {
if (in[offset] == '&') {
ws = 0;
LY_CHECK_RET(lyxml_parse_value_use_buf(ctx, &in, &offset, 4, &buf, &len, &size));
ADD_CHECK_OVERFLOW(offset, 1, error);
if (in[offset] != '#') {
if (!strncmp(&in[offset], "lt;", ly_strlen_const("lt;"))) {
buf[len++] = '<';
in += ly_strlen_const("<");
} else if (!strncmp(&in[offset], "gt;", ly_strlen_const("gt;"))) {
buf[len++] = '>';
in += ly_strlen_const(">");
} else if (!strncmp(&in[offset], "amp;", ly_strlen_const("amp;"))) {
buf[len++] = '&';
in += ly_strlen_const("&");
} else if (!strncmp(&in[offset], "apos;", ly_strlen_const("apos;"))) {
buf[len++] = '\'';
in += ly_strlen_const("'");
} else if (!strncmp(&in[offset], "quot;", ly_strlen_const("quot;"))) {
buf[len++] = '\"';
in += ly_strlen_const(""");
} else {
LOGVAL(ctx, NULL, LYVE_SYNTAX,
"Entity reference \"%.*s\" not supported, only predefined references allowed.",
10, &in[offset - 1]);
goto error;
}
offset = 0;
} else {
p = &in[offset - 1];
ADD_CHECK_OVERFLOW(offset, 1, error);
if (isdigit(in[offset])) {
for (n = 0; isdigit(in[offset]); offset++) {
n = (LY_BASE_DEC * n) + (in[offset] - '0');
}
} else if ((in[offset] == 'x') && isxdigit(in[offset + 1])) {
for (n = 0, ++offset; isxdigit(in[offset]); offset++) {
if (isdigit(in[offset])) {
u = (in[offset] - '0');
} else if (in[offset] > 'F') {
u = LY_BASE_DEC + (in[offset] - 'a');
} else {
u = LY_BASE_DEC + (in[offset] - 'A');
}
n = (LY_BASE_HEX * n) + u;
}
} else {
LOGVAL(ctx, NULL, LYVE_SYNTAX, "Invalid character reference \"%.12s\".", p);
goto error;
}
if (in[offset] != ';') {
LOGVAL(ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(&in[offset]), &in[offset], ";");
goto error;
}
ADD_CHECK_OVERFLOW(offset, 1, error);
if (ly_pututf8(&buf[len], n, &u)) {
LOGVAL(ctx, NULL, LYVE_SYNTAX, "Invalid character reference \"%.12s\" (0x%08" PRIx32 ").", p, n);
goto error;
}
len += u;
in += offset;
offset = 0;
}
} else if (!strncmp(in + offset, "<![CDATA[", ly_strlen_const("<![CDATA["))) {
in_aux = strstr(in + offset + ly_strlen_const("<![CDATA["), "]]>");
if (!in_aux) {
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_NTERM, "CDATA");
goto error;
}
u = in_aux - (in + offset + ly_strlen_const("<![CDATA["));
LY_CHECK_RET(lyxml_parse_value_use_buf(ctx, &in, &offset, u, &buf, &len, &size));
in += ly_strlen_const("<![CDATA[");
assert(!offset);
for (n = 0; n < u; ++n) {
if (in[n] == '\n') {
LY_IN_NEW_LINE(xmlctx->in);
} else if (!is_xmlws(in[n])) {
ws = 0;
}
}
memcpy(buf + len, in, u);
len += u;
in += u + ly_strlen_const("]]>");
} else if (in[offset] == endchar) {
if (buf) {
buf = ly_realloc(buf, len + offset + 1);
LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
size = len + offset + 1;
if (offset) {
memcpy(&buf[len], in, offset);
}
buf[len + offset] = '\0';
}
len += offset;
in += offset;
goto success;
} else {
if (!is_xmlws(in[offset])) {
ws = 0;
}
if (in[offset] == '\n') {
LY_IN_NEW_LINE(xmlctx->in);
}
in_aux = &in[offset];
LY_CHECK_ERR_GOTO(ly_getutf8(&in_aux, &n, &u), LOGVAL(ctx, NULL, LY_VCODE_INCHAR, in[offset]), error);
ADD_CHECK_OVERFLOW(offset, u, error);
}
}
LOGVAL(ctx, NULL, LY_VCODE_EOF);
error:
free(buf);
return LY_EVALID;
success:
if (buf) {
*value = buf;
*dynamic = 1;
} else {
*value = (char *)start;
*dynamic = 0;
}
*length = len;
*ws_only = ws;
xmlctx->in->current = in;
return LY_SUCCESS;
}
static LY_ERR
lyxml_close_element(struct lyxml_ctx *xmlctx, const char *prefix, uint32_t prefix_len, const char *name, uint32_t name_len,
ly_bool empty)
{
struct lyxml_elem *e;
if (!xmlctx->elements.count) {
LOGVAL(xmlctx->ctx, NULL, LYVE_SYNTAX, "Stray closing element tag (\"%.*s\").", (int)name_len, name);
return LY_EVALID;
}
e = (struct lyxml_elem *)xmlctx->elements.objs[xmlctx->elements.count - 1];
if ((e->prefix_len != prefix_len) || (e->name_len != name_len) ||
(prefix_len && strncmp(prefix, e->prefix, e->prefix_len)) || strncmp(name, e->name, e->name_len)) {
LOGVAL(xmlctx->ctx, NULL, LYVE_SYNTAX, "Opening (\"%.*s%s%.*s\") and closing (\"%.*s%s%.*s\") elements tag mismatch.",
(int)e->prefix_len, e->prefix ? e->prefix : "", e->prefix ? ":" : "", (int)e->name_len, e->name,
(int)prefix_len, prefix ? prefix : "", prefix ? ":" : "", (int)name_len, name);
return LY_EVALID;
}
ly_set_rm_index(&xmlctx->elements, xmlctx->elements.count - 1, free);
lyxml_ns_rm(xmlctx);
ign_xmlws(xmlctx);
if (empty && (xmlctx->in->current[0] == '/')) {
move_input(xmlctx, 1);
}
if (xmlctx->in->current[0] != '>') {
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
xmlctx->in->current, "element tag termination ('>')");
return LY_EVALID;
}
ly_in_skip(xmlctx->in, 1);
return LY_SUCCESS;
}
static LY_ERR
lyxml_open_element(struct lyxml_ctx *xmlctx, const char *prefix, uint32_t prefix_len, const char *name, uint32_t name_len)
{
LY_ERR ret = LY_SUCCESS;
struct lyxml_elem *e;
const char *prev_input;
uint64_t prev_line;
char *value;
uint32_t value_len;
ly_bool ws_only, dynamic, is_ns;
uint32_t c, parsed;
e = malloc(sizeof *e);
LY_CHECK_ERR_RET(!e, LOGMEM(xmlctx->ctx), LY_EMEM);
e->name = name;
e->prefix = prefix;
e->name_len = name_len;
e->prefix_len = prefix_len;
LY_CHECK_RET(ly_set_add(&xmlctx->elements, e, 1, NULL));
if (xmlctx->elements.count > LY_MAX_BLOCK_DEPTH) {
LOGERR(xmlctx->ctx, LY_EINVAL, "The maximum number of open elements has been exceeded.");
return LY_EINVAL;
}
ign_xmlws(xmlctx);
prev_input = xmlctx->in->current;
prev_line = xmlctx->in->line;
is_ns = 1;
while ((xmlctx->in->current[0] != '\0') && !(ret = ly_getutf8(&xmlctx->in->current, &c, &parsed))) {
if (!is_xmlqnamestartchar(c)) {
break;
}
xmlctx->in->current -= parsed;
LY_CHECK_GOTO(ret = lyxml_parse_qname(xmlctx, &prefix, &prefix_len, &name, &name_len), cleanup);
LY_CHECK_GOTO(ret = lyxml_next_attr_content(xmlctx, (const char **)&value, &value_len, &ws_only, &dynamic), cleanup);
if ((prefix && !ly_strncmp("xmlns", prefix, prefix_len)) || (!prefix && !ly_strncmp("xmlns", name, name_len))) {
ret = lyxml_ns_add(xmlctx, prefix ? name : NULL, prefix ? name_len : 0,
dynamic ? value : strndup(value, value_len));
dynamic = 0;
LY_CHECK_GOTO(ret, cleanup);
} else {
is_ns = 0;
}
if (dynamic) {
free(value);
}
ign_xmlws(xmlctx);
if (is_ns) {
prev_input = xmlctx->in->current;
prev_line = xmlctx->in->line;
}
}
cleanup:
if (!ret) {
xmlctx->in->current = prev_input;
xmlctx->in->line = prev_line;
}
return ret;
}
static LY_ERR
lyxml_next_attr_content(struct lyxml_ctx *xmlctx, const char **value, uint32_t *value_len, ly_bool *ws_only, ly_bool *dynamic)
{
char quot;
ign_xmlws(xmlctx);
if (xmlctx->in->current[0] == '\0') {
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_EOF);
return LY_EVALID;
} else if (xmlctx->in->current[0] != '=') {
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
xmlctx->in->current, "'='");
return LY_EVALID;
}
move_input(xmlctx, 1);
ign_xmlws(xmlctx);
if (xmlctx->in->current[0] == '\0') {
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_EOF);
return LY_EVALID;
} else if ((xmlctx->in->current[0] != '\'') && (xmlctx->in->current[0] != '\"')) {
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
xmlctx->in->current, "either single or double quotation mark");
return LY_EVALID;
}
quot = xmlctx->in->current[0];
move_input(xmlctx, 1);
LY_CHECK_RET(lyxml_parse_value(xmlctx, quot, (char **)value, value_len, ws_only, dynamic));
ly_in_skip(xmlctx->in, 1);
return LY_SUCCESS;
}
static LY_ERR
lyxml_next_attribute(struct lyxml_ctx *xmlctx, const char **prefix, uint32_t *prefix_len, const char **name, uint32_t *name_len)
{
const char *in;
char *value;
uint32_t c, parsed;
uint32_t value_len;
ly_bool ws_only, dynamic;
ign_xmlws(xmlctx);
while ((xmlctx->in->current[0] != '>') && (xmlctx->in->current[0] != '/')) {
in = xmlctx->in->current;
if (in[0] == '\0') {
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_EOF);
return LY_EVALID;
} else if ((ly_getutf8(&in, &c, &parsed) || !is_xmlqnamestartchar(c))) {
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(in - parsed), in - parsed,
"element tag end ('>' or '/>') or an attribute");
return LY_EVALID;
}
LY_CHECK_RET(lyxml_parse_qname(xmlctx, prefix, prefix_len, name, name_len));
if ((!*prefix || ly_strncmp("xmlns", *prefix, *prefix_len)) && (*prefix || ly_strncmp("xmlns", *name, *name_len))) {
break;
}
LY_CHECK_RET(lyxml_next_attr_content(xmlctx, (const char **)&value, &value_len, &ws_only, &dynamic));
if (dynamic) {
free(value);
}
ign_xmlws(xmlctx);
}
return LY_SUCCESS;
}
static LY_ERR
lyxml_next_element(struct lyxml_ctx *xmlctx, const char **prefix, uint32_t *prefix_len, const char **name, uint32_t *name_len,
ly_bool *closing)
{
LY_CHECK_RET(lyxml_skip_until_end_or_after_otag(xmlctx));
if (xmlctx->in->current[0] == '\0') {
*prefix = *name = NULL;
*prefix_len = *name_len = 0;
return LY_SUCCESS;
}
if (xmlctx->in->current[0] == '/') {
move_input(xmlctx, 1);
*closing = 1;
} else {
*closing = 0;
}
ign_xmlws(xmlctx);
LY_CHECK_RET(lyxml_parse_qname(xmlctx, prefix, prefix_len, name, name_len));
return LY_SUCCESS;
}
LY_ERR
lyxml_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyxml_ctx **xmlctx_p)
{
LY_ERR ret = LY_SUCCESS;
struct lyxml_ctx *xmlctx;
ly_bool closing;
xmlctx = calloc(1, sizeof *xmlctx);
LY_CHECK_ERR_RET(!xmlctx, LOGMEM(ctx), LY_EMEM);
xmlctx->ctx = ctx;
xmlctx->in = in;
ly_log_location(NULL, NULL, in);
LY_CHECK_GOTO(ret = lyxml_next_element(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name,
&xmlctx->name_len, &closing), cleanup);
if (xmlctx->in->current[0] == '\0') {
xmlctx->status = LYXML_END;
} else if (closing) {
LOGVAL(ctx, NULL, LYVE_SYNTAX, "Stray closing element tag (\"%.*s\").", (int)xmlctx->name_len, xmlctx->name);
ret = LY_EVALID;
goto cleanup;
} else {
LY_CHECK_GOTO(ret = lyxml_open_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len), cleanup);
xmlctx->status = LYXML_ELEMENT;
}
cleanup:
if (ret) {
lyxml_ctx_free(xmlctx);
} else {
*xmlctx_p = xmlctx;
}
return ret;
}
LY_ERR
lyxml_ctx_next(struct lyxml_ctx *xmlctx)
{
LY_ERR ret = LY_SUCCESS;
ly_bool closing;
struct lyxml_elem *e;
if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
free((char *)xmlctx->value);
xmlctx->value = NULL;
xmlctx->dynamic = 0;
}
switch (xmlctx->status) {
case LYXML_ELEM_CONTENT:
if (xmlctx->in->current[0] == '/') {
assert(xmlctx->elements.count);
e = (struct lyxml_elem *)xmlctx->elements.objs[xmlctx->elements.count - 1];
ret = lyxml_close_element(xmlctx, e->prefix, e->prefix_len, e->name, e->name_len, 1);
LY_CHECK_GOTO(ret, cleanup);
xmlctx->status = LYXML_ELEM_CLOSE;
break;
}
case LYXML_ELEM_CLOSE:
ret = lyxml_next_element(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name, &xmlctx->name_len, &closing);
LY_CHECK_GOTO(ret, cleanup);
if (xmlctx->in->current[0] == '\0') {
xmlctx->status = LYXML_END;
} else if (closing) {
ret = lyxml_close_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len, 0);
LY_CHECK_GOTO(ret, cleanup);
xmlctx->status = LYXML_ELEM_CLOSE;
} else {
ret = lyxml_open_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len);
LY_CHECK_GOTO(ret, cleanup);
xmlctx->status = LYXML_ELEMENT;
}
break;
case LYXML_ELEMENT:
case LYXML_ATTR_CONTENT:
ret = lyxml_next_attribute(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name, &xmlctx->name_len);
LY_CHECK_GOTO(ret, cleanup);
if (xmlctx->in->current[0] == '>') {
ly_in_skip(xmlctx->in, 1);
if (!xmlctx->in->current[0]) {
LOGVAL(xmlctx->ctx, NULL, LY_VCODE_EOF);
ret = LY_EVALID;
goto cleanup;
}
ret = lyxml_parse_value(xmlctx, '<', (char **)&xmlctx->value, &xmlctx->value_len, &xmlctx->ws_only,
&xmlctx->dynamic);
LY_CHECK_GOTO(ret, cleanup);
if (!xmlctx->value_len) {
if (xmlctx->dynamic) {
free((char *) xmlctx->value);
}
xmlctx->value = "";
xmlctx->dynamic = 0;
}
xmlctx->status = LYXML_ELEM_CONTENT;
} else if (xmlctx->in->current[0] == '/') {
xmlctx->value = "";
xmlctx->value_len = 0;
xmlctx->ws_only = 1;
xmlctx->dynamic = 0;
xmlctx->status = LYXML_ELEM_CONTENT;
} else {
xmlctx->status = LYXML_ATTRIBUTE;
}
break;
case LYXML_ATTRIBUTE:
ret = lyxml_next_attr_content(xmlctx, &xmlctx->value, &xmlctx->value_len, &xmlctx->ws_only, &xmlctx->dynamic);
LY_CHECK_GOTO(ret, cleanup);
xmlctx->status = LYXML_ATTR_CONTENT;
break;
case LYXML_END:
break;
}
cleanup:
if (ret) {
xmlctx->status = LYXML_END;
}
return ret;
}
LY_ERR
lyxml_ctx_peek(struct lyxml_ctx *xmlctx, enum LYXML_PARSER_STATUS *next)
{
LY_ERR ret = LY_SUCCESS;
const char *prefix, *name, *prev_input;
uint32_t prefix_len, name_len;
ly_bool closing;
prev_input = xmlctx->in->current;
switch (xmlctx->status) {
case LYXML_ELEM_CONTENT:
if (xmlctx->in->current[0] == '/') {
*next = LYXML_ELEM_CLOSE;
break;
}
case LYXML_ELEM_CLOSE:
ret = lyxml_next_element(xmlctx, &prefix, &prefix_len, &name, &name_len, &closing);
LY_CHECK_GOTO(ret, cleanup);
if (xmlctx->in->current[0] == '\0') {
*next = LYXML_END;
} else if (closing) {
*next = LYXML_ELEM_CLOSE;
} else {
*next = LYXML_ELEMENT;
}
break;
case LYXML_ELEMENT:
case LYXML_ATTR_CONTENT:
ret = lyxml_next_attribute(xmlctx, &prefix, &prefix_len, &name, &name_len);
LY_CHECK_GOTO(ret, cleanup);
if ((xmlctx->in->current[0] == '>') || (xmlctx->in->current[0] == '/')) {
*next = LYXML_ELEM_CONTENT;
} else {
*next = LYXML_ATTRIBUTE;
}
break;
case LYXML_ATTRIBUTE:
*next = LYXML_ATTR_CONTENT;
break;
case LYXML_END:
*next = LYXML_END;
break;
}
cleanup:
xmlctx->in->current = prev_input;
return ret;
}
static void
lyxml_ns_rm_all(struct lyxml_ctx *xmlctx)
{
struct lyxml_ns *ns;
uint32_t i;
for (i = 0; i < xmlctx->ns.count; ++i) {
ns = xmlctx->ns.objs[i];
free(ns->prefix);
free(ns->uri);
free(ns);
}
ly_set_erase(&xmlctx->ns, NULL);
}
void
lyxml_ctx_free(struct lyxml_ctx *xmlctx)
{
if (!xmlctx) {
return;
}
ly_log_location_revert(0, 0, 1);
if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
free((char *)xmlctx->value);
}
ly_set_erase(&xmlctx->elements, free);
lyxml_ns_rm_all(xmlctx);
free(xmlctx);
}
static struct lyxml_elem *
lyxml_elem_dup(const struct lyxml_elem *elem)
{
struct lyxml_elem *dup;
dup = malloc(sizeof *dup);
LY_CHECK_ERR_RET(!dup, LOGMEM(NULL), NULL);
memcpy(dup, elem, sizeof *dup);
return dup;
}
static struct lyxml_ns *
lyxml_ns_dup(const struct lyxml_ns *ns)
{
struct lyxml_ns *dup;
dup = malloc(sizeof *dup);
LY_CHECK_ERR_RET(!dup, LOGMEM(NULL), NULL);
if (ns->prefix) {
dup->prefix = strdup(ns->prefix);
LY_CHECK_ERR_RET(!dup->prefix, LOGMEM(NULL); free(dup), NULL);
} else {
dup->prefix = NULL;
}
dup->uri = strdup(ns->uri);
LY_CHECK_ERR_RET(!dup->uri, LOGMEM(NULL); free(dup->prefix); free(dup), NULL);
dup->depth = ns->depth;
return dup;
}
LY_ERR
lyxml_ctx_backup(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup)
{
uint32_t i;
memcpy(backup, xmlctx, sizeof *backup);
if ((xmlctx->status == LYXML_ELEM_CONTENT) && xmlctx->dynamic) {
xmlctx->dynamic = 0;
}
backup->b_current = xmlctx->in->current;
backup->b_line = xmlctx->in->line;
backup->elements.objs = malloc(xmlctx->elements.size * sizeof(struct lyxml_elem));
LY_CHECK_ERR_RET(!backup->elements.objs, LOGMEM(xmlctx->ctx), LY_EMEM);
for (i = 0; i < xmlctx->elements.count; ++i) {
backup->elements.objs[i] = lyxml_elem_dup(xmlctx->elements.objs[i]);
LY_CHECK_RET(!backup->elements.objs[i], LY_EMEM);
}
backup->ns.objs = malloc(xmlctx->ns.size * sizeof(struct lyxml_ns));
LY_CHECK_ERR_RET(!backup->ns.objs, LOGMEM(xmlctx->ctx), LY_EMEM);
for (i = 0; i < xmlctx->ns.count; ++i) {
backup->ns.objs[i] = lyxml_ns_dup(xmlctx->ns.objs[i]);
LY_CHECK_RET(!backup->ns.objs[i], LY_EMEM);
}
return LY_SUCCESS;
}
void
lyxml_ctx_restore(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup)
{
if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
free((char *)xmlctx->value);
}
ly_set_erase(&xmlctx->elements, free);
lyxml_ns_rm_all(xmlctx);
xmlctx->in->current = backup->b_current;
xmlctx->in->line = backup->b_line;
backup->in = xmlctx->in;
memcpy(xmlctx, backup, sizeof *xmlctx);
}
LY_ERR
lyxml_dump_text(struct ly_out *out, const char *text, ly_bool attribute)
{
LY_ERR ret;
if (!text) {
return 0;
}
for (uint64_t u = 0; text[u]; u++) {
switch (text[u]) {
case '&':
ret = ly_print_(out, "&");
break;
case '<':
ret = ly_print_(out, "<");
break;
case '>':
ret = ly_print_(out, ">");
break;
case '"':
if (attribute) {
ret = ly_print_(out, """);
break;
}
default:
ret = ly_write_(out, &text[u], 1);
break;
}
LY_CHECK_RET(ret);
}
return LY_SUCCESS;
}
LY_ERR
lyxml_value_compare(const struct ly_ctx *ctx1, const char *value1, void *val_prefix_data1,
const struct ly_ctx *ctx2, const char *value2, void *val_prefix_data2)
{
const char *value1_iter, *value2_iter;
const char *value1_next, *value2_next;
uint32_t value1_len, value2_len;
ly_bool is_prefix1, is_prefix2;
const struct lys_module *mod1, *mod2;
LY_ERR ret;
if (!value1 && !value2) {
return LY_SUCCESS;
}
if ((value1 && !value2) || (!value1 && value2)) {
return LY_ENOT;
}
if (!ctx2) {
ctx2 = ctx1;
}
ret = LY_SUCCESS;
for (value1_iter = value1, value2_iter = value2;
value1_iter && value2_iter;
value1_iter = value1_next, value2_iter = value2_next) {
if ((ret = ly_value_prefix_next(value1_iter, NULL, &value1_len, &is_prefix1, &value1_next))) {
break;
}
if ((ret = ly_value_prefix_next(value2_iter, NULL, &value2_len, &is_prefix2, &value2_next))) {
break;
}
if (is_prefix1 != is_prefix2) {
ret = LY_ENOT;
break;
}
if (!is_prefix1) {
if (value1_len != value2_len) {
ret = LY_ENOT;
break;
}
if (strncmp(value1_iter, value2_iter, value1_len)) {
ret = LY_ENOT;
break;
}
continue;
}
mod1 = mod2 = NULL;
if (val_prefix_data1) {
mod1 = ly_resolve_prefix(ctx1, value1_iter, value1_len, LY_VALUE_XML, val_prefix_data1);
}
if (val_prefix_data2) {
mod2 = ly_resolve_prefix(ctx2, value2_iter, value2_len, LY_VALUE_XML, val_prefix_data2);
}
if (!mod1 || !mod2) {
ret = LY_ENOT;
break;
}
if (mod1->ctx == mod2->ctx) {
if ((mod1->name != mod2->name) || (mod1->revision != mod2->revision)) {
ret = LY_ENOT;
break;
}
} else {
if (strcmp(mod1->name, mod2->name)) {
ret = LY_ENOT;
break;
}
if (mod1->revision || mod2->revision) {
if (!mod1->revision || !mod2->revision) {
ret = LY_ENOT;
break;
}
if (strcmp(mod1->revision, mod2->revision)) {
ret = LY_ENOT;
break;
}
}
}
}
if (value1_iter || value2_iter) {
ret = LY_ENOT;
}
return ret;
}