blob: aa35fb490f3146b5047052e265394e9bca847049 [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("-->");
Radek Krejcif13b87b2020-12-01 22:02:17 +0100265 } else if (!strncmp(xmlctx->in->current, "DOCTYPE", ly_strlen_const("DOCTYPE"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100266 /* Document type declaration - not supported */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100267 LOGVAL(ctx, LY_VCODE_NSUPP, "Document Type Declaration");
Michal Vaskob36053d2020-03-26 15:49:30 +0100268 return LY_EVALID;
269 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100270 LOGVAL(ctx, LYVE_SYNTAX, "Unknown XML section \"%.20s\".", &xmlctx->in->current[-2]);
Michal Vaskob36053d2020-03-26 15:49:30 +0100271 return LY_EVALID;
272 }
Radek Krejcidd713ce2021-01-04 23:12:12 +0100273 LY_CHECK_RET(skip_section(xmlctx, endtag, endtag_len, sectname));
Michal Vasko63f3d842020-07-08 10:10:14 +0200274 } else if (xmlctx->in->current[0] == '?') {
Radek Krejcidd713ce2021-01-04 23:12:12 +0100275 LY_CHECK_RET(skip_section(xmlctx, "?>", 2, "Declaration"));
Michal Vaskob36053d2020-03-26 15:49:30 +0100276 } else {
277 /* other non-WS character */
278 break;
279 }
280 }
281
282 return LY_SUCCESS;
283}
284
Michal Vasko8cef5232020-06-15 17:59:47 +0200285/**
286 * @brief Parse QName.
287 *
288 * @param[in] xmlctx XML context to use.
289 * @param[out] prefix Parsed prefix, may be NULL.
290 * @param[out] prefix_len Length of @p prefix.
291 * @param[out] name Parsed name.
292 * @param[out] name_len Length of @p name.
293 * @return LY_ERR value.
294 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100295static LY_ERR
296lyxml_parse_qname(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
297{
298 const char *start, *end;
299
300 *prefix = NULL;
301 *prefix_len = 0;
302
303 LY_CHECK_RET(lyxml_parse_identifier(xmlctx, &start, &end));
304 if (end[0] == ':') {
305 /* we have prefixed identifier */
306 *prefix = start;
307 *prefix_len = end - start;
308
309 move_input(xmlctx, 1);
310 LY_CHECK_RET(lyxml_parse_identifier(xmlctx, &start, &end));
311 }
312
313 *name = start;
314 *name_len = end - start;
315 return LY_SUCCESS;
316}
317
318/**
Michal Vaskof54be382022-02-18 13:08:23 +0100319 * @brief Prepare buffer for new data.
320 *
321 * @param[in] ctx Context for logging.
322 * @param[in,out] in XML input data.
323 * @param[in,out] offset Current offset in @p in.
324 * @param[in] need_space Needed additional free space that is allocated.
325 * @param[in,out] buf Dynamic buffer.
326 * @param[in,out] len Current @p buf length (used characters).
327 * @param[in,out] size Current @p buf size (allocated characters).
328 * @return LY_ERR value.
329 */
330static LY_ERR
331lyxml_parse_value_use_buf(const struct ly_ctx *ctx, const char **in, size_t *offset, size_t need_space, char **buf,
332 size_t *len, size_t *size)
333{
334#define BUFSIZE 24
335#define BUFSIZE_STEP 128
336
337 if (!*buf) {
338 /* prepare output buffer */
339 *buf = malloc(BUFSIZE);
340 LY_CHECK_ERR_RET(!*buf, LOGMEM(ctx), LY_EMEM);
341 *size = BUFSIZE;
342 }
343
344 /* allocate needed space */
345 while (*len + *offset + need_space >= *size) {
346 *buf = ly_realloc(*buf, *size + BUFSIZE_STEP);
347 LY_CHECK_ERR_RET(!*buf, LOGMEM(ctx), LY_EMEM);
348 *size += BUFSIZE_STEP;
349 }
350
351 if (*offset) {
352 /* store what we have so far */
353 memcpy(&(*buf)[*len], *in, *offset);
354 *len += *offset;
355 *in += *offset;
356 *offset = 0;
357 }
358
359 return LY_SUCCESS;
360
361#undef BUFSIZE
362#undef BUFSIZE_STEP
363}
364
365/**
Michal Vasko8cef5232020-06-15 17:59:47 +0200366 * @brief Parse XML text content (value).
367 *
368 * @param[in] xmlctx XML context to use.
369 * @param[in] endchar Expected character to mark value end.
370 * @param[out] value Parsed value.
371 * @param[out] length Length of @p value.
372 * @param[out] ws_only Whether the value is empty/white-spaces only.
373 * @param[out] dynamic Whether the value was dynamically allocated.
374 * @return LY_ERR value.
375 */
Radek Krejci4b74d5e2018-09-26 14:30:55 +0200376static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200377lyxml_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 +0200378{
Michal Vaskob36053d2020-03-26 15:49:30 +0100379 const struct ly_ctx *ctx = xmlctx->ctx; /* shortcut */
aPiecekb287b212021-05-04 14:24:25 +0200380 const char *in = xmlctx->in->current, *start, *in_aux;
Michal Vaskob36053d2020-03-26 15:49:30 +0100381 char *buf = NULL;
Radek Krejci4ad42aa2019-07-23 16:55:58 +0200382 size_t offset; /* read offset in input buffer */
383 size_t len; /* length of the output string (write offset in output buffer) */
384 size_t size = 0; /* size of the output buffer */
Radek Krejci7a7fa902018-09-25 17:08:21 +0200385 void *p;
Radek Krejci117d2082018-09-26 10:05:14 +0200386 uint32_t n;
Michal Vaskob36053d2020-03-26 15:49:30 +0100387 size_t u;
Radek Krejci857189e2020-09-01 13:26:36 +0200388 ly_bool ws = 1;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200389
Michal Vaskob36053d2020-03-26 15:49:30 +0100390 assert(xmlctx);
Radek Krejcib1890642018-10-03 14:05:40 +0200391
Radek Krejcid70d1072018-10-09 14:20:47 +0200392 /* init */
Michal Vaskob36053d2020-03-26 15:49:30 +0100393 start = in;
Radek Krejcid70d1072018-10-09 14:20:47 +0200394 offset = len = 0;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200395
396 /* parse */
397 while (in[offset]) {
398 if (in[offset] == '&') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100399 /* non WS */
400 ws = 0;
Radek Krejcid70d1072018-10-09 14:20:47 +0200401
Michal Vaskof54be382022-02-18 13:08:23 +0100402 /* use buffer and allocate enough for the offset and next character,
Michal Vaskob36053d2020-03-26 15:49:30 +0100403 * we will need 4 bytes at most since we support only the predefined
404 * (one-char) entities and character references */
Michal Vaskof54be382022-02-18 13:08:23 +0100405 LY_CHECK_RET(lyxml_parse_value_use_buf(ctx, &in, &offset, 4, &buf, &len, &size));
Michal Vaskob36053d2020-03-26 15:49:30 +0100406
Radek Krejci7a7fa902018-09-25 17:08:21 +0200407 ++offset;
408 if (in[offset] != '#') {
409 /* entity reference - only predefined references are supported */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100410 if (!strncmp(&in[offset], "lt;", ly_strlen_const("lt;"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100411 buf[len++] = '<';
Radek Krejcif13b87b2020-12-01 22:02:17 +0100412 in += ly_strlen_const("&lt;");
413 } else if (!strncmp(&in[offset], "gt;", ly_strlen_const("gt;"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100414 buf[len++] = '>';
Radek Krejcif13b87b2020-12-01 22:02:17 +0100415 in += ly_strlen_const("&gt;");
416 } else if (!strncmp(&in[offset], "amp;", ly_strlen_const("amp;"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100417 buf[len++] = '&';
Radek Krejcif13b87b2020-12-01 22:02:17 +0100418 in += ly_strlen_const("&amp;");
419 } else if (!strncmp(&in[offset], "apos;", ly_strlen_const("apos;"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100420 buf[len++] = '\'';
Radek Krejcif13b87b2020-12-01 22:02:17 +0100421 in += ly_strlen_const("&apos;");
422 } else if (!strncmp(&in[offset], "quot;", ly_strlen_const("quot;"))) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100423 buf[len++] = '\"';
Radek Krejcif13b87b2020-12-01 22:02:17 +0100424 in += ly_strlen_const("&quot;");
Radek Krejci7a7fa902018-09-25 17:08:21 +0200425 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100426 LOGVAL(ctx, LYVE_SYNTAX, "Entity reference \"%.*s\" not supported, only predefined references allowed.",
427 10, &in[offset - 1]);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200428 goto error;
429 }
430 offset = 0;
431 } else {
Michal Vaskob36053d2020-03-26 15:49:30 +0100432 p = (void *)&in[offset - 1];
Radek Krejci7a7fa902018-09-25 17:08:21 +0200433 /* character reference */
434 ++offset;
435 if (isdigit(in[offset])) {
436 for (n = 0; isdigit(in[offset]); offset++) {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100437 n = (LY_BASE_DEC * n) + (in[offset] - '0');
Radek Krejci7a7fa902018-09-25 17:08:21 +0200438 }
Michal Vasko69730152020-10-09 16:30:07 +0200439 } else if ((in[offset] == 'x') && isxdigit(in[offset + 1])) {
Radek Krejci7a7fa902018-09-25 17:08:21 +0200440 for (n = 0, ++offset; isxdigit(in[offset]); offset++) {
441 if (isdigit(in[offset])) {
442 u = (in[offset] - '0');
443 } else if (in[offset] > 'F') {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100444 u = LY_BASE_DEC + (in[offset] - 'a');
Radek Krejci7a7fa902018-09-25 17:08:21 +0200445 } else {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100446 u = LY_BASE_DEC + (in[offset] - 'A');
Radek Krejci7a7fa902018-09-25 17:08:21 +0200447 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100448 n = (LY_BASE_HEX * n) + u;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200449 }
450 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100451 LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\".", 12, p);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200452 goto error;
453
454 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100455
Michal Vaskof54be382022-02-18 13:08:23 +0100456 if (in[offset] != ';') {
457 LOGVAL(ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(&in[offset]), &in[offset], ";");
458 goto error;
459 }
Radek Krejci7a7fa902018-09-25 17:08:21 +0200460 ++offset;
Michal Vaskof54be382022-02-18 13:08:23 +0100461 if (ly_pututf8(&buf[len], n, &u)) {
462 LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).", 12, p, n);
463 goto error;
464 }
Radek Krejci7a7fa902018-09-25 17:08:21 +0200465 len += u;
466 in += offset;
467 offset = 0;
468 }
Michal Vaskof54be382022-02-18 13:08:23 +0100469 } else if (!strncmp(in + offset, "<![CDATA[", ly_strlen_const("<![CDATA["))) {
470 /* CDATA, find the end */
471 in_aux = strstr(in + offset + ly_strlen_const("<![CDATA["), "]]>");
472 if (!in_aux) {
473 LOGVAL(xmlctx->ctx, LY_VCODE_NTERM, "CDATA");
474 goto error;
475 }
476 u = in_aux - (in + offset + ly_strlen_const("<![CDATA["));
477
478 /* use buffer, allocate enough for the whole CDATA */
479 LY_CHECK_RET(lyxml_parse_value_use_buf(ctx, &in, &offset, u, &buf, &len, &size));
480
481 /* skip CDATA tag */
482 in += ly_strlen_const("<![CDATA[");
483 assert(!offset);
484
485 /* analyze CDATA for non WS and newline chars */
486 for (n = 0; n < u; ++n) {
487 if (in[n] == '\n') {
488 LY_IN_NEW_LINE(xmlctx->in);
489 } else if (!is_xmlws(in[n])) {
490 ws = 0;
491 }
492 }
493
494 /* copy CDATA */
495 memcpy(buf + len, in, u);
496 len += u;
497
498 /* move input skipping the end tag */
499 in += u + ly_strlen_const("]]>");
Michal Vaskob36053d2020-03-26 15:49:30 +0100500 } else if (in[offset] == endchar) {
Radek Krejci7a7fa902018-09-25 17:08:21 +0200501 /* end of string */
Radek Krejcid70d1072018-10-09 14:20:47 +0200502 if (buf) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100503 /* realloc exact size string */
504 buf = ly_realloc(buf, len + offset + 1);
505 LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
506 size = len + offset + 1;
Michal Vasko08e9b112021-06-11 15:41:17 +0200507 if (offset) {
508 memcpy(&buf[len], in, offset);
509 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100510
511 /* set terminating NULL byte */
512 buf[len + offset] = '\0';
Radek Krejci7a7fa902018-09-25 17:08:21 +0200513 }
Radek Krejci7a7fa902018-09-25 17:08:21 +0200514 len += offset;
Michal Vaskob36053d2020-03-26 15:49:30 +0100515 in += offset;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200516 goto success;
517 } else {
Michal Vaskob36053d2020-03-26 15:49:30 +0100518 if (!is_xmlws(in[offset])) {
519 /* non WS */
520 ws = 0;
521 }
522
Radek Krejci7a7fa902018-09-25 17:08:21 +0200523 /* log lines */
524 if (in[offset] == '\n') {
Radek Krejcid54412f2020-12-17 20:25:35 +0100525 LY_IN_NEW_LINE(xmlctx->in);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200526 }
527
528 /* continue */
aPiecekb287b212021-05-04 14:24:25 +0200529 in_aux = &in[offset];
530 LY_CHECK_ERR_GOTO(ly_getutf8(&in_aux, &n, &u),
531 LOGVAL(ctx, LY_VCODE_INCHAR, in[offset]), error);
532 offset += u;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200533 }
534 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100535
536 /* EOF reached before endchar */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100537 LOGVAL(ctx, LY_VCODE_EOF);
Michal Vaskob36053d2020-03-26 15:49:30 +0100538
Radek Krejci7a7fa902018-09-25 17:08:21 +0200539error:
Michal Vaskob36053d2020-03-26 15:49:30 +0100540 free(buf);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200541 return LY_EVALID;
542
543success:
Radek Krejcid70d1072018-10-09 14:20:47 +0200544 if (buf) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100545 *value = buf;
546 *dynamic = 1;
547 } else {
548 *value = (char *)start;
549 *dynamic = 0;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200550 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100551 *length = len;
552 *ws_only = ws;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200553
Radek Krejcid54412f2020-12-17 20:25:35 +0100554 xmlctx->in->current = in;
Michal Vaskob36053d2020-03-26 15:49:30 +0100555 return LY_SUCCESS;
Radek Krejci7a7fa902018-09-25 17:08:21 +0200556}
557
Michal Vasko8cef5232020-06-15 17:59:47 +0200558/**
559 * @brief Parse XML closing element and match it to a stored starting element.
560 *
561 * @param[in] xmlctx XML context to use.
562 * @param[in] prefix Expected closing element prefix.
563 * @param[in] prefix_len Length of @p prefix.
564 * @param[in] name Expected closing element name.
565 * @param[in] name_len Length of @p name.
566 * @param[in] empty Whether we are parsing a special "empty" element (with joined starting and closing tag) with no value.
567 * @return LY_ERR value.
568 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100569static LY_ERR
570lyxml_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 +0200571 ly_bool empty)
Radek Krejcid972c252018-09-25 13:23:39 +0200572{
Michal Vaskob36053d2020-03-26 15:49:30 +0100573 struct lyxml_elem *e;
Radek Krejcid972c252018-09-25 13:23:39 +0200574
Michal Vaskob36053d2020-03-26 15:49:30 +0100575 /* match opening and closing element tags */
576 if (!xmlctx->elements.count) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100577 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Stray closing element tag (\"%.*s\").",
Radek Krejci422afb12021-03-04 16:38:16 +0100578 (int)name_len, name);
Michal Vaskob36053d2020-03-26 15:49:30 +0100579 return LY_EVALID;
580 }
Radek Krejcid972c252018-09-25 13:23:39 +0200581
Michal Vaskob36053d2020-03-26 15:49:30 +0100582 e = (struct lyxml_elem *)xmlctx->elements.objs[xmlctx->elements.count - 1];
Michal Vasko69730152020-10-09 16:30:07 +0200583 if ((e->prefix_len != prefix_len) || (e->name_len != name_len) ||
584 (prefix_len && strncmp(prefix, e->prefix, e->prefix_len)) || strncmp(name, e->name, e->name_len)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100585 LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Opening (\"%.*s%s%.*s\") and closing (\"%.*s%s%.*s\") elements tag mismatch.",
Radek Krejci422afb12021-03-04 16:38:16 +0100586 (int)e->prefix_len, e->prefix ? e->prefix : "", e->prefix ? ":" : "", (int)e->name_len, e->name,
587 (int)prefix_len, prefix ? prefix : "", prefix ? ":" : "", (int)name_len, name);
Michal Vaskob36053d2020-03-26 15:49:30 +0100588 return LY_EVALID;
589 }
Radek Krejcid972c252018-09-25 13:23:39 +0200590
Michal Vaskob36053d2020-03-26 15:49:30 +0100591 /* opening and closing element tags matches, remove record from the opening tags list */
592 ly_set_rm_index(&xmlctx->elements, xmlctx->elements.count - 1, free);
Radek Krejcid972c252018-09-25 13:23:39 +0200593
Michal Vaskob36053d2020-03-26 15:49:30 +0100594 /* remove also the namespaces connected with the element */
595 lyxml_ns_rm(xmlctx);
Radek Krejcid972c252018-09-25 13:23:39 +0200596
Michal Vaskob36053d2020-03-26 15:49:30 +0100597 /* skip WS */
598 ign_xmlws(xmlctx);
Radek Krejcid972c252018-09-25 13:23:39 +0200599
Michal Vaskob36053d2020-03-26 15:49:30 +0100600 /* special "<elem/>" element */
Michal Vasko63f3d842020-07-08 10:10:14 +0200601 if (empty && (xmlctx->in->current[0] == '/')) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100602 move_input(xmlctx, 1);
603 }
Michal Vasko52927e22020-03-16 17:26:14 +0100604
Michal Vaskob36053d2020-03-26 15:49:30 +0100605 /* parse closing tag */
Michal Vasko63f3d842020-07-08 10:10:14 +0200606 if (xmlctx->in->current[0] != '>') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100607 LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200608 xmlctx->in->current, "element tag termination ('>')");
Michal Vaskob36053d2020-03-26 15:49:30 +0100609 return LY_EVALID;
610 }
Michal Vasko52927e22020-03-16 17:26:14 +0100611
Michal Vaskob36053d2020-03-26 15:49:30 +0100612 /* move after closing tag without checking for EOF */
Michal Vasko63f3d842020-07-08 10:10:14 +0200613 ly_in_skip(xmlctx->in, 1);
Michal Vasko52927e22020-03-16 17:26:14 +0100614
Radek Krejcid972c252018-09-25 13:23:39 +0200615 return LY_SUCCESS;
616}
617
Michal Vasko8cef5232020-06-15 17:59:47 +0200618/**
619 * @brief Store parsed opening element and parse any included namespaces.
620 *
621 * @param[in] xmlctx XML context to use.
622 * @param[in] prefix Parsed starting element prefix.
623 * @param[in] prefix_len Length of @p prefix.
624 * @param[in] name Parsed starting element name.
625 * @param[in] name_len Length of @p name.
626 * @return LY_ERR value.
627 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100628static LY_ERR
629lyxml_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 +0200630{
Michal Vaskob36053d2020-03-26 15:49:30 +0100631 LY_ERR ret = LY_SUCCESS;
632 struct lyxml_elem *e;
633 const char *prev_input;
Michal Vasko4fd91922021-09-15 08:51:06 +0200634 uint64_t prev_line;
Michal Vaskob36053d2020-03-26 15:49:30 +0100635 char *value;
636 size_t parsed, value_len;
Radek Krejci857189e2020-09-01 13:26:36 +0200637 ly_bool ws_only, dynamic, is_ns;
Michal Vaskob36053d2020-03-26 15:49:30 +0100638 uint32_t c;
Radek Krejcib1890642018-10-03 14:05:40 +0200639
Michal Vaskob36053d2020-03-26 15:49:30 +0100640 /* store element opening tag information */
641 e = malloc(sizeof *e);
642 LY_CHECK_ERR_RET(!e, LOGMEM(xmlctx->ctx), LY_EMEM);
643 e->name = name;
644 e->prefix = prefix;
645 e->name_len = name_len;
646 e->prefix_len = prefix_len;
aPiecek93582ed2021-05-25 14:49:06 +0200647
Radek Krejci3d92e442020-10-12 12:48:13 +0200648 LY_CHECK_RET(ly_set_add(&xmlctx->elements, e, 1, NULL));
aPiecek93582ed2021-05-25 14:49:06 +0200649 if (xmlctx->elements.count > LY_MAX_BLOCK_DEPTH) {
Michal Vasko4fd91922021-09-15 08:51:06 +0200650 LOGERR(xmlctx->ctx, LY_EINVAL, "The maximum number of open elements has been exceeded.");
aPiecek93582ed2021-05-25 14:49:06 +0200651 ret = LY_EINVAL;
652 goto cleanup;
653 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100654
655 /* skip WS */
656 ign_xmlws(xmlctx);
657
658 /* parse and store all namespaces */
Michal Vasko63f3d842020-07-08 10:10:14 +0200659 prev_input = xmlctx->in->current;
Michal Vasko4fd91922021-09-15 08:51:06 +0200660 prev_line = xmlctx->in->line;
Michal Vaskob36053d2020-03-26 15:49:30 +0100661 is_ns = 1;
aPiecek785ad3d2021-05-10 15:51:13 +0200662 while ((xmlctx->in->current[0] != '\0') && !(ret = ly_getutf8(&xmlctx->in->current, &c, &parsed))) {
663 if (!is_xmlqnamestartchar(c)) {
664 break;
665 }
Michal Vasko63f3d842020-07-08 10:10:14 +0200666 xmlctx->in->current -= parsed;
Michal Vaskob36053d2020-03-26 15:49:30 +0100667
668 /* parse attribute name */
669 LY_CHECK_GOTO(ret = lyxml_parse_qname(xmlctx, &prefix, &prefix_len, &name, &name_len), cleanup);
670
671 /* parse the value */
672 LY_CHECK_GOTO(ret = lyxml_next_attr_content(xmlctx, (const char **)&value, &value_len, &ws_only, &dynamic), cleanup);
673
674 /* store every namespace */
675 if ((prefix && !ly_strncmp("xmlns", prefix, prefix_len)) || (!prefix && !ly_strncmp("xmlns", name, name_len))) {
Radek IÅ¡a017270d2021-02-16 10:26:15 +0100676 ret = lyxml_ns_add(xmlctx, prefix ? name : NULL, prefix ? name_len : 0,
677 dynamic ? value : strndup(value, value_len));
Michal Vaskob36053d2020-03-26 15:49:30 +0100678 dynamic = 0;
Radek IÅ¡a017270d2021-02-16 10:26:15 +0100679 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100680 } else {
681 /* not a namespace */
682 is_ns = 0;
683 }
684 if (dynamic) {
685 free(value);
686 }
687
688 /* skip WS */
689 ign_xmlws(xmlctx);
690
691 if (is_ns) {
692 /* we can actually skip all the namespaces as there is no reason to parse them again */
Michal Vasko63f3d842020-07-08 10:10:14 +0200693 prev_input = xmlctx->in->current;
Michal Vasko4fd91922021-09-15 08:51:06 +0200694 prev_line = xmlctx->in->line;
Michal Vaskob36053d2020-03-26 15:49:30 +0100695 }
Radek Krejcib1890642018-10-03 14:05:40 +0200696 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100697
698cleanup:
699 if (!ret) {
Michal Vasko63f3d842020-07-08 10:10:14 +0200700 xmlctx->in->current = prev_input;
Michal Vasko4fd91922021-09-15 08:51:06 +0200701 xmlctx->in->line = prev_line;
Michal Vaskob36053d2020-03-26 15:49:30 +0100702 }
703 return ret;
704}
705
Michal Vasko8cef5232020-06-15 17:59:47 +0200706/**
707 * @brief Move parser to the attribute content and parse it.
708 *
709 * @param[in] xmlctx XML context to use.
710 * @param[out] value Parsed attribute value.
711 * @param[out] value_len Length of @p value.
712 * @param[out] ws_only Whether the value is empty/white-spaces only.
713 * @param[out] dynamic Whether the value was dynamically allocated.
714 * @return LY_ERR value.
715 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100716static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200717lyxml_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 +0100718{
719 char quot;
720
721 /* skip WS */
722 ign_xmlws(xmlctx);
723
724 /* skip '=' */
Michal Vasko63f3d842020-07-08 10:10:14 +0200725 if (xmlctx->in->current[0] == '\0') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100726 LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
Michal Vaskob36053d2020-03-26 15:49:30 +0100727 return LY_EVALID;
Michal Vasko63f3d842020-07-08 10:10:14 +0200728 } else if (xmlctx->in->current[0] != '=') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100729 LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200730 xmlctx->in->current, "'='");
Michal Vaskob36053d2020-03-26 15:49:30 +0100731 return LY_EVALID;
732 }
733 move_input(xmlctx, 1);
734
735 /* skip WS */
736 ign_xmlws(xmlctx);
737
738 /* find quotes */
Michal Vasko63f3d842020-07-08 10:10:14 +0200739 if (xmlctx->in->current[0] == '\0') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100740 LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
Michal Vaskob36053d2020-03-26 15:49:30 +0100741 return LY_EVALID;
Michal Vasko63f3d842020-07-08 10:10:14 +0200742 } else if ((xmlctx->in->current[0] != '\'') && (xmlctx->in->current[0] != '\"')) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100743 LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200744 xmlctx->in->current, "either single or double quotation mark");
Michal Vaskob36053d2020-03-26 15:49:30 +0100745 return LY_EVALID;
746 }
747
748 /* remember quote */
Michal Vasko63f3d842020-07-08 10:10:14 +0200749 quot = xmlctx->in->current[0];
Michal Vaskob36053d2020-03-26 15:49:30 +0100750 move_input(xmlctx, 1);
751
752 /* parse attribute value */
753 LY_CHECK_RET(lyxml_parse_value(xmlctx, quot, (char **)value, value_len, ws_only, dynamic));
754
755 /* move after ending quote (without checking for EOF) */
Michal Vasko63f3d842020-07-08 10:10:14 +0200756 ly_in_skip(xmlctx->in, 1);
Michal Vaskob36053d2020-03-26 15:49:30 +0100757
758 return LY_SUCCESS;
759}
760
Michal Vasko8cef5232020-06-15 17:59:47 +0200761/**
762 * @brief Move parser to the next attribute and parse it.
763 *
764 * @param[in] xmlctx XML context to use.
765 * @param[out] prefix Parsed attribute prefix.
766 * @param[out] prefix_len Length of @p prefix.
767 * @param[out] name Parsed attribute name.
768 * @param[out] name_len Length of @p name.
769 * @return LY_ERR value.
770 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100771static LY_ERR
772lyxml_next_attribute(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
773{
774 const char *in;
775 char *value;
776 uint32_t c;
777 size_t parsed, value_len;
Radek Krejci857189e2020-09-01 13:26:36 +0200778 ly_bool ws_only, dynamic;
Michal Vaskob36053d2020-03-26 15:49:30 +0100779
780 /* skip WS */
781 ign_xmlws(xmlctx);
782
783 /* parse only possible attributes */
Michal Vasko63f3d842020-07-08 10:10:14 +0200784 while ((xmlctx->in->current[0] != '>') && (xmlctx->in->current[0] != '/')) {
785 in = xmlctx->in->current;
Michal Vaskob36053d2020-03-26 15:49:30 +0100786 if (in[0] == '\0') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100787 LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
Michal Vaskob36053d2020-03-26 15:49:30 +0100788 return LY_EVALID;
789 } else if ((ly_getutf8(&in, &c, &parsed) || !is_xmlqnamestartchar(c))) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100790 LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(in - parsed), in - parsed,
Michal Vasko69730152020-10-09 16:30:07 +0200791 "element tag end ('>' or '/>') or an attribute");
Michal Vaskob36053d2020-03-26 15:49:30 +0100792 return LY_EVALID;
793 }
794
795 /* parse attribute name */
796 LY_CHECK_RET(lyxml_parse_qname(xmlctx, prefix, prefix_len, name, name_len));
797
798 if ((!*prefix || ly_strncmp("xmlns", *prefix, *prefix_len)) && (*prefix || ly_strncmp("xmlns", *name, *name_len))) {
799 /* standard attribute */
800 break;
801 }
802
803 /* namespace, skip it */
804 LY_CHECK_RET(lyxml_next_attr_content(xmlctx, (const char **)&value, &value_len, &ws_only, &dynamic));
805 if (dynamic) {
806 free(value);
807 }
808
809 /* skip WS */
810 ign_xmlws(xmlctx);
811 }
812
813 return LY_SUCCESS;
814}
815
Michal Vasko8cef5232020-06-15 17:59:47 +0200816/**
817 * @brief Move parser to the next element and parse it.
818 *
819 * @param[in] xmlctx XML context to use.
820 * @param[out] prefix Parsed element prefix.
821 * @param[out] prefix_len Length of @p prefix.
822 * @param[out] name Parse element name.
823 * @param[out] name_len Length of @p name.
Radek Krejci1deb5be2020-08-26 16:43:36 +0200824 * @param[out] closing Flag if the element is closing (includes '/').
Michal Vasko8cef5232020-06-15 17:59:47 +0200825 * @return LY_ERR value.
826 */
Michal Vaskob36053d2020-03-26 15:49:30 +0100827static LY_ERR
828lyxml_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 +0200829 ly_bool *closing)
Michal Vaskob36053d2020-03-26 15:49:30 +0100830{
831 /* skip WS until EOF or after opening tag '<' */
832 LY_CHECK_RET(lyxml_skip_until_end_or_after_otag(xmlctx));
Michal Vasko63f3d842020-07-08 10:10:14 +0200833 if (xmlctx->in->current[0] == '\0') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100834 /* set return values */
835 *prefix = *name = NULL;
836 *prefix_len = *name_len = 0;
837 return LY_SUCCESS;
838 }
839
Michal Vasko63f3d842020-07-08 10:10:14 +0200840 if (xmlctx->in->current[0] == '/') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100841 move_input(xmlctx, 1);
842 *closing = 1;
843 } else {
844 *closing = 0;
845 }
846
847 /* skip WS */
848 ign_xmlws(xmlctx);
849
850 /* parse element name */
851 LY_CHECK_RET(lyxml_parse_qname(xmlctx, prefix, prefix_len, name, name_len));
852
853 return LY_SUCCESS;
854}
855
856LY_ERR
Michal Vasko63f3d842020-07-08 10:10:14 +0200857lyxml_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyxml_ctx **xmlctx_p)
Michal Vaskob36053d2020-03-26 15:49:30 +0100858{
859 LY_ERR ret = LY_SUCCESS;
860 struct lyxml_ctx *xmlctx;
Radek Krejci857189e2020-09-01 13:26:36 +0200861 ly_bool closing;
Michal Vaskob36053d2020-03-26 15:49:30 +0100862
863 /* new context */
864 xmlctx = calloc(1, sizeof *xmlctx);
865 LY_CHECK_ERR_RET(!xmlctx, LOGMEM(ctx), LY_EMEM);
866 xmlctx->ctx = ctx;
Michal Vasko63f3d842020-07-08 10:10:14 +0200867 xmlctx->in = in;
Michal Vaskob36053d2020-03-26 15:49:30 +0100868
Radek Krejciddace2c2021-01-08 11:30:56 +0100869 LOG_LOCINIT(NULL, NULL, NULL, in);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100870
Michal Vaskob36053d2020-03-26 15:49:30 +0100871 /* parse next element, if any */
872 LY_CHECK_GOTO(ret = lyxml_next_element(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name,
Michal Vasko69730152020-10-09 16:30:07 +0200873 &xmlctx->name_len, &closing), cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100874
Michal Vasko63f3d842020-07-08 10:10:14 +0200875 if (xmlctx->in->current[0] == '\0') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100876 /* update status */
877 xmlctx->status = LYXML_END;
878 } else if (closing) {
Radek Krejci422afb12021-03-04 16:38:16 +0100879 LOGVAL(ctx, LYVE_SYNTAX, "Stray closing element tag (\"%.*s\").", (int)xmlctx->name_len, xmlctx->name);
Michal Vaskob36053d2020-03-26 15:49:30 +0100880 ret = LY_EVALID;
881 goto cleanup;
882 } else {
883 /* open an element, also parses all enclosed namespaces */
884 LY_CHECK_GOTO(ret = lyxml_open_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len), cleanup);
885
886 /* update status */
887 xmlctx->status = LYXML_ELEMENT;
888 }
889
890cleanup:
891 if (ret) {
892 lyxml_ctx_free(xmlctx);
893 } else {
894 *xmlctx_p = xmlctx;
895 }
896 return ret;
897}
898
899LY_ERR
900lyxml_ctx_next(struct lyxml_ctx *xmlctx)
901{
902 LY_ERR ret = LY_SUCCESS;
Radek Krejci857189e2020-09-01 13:26:36 +0200903 ly_bool closing;
Michal Vaskob36053d2020-03-26 15:49:30 +0100904 struct lyxml_elem *e;
905
906 /* if the value was not used, free it */
907 if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
908 free((char *)xmlctx->value);
909 xmlctx->value = NULL;
910 xmlctx->dynamic = 0;
911 }
912
913 switch (xmlctx->status) {
Michal Vaskob36053d2020-03-26 15:49:30 +0100914 case LYXML_ELEM_CONTENT:
Radek Krejcif13b87b2020-12-01 22:02:17 +0100915 /* content |</elem> */
916
Michal Vaskob36053d2020-03-26 15:49:30 +0100917 /* handle special case when empty content for "<elem/>" was returned */
Michal Vasko63f3d842020-07-08 10:10:14 +0200918 if (xmlctx->in->current[0] == '/') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100919 assert(xmlctx->elements.count);
920 e = (struct lyxml_elem *)xmlctx->elements.objs[xmlctx->elements.count - 1];
921
922 /* close the element (parses closing tag) */
Michal Vasko63f3d842020-07-08 10:10:14 +0200923 ret = lyxml_close_element(xmlctx, e->prefix, e->prefix_len, e->name, e->name_len, 1);
924 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100925
926 /* update status */
927 xmlctx->status = LYXML_ELEM_CLOSE;
928 break;
929 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100930 /* fall through */
Michal Vaskob36053d2020-03-26 15:49:30 +0100931 case LYXML_ELEM_CLOSE:
Radek Krejcif13b87b2020-12-01 22:02:17 +0100932 /* </elem>| <elem2>* */
933
Michal Vaskob36053d2020-03-26 15:49:30 +0100934 /* parse next element, if any */
Michal Vasko63f3d842020-07-08 10:10:14 +0200935 ret = lyxml_next_element(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name, &xmlctx->name_len, &closing);
936 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100937
Michal Vasko63f3d842020-07-08 10:10:14 +0200938 if (xmlctx->in->current[0] == '\0') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100939 /* update status */
940 xmlctx->status = LYXML_END;
941 } else if (closing) {
942 /* close an element (parses also closing tag) */
Michal Vasko63f3d842020-07-08 10:10:14 +0200943 ret = lyxml_close_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len, 0);
944 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100945
946 /* update status */
947 xmlctx->status = LYXML_ELEM_CLOSE;
948 } else {
949 /* open an element, also parses all enclosed namespaces */
Michal Vasko63f3d842020-07-08 10:10:14 +0200950 ret = lyxml_open_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len);
951 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100952
953 /* update status */
954 xmlctx->status = LYXML_ELEMENT;
955 }
956 break;
957
Michal Vaskob36053d2020-03-26 15:49:30 +0100958 case LYXML_ELEMENT:
Radek Krejcif13b87b2020-12-01 22:02:17 +0100959 /* <elem| attr='val'* > content */
Michal Vaskob36053d2020-03-26 15:49:30 +0100960 case LYXML_ATTR_CONTENT:
Radek Krejcif13b87b2020-12-01 22:02:17 +0100961 /* attr='val'| attr='val'* > content */
962
Michal Vaskob36053d2020-03-26 15:49:30 +0100963 /* parse attribute name, if any */
Michal Vasko63f3d842020-07-08 10:10:14 +0200964 ret = lyxml_next_attribute(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name, &xmlctx->name_len);
965 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100966
Michal Vasko63f3d842020-07-08 10:10:14 +0200967 if (xmlctx->in->current[0] == '>') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100968 /* no attributes but a closing tag */
Michal Vasko63f3d842020-07-08 10:10:14 +0200969 ly_in_skip(xmlctx->in, 1);
970 if (!xmlctx->in->current[0]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100971 LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
Michal Vaskof55ae202020-06-30 15:49:36 +0200972 ret = LY_EVALID;
973 goto cleanup;
974 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100975
976 /* parse element content */
Michal Vasko63f3d842020-07-08 10:10:14 +0200977 ret = lyxml_parse_value(xmlctx, '<', (char **)&xmlctx->value, &xmlctx->value_len, &xmlctx->ws_only,
Michal Vasko69730152020-10-09 16:30:07 +0200978 &xmlctx->dynamic);
Michal Vasko63f3d842020-07-08 10:10:14 +0200979 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +0100980
981 if (!xmlctx->value_len) {
Radek IÅ¡a017270d2021-02-16 10:26:15 +0100982 /* empty value should by alocated staticaly, but check for in any case */
983 if (xmlctx->dynamic) {
984 free((char *) xmlctx->value);
985 }
Michal Vaskob36053d2020-03-26 15:49:30 +0100986 /* use empty value, easier to work with */
987 xmlctx->value = "";
Radek IÅ¡a017270d2021-02-16 10:26:15 +0100988 xmlctx->dynamic = 0;
Michal Vaskob36053d2020-03-26 15:49:30 +0100989 }
990
991 /* update status */
992 xmlctx->status = LYXML_ELEM_CONTENT;
Michal Vasko63f3d842020-07-08 10:10:14 +0200993 } else if (xmlctx->in->current[0] == '/') {
Michal Vaskob36053d2020-03-26 15:49:30 +0100994 /* no content but we still return it */
995 xmlctx->value = "";
996 xmlctx->value_len = 0;
997 xmlctx->ws_only = 1;
998 xmlctx->dynamic = 0;
999
1000 /* update status */
1001 xmlctx->status = LYXML_ELEM_CONTENT;
1002 } else {
1003 /* update status */
1004 xmlctx->status = LYXML_ATTRIBUTE;
1005 }
1006 break;
1007
Michal Vaskob36053d2020-03-26 15:49:30 +01001008 case LYXML_ATTRIBUTE:
Radek Krejcif13b87b2020-12-01 22:02:17 +01001009 /* attr|='val' */
1010
Michal Vaskob36053d2020-03-26 15:49:30 +01001011 /* skip formatting and parse value */
Michal Vasko63f3d842020-07-08 10:10:14 +02001012 ret = lyxml_next_attr_content(xmlctx, &xmlctx->value, &xmlctx->value_len, &xmlctx->ws_only, &xmlctx->dynamic);
1013 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +01001014
1015 /* update status */
1016 xmlctx->status = LYXML_ATTR_CONTENT;
1017 break;
1018
Michal Vaskob36053d2020-03-26 15:49:30 +01001019 case LYXML_END:
Radek Krejcif13b87b2020-12-01 22:02:17 +01001020 /* </elem> |EOF */
Michal Vaskob36053d2020-03-26 15:49:30 +01001021 /* nothing to do */
1022 break;
1023 }
1024
1025cleanup:
1026 if (ret) {
1027 /* invalidate context */
1028 xmlctx->status = LYXML_END;
1029 }
1030 return ret;
1031}
1032
1033LY_ERR
1034lyxml_ctx_peek(struct lyxml_ctx *xmlctx, enum LYXML_PARSER_STATUS *next)
1035{
1036 LY_ERR ret = LY_SUCCESS;
1037 const char *prefix, *name, *prev_input;
1038 size_t prefix_len, name_len;
Radek Krejci857189e2020-09-01 13:26:36 +02001039 ly_bool closing;
Michal Vaskob36053d2020-03-26 15:49:30 +01001040
Michal Vasko63f3d842020-07-08 10:10:14 +02001041 prev_input = xmlctx->in->current;
Michal Vaskob36053d2020-03-26 15:49:30 +01001042
1043 switch (xmlctx->status) {
1044 case LYXML_ELEM_CONTENT:
Michal Vasko63f3d842020-07-08 10:10:14 +02001045 if (xmlctx->in->current[0] == '/') {
Michal Vaskob36053d2020-03-26 15:49:30 +01001046 *next = LYXML_ELEM_CLOSE;
1047 break;
1048 }
Radek Krejcif13b87b2020-12-01 22:02:17 +01001049 /* fall through */
Michal Vaskob36053d2020-03-26 15:49:30 +01001050 case LYXML_ELEM_CLOSE:
1051 /* parse next element, if any */
Michal Vasko63f3d842020-07-08 10:10:14 +02001052 ret = lyxml_next_element(xmlctx, &prefix, &prefix_len, &name, &name_len, &closing);
1053 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +01001054
Michal Vasko63f3d842020-07-08 10:10:14 +02001055 if (xmlctx->in->current[0] == '\0') {
Michal Vaskob36053d2020-03-26 15:49:30 +01001056 *next = LYXML_END;
1057 } else if (closing) {
1058 *next = LYXML_ELEM_CLOSE;
1059 } else {
1060 *next = LYXML_ELEMENT;
1061 }
1062 break;
1063 case LYXML_ELEMENT:
1064 case LYXML_ATTR_CONTENT:
1065 /* parse attribute name, if any */
Michal Vasko63f3d842020-07-08 10:10:14 +02001066 ret = lyxml_next_attribute(xmlctx, &prefix, &prefix_len, &name, &name_len);
1067 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskob36053d2020-03-26 15:49:30 +01001068
Michal Vasko63f3d842020-07-08 10:10:14 +02001069 if ((xmlctx->in->current[0] == '>') || (xmlctx->in->current[0] == '/')) {
Michal Vaskob36053d2020-03-26 15:49:30 +01001070 *next = LYXML_ELEM_CONTENT;
1071 } else {
1072 *next = LYXML_ATTRIBUTE;
1073 }
1074 break;
1075 case LYXML_ATTRIBUTE:
1076 *next = LYXML_ATTR_CONTENT;
1077 break;
1078 case LYXML_END:
1079 *next = LYXML_END;
1080 break;
1081 }
1082
1083cleanup:
Michal Vasko63f3d842020-07-08 10:10:14 +02001084 xmlctx->in->current = prev_input;
Michal Vaskob36053d2020-03-26 15:49:30 +01001085 return ret;
1086}
1087
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001088/**
1089 * @brief Free all namespaces in XML context.
1090 *
1091 * @param[in] xmlctx XML context to use.
1092 */
1093static void
1094lyxml_ns_rm_all(struct lyxml_ctx *xmlctx)
1095{
1096 struct lyxml_ns *ns;
1097 uint32_t i;
1098
1099 for (i = 0; i < xmlctx->ns.count; ++i) {
1100 ns = xmlctx->ns.objs[i];
1101
1102 free(ns->prefix);
1103 free(ns->uri);
1104 free(ns);
1105 }
1106 ly_set_erase(&xmlctx->ns, NULL);
1107}
1108
Michal Vaskob36053d2020-03-26 15:49:30 +01001109void
1110lyxml_ctx_free(struct lyxml_ctx *xmlctx)
1111{
Michal Vaskob36053d2020-03-26 15:49:30 +01001112 if (!xmlctx) {
1113 return;
1114 }
1115
Radek Krejciddace2c2021-01-08 11:30:56 +01001116 LOG_LOCBACK(0, 0, 0, 1);
Radek Krejci2efc45b2020-12-22 16:25:44 +01001117
Michal Vaskob36053d2020-03-26 15:49:30 +01001118 if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
1119 free((char *)xmlctx->value);
1120 }
1121 ly_set_erase(&xmlctx->elements, free);
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001122 lyxml_ns_rm_all(xmlctx);
Michal Vaskob36053d2020-03-26 15:49:30 +01001123 free(xmlctx);
Radek Krejcib1890642018-10-03 14:05:40 +02001124}
Radek Krejcie7b95092019-05-15 11:03:07 +02001125
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001126/**
1127 * @brief Duplicate an XML element.
1128 *
1129 * @param[in] elem Element to duplicate.
1130 * @return Element duplicate.
1131 * @return NULL on error.
1132 */
1133static struct lyxml_elem *
1134lyxml_elem_dup(const struct lyxml_elem *elem)
1135{
1136 struct lyxml_elem *dup;
1137
1138 dup = malloc(sizeof *dup);
1139 LY_CHECK_ERR_RET(!dup, LOGMEM(NULL), NULL);
1140
1141 memcpy(dup, elem, sizeof *dup);
1142
1143 return dup;
1144}
1145
1146/**
1147 * @brief Duplicate an XML namespace.
1148 *
1149 * @param[in] ns Namespace to duplicate.
1150 * @return Namespace duplicate.
1151 * @return NULL on error.
1152 */
1153static struct lyxml_ns *
1154lyxml_ns_dup(const struct lyxml_ns *ns)
1155{
1156 struct lyxml_ns *dup;
1157
1158 dup = malloc(sizeof *dup);
1159 LY_CHECK_ERR_RET(!dup, LOGMEM(NULL), NULL);
1160
1161 if (ns->prefix) {
1162 dup->prefix = strdup(ns->prefix);
1163 LY_CHECK_ERR_RET(!dup->prefix, LOGMEM(NULL); free(dup), NULL);
1164 } else {
1165 dup->prefix = NULL;
1166 }
1167 dup->uri = strdup(ns->uri);
1168 LY_CHECK_ERR_RET(!dup->uri, LOGMEM(NULL); free(dup->prefix); free(dup), NULL);
1169 dup->depth = ns->depth;
1170
1171 return dup;
1172}
1173
1174LY_ERR
1175lyxml_ctx_backup(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup)
1176{
1177 uint32_t i;
1178
1179 /* first make shallow copy */
1180 memcpy(backup, xmlctx, sizeof *backup);
1181
1182 if ((xmlctx->status == LYXML_ELEM_CONTENT) && xmlctx->dynamic) {
1183 /* it was backed up, do not free */
1184 xmlctx->dynamic = 0;
1185 }
1186
1187 /* backup in current pointer only */
1188 backup->in = (void *)xmlctx->in->current;
1189
1190 /* duplicate elements */
1191 backup->elements.objs = malloc(xmlctx->elements.size * sizeof(struct lyxml_elem));
Michal Vasko0c2403d2021-09-15 08:52:18 +02001192 LY_CHECK_ERR_RET(!backup->elements.objs, LOGMEM(xmlctx->ctx), LY_EMEM);
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001193 for (i = 0; i < xmlctx->elements.count; ++i) {
1194 backup->elements.objs[i] = lyxml_elem_dup(xmlctx->elements.objs[i]);
Michal Vasko0c2403d2021-09-15 08:52:18 +02001195 LY_CHECK_RET(!backup->elements.objs[i], LY_EMEM);
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001196 }
1197
1198 /* duplicate ns */
1199 backup->ns.objs = malloc(xmlctx->ns.size * sizeof(struct lyxml_ns));
Michal Vasko0c2403d2021-09-15 08:52:18 +02001200 LY_CHECK_ERR_RET(!backup->ns.objs, LOGMEM(xmlctx->ctx), LY_EMEM);
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001201 for (i = 0; i < xmlctx->ns.count; ++i) {
1202 backup->ns.objs[i] = lyxml_ns_dup(xmlctx->ns.objs[i]);
Michal Vasko0c2403d2021-09-15 08:52:18 +02001203 LY_CHECK_RET(!backup->ns.objs[i], LY_EMEM);
Michal Vaskoda8fbbf2021-06-16 11:44:44 +02001204 }
1205
1206 return LY_SUCCESS;
1207}
1208
1209void
1210lyxml_ctx_restore(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup)
1211{
1212 if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
1213 /* free dynamic value */
1214 free((char *)xmlctx->value);
1215 }
1216
1217 /* free elements */
1218 ly_set_erase(&xmlctx->elements, free);
1219
1220 /* free ns */
1221 lyxml_ns_rm_all(xmlctx);
1222
1223 /* restore in current pointer */
1224 xmlctx->in->current = (void *)backup->in;
1225 backup->in = xmlctx->in;
1226
1227 /* restore backup */
1228 memcpy(xmlctx, backup, sizeof *xmlctx);
1229}
1230
Radek Krejcie7b95092019-05-15 11:03:07 +02001231LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +02001232lyxml_dump_text(struct ly_out *out, const char *text, ly_bool attribute)
Radek Krejcie7b95092019-05-15 11:03:07 +02001233{
Michal Vasko5233e962020-08-14 14:26:20 +02001234 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +02001235
1236 if (!text) {
1237 return 0;
1238 }
1239
Radek Krejci1deb5be2020-08-26 16:43:36 +02001240 for (uint64_t u = 0; text[u]; u++) {
Radek Krejcie7b95092019-05-15 11:03:07 +02001241 switch (text[u]) {
1242 case '&':
Michal Vasko5233e962020-08-14 14:26:20 +02001243 ret = ly_print_(out, "&amp;");
Radek Krejcie7b95092019-05-15 11:03:07 +02001244 break;
1245 case '<':
Michal Vasko5233e962020-08-14 14:26:20 +02001246 ret = ly_print_(out, "&lt;");
Radek Krejcie7b95092019-05-15 11:03:07 +02001247 break;
1248 case '>':
1249 /* not needed, just for readability */
Michal Vasko5233e962020-08-14 14:26:20 +02001250 ret = ly_print_(out, "&gt;");
Radek Krejcie7b95092019-05-15 11:03:07 +02001251 break;
1252 case '"':
1253 if (attribute) {
Michal Vasko5233e962020-08-14 14:26:20 +02001254 ret = ly_print_(out, "&quot;");
Radek Krejcie7b95092019-05-15 11:03:07 +02001255 break;
1256 }
Radek Krejcif13b87b2020-12-01 22:02:17 +01001257 /* fall through */
Radek Krejcie7b95092019-05-15 11:03:07 +02001258 default:
Michal Vasko5233e962020-08-14 14:26:20 +02001259 ret = ly_write_(out, &text[u], 1);
1260 break;
Radek Krejcie7b95092019-05-15 11:03:07 +02001261 }
Michal Vasko5233e962020-08-14 14:26:20 +02001262 LY_CHECK_RET(ret);
Radek Krejcie7b95092019-05-15 11:03:07 +02001263 }
1264
Michal Vasko5233e962020-08-14 14:26:20 +02001265 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +02001266}
1267
Michal Vasko52927e22020-03-16 17:26:14 +01001268LY_ERR
aPiecek2f63f952021-03-30 12:22:18 +02001269lyxml_value_compare(const struct ly_ctx *ctx1, const char *value1, void *val_prefix_data1,
1270 const struct ly_ctx *ctx2, const char *value2, void *val_prefix_data2)
Michal Vasko52927e22020-03-16 17:26:14 +01001271{
aPiecek2f63f952021-03-30 12:22:18 +02001272 const char *value1_iter, *value2_iter;
1273 const char *value1_next, *value2_next;
1274 uint32_t value1_len, value2_len;
1275 ly_bool is_prefix1, is_prefix2;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +01001276 const struct lys_module *mod1, *mod2;
aPiecek2f63f952021-03-30 12:22:18 +02001277 LY_ERR ret;
Michal Vasko52927e22020-03-16 17:26:14 +01001278
1279 if (!value1 && !value2) {
1280 return LY_SUCCESS;
1281 }
1282 if ((value1 && !value2) || (!value1 && value2)) {
1283 return LY_ENOT;
1284 }
1285
aPiecek2f63f952021-03-30 12:22:18 +02001286 if (!ctx2) {
1287 ctx2 = ctx1;
1288 }
Michal Vasko52927e22020-03-16 17:26:14 +01001289
aPiecek2f63f952021-03-30 12:22:18 +02001290 ret = LY_SUCCESS;
1291 for (value1_iter = value1, value2_iter = value2;
1292 value1_iter && value2_iter;
1293 value1_iter = value1_next, value2_iter = value2_next) {
aPieceke3f828d2021-05-10 15:34:41 +02001294 if ((ret = ly_value_prefix_next(value1_iter, NULL, &value1_len, &is_prefix1, &value1_next))) {
1295 break;
1296 }
1297 if ((ret = ly_value_prefix_next(value2_iter, NULL, &value2_len, &is_prefix2, &value2_next))) {
1298 break;
1299 }
aPiecek2f63f952021-03-30 12:22:18 +02001300
1301 if (is_prefix1 != is_prefix2) {
1302 ret = LY_ENOT;
1303 break;
1304 }
1305
1306 if (!is_prefix1) {
1307 if (value1_len != value2_len) {
1308 ret = LY_ENOT;
1309 break;
1310 }
1311 if (strncmp(value1_iter, value2_iter, value1_len)) {
1312 ret = LY_ENOT;
1313 break;
1314 }
1315 continue;
1316 }
1317
1318 mod1 = mod2 = NULL;
1319 if (val_prefix_data1) {
1320 /* find module of the first prefix, if any */
Radek Krejci8df109d2021-04-23 12:19:08 +02001321 mod1 = ly_resolve_prefix(ctx1, value1_iter, value1_len, LY_VALUE_XML, val_prefix_data1);
aPiecek2f63f952021-03-30 12:22:18 +02001322 }
1323 if (val_prefix_data2) {
Radek Krejci8df109d2021-04-23 12:19:08 +02001324 mod2 = ly_resolve_prefix(ctx2, value2_iter, value2_len, LY_VALUE_XML, val_prefix_data2);
aPiecek2f63f952021-03-30 12:22:18 +02001325 }
1326 if (!mod1 || !mod2) {
1327 /* not a prefix or maps to different namespaces */
1328 ret = LY_ENOT;
1329 break;
1330 }
1331
1332 if (mod1->ctx == mod2->ctx) {
1333 /* same contexts */
1334 if ((mod1->name != mod2->name) || (mod1->revision != mod2->revision)) {
1335 ret = LY_ENOT;
1336 break;
1337 }
1338 } else {
1339 /* different contexts */
1340 if (strcmp(mod1->name, mod2->name)) {
1341 ret = LY_ENOT;
Michal Vasko52927e22020-03-16 17:26:14 +01001342 break;
1343 }
1344
aPiecek2f63f952021-03-30 12:22:18 +02001345 if (mod1->revision || mod2->revision) {
1346 if (!mod1->revision || !mod2->revision) {
1347 ret = LY_ENOT;
1348 break;
1349 }
1350 if (strcmp(mod1->revision, mod2->revision)) {
1351 ret = LY_ENOT;
1352 break;
1353 }
1354 }
Michal Vasko52927e22020-03-16 17:26:14 +01001355 }
Michal Vasko52927e22020-03-16 17:26:14 +01001356 }
1357
aPiecek2f63f952021-03-30 12:22:18 +02001358 if (value1_iter || value2_iter) {
1359 ret = LY_ENOT;
1360 }
1361
1362 return ret;
Michal Vasko52927e22020-03-16 17:26:14 +01001363}