Coverage Report

Created: 2026-01-25 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libyang/src/xml.c
Line
Count
Source
1
/**
2
 * @file xml.c
3
 * @author Radek Krejci <rkrejci@cesnet.cz>
4
 * @author Michal Vasko <mvasko@cesnet.cz>
5
 * @brief Generic XML parser implementation for libyang
6
 *
7
 * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
8
 *
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
16
#define _GNU_SOURCE
17
18
#include "xml.h"
19
20
#include <assert.h>
21
#include <ctype.h>
22
#include <stdint.h>
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include "common.h"
27
#include "compat.h"
28
#include "in_internal.h"
29
#include "out_internal.h"
30
#include "tree.h"
31
#include "tree_schema_internal.h"
32
33
/* Move input p by s characters, if EOF log with lyxml_ctx c */
34
#define move_input(c, s) \
35
0
    ly_in_skip(c->in, s); \
36
0
    LY_CHECK_ERR_RET(!c->in->current[0], LOGVAL(c->ctx, LY_VCODE_EOF), LY_EVALID)
37
38
/* Ignore whitespaces in the input string p */
39
#define ign_xmlws(c) \
40
0
    while (is_xmlws(*(c)->in->current)) { \
41
0
        if (*(c)->in->current == '\n') {  \
42
0
            LY_IN_NEW_LINE((c)->in);      \
43
0
        }                                 \
44
0
        ly_in_skip(c->in, 1);             \
45
0
    }
46
47
static 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);
49
50
/**
51
 * @brief Ignore and skip any characters until the delim of the size delim_len is read, including the delim
52
 *
53
 * @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.
58
 */
59
LY_ERR
60
skip_section(struct lyxml_ctx *xmlctx, const char *delim, size_t delim_len, const char *sectname)
61
0
{
62
0
    size_t i;
63
0
    register const char *input, *a, *b;
64
0
    uint64_t parsed = 0, newlines = 0;
65
66
0
    for (input = xmlctx->in->current; *input; ++input, ++parsed) {
67
0
        if (*input != *delim) {
68
0
            if (*input == '\n') {
69
0
                ++newlines;
70
0
            }
71
0
            continue;
72
0
        }
73
0
        a = input;
74
0
        b = delim;
75
0
        for (i = 0; i < delim_len; ++i) {
76
0
            if (*a++ != *b++) {
77
0
                break;
78
0
            }
79
0
        }
80
0
        if (i == delim_len) {
81
            /* delim found */
82
0
            xmlctx->in->line += newlines;
83
0
            ly_in_skip(xmlctx->in, parsed + delim_len);
84
0
            return LY_SUCCESS;
85
0
        }
86
0
    }
87
88
    /* delim not found,
89
     * do not update input handler to refer to the beginning of the section in error message */
90
0
    LOGVAL(xmlctx->ctx, LY_VCODE_NTERM, sectname);
91
0
    return LY_EVALID;
92
0
}
93
94
/**
95
 * @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
 */
107
static LY_ERR
108
lyxml_parse_identifier(struct lyxml_ctx *xmlctx, const char **start, const char **end)
109
0
{
110
0
    const char *s, *in;
111
0
    uint32_t c;
112
0
    size_t parsed;
113
0
    LY_ERR rc;
114
115
0
    in = s = xmlctx->in->current;
116
117
    /* check NameStartChar (minus colon) */
118
0
    LY_CHECK_ERR_RET(ly_getutf8(&in, &c, &parsed),
119
0
            LOGVAL(xmlctx->ctx, LY_VCODE_INCHAR, in[0]),
120
0
            LY_EVALID);
121
0
    LY_CHECK_ERR_RET(!is_xmlqnamestartchar(c),
122
0
            LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Identifier \"%s\" starts with an invalid character.", in - parsed),
123
0
            LY_EVALID);
124
125
    /* check rest of the identifier */
126
0
    do {
127
        /* move only successfully parsed bytes */
128
0
        ly_in_skip(xmlctx->in, parsed);
129
130
0
        rc = ly_getutf8(&in, &c, &parsed);
131
0
        LY_CHECK_ERR_RET(rc, LOGVAL(xmlctx->ctx, LY_VCODE_INCHAR, in[0]), LY_EVALID);
132
0
    } while (is_xmlqnamechar(c));
133
134
0
    *start = s;
135
0
    *end = xmlctx->in->current;
136
0
    return LY_SUCCESS;
137
0
}
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
 */
156
LY_ERR
157
lyxml_ns_add(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, char *uri)
158
0
{
159
0
    LY_ERR ret = LY_SUCCESS;
160
0
    struct lyxml_ns *ns;
161
162
0
    ns = malloc(sizeof *ns);
163
0
    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
0
    ns->depth = xmlctx->elements.count;
169
170
0
    ns->uri = uri;
171
0
    if (prefix) {
172
0
        ns->prefix = strndup(prefix, prefix_len);
173
0
        LY_CHECK_ERR_RET(!ns->prefix, LOGMEM(xmlctx->ctx); free(ns->uri); free(ns), LY_EMEM);
174
0
    } else {
175
0
        ns->prefix = NULL;
176
0
    }
177
178
0
    ret = ly_set_add(&xmlctx->ns, ns, 1, NULL);
179
0
    LY_CHECK_ERR_RET(ret, free(ns->prefix); free(ns->uri); free(ns), ret);
180
181
0
    return LY_SUCCESS;
182
0
}
183
184
/**
185
 * @brief Remove all the namespaces defined in the element recently closed (removed from the xmlctx->elements).
186
 *
187
 * @param[in] xmlctx XML context to work with.
188
 */
