blob: 663d3e3fa52d020660b266bc9e127e76c0b56dec [file] [log] [blame]
Radek Krejcid91dbaf2018-09-21 15:51:39 +02001/**
2 * @file xml.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
Michal Vaskob36053d2020-03-26 15:49:30 +01004 * @author Michal Vasko <mvasko@cesnet.cz>
Radek Krejcid91dbaf2018-09-21 15:51:39 +02005 * @brief Generic XML parser implementation for libyang
6 *
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02007 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Radek Krejcid91dbaf2018-09-21 15:51:39 +02008 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
Radek Krejci535ea9f2020-05-29 16:01:05 +020016#define _GNU_SOURCE
17
18#include "xml.h"
Radek Krejci4b74d5e2018-09-26 14:30:55 +020019
Radek Krejcib1890642018-10-03 14:05:40 +020020#include <assert.h>
Radek Krejci7a7fa902018-09-25 17:08:21 +020021#include <ctype.h>
Radek Krejcid91dbaf2018-09-21 15:51:39 +020022#include <stdint.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020023#include <stdlib.h>
Radek Krejci4b74d5e2018-09-26 14:30:55 +020024#include <string.h>
Radek Krejcid91dbaf2018-09-21 15:51:39 +020025
Radek Krejci535ea9f2020-05-29 16:01:05 +020026#include "common.h"
Michal Vasko5aa44c02020-06-29 11:47:02 +020027#include "compat.h"
Michal Vaskoafac7822020-10-20 14:22:26 +020028#include "in_internal.h"
29#include "out_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020030#include "tree.h"
Radek Krejci77114102021-03-10 15:21:57 +010031#include "tree_schema_internal.h"
Radek Krejcid91dbaf2018-09-21 15:51:39 +020032
Michal Vaskob36053d2020-03-26 15:49:30 +010033/* Move input p by s characters, if EOF log with lyxml_ctx c */
Radek Krejci2efc45b2020-12-22 16:25:44 +010034#define move_input(c, s) \
35 ly_in_skip(c->in, s); \
36 LY_CHECK_ERR_RET(!c->in->current[0], LOGVAL(c->ctx, LY_VCODE_EOF), LY_EVALID)
Radek Krejcid91dbaf2018-09-21 15:51:39 +020037
Radek Krejcib1890642018-10-03 14:05:40 +020038/* Ignore whitespaces in the input string p */
Radek Krejcidd713ce2021-01-04 23:12:12 +010039#define ign_xmlws(c) \
40 while (is_xmlws(*(c)->in->current)) { \
41 if (*(c)->in->current == '\n') { \
42 LY_IN_NEW_LINE((c)->in); \
43 } \
44 ly_in_skip(c->in, 1); \
45 }
Michal Vaskob36053d2020-03-26 15:49:30 +010046
Radek Krejci857189e2020-09-01 13:26:36 +020047static LY_ERR lyxml_next_attr_content(struct lyxml_ctx *xmlctx, const char **value, size_t *value_len, ly_bool *ws_only,
48 ly_bool *dynamic);
Radek Krejcid91dbaf2018-09-21 15:51:39 +020049
Radek Krejci4b74d5e2018-09-26 14:30:55 +020050/**
Radek Krejcidd713ce2021-01-04 23:12:12 +010051 * @brief Ignore and skip any characters until the delim of the size delim_len is read, including the delim
Radek Krejci4b74d5e2018-09-26 14:30:55 +020052 *
Radek Krejcidd713ce2021-01-04 23:12:12 +010053 * @param[in] xmlctx XML parser context to provide input handler and libyang context
54 * @param[in] in input handler to read the data, it is updated only in case the section is correctly terminated.
55 * @param[in] delim Delimiter to detect end of the section.
56 * @param[in] delim_len Length of the delimiter string to use.
57 * @param[in] sectname Section name to refer in error message.
Michal Vasko63f3d842020-07-08 10:10:14 +020058 */
Radek Krejcidd713ce2021-01-04 23:12:12 +010059LY_ERR
60skip_section(struct lyxml_ctx *xmlctx, const char *delim, size_t delim_len, const char *sectname)
Radek Krejcid91dbaf2018-09-21 15:51:39 +020061{
62 size_t i;
Radek Krejcidd713ce2021-01-04 23:12:12 +010063 register const char *input, *a, *b;
64 uint64_t parsed = 0, newlines = 0;
Radek Krejcid91dbaf2018-09-21 15:51:39 +020065
Radek Krejcidd713ce2021-01-04 23:12:12 +010066 for (input = xmlctx->in->current; *input; ++input, ++parsed) {
Radek Krejcid91dbaf2018-09-21 15:51:39 +020067 if (*input != *delim) {
Radek Krejcidd713ce2021-01-04 23:12:12 +010068 if (*input == '\n') {
69 ++newlines;
70 }
Radek Krejcid91dbaf2018-09-21 15:51:39 +020071 continue;
72 }
73 a = input;
74 b = delim;
75 for (i = 0; i < delim_len; ++i) {
76 if (*a++ != *b++) {
77 break;
78 }
79 }
80 if (i == delim_len) {
Michal Vasko63f3d842020-07-08 10:10:14 +020081 /* delim found */
Radek Krejcidd713ce2021-01-04 23:12:12 +010082 xmlctx->in->line += newlines;
83 ly_in_skip(xmlctx->in, parsed + delim_len);
84 return LY_SUCCESS;
Radek Krejcid91dbaf2018-09-21 15:51:39 +020085 }
86 }
Michal Vasko63f3d842020-07-08 10:10:14 +020087
Radek Krejcidd713ce2021-01-04 23:12:12 +010088 /* delim not found,
89 * do not update input handler to refer to the beginning of the section in error message */
90 LOGVAL(xmlctx->ctx, LY_VCODE_NTERM, sectname);
91 return LY_EVALID;
Radek Krejcid91dbaf2018-09-21 15:51:39 +020092}
93
Radek Krejci4b74d5e2018-09-26 14:30:55 +020094/**
Michal Vaskob36053d2020-03-26 15:49:30 +010095 * @brief Check/Get an XML identifier from the input string.
96 *
97 * The identifier must have at least one valid character complying the name start character constraints.
98 * The identifier is terminated by the first character, which does not comply to the name character constraints.
99 *
100 * See https://www.w3.org/TR/xml-names/#NT-NCName
101 *
102 * @param[in] xmlctx XML context.
103 * @param[out] start Pointer to the start of the identifier.
104 * @param[out] end Pointer ot the end of the identifier.
105 * @return LY_ERR value.
106 */
107static LY_ERR
108lyxml_parse_identifier(struct lyxml_ctx *xmlctx, const char **start, const char **end)
109{
110 const char *s, *in;
111 uint32_t c;
112 size_t parsed;
113 LY_ERR rc;
114
Michal Vasko63f3d842020-07-08 10:10:14 +0200115 in = s = xmlctx->in->current;
Michal Vaskob36053d2020-03-26 15:49:30 +0100116
117 /* check NameStartChar (minus colon) */
118 LY_CHECK_ERR_RET(ly_getutf8(&in, &c, &parsed),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100119 LOGVAL(xmlctx->ctx, LY_VCODE_INCHAR, in[0]),
Michal Vasko69730152020-10-09 16:30:07 +0200120 LY_EVALID);
Michal Vaskob36053d2020-03-26 15:49:30 +0100121 LY_CHECK_ERR_RET(!is_xmlqnamestartchar(c),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100122 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Identifier \"%s\" starts with an invalid character.", in - parsed),
Michal Vasko69730152020-10-09 16:30:07 +0200123 LY_EVALID);
Michal Vaskob36053d2020-03-26 15:49:30 +0100124
125 /* check rest of the identifier */
126 do {
127 /* move only successfully parsed bytes */
Michal Vasko63f3d842020-07-08 10:10:14 +0200128 ly_in_skip(xmlctx->in, parsed);
Michal Vaskob36053d2020-03-26 15:49:30 +0100129
130 rc = ly_getutf8(&in, &c, &parsed);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100131 LY_CHECK_ERR_RET(rc, LOGVAL(xmlctx->ctx, LY_VCODE_INCHAR, in[0]), LY_EVALID);
Michal Vaskob36053d2020-03-26 15:49:30 +0100132 } while (is_xmlqnamechar(c));
133
134 *start = s;
Michal Vasko63f3d842020-07-08 10:10:14 +0200135 *end = xmlctx->in->current;
Michal Vaskob36053d2020-03-26 15:49:30 +0100136 return LY_SUCCESS;
137}
138
139/**
140 * @brief Add namespace definition into XML context.
141 *
142 * Namespaces from a single element are supposed to be added sequentially together (not interleaved by a namespace from other
143 * element). This mimic namespace visibility, since the namespace defined in element E is not visible from its parents or
144 * siblings. On the other hand, namespace from a parent element can be redefined in a child element. This is also reflected
145 * by lyxml_ns_get() which returns the most recent namespace definition for the given prefix.
146 *
147 * When leaving processing of a subtree of some element (after it is removed from xmlctx->elements), caller is supposed to call
148 * lyxml_ns_rm() to remove all the namespaces defined in such an element from the context.
149 *
150 * @param[in] xmlctx XML context to work with.
151 * @param[in] prefix Pointer to the namespace prefix. Can be NULL for default namespace.
152 * @param[in] prefix_len Length of the prefix.
153 * @param[in] uri Namespace URI (value) to store directly. Value is always spent.
154 * @return LY_ERR values.
155 */
156LY_ERR
157lyxml_ns_add(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, char *uri)
158{
Radek Krejciba03a5a2020-08-27 14:40:41 +0200159 LY_ERR ret = LY_SUCCESS;
Michal Vaskob36053d2020-03-26 15:49:30 +0100160 struct lyxml_ns *ns;
161
162 ns = malloc(sizeof *ns);
163 LY_CHECK_ERR_RET(!ns, LOGMEM(xmlctx->ctx), LY_EMEM);
164
165 /* we need to connect the depth of the element where the namespace is defined with the
166 * namespace record to be able to maintain (remove) the record when the parser leaves
167 * (to its sibling or back to the parent) the element where the namespace was defined */
168 ns->depth = xmlctx->elements.count;
169
170 ns->uri = uri;
171 if (prefix) {
172 ns->prefix = strndup(prefix, prefix_len);
173 LY_CHECK_ERR_RET(!ns->prefix, LOGMEM(xmlctx->ctx); free(ns->uri); free(ns), LY_EMEM);
174 } else {
175 ns->prefix = NULL;
176 }
177
Radek Krejci3d92e442020-10-12 12:48:13 +0200178 ret = ly_set_add(&xmlctx->ns, ns, 1, NULL);
Radek Krejciba03a5a2020-08-27 14:40:41 +0200179 LY_CHECK_ERR_RET(ret, free(ns->prefix); free(ns->uri); free(ns), ret);
180
Michal Vaskob36053d2020-03-26 15:49:30 +0100181 return LY_SUCCESS;
182}
183
Michal Vaskob36053d2020-03-26 15:49:30 +0100184void
185lyxml_ns_rm(struct lyxml_ctx *xmlctx)
186{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200187 for (uint32_t u = xmlctx->ns.count - 1; u + 1 > 0; --u) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100188 if (((struct lyxml_ns *)xmlctx->ns.objs[u])->depth != xmlctx->elements.count + 1) {
189 /* we are done, the namespaces from a single element are supposed to be together */
190 break;
191 }
192 /* remove the ns structure */
193 free(((struct lyxml_ns *)xmlctx->ns.objs[u])->prefix);
194 free(((struct lyxml_ns *)xmlctx->ns.objs[u])->uri);
195 free(xmlctx->ns.objs[u]);
196 --xmlctx->ns.count;
197 }
198
199 if (!xmlctx->ns.count) {
200 /* cleanup the xmlctx's namespaces storage */
201 ly_set_erase(&xmlctx->ns, NULL);
202 }
203}
204
Michal Vaskob36053d2020-03-26 15:49:30 +0100205const struct lyxml_ns *
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200206lyxml_ns_get(const struct ly_set *ns_set, const char *prefix, size_t prefix_len)
Michal Vaskob36053d2020-03-26 15:49:30 +0100207{
Michal Vaskob36053d2020-03-26 15:49:30 +0100208 struct lyxml_ns *ns;
209
Radek Krejci1deb5be2020-08-26 16:43:36 +0200210 for (uint32_t u = ns_set->count - 1; u + 1 > 0; --u) {
Michal Vaskoc8a230d2020-08-14 12:17:10 +0200211 ns = (struct lyxml_ns *)ns_set->objs[u];
Michal Vaskob36053d2020-03-26 15:49:30 +0100212 if (prefix && prefix_len) {
213 if (ns->prefix && !ly_strncmp(ns->prefix, prefix, prefix_len)) {
214 return ns;
215 }
216 } else if (!ns->prefix) {
217 /* default namespace */
218 return ns;
219 }
220 }
221
222 return NULL;
223}
224
Michal Vasko8cef5232020-06-15 17:59:47 +0200225/**
226 * @brief Skip in the input until EOF or just after the opening tag.
227 * Handles special XML constructs (comment, cdata, doctype).
228 *
229 * @param[in] xmlctx XML context to use.
230 * @return LY_ERR value.
231 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100232static LY_ERR
233lyxml_skip_until_end_or_after_otag(struct lyxml_ctx *xmlctx)
234{
235 const struct ly_ctx *ctx = xmlctx->ctx; /* shortcut */
Michal Vasko63f3d842020-07-08 10:10:14 +0200236 const char *endtag, *sectname;
Radek Krejcidd713ce2021-01-04 23:12:12 +0100237 size_t endtag_len;
Michal Vaskob36053d2020-03-26 15:49:30 +0100238
239 while (1) {
240 ign_xmlws(xmlctx);
241
Michal Vasko63f3d842020-07-08 10:10:14 +0200242 if (xmlctx->in->current[0] == '\0') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100243 /* EOF */
244 if (xmlctx->elements.count) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100245 LOGVAL(ctx, LY_VCODE_EOF);
Michal Vaskob36053d2020-03-26 15:49:30 +0100246 return LY_EVALID;
247 }
248 return LY_SUCCESS;
Michal Vasko63f3d842020-07-08 10:10:14 +0200249 } else if (xmlctx->in->current[0] != '<') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100250 LOGVAL(ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200251 xmlctx->in->current, "element tag start ('<')");
Michal Vaskob36053d2020-03-26 15:49:30 +0100252 return LY_EVALID;
253 }
254 move_input(xmlctx, 1);
255
Michal Vasko63f3d842020-07-08 10:10:14 +0200256 if (xmlctx->in->current[0] == '!') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100257 move_input(xmlctx, 1);
258 /* sections to ignore */
Michal Vasko63f3d842020-07-08 10:10:14 +0200259 if (!strncmp(xmlctx->in->current, "--", 2)) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100260 /* comment */
261 move_input(xmlctx, 2);
262 sectname = "Comment";
263 endtag = "-->";
Radek Krejcif13b87b2020-12-01 22:02:17 +0100264 endtag_len = ly_strlen_const("-->");
265 } else if (!strncmp(xmlctx->in->current, "[CDATA[", ly_strlen_const("[CDATA["))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100266 /* CDATA section */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100267 move_input(xmlctx, ly_strlen_const("[CDATA["));
Michal Vaskob36053d2020-03-26 15:49:30 +0100268 sectname = "CData";
269 endtag = "]]>";
Radek Krejcif13b87b2020-12-01 22:02:17 +0100270 endtag_len = ly_strlen_const("]]>");
271 } else if (!strncmp(xmlctx->in->current, "DOCTYPE", ly_strlen_const("DOCTYPE"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100272 /* Document type declaration - not supported */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100273 LOGVAL(ctx, LY_VCODE_NSUPP, "Document Type Declaration");
Michal Vaskob36053d2020-03-26 15:49:30 +0100274 return LY_EVALID;
275 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100276 LOGVAL(ctx, LYVE_SYNTAX, "Unknown XML section \"%.20s\".", &xmlctx->in->current[-2]);
Michal Vaskob36053d2020-03-26 15:49:30 +0100277 return LY_EVALID;
278 }
Radek Krejcidd713ce2021-01-04 23:12:12 +0100279 LY_CHECK_RET(skip_section(xmlctx, endtag, endtag_len, sectname));
Michal Vasko63f3d842020-07-08 10:10:14 +0200280 } else if (xmlctx->in->current[0] == '?') {
Radek Krejcidd713ce2021-01-04 23:12:12 +0100281 LY_CHECK_RET(skip_section(xmlctx, "?>", 2, "Declaration"));
Michal Vaskob36053d2020-03-26 15:49:30 +0100282 } else {
283 /* other non-WS character */
284 break;
285 }
286 }
287
288 return LY_SUCCESS;
289}
290
Michal Vasko8cef5232020-06-15 17:59:47 +0200291/**
292 * @brief Parse QName.
293 *
294 * @param[in] xmlctx XML context to use.
295 * @param[out] prefix Parsed prefix, may be NULL.
296 * @param[out] prefix_len Length of @p prefix.
297 * @param[out] name Parsed name.
298 * @param[out] name_len Length of @p name.
299 * @return LY_ERR value.
300 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100301static LY_ERR
302lyxml_parse_qname(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
303{
304 const char *start, *end;
305
306 *prefix = NULL;
307 *prefix_len = 0;
308
309 LY_CHECK_RET(lyxml_parse_identifier(xmlctx, &start, &end));
310 if (end[0] == ':') {
311 /* we have prefixed identifier */
312 *prefix = start;
313 *prefix_len = end - start;
314
315 move_input(xmlctx, 1);
316 LY_CHECK_RET(lyxml_parse_identifier(xmlctx, &start, &end));
317 }
318
319 *name = start;
320 *name_len = end - start;
321 return LY_SUCCESS;
322}
323
324/**
Michal Vasko8cef5232020-06-15 17:59:47 +0200325 * @brief Parse XML text content (value).
326 *
327 * @param[in] xmlctx XML context to use.
328 * @param[in] endchar Expected character to mark value end.
329 * @param[out] value Parsed value.
330 * @param[out] length Length of @p value.
331 * @param[out] ws_only Whether the value is empty/white-spaces only.
332 * @param[out] dynamic Whether the value was dynamically allocated.
333 * @return LY_ERR value.
334 */
Radek Krejci4b74d5e2018-09-26 14:30:55 +0200335static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200336lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *length, ly_bool *ws_only, ly_bool *dynamic)
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200337{
Michal Vaskob36053d2020-03-26 15:49:30 +0100338#define BUFSIZE 24
339#define BUFSIZE_STEP 128
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200340
Michal Vaskob36053d2020-03-26 15:49:30 +0100341 const struct ly_ctx *ctx = xmlctx->ctx; /* shortcut */
aPiecekb287b212021-05-04 14:24:25 +0200342 const char *in = xmlctx->in->current, *start, *in_aux;
Michal Vaskob36053d2020-03-26 15:49:30 +0100343 char *buf = NULL;
Radek Krejci4ad42aa2019-07-23 16:55:58 +0200344 size_t offset; /* read offset in input buffer */
345 size_t len; /* length of the output string (write offset in output buffer) */
346 size_t size = 0; /* size of the output buffer */
Radek Krejci7a7fa902018-09-25 17:08:21 +0200347 void *p;
Radek Krejci117d2082018-09-26 10:05:14 +0200348 uint32_t n;
Michal Vaskob36053d2020-03-26 15:49:30 +0100349 size_t u;
Radek Krejci857189e2020-09-01 13:26:36 +0200350 ly_bool ws = 1;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200351
Michal Vaskob36053d2020-03-26 15:49:30 +0100352 assert(xmlctx);
Radek Krejcib1890642018-10-03 14:05:40 +0200353
Radek Krejcid70d1072018-10-09 14:20:47 +0200354 /* init */
Michal Vaskob36053d2020-03-26 15:49:30 +0100355 start = in;
Radek Krejcid70d1072018-10-09 14:20:47 +0200356 offset = len = 0;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200357
358 /* parse */
359 while (in[offset]) {
360 if (in[offset] == '&') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100361 /* non WS */
362 ws = 0;
Radek Krejcid70d1072018-10-09 14:20:47 +0200363
Michal Vaskob36053d2020-03-26 15:49:30 +0100364 if (!buf) {
365 /* prepare output buffer */
366 buf = malloc(BUFSIZE);
367 LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
368 size = BUFSIZE;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200369 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100370
371 /* allocate enough for the offset and next character,
372 * we will need 4 bytes at most since we support only the predefined
373 * (one-char) entities and character references */
Juraj Vijtiukcb017cc2020-07-08 16:19:58 +0200374 while (len + offset + 4 >= size) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100375 buf = ly_realloc(buf, size + BUFSIZE_STEP);
376 LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
377 size += BUFSIZE_STEP;
378 }
379
380 if (offset) {
381 /* store what we have so far */
382 memcpy(&buf[len], in, offset);
383 len += offset;
384 in += offset;
385 offset = 0;
386 }
387
Radek Krejci7a7fa902018-09-25 17:08:21 +0200388 ++offset;
389 if (in[offset] != '#') {
390 /* entity reference - only predefined references are supported */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100391 if (!strncmp(&in[offset], "lt;", ly_strlen_const("lt;"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100392 buf[len++] = '<';
Radek Krejcif13b87b2020-12-01 22:02:17 +0100393 in += ly_strlen_const("&lt;");
394 } else if (!strncmp(&in[offset], "gt;", ly_strlen_const("gt;"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100395 buf[len++] = '>';
Radek Krejcif13b87b2020-12-01 22:02:17 +0100396 in += ly_strlen_const("&gt;");
397 } else if (!strncmp(&in[offset], "amp;", ly_strlen_const("amp;"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100398 buf[len++] = '&';
Radek Krejcif13b87b2020-12-01 22:02:17 +0100399 in += ly_strlen_const("&amp;");
400 } else if (!strncmp(&in[offset], "apos;", ly_strlen_const("apos;"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100401 buf[len++] = '\'';
Radek Krejcif13b87b2020-12-01 22:02:17 +0100402 in += ly_strlen_const("&apos;");
403 } else if (!strncmp(&in[offset], "quot;", ly_strlen_const("quot;"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100404 buf[len++] = '\"';
Radek Krejcif13b87b2020-12-01 22:02:17 +0100405 in += ly_strlen_const("&quot;");
Radek Krejci7a7fa902018-09-25 17:08:21 +0200406 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100407 LOGVAL(ctx, LYVE_SYNTAX, "Entity reference \"%.*s\" not supported, only predefined references allowed.",
408 10, &in[offset - 1]);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200409 goto error;
410 }
411 offset = 0;
412 } else {
Michal Vaskob36053d2020-03-26 15:49:30 +0100413 p = (void *)&in[offset - 1];
Radek Krejci7a7fa902018-09-25 17:08:21 +0200414 /* character reference */
415 ++offset;
416 if (isdigit(in[offset])) {
417 for (n = 0; isdigit(in[offset]); offset++) {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100418 n = (LY_BASE_DEC * n) + (in[offset] - '0');
Radek Krejci7a7fa902018-09-25 17:08:21 +0200419 }
Michal Vasko69730152020-10-09 16:30:07 +0200420 } else if ((in[offset] == 'x') && isxdigit(in[offset + 1])) {
Radek Krejci7a7fa902018-09-25 17:08:21 +0200421 for (n = 0, ++offset; isxdigit(in[offset]); offset++) {
422 if (isdigit(in[offset])) {
423 u = (in[offset] - '0');
424 } else if (in[offset] > 'F') {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100425 u = LY_BASE_DEC + (in[offset] - 'a');
Radek Krejci7a7fa902018-09-25 17:08:21 +0200426 } else {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100427 u = LY_BASE_DEC + (in[offset] - 'A');
Radek Krejci7a7fa902018-09-25 17:08:21 +0200428 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100429 n = (LY_BASE_HEX * n) + u;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200430 }
431 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100432 LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\".", 12, p);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200433 goto error;
434
435 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100436
Radek Krejci7a7fa902018-09-25 17:08:21 +0200437 LY_CHECK_ERR_GOTO(in[offset] != ';',
Radek Krejci2efc45b2020-12-22 16:25:44 +0100438 LOGVAL(ctx, LY_VCODE_INSTREXP,
Michal Vasko69730152020-10-09 16:30:07 +0200439 LY_VCODE_INSTREXP_len(&in[offset]), &in[offset], ";"),
440 error);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200441 ++offset;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200442 LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], n, &u),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100443 LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).", 12, p, n),
Michal Vasko69730152020-10-09 16:30:07 +0200444 error);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200445 len += u;
446 in += offset;
447 offset = 0;
448 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100449 } else if (in[offset] == endchar) {
Radek Krejci7a7fa902018-09-25 17:08:21 +0200450 /* end of string */
Radek Krejcid70d1072018-10-09 14:20:47 +0200451 if (buf) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100452 /* realloc exact size string */
453 buf = ly_realloc(buf, len + offset + 1);
454 LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
455 size = len + offset + 1;
Michal Vasko08e9b112021-06-11 15:41:17 +0200456 if (offset) {
457 memcpy(&buf[len], in, offset);
458 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100459
460 /* set terminating NULL byte */
461 buf[len + offset] = '\0';
Radek Krejci7a7fa902018-09-25 17:08:21 +0200462 }
Radek Krejci7a7fa902018-09-25 17:08:21 +0200463 len += offset;
Michal Vaskob36053d2020-03-26 15:49:30 +0100464 in += offset;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200465 goto success;
466 } else {
Michal Vaskob36053d2020-03-26 15:49:30 +0100467 if (!is_xmlws(in[offset])) {
468 /* non WS */
469 ws = 0;
470 }
471
Radek Krejci7a7fa902018-09-25 17:08:21 +0200472 /* log lines */
473 if (in[offset] == '\n') {
Radek Krejcid54412f2020-12-17 20:25:35 +0100474 LY_IN_NEW_LINE(xmlctx->in);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200475 }
476
477 /* continue */
aPiecekb287b212021-05-04 14:24:25 +0200478 in_aux = &in[offset];
479 LY_CHECK_ERR_GOTO(ly_getutf8(&in_aux, &n, &u),
480 LOGVAL(ctx, LY_VCODE_INCHAR, in[offset]), error);
481 offset += u;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200482 }
483 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100484
485 /* EOF reached before endchar */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100486 LOGVAL(ctx, LY_VCODE_EOF);
Michal Vaskob36053d2020-03-26 15:49:30 +0100487
Radek Krejci7a7fa902018-09-25 17:08:21 +0200488error:
Michal Vaskob36053d2020-03-26 15:49:30 +0100489 free(buf);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200490 return LY_EVALID;
491
492success:
Radek Krejcid70d1072018-10-09 14:20:47 +0200493 if (buf) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100494 *value = buf;
495 *dynamic = 1;
496 } else {
497 *value = (char *)start;
498 *dynamic = 0;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200499 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100500 *length = len;
501 *ws_only = ws;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200502
Radek Krejcid54412f2020-12-17 20:25:35 +0100503 xmlctx->in->current = in;
Michal Vaskob36053d2020-03-26 15:49:30 +0100504 return LY_SUCCESS;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200505
506#undef BUFSIZE
507#undef BUFSIZE_STEP
Radek Krejci7a7fa902018-09-25 17:08:21 +0200508}
509
Michal Vasko8cef5232020-06-15 17:59:47 +0200510/**
511 * @brief Parse XML closing element and match it to a stored starting element.
512 *
513 * @param[in] xmlctx XML context to use.
514 * @param[in] prefix Expected closing element prefix.
515 * @param[in] prefix_len Length of @p prefix.
516 * @param[in] name Expected closing element name.
517 * @param[in] name_len Length of @p name.
518 * @param[in] empty Whether we are parsing a special "empty" element (with joined starting and closing tag) with no value.
519 * @return LY_ERR value.
520 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100521static LY_ERR
522lyxml_close_element(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, const char *name, size_t name_len,
Radek Krejci857189e2020-09-01 13:26:36 +0200523 ly_bool empty)
Radek Krejcid972c252018-09-25 13:23:39 +0200524{
Michal Vaskob36053d2020-03-26 15:49:30 +0100525 struct lyxml_elem *e;
Radek Krejcid972c252018-09-25 13:23:39 +0200526
Michal Vaskob36053d2020-03-26 15:49:30 +0100527 /* match opening and closing element tags */
528 if (!xmlctx->elements.count) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100529 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Stray closing element tag (\"%.*s\").",
Radek Krejci422afb12021-03-04 16:38:16 +0100530 (int)name_len, name);
Michal Vaskob36053d2020-03-26 15:49:30 +0100531 return LY_EVALID;
532 }
Radek Krejcid972c252018-09-25 13:23:39 +0200533
Michal Vaskob36053d2020-03-26 15:49:30 +0100534 e = (struct lyxml_elem *)xmlctx->elements.objs[xmlctx->elements.count - 1];
Michal Vasko69730152020-10-09 16:30:07 +0200535 if ((e->prefix_len != prefix_len) || (e->name_len != name_len) ||
536 (prefix_len && strncmp(prefix, e->prefix, e->prefix_len)) || strncmp(name, e->name, e->name_len)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100537 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Opening (\"%.*s%s%.*s\") and closing (\"%.*s%s%.*s\") elements tag mismatch.",
Radek Krejci422afb12021-03-04 16:38:16 +0100538 (int)e->prefix_len, e->prefix ? e->prefix : "", e->prefix ? ":" : "", (int)e->name_len, e->name,
539 (int)prefix_len, prefix ? prefix : "", prefix ? ":" : "", (int)name_len, name);
Michal Vaskob36053d2020-03-26 15:49:30 +0100540 return LY_EVALID;
541 }
Radek Krejcid972c252018-09-25 13:23:39 +0200542
Michal Vaskob36053d2020-03-26 15:49:30 +0100543 /* opening and closing element tags matches, remove record from the opening tags list */
544 ly_set_rm_index(&xmlctx->elements, xmlctx->elements.count - 1, free);
Radek Krejcid972c252018-09-25 13:23:39 +0200545
Michal Vaskob36053d2020-03-26 15:49:30 +0100546 /* remove also the namespaces connected with the element */
547 lyxml_ns_rm(xmlctx);
Radek Krejcid972c252018-09-25 13:23:39 +0200548
Michal Vaskob36053d2020-03-26 15:49:30 +0100549 /* skip WS */
550 ign_xmlws(xmlctx);
Radek Krejcid972c252018-09-25 13:23:39 +0200551
Michal Vaskob36053d2020-03-26 15:49:30 +0100552 /* special "<elem/>" element */
Michal Vasko63f3d842020-07-08 10:10:14 +0200553 if (empty && (xmlctx->in->current[0] == '/')) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100554 move_input(xmlctx, 1);
555 }
Michal Vasko52927e22020-03-16 17:26:14 +0100556
Michal Vaskob36053d2020-03-26 15:49:30 +0100557 /* parse closing tag */
Michal Vasko63f3d842020-07-08 10:10:14 +0200558 if (xmlctx->in->current[0] != '>') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100559 LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200560 xmlctx->in->current, "element tag termination ('>')");
Michal Vaskob36053d2020-03-26 15:49:30 +0100561 return LY_EVALID;
562 }
Michal Vasko52927e22020-03-16 17:26:14 +0100563
Michal Vaskob36053d2020-03-26 15:49:30 +0100564 /* move after closing tag without checking for EOF */
Michal Vasko63f3d842020-07-08 10:10:14 +0200565 ly_in_skip(xmlctx->in, 1);
Michal Vasko52927e22020-03-16 17:26:14 +0100566
Radek Krejcid972c252018-09-25 13:23:39 +0200567 return LY_SUCCESS;
568}
569
Michal Vasko8cef5232020-06-15 17:59:47 +0200570/**
571 * @brief Store parsed opening element and parse any included namespaces.
572 *
573 * @param[in] xmlctx XML context to use.
574 * @param[in] prefix Parsed starting element prefix.
575 * @param[in] prefix_len Length of @p prefix.
576 * @param[in] name Parsed starting element name.
577 * @param[in] name_len Length of @p name.
578 * @return LY_ERR value.
579 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100580static LY_ERR
581lyxml_open_element(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, const char *name, size_t name_len)
Radek Krejcib1890642018-10-03 14:05:40 +0200582{
Michal Vaskob36053d2020-03-26 15:49:30 +0100583 LY_ERR ret = LY_SUCCESS;
584 struct lyxml_elem *e;
585 const char *prev_input;
Michal Vasko4fd91922021-09-15 08:51:06 +0200586 uint64_t prev_line;
Michal Vaskob36053d2020-03-26 15:49:30 +0100587 char *value;
588 size_t parsed, value_len;
Radek Krejci857189e2020-09-01 13:26:36 +0200589 ly_bool ws_only, dynamic, is_ns;
Michal Vaskob36053d2020-03-26 15:49:30 +0100590 uint32_t c;
Radek Krejcib1890642018-10-03 14:05:40 +0200591
Michal Vaskob36053d2020-03-26 15:49:30 +0100592 /* store element opening tag information */
593 e = malloc(sizeof *e);
594 LY_CHECK_ERR_RET(!e, LOGMEM(xmlctx->ctx), LY_EMEM);
595 e->name = name;
596 e->prefix = prefix;
597 e->name_len = name_len;
598 e->prefix_len = prefix_len;
aPiecek93582ed2021-05-25 14:49:06 +0200599
Radek Krejci3d92e442020-10-12 12:48:13 +0200600 LY_CHECK_RET(ly_set_add(&xmlctx->elements, e, 1, NULL));
aPiecek93582ed2021-05-25 14:49:06 +0200601 if (xmlctx->elements.count > LY_MAX_BLOCK_DEPTH) {
Michal Vasko4fd91922021-09-15 08:51:06 +0200602 LOGERR(xmlctx->ctx, LY_EINVAL, "The maximum number of open elements has been exceeded.");
aPiecek93582ed2021-05-25 14:49:06 +0200603 ret = LY_EINVAL;
604 goto cleanup;
605 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100606
607 /* skip WS */
608 ign_xmlws(xmlctx);
609
610 /* parse and store all namespaces */
Michal Vasko63f3d842020-07-08 10:10:14 +0200611 prev_input = xmlctx->in->current;
Michal Vasko4fd91922021-09-15 08:51:06 +0200612 prev_line = xmlctx->in->line;
Michal Vaskob36053d2020-03-26 15:49:30 +0100613 is_ns = 1;
aPiecek785ad3d2021-05-10 15:51:13 +0200614 while ((xmlctx->in->current[0] != '\0') && !(ret = ly_getutf8(&xmlctx->in->current, &c, &parsed))) {
615 if (!is_xmlqnamestartchar(c)) {
616 break;
617 }
Michal Vasko63f3d842020-07-08 10:10:14 +0200618 xmlctx->in->current -= parsed;
Michal Vaskob36053d2020-03-26 15:49:30 +0100619
620 /* parse attribute name */
621 LY_CHECK_GOTO(ret = lyxml_parse_qname(xmlctx, &prefix, &prefix_len, &name, &name_len), cleanup);
622
623 /* parse the value */
624 LY_CHECK_GOTO(ret = lyxml_next_attr_content(xmlctx, (const char **)&value, &value_len, &ws_only, &dynamic), cleanup);
625
626 /* store every namespace */
627 if ((prefix && !ly_strncmp("xmlns", prefix, prefix_len)) || (!prefix && !ly_strncmp("xmlns", name, name_len))) {
Radek IÅ¡a017270d2021-02-16 10:26:15 +0100628 ret = lyxml_ns_add(xmlctx, prefix ? name : NULL, prefix ? name_len : 0,
629 dynamic ? value : strndup(value, value_len));
Michal Vaskob36053d2020-03-26 15:49:30 +0100630 dynamic = 0;
Radek IÅ¡a017270d2021-02-16 10:26:15 +0100631 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100632 } else {
633 /* not a namespace */
634 is_ns = 0;
635 }
636 if (dynamic) {
637 free(value);
638 }
639
640 /* skip WS */
641 ign_xmlws(xmlctx);
642
643 if (is_ns) {
644 /* we can actually skip all the namespaces as there is no reason to parse them again */
Michal Vasko63f3d842020-07-08 10:10:14 +0200645 prev_input = xmlctx->in->current;
Michal Vasko4fd91922021-09-15 08:51:06 +0200646 prev_line = xmlctx->in->line;
Michal Vaskob36053d2020-03-26 15:49:30 +0100647 }
Radek Krejcib1890642018-10-03 14:05:40 +0200648 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100649
650cleanup:
651 if (!ret) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200652 xmlctx->in->current = prev_input;
Michal Vasko4fd91922021-09-15 08:51:06 +0200653 xmlctx->in->line = prev_line;
Michal Vaskob36053d2020-03-26 15:49:30 +0100654 }
655 return ret;
656}
657
Michal Vasko8cef5232020-06-15 17:59:47 +0200658/**
659 * @brief Move parser to the attribute content and parse it.
660 *
661 * @param[in] xmlctx XML context to use.
662 * @param[out] value Parsed attribute value.
663 * @param[out] value_len Length of @p value.
664 * @param[out] ws_only Whether the value is empty/white-spaces only.
665 * @param[out] dynamic Whether the value was dynamically allocated.
666 * @return LY_ERR value.
667 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100668static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200669lyxml_next_attr_content(struct lyxml_ctx *xmlctx, const char **value, size_t *value_len, ly_bool *ws_only, ly_bool *dynamic)
Michal Vaskob36053d2020-03-26 15:49:30 +0100670{
671 char quot;
672
673 /* skip WS */
674 ign_xmlws(xmlctx);
675
676 /* skip '=' */
Michal Vasko63f3d842020-07-08 10:10:14 +0200677 if (xmlctx->in->current[0] == '\0') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100678 LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
Michal Vaskob36053d2020-03-26 15:49:30 +0100679 return LY_EVALID;
Michal Vasko63f3d842020-07-08 10:10:14 +0200680 } else if (xmlctx->in->current[0] != '=') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100681 LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200682 xmlctx->in->current, "'='");
Michal Vaskob36053d2020-03-26 15:49:30 +0100683 return LY_EVALID;
684 }
685 move_input(xmlctx, 1);
686
687 /* skip WS */
688 ign_xmlws(xmlctx);
689
690 /* find quotes */
Michal Vasko63f3d842020-07-08 10:10:14 +0200691 if (xmlctx->in->current[0] == '\0') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100692 LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
Michal Vaskob36053d2020-03-26 15:49:30 +0100693 return LY_EVALID;
Michal Vasko63f3d842020-07-08 10:10:14 +0200694 } else if ((xmlctx->in->current[0] != '\'') && (xmlctx->in->current[0] != '\"')) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100695 LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200696 xmlctx->in->current, "either single or double quotation mark");
Michal Vaskob36053d2020-03-26 15:49:30 +0100697 return LY_EVALID;
698 }
699
700 /* remember quote */
Michal Vasko63f3d842020-07-08 10:10:14 +0200701 quot = xmlctx->in->current[0];
Michal Vaskob36053d2020-03-26 15:49:30 +0100702 move_input(xmlctx, 1);
703
704 /* parse attribute value */
705 LY_CHECK_RET(lyxml_parse_value(xmlctx, quot, (char **)value, value_len, ws_only, dynamic));
706
707 /* move after ending quote (without checking for EOF) */
Michal Vasko63f3d842020-07-08 10:10:14 +0200708 ly_in_skip(xmlctx->in, 1);
Michal Vaskob36053d2020-03-26 15:49:30 +0100709
710 return LY_SUCCESS;
711}
712
Michal Vasko8cef5232020-06-15 17:59:47 +0200713/**
714 * @brief Move parser to the next attribute and parse it.
715 *
716 * @param[in] xmlctx XML context to use.
717 * @param[out] prefix Parsed attribute prefix.
718 * @param[out] prefix_len Length of @p prefix.
719 * @param[out] name Parsed attribute name.
720 * @param[out] name_len Length of @p name.
721 * @return LY_ERR value.
722 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100723static LY_ERR
724lyxml_next_attribute(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
725{
726 const char *in;
727 char *value;
728 uint32_t c;
729 size_t parsed, value_len;
Radek Krejci857189e2020-09-01 13:26:36 +0200730 ly_bool ws_only, dynamic;
Michal Vaskob36053d2020-03-26 15:49:30 +0100731
732 /* skip WS */
733 ign_xmlws(xmlctx);
734
735 /* parse only possible attributes */
Michal Vasko63f3d842020-07-08 10:10:14 +0200736 while ((xmlctx->in->current[0] != '>') && (xmlctx->in->current[0] != '/')) {
737 in = xmlctx->in->current;
Michal Vaskob36053d2020-03-26 15:49:30 +0100738 if (in[0] == '\0') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100739 LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
Michal Vaskob36053d2020-03-26 15:49:30 +0100740 return LY_EVALID;
741 } else if ((ly_getutf8(&in, &c, &parsed) || !is_xmlqnamestartchar(c))) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100742 LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(in - parsed), in - parsed,
Michal Vasko69730152020-10-09 16:30:07 +0200743 "element tag end ('>' or '/>') or an attribute");
Michal Vaskob36053d2020-03-26 15:49:30 +0100744 return LY_EVALID;
745 }
746
747 /* parse attribute name */
748 LY_CHECK_RET(lyxml_parse_qname(xmlctx, prefix, prefix_len, name, name_len));
749
750 if ((!*prefix || ly_strncmp("xmlns", *prefix, *prefix_len)) && (*prefix || ly_strncmp("xmlns", *name, *name_len))) {
751 /* standard attribute */
752 break;
753 }
754
755 /* namespace, skip it */
756 LY_CHECK_RET(lyxml_next_attr_content(xmlctx, (const char **)&value, &value_len, &ws_only, &dynamic));
757 if (dynamic) {
758 free(value);
759 }
760
761 /* skip WS */
762 ign_xmlws(xmlctx);
763 }
764
765 return LY_SUCCESS;
766}
767
Michal Vasko8cef5232020-06-15 17:59:47 +0200768/**
769 * @brief Move parser to the next element and parse it.
770 *
771 * @param[in] xmlctx XML context to use.
772 * @param[out] prefix Parsed element prefix.
773 * @param[out] prefix_len Length of @p prefix.
774 * @param[out] name Parse element name.
775 * @param[out] name_len Length of @p name.
Radek Krejci1deb5be2020-08-26 16:43:36 +0200776 * @param[out] closing Flag if the element is closing (includes '/').
Michal Vasko8cef5232020-06-15 17:59:47 +0200777 * @return LY_ERR value.
778 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100779static LY_ERR
780lyxml_next_element(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len,
Radek Krejci857189e2020-09-01 13:26:36 +0200781 ly_bool *closing)
Michal Vaskob36053d2020-03-26 15:49:30 +0100782{
783 /* skip WS until EOF or after opening tag '<' */
784 LY_CHECK_RET(lyxml_skip_until_end_or_after_otag(xmlctx));
Michal Vasko63f3d842020-07-08 10:10:14 +0200785 if (xmlctx->in->current[0] == '\0') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100786 /* set return values */
787 *prefix = *name = NULL;
788 *prefix_len = *name_len = 0;
789 return LY_SUCCESS;
790 }
791
Michal Vasko63f3d842020-07-08 10:10:14 +0200792 if (xmlctx->in->current[0] == '/') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100793 move_input(xmlctx, 1);
794 *closing = 1;
795 } else {
796 *closing = 0;
797 }
798
799 /* skip WS */
800 ign_xmlws(xmlctx);
801
802 /* parse element name */
803 LY_CHECK_RET(lyxml_parse_qname(xmlctx, prefix, prefix_len, name, name_len));
804
805 return LY_SUCCESS;
806}
807
808LY_ERR
Michal Vasko63f3d842020-07-08 10:10:14 +0200809lyxml_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyxml_ctx **xmlctx_p)
Michal Vaskob36053d2020-03-26 15:49:30 +0100810{
811 LY_ERR ret = LY_SUCCESS;
812 struct lyxml_ctx *xmlctx;
Radek Krejci857189e2020-09-01 13:26:36 +0200813 ly_bool closing;
Michal Vaskob36053d2020-03-26 15:49:30 +0100814
815 /* new context */
816 xmlctx = calloc(1, sizeof *xmlctx);
817 LY_CHECK_ERR_RET(!xmlctx, LOGMEM(ctx), LY_EMEM);
818 xmlctx->ctx = ctx;
Michal Vasko63f3d842020-07-08 10:10:14 +0200819 xmlctx->in = in;
Michal Vaskob36053d2020-03-26 15:49:30 +0100820
Radek Krejciddace2c2021-01-08 11:30:56 +0100821 LOG_LOCINIT(NULL, NULL, NULL, in);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100822
Michal Vaskob36053d2020-03-26 15:49:30 +0100823 /* parse next element, if any */
824 LY_CHECK_GOTO(ret = lyxml_next_element(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name,
Michal Vasko69730152020-10-09 16:30:07 +0200825 &xmlctx->name_len, &closing), cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100826
Michal Vasko63f3d842020-07-08 10:10:14 +0200827 if (xmlctx->in->current[0] == '\0') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100828 /* update status */
829 xmlctx->status = LYXML_END;
830 } else if (closing) {
Radek Krejci422afb12021-03-04 16:38:16 +0100831 LOGVAL(ctx, LYVE_SYNTAX, "Stray closing element tag (\"%.*s\").", (int)xmlctx->name_len, xmlctx->name);
Michal Vaskob36053d2020-03-26 15:49:30 +0100832 ret = LY_EVALID;
833 goto cleanup;
834 } else {
835 /* open an element, also parses all enclosed namespaces */
836 LY_CHECK_GOTO(ret = lyxml_open_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len), cleanup);
837
838 /* update status */
839 xmlctx->status = LYXML_ELEMENT;
840 }
841
842cleanup:
843 if (ret) {
844 lyxml_ctx_free(xmlctx);
845 } else {
846 *xmlctx_p = xmlctx;
847 }
848 return ret;
849}
850
851LY_ERR
852lyxml_ctx_next(struct lyxml_ctx *xmlctx)
853{
854 LY_ERR ret = LY_SUCCESS;
Radek Krejci857189e2020-09-01 13:26:36 +0200855 ly_bool closing;
Michal Vaskob36053d2020-03-26 15:49:30 +0100856 struct lyxml_elem *e;
857
858 /* if the value was not used, free it */
859 if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
860 free((char *)xmlctx->value);
861 xmlctx->value = NULL;
862 xmlctx->dynamic = 0;
863 }
864
865 switch (xmlctx->status) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100866 case LYXML_ELEM_CONTENT:
Radek Krejcif13b87b2020-12-01 22:02:17 +0100867 /* content |</elem> */
868
Michal Vaskob36053d2020-03-26 15:49:30 +0100869 /* handle special case when empty content for "<elem/>" was returned */
Michal Vasko63f3d842020-07-08 10:10:14 +0200870 if (xmlctx->in->current[0] == '/') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100871 assert(xmlctx->elements.count);
872 e = (struct lyxml_elem *)xmlctx->elements.objs[xmlctx->elements.count - 1];
873
874 /* close the element (parses closing tag) */
Michal Vasko63f3d842020-07-08 10:10:14 +0200875 ret = lyxml_close_element(xmlctx, e->prefix, e->prefix_len, e->name, e->name_len, 1);
876 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100877
878 /* update status */
879 xmlctx->status = LYXML_ELEM_CLOSE;
880 break;
881 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100882 /* fall through */
Michal Vaskob36053d2020-03-26 15:49:30 +0100883 case LYXML_ELEM_CLOSE:
Radek Krejcif13b87b2020-12-01 22:02:17 +0100884 /* </elem>| <elem2>* */
885
Michal Vaskob36053d2020-03-26 15:49:30 +0100886 /* parse next element, if any */
Michal Vasko63f3d842020-07-08 10:10:14 +0200887 ret = lyxml_next_element(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name, &xmlctx->name_len, &closing);
888 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100889
Michal Vasko63f3d842020-07-08 10:10:14 +0200890 if (xmlctx->in->current[0] == '\0') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100891 /* update status */
892 xmlctx->status = LYXML_END;
893 } else if (closing) {
894 /* close an element (parses also closing tag) */
Michal Vasko63f3d842020-07-08 10:10:14 +0200895 ret = lyxml_close_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len, 0);
896 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100897
898 /* update status */
899 xmlctx->status = LYXML_ELEM_CLOSE;
900 } else {
901 /* open an element, also parses all enclosed namespaces */
Michal Vasko63f3d842020-07-08 10:10:14 +0200902 ret = lyxml_open_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len);
903 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100904
905 /* update status */
906 xmlctx->status = LYXML_ELEMENT;
907 }
908 break;
909
Michal Vaskob36053d2020-03-26 15:49:30 +0100910 case LYXML_ELEMENT:
Radek Krejcif13b87b2020-12-01 22:02:17 +0100911 /* <elem| attr='val'* > content */
Michal Vaskob36053d2020-03-26 15:49:30 +0100912 case LYXML_ATTR_CONTENT:
Radek Krejcif13b87b2020-12-01 22:02:17 +0100913 /* attr='val'| attr='val'* > content */
914
Michal Vaskob36053d2020-03-26 15:49:30 +0100915 /* parse attribute name, if any */
Michal Vasko63f3d842020-07-08 10:10:14 +0200916 ret = lyxml_next_attribute(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name, &xmlctx->name_len);
917 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100918
Michal Vasko63f3d842020-07-08 10:10:14 +0200919 if (xmlctx->in->current[0] == '>') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100920 /* no attributes but a closing tag */
Michal Vasko63f3d842020-07-08 10:10:14 +0200921 ly_in_skip(xmlctx->in, 1);
922 if (!xmlctx->in->current[0]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100923 LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
Michal Vaskof55ae202020-06-30 15:49:36 +0200924 ret = LY_EVALID;
925 goto cleanup;
926 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100927
928 /* parse element content */
Michal Vasko63f3d842020-07-08 10:10:14 +0200929 ret = lyxml_parse_value(xmlctx, '<', (char **)&xmlctx->value, &xmlctx->value_len, &xmlctx->ws_only,
Michal Vasko69730152020-10-09 16:30:07 +0200930 &xmlctx->dynamic);
Michal Vasko63f3d842020-07-08 10:10:14 +0200931 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100932
933 if (!xmlctx->value_len) {
Radek IÅ¡a017270d2021-02-16 10:26:15 +0100934 /* empty value should by alocated staticaly, but check for in any case */
935 if (xmlctx->dynamic) {
936 free((char *) xmlctx->value);
937 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100938 /* use empty value, easier to work with */
939 xmlctx->value = "";
Radek IÅ¡a017270d2021-02-16 10:26:15 +0100940 xmlctx->dynamic = 0;
Michal Vaskob36053d2020-03-26 15:49:30 +0100941 }
942
943 /* update status */
944 xmlctx->status = LYXML_ELEM_CONTENT;
Michal Vasko63f3d842020-07-08 10:10:14 +0200945 } else if (xmlctx->in->current[0] == '/') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100946 /* no content but we still return it */
947 xmlctx->value = "";
948 xmlctx->value_len = 0;
949 xmlctx->ws_only = 1;
950 xmlctx->dynamic = 0;
951
952 /* update status */
953 xmlctx->status = LYXML_ELEM_CONTENT;
954 } else {
955 /* update status */
956 xmlctx->status = LYXML_ATTRIBUTE;
957 }
958 break;
959
Michal Vaskob36053d2020-03-26 15:49:30 +0100960 case LYXML_ATTRIBUTE:
Radek Krejcif13b87b2020-12-01 22:02:17 +0100961 /* attr|='val' */
962
Michal Vaskob36053d2020-03-26 15:49:30 +0100963 /* skip formatting and parse value */
Michal Vasko63f3d842020-07-08 10:10:14 +0200964 ret = lyxml_next_attr_content(xmlctx, &xmlctx->value, &xmlctx->value_len, &xmlctx->ws_only, &xmlctx->dynamic);
965 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100966
967 /* update status */
968 xmlctx->status = LYXML_ATTR_CONTENT;
969 break;
970
Michal Vaskob36053d2020-03-26 15:49:30 +0100971 case LYXML_END:
Radek Krejcif13b87b2020-12-01 22:02:17 +0100972 /* </elem> |EOF */
Michal Vaskob36053d2020-03-26 15:49:30 +0100973 /* nothing to do */
974 break;
975 }
976
977cleanup:
978 if (ret) {
979 /* invalidate context */
980 xmlctx->status = LYXML_END;
981 }
982 return ret;
983}
984
985LY_ERR
986lyxml_ctx_peek(struct lyxml_ctx *xmlctx, enum LYXML_PARSER_STATUS *next)
987{
988 LY_ERR ret = LY_SUCCESS;
989 const char *prefix, *name, *prev_input;
990 size_t prefix_len, name_len;
Radek Krejci857189e2020-09-01 13:26:36 +0200991 ly_bool closing;
Michal Vaskob36053d2020-03-26 15:49:30 +0100992
Michal Vasko63f3d842020-07-08 10:10:14 +0200993 prev_input = xmlctx->in->current;
Michal Vaskob36053d2020-03-26 15:49:30 +0100994
995 switch (xmlctx->status) {
996 case LYXML_ELEM_CONTENT:
Michal Vasko63f3d842020-07-08 10:10:14 +0200997 if (xmlctx->in->current[0] == '/') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100998 *next = LYXML_ELEM_CLOSE;
999 break;
1000 }
Radek Krejcif13b87b2020-12-01 22:02:17 +01001001 /* fall through */
Michal Vaskob36053d2020-03-26 15:49:30 +01001002 case LYXML_ELEM_CLOSE:
1003 /* parse next element, if any */
Michal Vasko63f3d842020-07-08 10:10:14 +02001004 ret = lyxml_next_element(xmlctx, &prefix, &prefix_len, &name, &name_len, &closing);
1005 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +01001006
Michal Vasko63f3d842020-07-08 10:10:14 +02001007 if (xmlctx->in->current[0] == '\0') {
Michal Vaskob36053d2020-03-26 15:49:30 +01001008 *next = LYXML_END;
1009 } else if (closing) {
1010 *next = LYXML_ELEM_CLOSE;
1011 } else {
1012 *next = LYXML_ELEMENT;
1013 }
1014 break;
1015 case LYXML_ELEMENT:
1016 case LYXML_ATTR_CONTENT:
1017 /* parse attribute name, if any */
Michal Vasko63f3d842020-07-08 10:10:14 +02001018 ret = lyxml_next_attribute(xmlctx, &prefix, &prefix_len, &name, &name_len);
1019 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +01001020
Michal Vasko63f3d842020-07-08 10:10:14 +02001021 if ((xmlctx->in->current[0] == '>') || (xmlctx->in->current[0] == '/')) {
Michal Vaskob36053d2020-03-26 15:49:30 +01001022 *next = LYXML_ELEM_CONTENT;
1023 } else {
1024 *next = LYXML_ATTRIBUTE;
1025 }
1026 break;
1027 case LYXML_ATTRIBUTE:
1028 *next = LYXML_ATTR_CONTENT;
1029 break;
1030 case LYXML_END:
1031 *next = LYXML_END;
1032 break;
1033 }
1034
1035cleanup:
Michal Vasko63f3d842020-07-08 10:10:14 +02001036 xmlctx->in->current = prev_input;
Michal Vaskob36053d2020-03-26 15:49:30 +01001037 return ret;
1038}
1039
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001040/**
1041 * @brief Free all namespaces in XML context.
1042 *
1043 * @param[in] xmlctx XML context to use.
1044 */
1045static void
1046lyxml_ns_rm_all(struct lyxml_ctx *xmlctx)
1047{
1048 struct lyxml_ns *ns;
1049 uint32_t i;
1050
1051 for (i = 0; i < xmlctx->ns.count; ++i) {
1052 ns = xmlctx->ns.objs[i];
1053
1054 free(ns->prefix);
1055 free(ns->uri);
1056 free(ns);
1057 }
1058 ly_set_erase(&xmlctx->ns, NULL);
1059}
1060
Michal Vaskob36053d2020-03-26 15:49:30 +01001061void
1062lyxml_ctx_free(struct lyxml_ctx *xmlctx)
1063{
Michal Vaskob36053d2020-03-26 15:49:30 +01001064 if (!xmlctx) {
1065 return;
1066 }
1067
Radek Krejciddace2c2021-01-08 11:30:56 +01001068 LOG_LOCBACK(0, 0, 0, 1);
Radek Krejci2efc45b2020-12-22 16:25:44 +01001069
Michal Vaskob36053d2020-03-26 15:49:30 +01001070 if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
1071 free((char *)xmlctx->value);
1072 }
1073 ly_set_erase(&xmlctx->elements, free);
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001074 lyxml_ns_rm_all(xmlctx);
Michal Vaskob36053d2020-03-26 15:49:30 +01001075 free(xmlctx);
Radek Krejcib1890642018-10-03 14:05:40 +02001076}
Radek Krejcie7b95092019-05-15 11:03:07 +02001077
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001078/**
1079 * @brief Duplicate an XML element.
1080 *
1081 * @param[in] elem Element to duplicate.
1082 * @return Element duplicate.
1083 * @return NULL on error.
1084 */
1085static struct lyxml_elem *
1086lyxml_elem_dup(const struct lyxml_elem *elem)
1087{
1088 struct lyxml_elem *dup;
1089
1090 dup = malloc(sizeof *dup);
1091 LY_CHECK_ERR_RET(!dup, LOGMEM(NULL), NULL);
1092
1093 memcpy(dup, elem, sizeof *dup);
1094
1095 return dup;
1096}
1097
1098/**
1099 * @brief Duplicate an XML namespace.
1100 *
1101 * @param[in] ns Namespace to duplicate.
1102 * @return Namespace duplicate.
1103 * @return NULL on error.
1104 */
1105static struct lyxml_ns *
1106lyxml_ns_dup(const struct lyxml_ns *ns)
1107{
1108 struct lyxml_ns *dup;
1109
1110 dup = malloc(sizeof *dup);
1111 LY_CHECK_ERR_RET(!dup, LOGMEM(NULL), NULL);
1112
1113 if (ns->prefix) {
1114 dup->prefix = strdup(ns->prefix);
1115 LY_CHECK_ERR_RET(!dup->prefix, LOGMEM(NULL); free(dup), NULL);
1116 } else {
1117 dup->prefix = NULL;
1118 }
1119 dup->uri = strdup(ns->uri);
1120 LY_CHECK_ERR_RET(!dup->uri, LOGMEM(NULL); free(dup->prefix); free(dup), NULL);
1121 dup->depth = ns->depth;
1122
1123 return dup;
1124}
1125
1126LY_ERR
1127lyxml_ctx_backup(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup)
1128{
1129 uint32_t i;
1130
1131 /* first make shallow copy */
1132 memcpy(backup, xmlctx, sizeof *backup);
1133
1134 if ((xmlctx->status == LYXML_ELEM_CONTENT) && xmlctx->dynamic) {
1135 /* it was backed up, do not free */
1136 xmlctx->dynamic = 0;
1137 }
1138
1139 /* backup in current pointer only */
1140 backup->in = (void *)xmlctx->in->current;
1141
1142 /* duplicate elements */
1143 backup->elements.objs = malloc(xmlctx->elements.size * sizeof(struct lyxml_elem));
Michal Vasko0c2403d2021-09-15 08:52:18 +02001144 LY_CHECK_ERR_RET(!backup->elements.objs, LOGMEM(xmlctx->ctx), LY_EMEM);
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001145 for (i = 0; i < xmlctx->elements.count; ++i) {
1146 backup->elements.objs[i] = lyxml_elem_dup(xmlctx->elements.objs[i]);
Michal Vasko0c2403d2021-09-15 08:52:18 +02001147 LY_CHECK_RET(!backup->elements.objs[i], LY_EMEM);
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001148 }
1149
1150 /* duplicate ns */
1151 backup->ns.objs = malloc(xmlctx->ns.size * sizeof(struct lyxml_ns));
Michal Vasko0c2403d2021-09-15 08:52:18 +02001152 LY_CHECK_ERR_RET(!backup->ns.objs, LOGMEM(xmlctx->ctx), LY_EMEM);
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001153 for (i = 0; i < xmlctx->ns.count; ++i) {
1154 backup->ns.objs[i] = lyxml_ns_dup(xmlctx->ns.objs[i]);
Michal Vasko0c2403d2021-09-15 08:52:18 +02001155 LY_CHECK_RET(!backup->ns.objs[i], LY_EMEM);
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001156 }
1157
1158 return LY_SUCCESS;
1159}
1160
1161void
1162lyxml_ctx_restore(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup)
1163{
1164 if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
1165 /* free dynamic value */
1166 free((char *)xmlctx->value);
1167 }
1168
1169 /* free elements */
1170 ly_set_erase(&xmlctx->elements, free);
1171
1172 /* free ns */
1173 lyxml_ns_rm_all(xmlctx);
1174
1175 /* restore in current pointer */
1176 xmlctx->in->current = (void *)backup->in;
1177 backup->in = xmlctx->in;
1178
1179 /* restore backup */
1180 memcpy(xmlctx, backup, sizeof *xmlctx);
1181}
1182
Radek Krejcie7b95092019-05-15 11:03:07 +02001183LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +02001184lyxml_dump_text(struct ly_out *out, const char *text, ly_bool attribute)
Radek Krejcie7b95092019-05-15 11:03:07 +02001185{
Michal Vasko5233e962020-08-14 14:26:20 +02001186 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +02001187
1188 if (!text) {
1189 return 0;
1190 }
1191
Radek Krejci1deb5be2020-08-26 16:43:36 +02001192 for (uint64_t u = 0; text[u]; u++) {
Radek Krejcie7b95092019-05-15 11:03:07 +02001193 switch (text[u]) {
1194 case '&':
Michal Vasko5233e962020-08-14 14:26:20 +02001195 ret = ly_print_(out, "&amp;");
Radek Krejcie7b95092019-05-15 11:03:07 +02001196 break;
1197 case '<':
Michal Vasko5233e962020-08-14 14:26:20 +02001198 ret = ly_print_(out, "&lt;");
Radek Krejcie7b95092019-05-15 11:03:07 +02001199 break;
1200 case '>':
1201 /* not needed, just for readability */
Michal Vasko5233e962020-08-14 14:26:20 +02001202 ret = ly_print_(out, "&gt;");
Radek Krejcie7b95092019-05-15 11:03:07 +02001203 break;
1204 case '"':
1205 if (attribute) {
Michal Vasko5233e962020-08-14 14:26:20 +02001206 ret = ly_print_(out, "&quot;");
Radek Krejcie7b95092019-05-15 11:03:07 +02001207 break;
1208 }
Radek Krejcif13b87b2020-12-01 22:02:17 +01001209 /* fall through */
Radek Krejcie7b95092019-05-15 11:03:07 +02001210 default:
Michal Vasko5233e962020-08-14 14:26:20 +02001211 ret = ly_write_(out, &text[u], 1);
1212 break;
Radek Krejcie7b95092019-05-15 11:03:07 +02001213 }
Michal Vasko5233e962020-08-14 14:26:20 +02001214 LY_CHECK_RET(ret);
Radek Krejcie7b95092019-05-15 11:03:07 +02001215 }
1216
Michal Vasko5233e962020-08-14 14:26:20 +02001217 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +02001218}
1219
Michal Vasko52927e22020-03-16 17:26:14 +01001220LY_ERR
aPiecek2f63f952021-03-30 12:22:18 +02001221lyxml_value_compare(const struct ly_ctx *ctx1, const char *value1, void *val_prefix_data1,
1222 const struct ly_ctx *ctx2, const char *value2, void *val_prefix_data2)
Michal Vasko52927e22020-03-16 17:26:14 +01001223{
aPiecek2f63f952021-03-30 12:22:18 +02001224 const char *value1_iter, *value2_iter;
1225 const char *value1_next, *value2_next;
1226 uint32_t value1_len, value2_len;
1227 ly_bool is_prefix1, is_prefix2;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +01001228 const struct lys_module *mod1, *mod2;
aPiecek2f63f952021-03-30 12:22:18 +02001229 LY_ERR ret;
Michal Vasko52927e22020-03-16 17:26:14 +01001230
1231 if (!value1 && !value2) {
1232 return LY_SUCCESS;
1233 }
1234 if ((value1 && !value2) || (!value1 && value2)) {
1235 return LY_ENOT;
1236 }
1237
aPiecek2f63f952021-03-30 12:22:18 +02001238 if (!ctx2) {
1239 ctx2 = ctx1;
1240 }
Michal Vasko52927e22020-03-16 17:26:14 +01001241
aPiecek2f63f952021-03-30 12:22:18 +02001242 ret = LY_SUCCESS;
1243 for (value1_iter = value1, value2_iter = value2;
1244 value1_iter && value2_iter;
1245 value1_iter = value1_next, value2_iter = value2_next) {
aPieceke3f828d2021-05-10 15:34:41 +02001246 if ((ret = ly_value_prefix_next(value1_iter, NULL, &value1_len, &is_prefix1, &value1_next))) {
1247 break;
1248 }
1249 if ((ret = ly_value_prefix_next(value2_iter, NULL, &value2_len, &is_prefix2, &value2_next))) {
1250 break;
1251 }
aPiecek2f63f952021-03-30 12:22:18 +02001252
1253 if (is_prefix1 != is_prefix2) {
1254 ret = LY_ENOT;
1255 break;
1256 }
1257
1258 if (!is_prefix1) {
1259 if (value1_len != value2_len) {
1260 ret = LY_ENOT;
1261 break;
1262 }
1263 if (strncmp(value1_iter, value2_iter, value1_len)) {
1264 ret = LY_ENOT;
1265 break;
1266 }
1267 continue;
1268 }
1269
1270 mod1 = mod2 = NULL;
1271 if (val_prefix_data1) {
1272 /* find module of the first prefix, if any */
Radek Krejci8df109d2021-04-23 12:19:08 +02001273 mod1 = ly_resolve_prefix(ctx1, value1_iter, value1_len, LY_VALUE_XML, val_prefix_data1);
aPiecek2f63f952021-03-30 12:22:18 +02001274 }
1275 if (val_prefix_data2) {
Radek Krejci8df109d2021-04-23 12:19:08 +02001276 mod2 = ly_resolve_prefix(ctx2, value2_iter, value2_len, LY_VALUE_XML, val_prefix_data2);
aPiecek2f63f952021-03-30 12:22:18 +02001277 }
1278 if (!mod1 || !mod2) {
1279 /* not a prefix or maps to different namespaces */
1280 ret = LY_ENOT;
1281 break;
1282 }
1283
1284 if (mod1->ctx == mod2->ctx) {
1285 /* same contexts */
1286 if ((mod1->name != mod2->name) || (mod1->revision != mod2->revision)) {
1287 ret = LY_ENOT;
1288 break;
1289 }
1290 } else {
1291 /* different contexts */
1292 if (strcmp(mod1->name, mod2->name)) {
1293 ret = LY_ENOT;
Michal Vasko52927e22020-03-16 17:26:14 +01001294 break;
1295 }
1296
aPiecek2f63f952021-03-30 12:22:18 +02001297 if (mod1->revision || mod2->revision) {
1298 if (!mod1->revision || !mod2->revision) {
1299 ret = LY_ENOT;
1300 break;
1301 }
1302 if (strcmp(mod1->revision, mod2->revision)) {
1303 ret = LY_ENOT;
1304 break;
1305 }
1306 }
Michal Vasko52927e22020-03-16 17:26:14 +01001307 }
Michal Vasko52927e22020-03-16 17:26:14 +01001308 }
1309
aPiecek2f63f952021-03-30 12:22:18 +02001310 if (value1_iter || value2_iter) {
1311 ret = LY_ENOT;
1312 }
1313
1314 return ret;
Michal Vasko52927e22020-03-16 17:26:14 +01001315}