Coverage Report

Created: 2025-08-26 06:20

/src/libyang/src/common.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file common.c
3
 * @author Michal Vasko <mvasko@cesnet.cz>
4
 * @brief common internal definitions for libyang
5
 *
6
 * Copyright (c) 2018 CESNET, z.s.p.o.
7
 *
8
 * This source code is licensed under BSD 3-Clause License (the "License").
9
 * You may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     https://opensource.org/licenses/BSD-3-Clause
13
 */
14
15
#define _GNU_SOURCE
16
17
#include "common.h"
18
19
#include <assert.h>
20
#include <ctype.h>
21
#include <errno.h>
22
#include <inttypes.h>
23
#include <stdarg.h>
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <sys/mman.h>
28
#include <sys/stat.h>
29
#include <unistd.h>
30
31
#include "compat.h"
32
#include "tree_schema_internal.h"
33
34
void *
35
ly_realloc(void *ptr, size_t size)
36
0
{
37
0
    void *new_mem;
38
39
0
    new_mem = realloc(ptr, size);
40
0
    if (!new_mem) {
41
0
        free(ptr);
42
0
    }
43
44
0
    return new_mem;
45
0
}
46
47
char *
48
ly_strnchr(const char *s, int c, size_t len)
49
0
{
50
0
    for ( ; len && (*s != (char)c); ++s, --len) {}
51
0
    return len ? (char *)s : NULL;
52
0
}
53
54
int
55
ly_strncmp(const char *refstr, const char *str, size_t str_len)
56
0
{
57
0
    int rc = strncmp(refstr, str, str_len);
58
59
0
    if (!rc && (refstr[str_len] == '\0')) {
60
0
        return 0;
61
0
    } else {
62
0
        return rc ? rc : 1;
63
0
    }
64
0
}
65
66
0
#define LY_OVERFLOW_ADD(MAX, X, Y) ((X > MAX - Y) ? 1 : 0)
67
68
0
#define LY_OVERFLOW_MUL(MAX, X, Y) ((X > MAX / Y) ? 1 : 0)
69
70
LY_ERR
71
ly_strntou8(const char *nptr, size_t len, uint8_t *ret)
72
0
{
73
0
    uint8_t num = 0, dig, dec_pow;
74
75
0
    if (len > 3) {
76
        /* overflow for sure */
77
0
        return LY_EDENIED;
78
0
    }
79
80
0
    dec_pow = 1;
81
0
    for ( ; len && isdigit(nptr[len - 1]); --len) {
82
0
        dig = nptr[len - 1] - 48;
83
84
0
        if (LY_OVERFLOW_MUL(UINT8_MAX, dig, dec_pow)) {
85
0
            return LY_EDENIED;
86
0
        }
87
0
        dig *= dec_pow;
88
89
0
        if (LY_OVERFLOW_ADD(UINT8_MAX, num, dig)) {
90
0
            return LY_EDENIED;
91
0
        }
92
0
        num += dig;
93
94
0
        dec_pow *= 10;
95
0
    }
96
97
0
    if (len) {
98
0
        return LY_EVALID;
99
0
    }
100
0
    *ret = num;
101
0
    return LY_SUCCESS;
102
0
}
103
104
LY_ERR
105
ly_value_prefix_next(const char *str_begin, const char *str_end, uint32_t *len, ly_bool *is_prefix, const char **str_next)
106
0
{
107
0
    const char *stop, *prefix;
108
0
    size_t bytes_read;
109
0
    uint32_t c;
110
0
    ly_bool prefix_found;
111
0
    LY_ERR ret = LY_SUCCESS;
112
113
0
    assert(len && is_prefix && str_next);
114
115
0
#define IS_AT_END(PTR, STR_END) (STR_END ? PTR == STR_END : !(*PTR))
116
117
0
    *str_next = NULL;
118
0
    *is_prefix = 0;
119
0
    *len = 0;
120
121
0
    if (!str_begin || !(*str_begin) || (str_begin == str_end)) {
122
0
        return ret;
123
0
    }
124
125
0
    stop = str_begin;
126
0
    prefix = NULL;
127
0
    prefix_found = 0;
128
129
0
    do {
130
        /* look for the beginning of the YANG value */
131
0
        do {
132
0
            LY_CHECK_RET(ly_getutf8(&stop, &c, &bytes_read));
133
0
        } while (!is_xmlqnamestartchar(c) && !IS_AT_END(stop, str_end));
134
135
0
        if (IS_AT_END(stop, str_end)) {
136
0
            break;
137
0
        }
138
139
        /* maybe the prefix was found */
140
0
        prefix = stop - bytes_read;
141
142
        /* look for the the end of the prefix */
143
0
        do {
144
0
            LY_CHECK_RET(ly_getutf8(&stop, &c, &bytes_read));
145
0
        } while (is_xmlqnamechar(c) && !IS_AT_END(stop, str_end));
146
147
0
        prefix_found = c == ':' ? 1 : 0;
148
149
        /* if it wasn't the prefix, keep looking */
150
0
    } while (!IS_AT_END(stop, str_end) && !prefix_found);
151
152
0
    if ((str_begin == prefix) && prefix_found) {
153
        /* prefix found at the beginning of the input string */
154
0
        *is_prefix = 1;
155
0
        *str_next = IS_AT_END(stop, str_end) ? NULL : stop;
156
0
        *len = (stop - bytes_read) - str_begin;
157
0
    } else if ((str_begin != prefix) && (prefix_found)) {
158
        /* there is a some string before prefix */
159
0
        *str_next = prefix;
160
0
        *len = prefix - str_begin;
161
0
    } else {
162
        /* no prefix found */
163
0
        *len = stop - str_begin;
164
0
    }
165
166
0
#undef IS_AT_END
167
168
0
    return ret;
169
0
}
170
171
LY_ERR
172
ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
173
0
{
174
0
    uint32_t c, aux;
175
0
    size_t len;
176
177
0
    if (bytes_read) {
178
0
        (*bytes_read) = 0;
179
0
    }
180
181
0
    c = (*input)[0];
182
0
    LY_CHECK_RET(!c, LY_EINVAL);
183
184
0
    if (!(c & 0x80)) {
185
        /* one byte character */
186
0
        len = 1;
187
188
0
        if ((c < 0x20) && (c != 0x9) && (c != 0xa) && (c != 0xd)) {
189
0
            return LY_EINVAL;
190
0
        }
191
0
    } else if ((c & 0xe0) == 0xc0) {
192
        /* two bytes character */
193
0
        len = 2;
194
195
0
        aux = (*input)[1];
196
0
        if ((aux & 0xc0) != 0x80) {
197
0
            return LY_EINVAL;
198
0
        }
199
0
        c = ((c & 0x1f) << 6) | (aux & 0x3f);
200
201
0
        if (c < 0x80) {
202
0
            return LY_EINVAL;
203
0
        }
204
0
    } else if ((c & 0xf0) == 0xe0) {
205
        /* three bytes character */
206
0
        len = 3;
207
208
0
        c &= 0x0f;
209
0
        for (uint64_t i = 1; i <= 2; i++) {
210
0
            aux = (*input)[i];
211
0
            if ((aux & 0xc0) != 0x80) {
212
0
                return LY_EINVAL;
213
0
            }
214
215
0
            c = (c << 6) | (aux & 0x3f);
216
0
        }
217
218
0
        if ((c < 0x800) || ((c > 0xd7ff) && (c < 0xe000)) || (c > 0xfffd)) {
219
0
            return LY_EINVAL;
220
0
        }
221
0
    } else if ((c & 0xf8) == 0xf0) {
222
        /* four bytes character */
223
0
        len = 4;
224
225
0
        c &= 0x07;
226
0
        for (uint64_t i = 1; i <= 3; i++) {
227
0
            aux = (*input)[i];
228
0
            if ((aux & 0xc0) != 0x80) {
229
0
                return LY_EINVAL;
230
0
            }
231
232
0
            c = (c << 6) | (aux & 0x3f);
233
0
        }
234
235
0
        if ((c < 0x1000) || (c > 0x10ffff)) {
236
0
            return LY_EINVAL;
237
0
        }
238
0
    } else {
239
0
        return LY_EINVAL;
240
0
    }
241
242
0
    (*utf8_char) = c;
243
0
    (*input) += len;
244
0
    if (bytes_read) {
245
0
        (*bytes_read) = len;
246
0
    }
247
0
    return LY_SUCCESS;
248
0
}
249
250
LY_ERR
251
ly_pututf8(char *dst, uint32_t value, size_t *bytes_written)
252
0
{
253
0
    if (value < 0x80) {
254
        /* one byte character */
255
0
        if ((value < 0x20) &&
256
0
                (value != 0x09) &&
257
0
                (value != 0x0a) &&
258
0
                (value != 0x0d)) {
259
0
            return LY_EINVAL;
260
0
        }
261
262
0
        dst[0] = value;
263
0
        (*bytes_written) = 1;
264
0
    } else if (value < 0x800) {
265
        /* two bytes character */
266
0
        dst[0] = 0xc0 | (value >> 6);
267
0
        dst[1] = 0x80 | (value & 0x3f);
268
0
        (*bytes_written) = 2;
269
0
    } else if (value < 0xfffe) {
270
        /* three bytes character */
271
0
        if (((value & 0xf800) == 0xd800) ||
272
0
                ((value >= 0xfdd0) && (value <= 0xfdef))) {
273
            /* exclude surrogate blocks %xD800-DFFF */
274
            /* exclude noncharacters %xFDD0-FDEF */
275
0
            return LY_EINVAL;
276
0
        }
277
278
0
        dst[0] = 0xe0 | (value >> 12);
279
0
        dst[1] = 0x80 | ((value >> 6) & 0x3f);
280
0
        dst[2] = 0x80 | (value & 0x3f);
281
282
0
        (*bytes_written) = 3;
283
0
    } else if (value < 0x10fffe) {
284
0
        if ((value & 0xffe) == 0xffe) {
285
            /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF,
286
             * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF,
287
             * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */
288
0
            return LY_EINVAL;
289
0
        }
290
        /* four bytes character */
291
0
        dst[0] = 0xf0 | (value >> 18);
292
0
        dst[1] = 0x80 | ((value >> 12) & 0x3f);
293
0
        dst[2] = 0x80 | ((value >> 6) & 0x3f);
294
0
        dst[3] = 0x80 | (value & 0x3f);
295
296
0
        (*bytes_written) = 4;
297
0
    } else {
298
0
        return LY_EINVAL;
299
0
    }
300
0
    return LY_SUCCESS;
301
0
}
302
303
/**
304
 * @brief Static table of the UTF8 characters lengths according to their first byte.
305
 */