189
void
190
lyxml_ns_rm(struct lyxml_ctx *xmlctx)
191
0
{
192
0
    for (uint32_t u = xmlctx->ns.count - 1; u + 1 > 0; --u) {
193
0
        if (((struct lyxml_ns *)xmlctx->ns.objs[u])->depth != xmlctx->elements.count + 1) {
194
            /* we are done, the namespaces from a single element are supposed to be together */
195
0
            break;
196
0
        }
197
        /* remove the ns structure */
198
0
        free(((struct lyxml_ns *)xmlctx->ns.objs[u])->prefix);
199
0
        free(((struct lyxml_ns *)xmlctx->ns.objs[u])->uri);
200
0
        free(xmlctx->ns.objs[u]);
201
0
        --xmlctx->ns.count;
202
0
    }
203
204
0
    if (!xmlctx->ns.count) {
205
        /* cleanup the xmlctx's namespaces storage */
206
0
        ly_set_erase(&xmlctx->ns, NULL);
207
0
    }
208
0
}
209
210
const struct lyxml_ns *
211
lyxml_ns_get(const struct ly_set *ns_set, const char *prefix, size_t prefix_len)
212
0
{
213
0
    struct lyxml_ns *ns;
214
215
0
    for (uint32_t u = ns_set->count - 1; u + 1 > 0; --u) {
216
0
        ns = (struct lyxml_ns *)ns_set->objs[u];
217
0
        if (prefix && prefix_len) {
218
0
            if (ns->prefix && !ly_strncmp(ns->prefix, prefix, prefix_len)) {
219
0
                return ns;
220
0
            }
221
0
        } else if (!ns->prefix) {
222
            /* default namespace */
223
0
            return ns;
224
0
        }
225
0
    }
226
227
0
    return NULL;
228
0
}
229
230
/**
231
 * @brief Skip in the input until EOF or just after the opening tag.
232
 * Handles special XML constructs (comment, cdata, doctype).
233
 *
234
 * @param[in] xmlctx XML context to use.
235
 * @return LY_ERR value.
236
 */
237
static LY_ERR
238
lyxml_skip_until_end_or_after_otag(struct lyxml_ctx *xmlctx)
239
0
{
240
0
    const struct ly_ctx *ctx = xmlctx->ctx; /* shortcut */
241
0
    const char *endtag, *sectname;
242
0
    size_t endtag_len;
243
244
0
    while (1) {
245
0
        ign_xmlws(xmlctx);
246
247
0
        if (xmlctx->in->current[0] == '\0') {
248
            /* EOF */
249
0
            if (xmlctx->elements.count) {
250
0
                LOGVAL(ctx, LY_VCODE_EOF);
251
0
                return LY_EVALID;
252
0
            }
253
0
            return LY_SUCCESS;
254
0
        } else if (xmlctx->in->current[0] != '<') {
255
0
            LOGVAL(ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
256
0
                    xmlctx->in->current, "element tag start ('<')");
257
0
            return LY_EVALID;
258
0
        }
259
0
        move_input(xmlctx, 1);
260
261
0
        if (xmlctx->in->current[0] == '!') {
262
0
            move_input(xmlctx, 1);
263
            /* sections to ignore */
264
0
            if (!strncmp(xmlctx->in->current, "--", 2)) {
265
                /* comment */
266
0
                move_input(xmlctx, 2);
267
0
                sectname = "Comment";
268
0
                endtag = "-->";
269
0
                endtag_len = ly_strlen_const("-->");
270
0
            } else if (!strncmp(xmlctx->in->current, "[CDATA[", ly_strlen_const("[CDATA["))) {
271
                /* CDATA section */
272
0
                move_input(xmlctx, ly_strlen_const("[CDATA["));
273
0
                sectname = "CData";
274
0
                endtag = "]]>";
275
0
                endtag_len = ly_strlen_const("]]>");
276
0
            } else if (!strncmp(xmlctx->in->current, "DOCTYPE", ly_strlen_const("DOCTYPE"))) {
277
                /* Document type declaration - not supported */
278
0
                LOGVAL(ctx,  LY_VCODE_NSUPP, "Document Type Declaration");
279
0
                return LY_EVALID;
280
0
            } else {
281
0
                LOGVAL(ctx, LYVE_SYNTAX, "Unknown XML section \"%.20s\".", &xmlctx->in->current[-2]);
282
0
                return LY_EVALID;
283
0
            }
284
0
            LY_CHECK_RET(skip_section(xmlctx, endtag, endtag_len, sectname));
285
0
        } else if (xmlctx->in->current[0] == '?') {
286
0
            LY_CHECK_RET(skip_section(xmlctx, "?>", 2, "Declaration"));
287
0
        } else {
288
            /* other non-WS character */
289
0
            break;
290
0
        }
291
0
    }
292
293
0
    return LY_SUCCESS;
294
0
}
295
296
/**
297
 * @brief Parse QName.
298
 *
299
 * @param[in] xmlctx XML context to use.
300
 * @param[out] prefix Parsed prefix, may be NULL.
301
 * @param[out] prefix_len Length of @p prefix.
302
 * @param[out] name Parsed name.
303
 * @param[out] name_len Length of @p name.
304
 * @return LY_ERR value.
305
 */
306
static LY_ERR
307
lyxml_parse_qname(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
308
0
{
309
0
    const char *start, *end;
310
311
0
    *prefix = NULL;
312
0
    *prefix_len = 0;
313
314
0
    LY_CHECK_RET(lyxml_parse_identifier(xmlctx, &start, &end));
315
0
    if (end[0] == ':') {
316
        /* we have prefixed identifier */
317
0
        *prefix = start;
318
0
        *prefix_len = end - start;
319
320
0
        move_input(xmlctx, 1);
321
0
        LY_CHECK_RET(lyxml_parse_identifier(xmlctx, &start, &end));
322
0
    }
323
324
0
    *name = start;
325
0
    *name_len = end - start;
326
0
    return LY_SUCCESS;
327
0
}
328
329
/**
330
 * @brief Parse XML text content (value).
331
 *
332
 * @param[in] xmlctx XML context to use.
333
 * @param[in] endchar Expected character to mark value end.
334
 * @param[out] value Parsed value.
335
 * @param[out] length Length of @p value.
336
 * @param[out] ws_only Whether the value is empty/white-spaces only.
337
 * @param[out] dynamic Whether the value was dynamically allocated.
338
 * @return LY_ERR value.
339
 */
340
static LY_ERR
341
lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *length, ly_bool *ws_only, ly_bool *dynamic)
342
0
{
343
0
#define BUFSIZE 24
344
0
#define BUFSIZE_STEP 128
345
346
0
    const struct ly_ctx *ctx = xmlctx->ctx; /* shortcut */
347
0
    const char *in = xmlctx->in->current, *start, *in_aux;
348
0
    char *buf = NULL;
349
0
    size_t offset;   /* read offset in input buffer */
350
0
    size_t len;      /* length of the output string (write offset in output buffer) */
351
0
    size_t size = 0; /* size of the output buffer */
352
0
    void *p;
353
0
    uint32_t n;
354
0
    size_t u;
355
0
    ly_bool ws = 1;
356
357
0
    assert(xmlctx);
358
359
    /* init */
360
0
    start = in;
361
0
    offset = len = 0;
362
363
    /* parse */
364
0
    while (in[offset]) {
365
0
        if (in[offset] == '&') {
366
            /* non WS */
367
0
            ws = 0;
368
369
0
            if (!buf) {
370
                /* prepare output buffer */
371
0
                buf = malloc(BUFSIZE);
372
0
                LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
373
0
                size = BUFSIZE;
374
0
            }
375
376
            /* allocate enough for the offset and next character,
377
             * we will need 4 bytes at most since we support only the predefined
378
             * (one-char) entities and character references */
379
0
            while (len + offset + 4 >= size) {
380
0
                buf = ly_realloc(buf, size + BUFSIZE_STEP);
381
0
                LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
382
0
                size += BUFSIZE_STEP;
383
0
            }
384
385
0
            if (offset) {
386
                /* store what we have so far */
387
0
                memcpy(&buf[len], in, offset);
388
0
                len += offset;
389
0
                in += offset;
390
0
                offset = 0;
391
0
            }
392
393
0
            ++offset;
394
0
            if (in[offset] != '#') {
395
                /* entity reference - only predefined references are supported */
396
0
                if (!strncmp(&in[offset], "lt;", ly_strlen_const("lt;"))) {
397
0
                    buf[len++] = '<';
398
0
                    in += ly_strlen_const("&lt;");
399
0
                } else if (!strncmp(&in[offset], "gt;", ly_strlen_const("gt;"))) {
400
0
                    buf[len++] = '>';
401
0
                    in += ly_strlen_const("&gt;");
402
0
                } else if (!strncmp(&in[offset], "amp;", ly_strlen_const("amp;"))) {
403
0
                    buf[len++] = '&';
404
0
                    in += ly_strlen_const("&amp;");
405
0
                } else if (!strncmp(&in[offset], "apos;", ly_strlen_const("apos;"))) {
406
0
                    buf[len++] = '\'';
407
0
                    in += ly_strlen_const("&apos;");
408
0
                } else if (!strncmp(&in[offset], "quot;", ly_strlen_const("quot;"))) {
409
0
                    buf[len++] = '\"';
410
0
                    in += ly_strlen_const("&quot;");
411
0
                } else {
412
0
                    LOGVAL(ctx, LYVE_SYNTAX, "Entity reference \"%.*s\" not supported, only predefined references allowed.",
413
0
                            10, &in[offset - 1]);
414
0
                    goto error;
415
0
                }
416
0
                offset = 0;
417
0
            } else {
418
0
                p = (void *)&in[offset - 1];
419
                /* character reference */
420
0
                ++offset;
421
0
                if (isdigit(in[offset])) {
422
0
                    for (n = 0; isdigit(in[offset]); offset++) {
423
0
                        n = (LY_BASE_DEC * n) + (in[offset] - '0');
424
0
                    }
425
0
                } else if ((in[offset] == 'x') && isxdigit(in[offset + 1])) {
426
0
                    for (n = 0, ++offset; isxdigit(in[offset]); offset++) {
427
0
                        if (isdigit(in[offset])) {
428
0
                            u = (in[offset] - '0');
429
0
                        } else if (in[offset] > 'F') {
430
0
                            u = LY_BASE_DEC + (in[offset] - 'a');
431
0
                        } else {
432
0
                            u = LY_BASE_DEC + (in[offset] - 'A');
433
0
                        }
434
0
                        n = (LY_BASE_HEX * n) + u;
435
0
                    }
436
0
                } else {
437
0
                    LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\".", 12, p);
438
0
                    goto error;
439
440
0
                }
441
442
0
                LY_CHECK_ERR_GOTO(in[offset] != ';',
443
0
                        LOGVAL(ctx, LY_VCODE_INSTREXP,
444
0
                        LY_VCODE_INSTREXP_len(&in[offset]), &in[offset], ";"),
445
0
                        error);
446
0
                ++offset;
447
0
                LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], n, &u),
448
0
                        LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).", 12, p, n),
449
0
                        error);
450
0
                len += u;
451
0
                in += offset;
452
0
                offset = 0;
453
0
            }
454
0
        } else if (in[offset] == endchar) {
455
            /* end of string */
456
0
            if (buf) {
457
                /* realloc exact size string */
458
0
                buf = ly_realloc(buf, len + offset + 1);
459
0
                LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
460
0
                size = len + offset + 1;
461
0
                memcpy(&buf[len], in, offset);
462
463
                /* set terminating NULL byte */
464
0
                buf[len + offset] = '\0';
465
0
            }
466
0
            len += offset;
467
0
            in += offset;
468
0
            goto success;
469
0
        } else {
470
0
            if (!is_xmlws(in[offset])) {
471
                /* non WS */
472
0
                ws = 0;
473
0
            }
474
475
            /* log lines */
476
0
            if (in[offset] == '\n') {
477
0
                LY_IN_NEW_LINE(xmlctx->in);
478
0
            }
479
480
            /* continue */
481
0
            in_aux = &in[offset];
482
0
            LY_CHECK_ERR_GOTO(ly_getutf8(&in_aux, &n, &u),
483
0
                    LOGVAL(ctx, LY_VCODE_INCHAR, in[offset]), error);
484
0
            offset += u;
485
0
        }