306
static const unsigned char utf8_char_length_table[] = {
307
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
308
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
309
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
310
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
311
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
312
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
313
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
314
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
315
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
316
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
317
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
318
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
319
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
320
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
321
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
322
    4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
323
};
324
325
size_t
326
ly_utf8len(const char *str, size_t bytes)
327
0
{
328
0
    size_t len = 0;
329
0
    const char *ptr = str;
330
331
0
    while (((size_t)(ptr - str) < bytes) && *ptr) {
332
0
        ++len;
333
0
        ptr += utf8_char_length_table[((unsigned char)(*ptr))];
334
0
    }
335
0
    return len;
336
0
}
337
338
size_t
339
LY_VCODE_INSTREXP_len(const char *str)
340
0
{
341
0
    size_t len = 0;
342
343
0
    if (!str) {
344
0
        return len;
345
0
    } else if (!str[0]) {
346
0
        return 1;
347
0
    }
348
0
    for (len = 1; len < LY_VCODE_INSTREXP_MAXLEN && str[len]; ++len) {}
349
0
    return len;
350
0
}
351
352
LY_ERR
353
ly_mmap(struct ly_ctx *ctx, int fd, size_t *length, void **addr)
354
0
{
355
0
    struct stat sb;
356
0
    long pagesize;
357
0
    size_t m;
358
359
0
    assert(length);
360
0
    assert(addr);
361
0
    assert(fd >= 0);
362
363
0
    if (fstat(fd, &sb) == -1) {
364
0
        LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
365
0
        return LY_ESYS;
366
0
    }
367
0
    if (!S_ISREG(sb.st_mode)) {
368
0
        LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
369
0
        return LY_ESYS;
370
0
    }
371
0
    if (!sb.st_size) {
372
0
        *addr = NULL;
373
0
        return LY_SUCCESS;
374
0
    }
375
0
    pagesize = sysconf(_SC_PAGESIZE);
376
377
0
    m = sb.st_size % pagesize;
378
0
    if (m && (pagesize - m >= 1)) {
379
        /* there will be enough space (at least 1 byte) after the file content mapping to provide zeroed NULL-termination byte */
380
0
        *length = sb.st_size + 1;
381
0
        *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
382
0
    } else {
383
        /* there will not be enough bytes after the file content mapping for the additional bytes and some of them
384
         * would overflow into another page that would not be zerroed and any access into it would generate SIGBUS.
385
         * Therefore we have to do the following hack with double mapping. First, the required number of bytes
386
         * (including the additinal bytes) is required as anonymous and thus they will be really provided (actually more
387
         * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
388
         * where the anonymous mapping starts. */
389
0
        *length = sb.st_size + pagesize;
390
0
        *addr = mmap(NULL, *length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
391
0
        *addr = mmap(*addr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
392
0
    }
393
0
    if (*addr == MAP_FAILED) {
394
0
        LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
395
0
        return LY_ESYS;
396
0
    }
397
398
0
    return LY_SUCCESS;
399
0
}
400
401
LY_ERR
402
ly_munmap(void *addr, size_t length)
403
0
{
404
0
    if (munmap(addr, length)) {
405
0
        return LY_ESYS;
406
0
    }
407
0
    return LY_SUCCESS;
408
0
}
409
410
LY_ERR
411
ly_strcat(char **dest, const char *format, ...)
412
0
{
413
0
    va_list fp;
414
0
    char *addition = NULL;
415
0
    size_t len;
416
417
0
    va_start(fp, format);
418
0
    len = vasprintf(&addition, format, fp);
419
0
    len += (*dest ? strlen(*dest) : 0) + 1;
420
421
0
    if (*dest) {
422
0
        *dest = ly_realloc(*dest, len);
423
0
        if (!*dest) {
424
0
            va_end(fp);
425
0
            return LY_EMEM;
426
0
        }
427
0
        *dest = strcat(*dest, addition);
428
0
        free(addition);
429
0
    } else {
430
0
        *dest = addition;
431
0
    }
432
433
0
    va_end(fp);
434
0
    return LY_SUCCESS;
435
0
}
436
437
LY_ERR
438
ly_parse_int(const char *val_str, size_t val_len, int64_t min, int64_t max, int base, int64_t *ret)
439
0
{
440
0
    LY_ERR rc = LY_SUCCESS;
441
0
    char *ptr, *str;
442
0
    int64_t i;
443
444
0
    LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
445
446
    /* duplicate the value */
447
0
    str = strndup(val_str, val_len);
448
0
    LY_CHECK_RET(!str, LY_EMEM);
449
450
    /* parse the value to avoid accessing following bytes */
451
0
    errno = 0;
452
0
    i = strtoll(str, &ptr, base);
453
0
    if (errno || (ptr == str)) {
454
        /* invalid string */
455
0
        rc = LY_EVALID;
456
0
    } else if ((i < min) || (i > max)) {
457
        /* invalid number */
458
0
        rc = LY_EDENIED;
459
0
    } else if (*ptr) {
460
0
        while (isspace(*ptr)) {
461
0
            ++ptr;
462
0
        }
463
0
        if (*ptr) {
464
            /* invalid characters after some number */
465
0
            rc = LY_EVALID;
466
0
        }
467
0
    }
468
469
    /* cleanup */
470
0
    free(str);
471
0
    if (!rc) {
472
0
        *ret = i;
473
0
    }
474
0
    return rc;
475
0
}
476
477
LY_ERR
478
ly_parse_uint(const char *val_str, size_t val_len, uint64_t max, int base, uint64_t *ret)
479
0
{
480
0
    LY_ERR rc = LY_SUCCESS;
481
0
    char *ptr, *str;
482
0
    uint64_t u;
483
484
0
    LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
485
486
    /* duplicate the value to avoid accessing following bytes */
487
0
    str = strndup(val_str, val_len);
488
0
    LY_CHECK_RET(!str, LY_EMEM);
489
490
    /* parse the value */
491
0
    errno = 0;
492
0
    u = strtoull(str, &ptr, base);
493
0
    if (errno || (ptr == str)) {
494
        /* invalid string */
495
0
        rc = LY_EVALID;
496
0
    } else if ((u > max) || (u && (str[0] == '-'))) {
497
        /* invalid number */
498
0
        rc = LY_EDENIED;
499
0
    } else if (*ptr) {
500
0
        while (isspace(*ptr)) {
501
0
            ++ptr;
502
0
        }
503
0
        if (*ptr) {
504
            /* invalid characters after some number */
505
0
            rc = LY_EVALID;
506
0
        }
507
0
    }
508
509
    /* cleanup */
510
0
    free(str);
511
0
    if (!rc) {
512
0
        *ret = u;
513
0
    }
514
0
    return rc;
515
0
}
516
517
/**
518
 * @brief Parse an identifier.
519
 *
520
 * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
521
 * identifier          = (ALPHA / "_")
522
 *                       *(ALPHA / DIGIT / "_" / "-" / ".")
523
 *
524
 * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
525
 * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
526
 */
527
static LY_ERR
528
lys_parse_id(const char **id)
529
0
{
530
0
    assert(id && *id);
531
532
0
    if (!is_yangidentstartchar(**id)) {
533
0
        return LY_EINVAL;
534
0
    }
535
0
    ++(*id);
536
537
0
    while (is_yangidentchar(**id)) {
538
0
        ++(*id);
539
0
    }
540
0
    return LY_SUCCESS;
541
0
}
542
543
LY_ERR
544
ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
545
0
{
546
0
    assert(id && *id);
547
0
    assert(prefix && prefix_len);
548
0
    assert(name && name_len);
549
550
0
    *prefix = *id;
551
0
    *prefix_len = 0;
552
0
    *name = NULL;
553
0
    *name_len = 0;
554
555
0
    LY_CHECK_RET(lys_parse_id(id));
556
0
    if (**id == ':') {
557
        /* there is prefix */
558
0
        *prefix_len = *id - *prefix;
559
0
        ++(*id);
560
0
        *name = *id;
561
562
0
        LY_CHECK_RET(lys_parse_id(id));
563
0
        *name_len = *id - *name;
564
0
    } else {
565
        /* there is no prefix, so what we have as prefix now is actually the name */
566
0
        *name = *prefix;
567
0
        *name_len = *id - *name;
568
0
        *prefix = NULL;
569
0
    }
570
571
0
    return LY_SUCCESS;
572
0
}
573
574
LY_ERR
575
ly_parse_instance_predicate(const char **pred, size_t limit, LYD_FORMAT format,
576
        const char **prefix, size_t *prefix_len, const char **id, size_t *id_len, const char **value, size_t *value_len,
577
        const char **errmsg)
578
0
{
579
0
    LY_ERR ret = LY_EVALID;
580
0
    const char *in = *pred;
581
0
    size_t offset = 1;
582
0
    uint8_t expr = 0; /* 0 - position predicate; 1 - leaf-list-predicate; 2 - key-predicate */
583
0
    char quot;
584
585
0
    assert(in[0] == '[');
586
587
0
    *prefix = *id = *value = NULL;
588
0
    *prefix_len = *id_len = *value_len = 0;
589
590
    /* leading *WSP */
591
0
    for ( ; isspace(in[offset]); offset++) {}
592
593
0
    if (isdigit(in[offset])) {
594
        /* pos: "[" *WSP positive-integer-value *WSP "]" */
595
0
        if (in[offset] == '0') {
596
            /* zero */
597
0
            *errmsg = "The position predicate cannot be zero.";
598
0
            goto error;
599
0
        }
600
601
        /* positive-integer-value */
602
0
        *value = &in[offset++];
603
0
        for ( ; isdigit(in[offset]); offset++) {}
604
0
        *value_len = &in[offset] - *value;
605
606
0
    } else if (in[offset] == '.') {
607
        /* leaf-list-predicate: "[" *WSP "." *WSP "=" *WSP quoted-string *WSP "]" */
608
0
        *id = &in[offset];
609
0
        *id_len = 1;
610
0
        offset++;
611
0
        expr = 1;
612
0
    } else if (in[offset] == '-') {
613
        /* typically negative value */
614
0
        *errmsg = "Invalid instance predicate format (negative position or invalid node-identifier).";
615
0
        goto error;
616
0
    } else {
617
        /* key-predicate: "[" *WSP node-identifier *WSP "=" *WSP quoted-string *WSP "]" */
618
0
        in = &in[offset];
619
0
        if (ly_parse_nodeid(&in, prefix, prefix_len, id, id_len)) {
620
0
            *errmsg = "Invalid node-identifier.";
621
0
            goto error;
622
0
        }
623
0
        if ((format == LYD_XML) && !(*prefix)) {
624
            /* all node names MUST be qualified with explicit namespace prefix */
625
0
            *errmsg = "Missing prefix of a node name.";
626
0
            goto error;
627
0
        }
628
0
        offset = in - *pred;
629
0
        in = *pred;
630
0
        expr = 2;
631
0
    }
632
633
0
    if (expr) {
634
        /*  *WSP "=" *WSP quoted-string *WSP "]" */
635
0
        for ( ; isspace(in[offset]); offset++) {}
636
637
0
        if (in[offset] != '=') {
638
0
            if (expr == 1) {
639
0
                *errmsg = "Unexpected character instead of \'=\' in leaf-list-predicate.";
640
0
            } else { /* 2 */
641
0
                *errmsg = "Unexpected character instead of \'=\' in key-predicate.";
642
0
            }
643
0
            goto error;
644
0
        }
645
0
        offset++;
646
0
        for ( ; isspace(in[offset]); offset++) {}
647
648
        /* quoted-string */
649
0
        quot = in[offset++];
650
0
        if ((quot != '\'') && (quot != '\"')) {
651
0
            *errmsg = "String value is not quoted.";
652
0
            goto error;
653
0
        }
654
0
        *value = &in[offset];
655
0
        for ( ; offset < limit && (in[offset] != quot || (offset && in[offset - 1] == '\\')); offset++) {}
656
0
        if (in[offset] == quot) {
657
0
            *value_len = &in[offset] - *value;
658
0
            offset++;
659
0
        } else {
660
0
            *errmsg = "Value is not terminated quoted-string.";
661
0
            goto error;
662
0
        }
663
0
    }
664
665
    /* *WSP "]" */
666
0
    for ( ; isspace(in[offset]); offset++) {}
667
0
    if (in[offset] != ']') {
668
0
        if (expr == 0) {
669
0
            *errmsg = "Predicate (pos) is not terminated by \']\' character.";
670
0
        } else if (expr == 1) {
671
0
            *errmsg = "Predicate (leaf-list-predicate) is not terminated by \']\' character.";
672
0
        } else { /* 2 */
673
0
            *errmsg = "Predicate (key-predicate) is not terminated by \']\' character.";
674
0
        }
675
0
        goto error;
676
0
    }
677
0
    offset++;
678
679
0
    if (offset <= limit) {
680
0
        *pred = &in[offset];
681
0
        return LY_SUCCESS;
682
0
    }
683
684
    /* we read after the limit */
685
0
    *errmsg = "Predicate is incomplete.";
686
0
    *prefix = *id = *value = NULL;
687
0
    *prefix_len = *id_len = *value_len = 0;
688
0
    offset = limit;
689
0
    ret = LY_EINVAL;
690
691
0
error:
692
0
    *pred = &in[offset];
693
0
    return ret;
694
0
}