486
0
    }
487
488
    /* EOF reached before endchar */
489
0
    LOGVAL(ctx, LY_VCODE_EOF);
490
491
0
error:
492
0
    free(buf);
493
0
    return LY_EVALID;
494
495
0
success:
496
0
    if (buf) {
497
0
        *value = buf;
498
0
        *dynamic = 1;
499
0
    } else {
500
0
        *value = (char *)start;
501
0
        *dynamic = 0;
502
0
    }
503
0
    *length = len;
504
0
    *ws_only = ws;
505
506
0
    xmlctx->in->current = in;
507
0
    return LY_SUCCESS;
508
509
0
#undef BUFSIZE
510
0
#undef BUFSIZE_STEP
511
0
}
512
513
/**
514
 * @brief Parse XML closing element and match it to a stored starting element.
515
 *
516
 * @param[in] xmlctx XML context to use.
517
 * @param[in] prefix Expected closing element prefix.
518
 * @param[in] prefix_len Length of @p prefix.
519
 * @param[in] name Expected closing element name.
520
 * @param[in] name_len Length of @p name.
521
 * @param[in] empty Whether we are parsing a special "empty" element (with joined starting and closing tag) with no value.
522
 * @return LY_ERR value.
523
 */
524
static LY_ERR
525
lyxml_close_element(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, const char *name, size_t name_len,
526
        ly_bool empty)
527
0
{
528
0
    struct lyxml_elem *e;
529
530
    /* match opening and closing element tags */
531
0
    if (!xmlctx->elements.count) {
532
0
        LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Stray closing element tag (\"%.*s\").",
533
0
                (int)name_len, name);
534
0
        return LY_EVALID;
535
0
    }
536
537
0
    e = (struct lyxml_elem *)xmlctx->elements.objs[xmlctx->elements.count - 1];
538
0
    if ((e->prefix_len != prefix_len) || (e->name_len != name_len) ||
539
0
            (prefix_len && strncmp(prefix, e->prefix, e->prefix_len)) || strncmp(name, e->name, e->name_len)) {
540
0
        LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Opening (\"%.*s%s%.*s\") and closing (\"%.*s%s%.*s\") elements tag mismatch.",
541
0
                (int)e->prefix_len, e->prefix ? e->prefix : "", e->prefix ? ":" : "", (int)e->name_len, e->name,
542
0
                (int)prefix_len, prefix ? prefix : "", prefix ? ":" : "", (int)name_len, name);
543
0
        return LY_EVALID;
544
0
    }
545
546
    /* opening and closing element tags matches, remove record from the opening tags list */
547
0
    ly_set_rm_index(&xmlctx->elements, xmlctx->elements.count - 1, free);
548
549
    /* remove also the namespaces connected with the element */
550
0
    lyxml_ns_rm(xmlctx);
551
552
    /* skip WS */
553
0
    ign_xmlws(xmlctx);
554
555
    /* special "<elem/>" element */
556
0
    if (empty && (xmlctx->in->current[0] == '/')) {
557
0
        move_input(xmlctx, 1);
558
0
    }
559
560
    /* parse closing tag */
561
0
    if (xmlctx->in->current[0] != '>') {
562
0
        LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
563
0
                xmlctx->in->current, "element tag termination ('>')");
564
0
        return LY_EVALID;
565
0
    }
566
567
    /* move after closing tag without checking for EOF */
568
0
    ly_in_skip(xmlctx->in, 1);
569
570
0
    return LY_SUCCESS;
571
0
}
572
573
/**
574
 * @brief Store parsed opening element and parse any included namespaces.
575
 *
576
 * @param[in] xmlctx XML context to use.
577
 * @param[in] prefix Parsed starting element prefix.
578
 * @param[in] prefix_len Length of @p prefix.
579
 * @param[in] name Parsed starting element name.
580
 * @param[in] name_len Length of @p name.
581
 * @return LY_ERR value.
582
 */
583
static LY_ERR
584
lyxml_open_element(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, const char *name, size_t name_len)
585
0
{
586
0
    LY_ERR ret = LY_SUCCESS;
587
0
    struct lyxml_elem *e;
588
0
    const char *prev_input;
589
0
    char *value;
590
0
    size_t parsed, value_len;
591
0
    ly_bool ws_only, dynamic, is_ns;
592
0
    uint32_t c;
593
594
    /* store element opening tag information */
595
0
    e = malloc(sizeof *e);
596
0
    LY_CHECK_ERR_RET(!e, LOGMEM(xmlctx->ctx), LY_EMEM);
597
0
    e->name = name;
598
0
    e->prefix = prefix;
599
0
    e->name_len = name_len;
600
0
    e->prefix_len = prefix_len;
601
0
    LY_CHECK_RET(ly_set_add(&xmlctx->elements, e, 1, NULL));
602
603
    /* skip WS */
604
0
    ign_xmlws(xmlctx);
605
606
    /* parse and store all namespaces */
607
0
    prev_input = xmlctx->in->current;
608
0
    is_ns = 1;
609
0
    while ((xmlctx->in->current[0] != '\0') && !(ret = ly_getutf8(&xmlctx->in->current, &c, &parsed))) {
610
0
        if (!is_xmlqnamestartchar(c)) {
611
0
            break;
612
0
        }
613
0
        xmlctx->in->current -= parsed;
614
615
        /* parse attribute name */
616
0
        LY_CHECK_GOTO(ret = lyxml_parse_qname(xmlctx, &prefix, &prefix_len, &name, &name_len), cleanup);
617
618
        /* parse the value */
619
0
        LY_CHECK_GOTO(ret = lyxml_next_attr_content(xmlctx, (const char **)&value, &value_len, &ws_only, &dynamic), cleanup);
620
621
        /* store every namespace */
622
0
        if ((prefix && !ly_strncmp("xmlns", prefix, prefix_len)) || (!prefix && !ly_strncmp("xmlns", name, name_len))) {
623
0
            ret = lyxml_ns_add(xmlctx, prefix ? name : NULL, prefix ? name_len : 0,
624
0
                    dynamic ? value : strndup(value, value_len));
625
0
            dynamic = 0;
626
0
            LY_CHECK_GOTO(ret, cleanup);
627
0
        } else {
628
            /* not a namespace */
629
0
            is_ns = 0;
630
0
        }
631
0
        if (dynamic) {
632
0
            free(value);
633
0
        }
634
635
        /* skip WS */
636
0
        ign_xmlws(xmlctx);
637
638
0
        if (is_ns) {
639
            /* we can actually skip all the namespaces as there is no reason to parse them again */
640
0
            prev_input = xmlctx->in->current;
641
0
        }
642
0
    }
643
644
0
cleanup:
645
0
    if (!ret) {
646
0
        xmlctx->in->current = prev_input;
647
0
    }
648
0
    return ret;
649
0
}
650
651
/**
652
 * @brief Move parser to the attribute content and parse it.
653
 *
654
 * @param[in] xmlctx XML context to use.
655
 * @param[out] value Parsed attribute value.
656
 * @param[out] value_len Length of @p value.
657
 * @param[out] ws_only Whether the value is empty/white-spaces only.
658
 * @param[out] dynamic Whether the value was dynamically allocated.
659
 * @return LY_ERR value.
660
 */
661
static LY_ERR
662
lyxml_next_attr_content(struct lyxml_ctx *xmlctx, const char **value, size_t *value_len, ly_bool *ws_only, ly_bool *dynamic)
663
0
{
664
0
    char quot;
665
666
    /* skip WS */
667
0
    ign_xmlws(xmlctx);
668
669
    /* skip '=' */
670
0
    if (xmlctx->in->current[0] == '\0') {
671
0
        LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
672
0
        return LY_EVALID;
673
0
    } else if (xmlctx->in->current[0] != '=') {
674
0
        LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
675
0
                xmlctx->in->current, "'='");
676
0
        return LY_EVALID;
677
0
    }
678
0
    move_input(xmlctx, 1);
679
680
    /* skip WS */
681
0
    ign_xmlws(xmlctx);
682
683
    /* find quotes */
684
0
    if (xmlctx->in->current[0] == '\0') {
685
0
        LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
686
0
        return LY_EVALID;
687
0
    } else if ((xmlctx->in->current[0] != '\'') && (xmlctx->in->current[0] != '\"')) {
688
0
        LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(xmlctx->in->current),
689
0
                xmlctx->in->current, "either single or double quotation mark");
690
0
        return LY_EVALID;
691
0
    }
692
693
    /* remember quote */
694
0
    quot = xmlctx->in->current[0];
695
0
    move_input(xmlctx, 1);
696
697
    /* parse attribute value */
698
0
    LY_CHECK_RET(lyxml_parse_value(xmlctx, quot, (char **)value, value_len, ws_only, dynamic));
699
700
    /* move after ending quote (without checking for EOF) */
701
0
    ly_in_skip(xmlctx->in, 1);
702
703
0
    return LY_SUCCESS;
704
0
}
705
706
/**
707
 * @brief Move parser to the next attribute and parse it.
708
 *
709
 * @param[in] xmlctx XML context to use.
710
 * @param[out] prefix Parsed attribute prefix.
711
 * @param[out] prefix_len Length of @p prefix.
712
 * @param[out] name Parsed attribute name.
713
 * @param[out] name_len Length of @p name.
714
 * @return LY_ERR value.
715
 */
716
static LY_ERR
717
lyxml_next_attribute(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
718
0
{
719
0
    const char *in;
720
0
    char *value;
721
0
    uint32_t c;
722
0
    size_t parsed, value_len;
723
0
    ly_bool ws_only, dynamic;
724
725
    /* skip WS */
726
0
    ign_xmlws(xmlctx);
727
728
    /* parse only possible attributes */
729
0
    while ((xmlctx->in->current[0] != '>') && (xmlctx->in->current[0] != '/')) {
730
0
        in = xmlctx->in->current;
731
0
        if (in[0] == '\0') {
732
0
            LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
733
0
            return LY_EVALID;
734
0
        } else if ((ly_getutf8(&in, &c, &parsed) || !is_xmlqnamestartchar(c))) {
735
0
            LOGVAL(xmlctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(in - parsed), in - parsed,
736
0
                    "element tag end ('>' or '/>') or an attribute");
737
0
            return LY_EVALID;
738
0
        }
739
740
        /* parse attribute name */
741
0
        LY_CHECK_RET(lyxml_parse_qname(xmlctx, prefix, prefix_len, name, name_len));
742
743
0
        if ((!*prefix || ly_strncmp("xmlns", *prefix, *prefix_len)) && (*prefix || ly_strncmp("xmlns", *name, *name_len))) {
744
            /* standard attribute */
745
0
            break;
746
0
        }
747
748
        /* namespace, skip it */
749
0
        LY_CHECK_RET(lyxml_next_attr_content(xmlctx, (const char **)&value, &value_len, &ws_only, &dynamic));
750
0
        if (dynamic) {
751
0
            free(value);
752
0
        }
753
754
        /* skip WS */
755
0
        ign_xmlws(xmlctx);
756
0
    }
757
758
0
    return LY_SUCCESS;
759
0
}
760
761
/**
762
 * @brief Move parser to the next element and parse it.
763
 *
764
 * @param[in] xmlctx XML context to use.
765
 * @param[out] prefix Parsed element prefix.
766
 * @param[out] prefix_len Length of @p prefix.
767
 * @param[out] name Parse element name.
768
 * @param[out] name_len Length of @p name.
769
 * @param[out] closing Flag if the element is closing (includes '/').
770
 * @return LY_ERR value.
771
 */
772
static LY_ERR
773
lyxml_next_element(struct lyxml_ctx *xmlctx, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len,
774
        ly_bool *closing)
775
0
{
776
    /* skip WS until EOF or after opening tag '<' */
777
0
    LY_CHECK_RET(lyxml_skip_until_end_or_after_otag(xmlctx));
778
0
    if (xmlctx->in->current[0] == '\0') {
779
        /* set return values */
780
0
        *prefix = *name = NULL;
781
0
        *prefix_len = *name_len = 0;
782
0
        return LY_SUCCESS;
783
0
    }
784
785
0
    if (xmlctx->in->current[0] == '/') {
786
0
        move_input(xmlctx, 1);
787
0
        *closing = 1;
788
0
    } else {
789
0
        *closing = 0;
790
0
    }
791
792
    /* skip WS */
793
0
    ign_xmlws(xmlctx);
794
795
    /* parse element name */
796
0
    LY_CHECK_RET(lyxml_parse_qname(xmlctx, prefix, prefix_len, name, name_len));
797
798
0
    return LY_SUCCESS;
799
0
}
800
801
LY_ERR
802
lyxml_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyxml_ctx **xmlctx_p)
803
0
{
804
0
    LY_ERR ret = LY_SUCCESS;
805
0
    struct lyxml_ctx *xmlctx;
806
0
    ly_bool closing;
807
808
    /* new context */
809
0
    xmlctx = calloc(1, sizeof *xmlctx);
810
0
    LY_CHECK_ERR_RET(!xmlctx, LOGMEM(ctx), LY_EMEM);
811
0
    xmlctx->ctx = ctx;
812
0
    xmlctx->in = in;
813
814
0
    LOG_LOCINIT(NULL, NULL, NULL, in);
815
816
    /* parse next element, if any */
817
0
    LY_CHECK_GOTO(ret = lyxml_next_element(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name,
818
0
            &xmlctx->name_len, &closing), cleanup);
819
820
0
    if (xmlctx->in->current[0] == '\0') {
821
        /* update status */
822
0
        xmlctx->status = LYXML_END;
823
0
    } else if (closing) {
824
0
        LOGVAL(ctx, LYVE_SYNTAX, "Stray closing element tag (\"%.*s\").", (int)xmlctx->name_len, xmlctx->name);
825
0
        ret = LY_EVALID;
826
0
        goto cleanup;
827
0
    } else {
828
        /* open an element, also parses all enclosed namespaces */
829
0
        LY_CHECK_GOTO(ret = lyxml_open_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len), cleanup);
830
831
        /* update status */
832
0
        xmlctx->status = LYXML_ELEMENT;
833
0
    }
834
835
0
cleanup:
836
0
    if (ret) {
837
0
        lyxml_ctx_free(xmlctx);
838
0
    } else {
839
0
        *xmlctx_p = xmlctx;
840
0
    }
841
0
    return ret;
842
0
}
843
844
LY_ERR
845
lyxml_ctx_next(struct lyxml_ctx *xmlctx)
846
0
{
847
0
    LY_ERR ret = LY_SUCCESS;
848
0
    ly_bool closing;
849
0
    struct lyxml_elem *e;
850
851
    /* if the value was not used, free it */
852
0
    if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
853
0
        free((char *)xmlctx->value);
854
0
        xmlctx->value = NULL;
855
0
        xmlctx->dynamic = 0;
856
0
    }
857
858
0
    switch (xmlctx->status) {
859
0
    case LYXML_ELEM_CONTENT:
860
        /* content |</elem> */
861
862
        /* handle special case when empty content for "<elem/>" was returned */
863
0
        if (xmlctx->in->current[0] == '/') {
864
0
            assert(xmlctx->elements.count);
865
0
            e = (struct lyxml_elem *)xmlctx->elements.objs[xmlctx->elements.count - 1];
866
867
            /* close the element (parses closing tag) */
868
0
            ret = lyxml_close_element(xmlctx, e->prefix, e->prefix_len, e->name, e->name_len, 1);
869
0
            LY_CHECK_GOTO(ret, cleanup);
870
871
            /* update status */
872
0
            xmlctx->status = LYXML_ELEM_CLOSE;
873
0
            break;
874
0
        }
875
    /* fall through */
876
0
    case LYXML_ELEM_CLOSE:
877
        /* </elem>| <elem2>* */
878
879
        /* parse next element, if any */
880
0
        ret = lyxml_next_element(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name, &xmlctx->name_len, &closing);
881
0
        LY_CHECK_GOTO(ret, cleanup);
882
883
0
        if (xmlctx->in->current[0] == '\0') {
884
            /* update status */
885
0
            xmlctx->status = LYXML_END;
886
0
        } else if (closing) {
887
            /* close an element (parses also closing tag) */
888
0
            ret = lyxml_close_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len, 0);
889
0
            LY_CHECK_GOTO(ret, cleanup);
890
891
            /* update status */
892
0
            xmlctx->status = LYXML_ELEM_CLOSE;
893
0
        } else {
894
            /* open an element, also parses all enclosed namespaces */
895
0
            ret = lyxml_open_element(xmlctx, xmlctx->prefix, xmlctx->prefix_len, xmlctx->name, xmlctx->name_len);
896
0
            LY_CHECK_GOTO(ret, cleanup);
897
898
            /* update status */
899
0
            xmlctx->status = LYXML_ELEMENT;
900
0
        }
901
0
        break;
902
903
0
    case LYXML_ELEMENT:
904
    /* <elem| attr='val'* > content  */
905
0
    case LYXML_ATTR_CONTENT:
906
        /* attr='val'| attr='val'* > content */
907
908
        /* parse attribute name, if any */
909
0
        ret = lyxml_next_attribute(xmlctx, &xmlctx->prefix, &xmlctx->prefix_len, &xmlctx->name, &xmlctx->name_len);
910
0
        LY_CHECK_GOTO(ret, cleanup);
911
912
0
        if (xmlctx->in->current[0] == '>') {
913
            /* no attributes but a closing tag */
914
0
            ly_in_skip(xmlctx->in, 1);
915
0
            if (!xmlctx->in->current[0]) {
916
0
                LOGVAL(xmlctx->ctx, LY_VCODE_EOF);
917
0
                ret = LY_EVALID;
918
0
                goto cleanup;
919
0
            }
920
921
            /* parse element content */
922
0
            ret = lyxml_parse_value(xmlctx, '<', (char **)&xmlctx->value, &xmlctx->value_len, &xmlctx->ws_only,
923
0
                    &xmlctx->dynamic);
924
0
            LY_CHECK_GOTO(ret, cleanup);
925
926
0
            if (!xmlctx->value_len) {
927
                /* empty value should by alocated staticaly, but check for in any case */
928
0
                if (xmlctx->dynamic) {
929
0
                    free((char *) xmlctx->value);
930
0
                }
931
                /* use empty value, easier to work with */
932
0
                xmlctx->value = "";
933
0
                xmlctx->dynamic = 0;
934
0
            }
935
936
            /* update status */
937
0
            xmlctx->status = LYXML_ELEM_CONTENT;
938
0
        } else if (xmlctx->in->current[0] == '/') {
939
            /* no content but we still return it */
940
0
            xmlctx->value = "";
941
0
            xmlctx->value_len = 0;
942
0
            xmlctx->ws_only = 1;
943
0
            xmlctx->dynamic = 0;
944
945
            /* update status */
946
0
            xmlctx->status = LYXML_ELEM_CONTENT;
947
0
        } else {
948
            /* update status */
949
0
            xmlctx->status = LYXML_ATTRIBUTE;
950
0
        }
951
0
        break;
952
953
0
    case LYXML_ATTRIBUTE:
954
        /* attr|='val' */
955
956
        /* skip formatting and parse value */
957
0
        ret = lyxml_next_attr_content(xmlctx, &xmlctx->value, &xmlctx->value_len, &xmlctx->ws_only, &xmlctx->dynamic);
958
0
        LY_CHECK_GOTO(ret, cleanup);
959
960
        /* update status */
961
0
        xmlctx->status = LYXML_ATTR_CONTENT;
962
0
        break;
963
964
0
    case LYXML_END:
965
        /* </elem>   |EOF */
966
        /* nothing to do */
967
0
        break;
968
0
    }
969
970
0
cleanup:
971
0
    if (ret) {
972
        /* invalidate context */
973
0
        xmlctx->status = LYXML_END;
974
0
    }
975
0
    return ret;
976
0
}
977
978
LY_ERR
979
lyxml_ctx_peek(struct lyxml_ctx *xmlctx, enum LYXML_PARSER_STATUS *next)
980
0
{
981
0
    LY_ERR ret = LY_SUCCESS;
982
0
    const char *prefix, *name, *prev_input;
983
0
    size_t prefix_len, name_len;
984
0
    ly_bool closing;
985
986
0
    prev_input = xmlctx->in->current;
987
988
0
    switch (xmlctx->status) {
989
0
    case LYXML_ELEM_CONTENT:
990
0
        if (xmlctx->in->current[0] == '/') {
991
0
            *next = LYXML_ELEM_CLOSE;
992
0
            break;
993
0
        }
994
    /* fall through */
995
0
    case LYXML_ELEM_CLOSE:
996
        /* parse next element, if any */
997
0
        ret = lyxml_next_element(xmlctx, &prefix, &prefix_len, &name, &name_len, &closing);
998
0
        LY_CHECK_GOTO(ret, cleanup);
999
1000
0
        if (xmlctx->in->current[0] == '\0') {
1001
0
            *next = LYXML_END;
1002
0
        } else if (closing) {
1003
0
            *next = LYXML_ELEM_CLOSE;
1004
0
        } else {
1005
0
            *next = LYXML_ELEMENT;
1006
0
        }
1007
0
        break;
1008
0
    case LYXML_ELEMENT:
1009
0
    case LYXML_ATTR_CONTENT:
1010
        /* parse attribute name, if any */
1011
0
        ret = lyxml_next_attribute(xmlctx, &prefix, &prefix_len, &name, &name_len);
1012
0
        LY_CHECK_GOTO(ret, cleanup);
1013
1014
0
        if ((xmlctx->in->current[0] == '>') || (xmlctx->in->current[0] == '/')) {
1015
0
            *next = LYXML_ELEM_CONTENT;
1016
0
        } else {
1017
0
            *next = LYXML_ATTRIBUTE;
1018
0
        }
1019
0
        break;
1020
0
    case LYXML_ATTRIBUTE:
1021
0
        *next = LYXML_ATTR_CONTENT;
1022
0
        break;
1023
0
    case LYXML_END:
1024
0
        *next = LYXML_END;
1025
0
        break;
1026
0
    }
1027
1028
0
cleanup:
1029
0
    xmlctx->in->current = prev_input;
1030
0
    return ret;
1031
0
}
1032
1033
void
1034
lyxml_ctx_free(struct lyxml_ctx *xmlctx)
1035
0
{
1036
0
    uint32_t u;
1037
1038
0
    if (!xmlctx) {
1039
0
        return;
1040
0
    }
1041
1042
0
    LOG_LOCBACK(0, 0, 0, 1);
1043
1044
0
    if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
1045
0
        free((char *)xmlctx->value);
1046
0
    }
1047
0
    ly_set_erase(&xmlctx->elements, free);
1048
0
    for (u = xmlctx->ns.count - 1; u + 1 > 0; --u) {
1049
        /* remove the ns structure */
1050
0
        free(((struct lyxml_ns *)xmlctx->ns.objs[u])->prefix);
1051
0
        free(((struct lyxml_ns *)xmlctx->ns.objs[u])->uri);
1052
0
        free(xmlctx->ns.objs[u]);
1053
0
    }
1054
0
    ly_set_erase(&xmlctx->ns, NULL);
1055
0
    free(xmlctx);
1056
0
}
1057
1058
LY_ERR
1059
lyxml_dump_text(struct ly_out *out, const char *text, ly_bool attribute)
1060
0
{
1061
0
    LY_ERR ret;
1062
1063
0
    if (!text) {
1064
0
        return 0;
1065
0
    }
1066
1067
0
    for (uint64_t u = 0; text[u]; u++) {
1068
0
        switch (text[u]) {
1069
0
        case '&':
1070
0
            ret = ly_print_(out, "&amp;");
1071
0
            break;
1072
0
        case '<':
1073
0
            ret = ly_print_(out, "&lt;");
1074
0
            break;
1075
0
        case '>':
1076
            /* not needed, just for readability */
1077
0
            ret = ly_print_(out, "&gt;");
1078
0
            break;
1079
0
        case '"':
1080
0
            if (attribute) {
1081
0
                ret = ly_print_(out, "&quot;");
1082
0
                break;
1083
0
            }
1084
        /* fall through */
1085
0
        default:
1086
0
            ret = ly_write_(out, &text[u], 1);
1087
0
            break;
1088
0
        }
1089
0
        LY_CHECK_RET(ret);
1090
0
    }
1091
1092
0
    return LY_SUCCESS;
1093
0
}
1094
1095
LY_ERR
1096
lyxml_value_compare(const struct ly_ctx *ctx1, const char *value1, void *val_prefix_data1,
1097
        const struct ly_ctx *ctx2, const char *value2, void *val_prefix_data2)
1098
0
{
1099
0
    const char *value1_iter, *value2_iter;
1100
0
    const char *value1_next, *value2_next;
1101
0
    uint32_t value1_len, value2_len;
1102
0
    ly_bool is_prefix1, is_prefix2;
1103
0
    const struct lys_module *mod1, *mod2;
1104
0
    LY_ERR ret;
1105
1106
0
    if (!value1 && !value2) {
1107
0
        return LY_SUCCESS;
1108
0
    }
1109
0
    if ((value1 && !value2) || (!value1 && value2)) {
1110
0
        return LY_ENOT;
1111
0
    }
1112
1113
0
    if (!ctx2) {
1114
0
        ctx2 = ctx1;
1115
0
    }
1116
1117
0
    ret = LY_SUCCESS;
1118
0
    for (value1_iter = value1, value2_iter = value2;
1119
0
            value1_iter && value2_iter;
1120
0
            value1_iter = value1_next, value2_iter = value2_next) {
1121
0
        if ((ret = ly_value_prefix_next(value1_iter, NULL, &value1_len, &is_prefix1, &value1_next))) {
1122
0
            break;
1123
0
        }
1124
0
        if ((ret = ly_value_prefix_next(value2_iter, NULL, &value2_len, &is_prefix2, &value2_next))) {
1125
0
            break;
1126
0
        }
1127
1128
0
        if (is_prefix1 != is_prefix2) {
1129
0
            ret = LY_ENOT;
1130
0
            break;
1131
0
        }
1132
1133
0
        if (!is_prefix1) {
1134
0
            if (value1_len != value2_len) {
1135
0
                ret = LY_ENOT;
1136
0
                break;
1137
0
            }
1138
0
            if (strncmp(value1_iter, value2_iter, value1_len)) {
1139
0
                ret = LY_ENOT;
1140
0
                break;
1141
0
            }
1142
0
            continue;
1143
0
        }
1144
1145
0
        mod1 = mod2 = NULL;
1146
0
        if (val_prefix_data1) {
1147
            /* find module of the first prefix, if any */
1148
0
            mod1 = ly_resolve_prefix(ctx1, value1_iter, value1_len, LY_VALUE_XML, val_prefix_data1);
1149
0
        }
1150
0
        if (val_prefix_data2) {
1151
0
            mod2 = ly_resolve_prefix(ctx2, value2_iter, value2_len, LY_VALUE_XML, val_prefix_data2);
1152
0
        }
1153
0
        if (!mod1 || !mod2) {
1154
            /* not a prefix or maps to different namespaces */
1155
0
            ret = LY_ENOT;
1156
0
            break;
1157
0
        }
1158
1159
0
        if (mod1->ctx == mod2->ctx) {
1160
            /* same contexts */
1161
0
            if ((mod1->name != mod2->name) || (mod1->revision != mod2->revision)) {
1162
0
                ret = LY_ENOT;
1163
0
                break;
1164
0
            }
1165
0
        } else {
1166
            /* different contexts */
1167
0
            if (strcmp(mod1->name, mod2->name)) {
1168
0
                ret = LY_ENOT;
1169
0
                break;
1170
0
            }
1171
1172
0
            if (mod1->revision || mod2->revision) {
1173
0
                if (!mod1->revision || !mod2->revision) {
1174
0
                    ret = LY_ENOT;
1175
0
                    break;
1176
0
                }
1177
0
                if (strcmp(mod1->revision, mod2->revision)) {
1178
0
                    ret = LY_ENOT;
1179
0
                    break;
1180
0
                }
1181
0
            }
1182
0
        }
1183
0
    }
1184
1185
0
    if (value1_iter || value2_iter) {
1186
0
        ret = LY_ENOT;
1187
0
    }
1188
1189
0
    return ret;
1190
0
}