Coverage Report

Created: 2026-04-09 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libyang/src/json.c
Line
Count
Source
1
/**
2
 * @file json.c
3
 * @author Radek Krejci <rkrejci@cesnet.cz>
4
 * @brief Generic JSON format parser for libyang
5
 *
6
 * Copyright (c) 2020 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
#include <assert.h>
16
#include <ctype.h>
17
#include <errno.h>
18
#include <stdlib.h>
19
#include <string.h>
20
#include <sys/types.h>
21
22
#include "common.h"
23
#include "in_internal.h"
24
#include "json.h"
25
26
#define JSON_PUSH_STATUS_RET(CTX, STATUS) \
27
0
    LY_CHECK_RET(ly_set_add(&CTX->status, (void*)STATUS, 1, NULL))
28
29
#define JSON_POP_STATUS_RET(CTX) \
30
0
    assert(CTX->status.count); CTX->status.count--;
31
32
const char *
33
lyjson_token2str(enum LYJSON_PARSER_STATUS status)
34
0
{
35
0
    switch (status) {
36
0
    case LYJSON_ERROR:
37
0
        return "error";
38
0
    case LYJSON_ROOT:
39
0
        return "document root";
40
0
    case LYJSON_FALSE:
41
0
        return "false";
42
0
    case LYJSON_TRUE:
43
0
        return "true";
44
0
    case LYJSON_NULL:
45
0
        return "null";
46
0
    case LYJSON_OBJECT:
47
0
        return "object";
48
0
    case LYJSON_OBJECT_CLOSED:
49
0
        return "object closed";
50
0
    case LYJSON_OBJECT_EMPTY:
51
0
        return "empty object";
52
0
    case LYJSON_ARRAY:
53
0
        return "array";
54
0
    case LYJSON_ARRAY_CLOSED:
55
0
        return "array closed";
56
0
    case LYJSON_ARRAY_EMPTY:
57
0
        return "empty array";
58
0
    case LYJSON_NUMBER:
59
0
        return "number";
60
0
    case LYJSON_STRING:
61
0
        return "string";
62
0
    case LYJSON_END:
63
0
        return "end of input";
64
0
    }
65
66
0
    return "";
67
0
}
68
69
static LY_ERR
70
skip_ws(struct lyjson_ctx *jsonctx)
71
0
{
72
    /* skip leading whitespaces */
73
0
    while (*jsonctx->in->current != '\0' && is_jsonws(*jsonctx->in->current)) {
74
0
        if (*jsonctx->in->current == '\n') {
75
0
            LY_IN_NEW_LINE(jsonctx->in);
76
0
        }
77
0
        ly_in_skip(jsonctx->in, 1);
78
0
    }
79
0
    if (*jsonctx->in->current == '\0') {
80
0
        JSON_PUSH_STATUS_RET(jsonctx, LYJSON_END);
81
0
    }
82
83
0
    return LY_SUCCESS;
84
0
}
85
86
/*
87
 * @brief Set value corresponding to the current context's status
88
 */
89
static void
90
lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic)
91
0
{
92
0
    assert(jsonctx);
93
94
0
    if (jsonctx->dynamic) {
95
0
        free((char *)jsonctx->value);
96
0
    }
97
0
    jsonctx->value = value;
98
0
    jsonctx->value_len = value_len;
99
0
    jsonctx->dynamic = dynamic;
100
0
}
101
102
static LY_ERR
103
lyjson_check_next(struct lyjson_ctx *jsonctx)
104
0
{
105
0
    if (jsonctx->status.count == 1) {
106
        /* top level value (JSON-text), ws expected */
107
0
        if ((*jsonctx->in->current == '\0') || is_jsonws(*jsonctx->in->current)) {
108
0
            return LY_SUCCESS;
109
0
        }
110
0
    } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_OBJECT) {
111
0
        LY_CHECK_RET(skip_ws(jsonctx));
112
0
        if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == '}')) {
113
0
            return LY_SUCCESS;
114
0
        }
115
0
    } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_ARRAY) {
116
0
        LY_CHECK_RET(skip_ws(jsonctx));
117
0
        if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == ']')) {
118
0
            return LY_SUCCESS;
119
0
        }
120
0
    }
121
122
0
    LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Unexpected character \"%c\" after JSON %s.",
123
0
            *jsonctx->in->current, lyjson_token2str(lyjson_ctx_status(jsonctx, 0)));
124
0
    return LY_EVALID;
125
0
}
126
127
/**
128
 * Input is expected to start after the opening quotation-mark.
129
 * When succeeds, input is moved after the closing quotation-mark.
130
 */
131
static LY_ERR
132
lyjson_string_(struct lyjson_ctx *jsonctx)
133
0
{
134
0
#define BUFSIZE 24
135
0
#define BUFSIZE_STEP 128
136
137
0
    const char *in = jsonctx->in->current, *start;
138
0
    char *buf = NULL;
139
0
    size_t offset;   /* read offset in input buffer */
140
0
    size_t len;      /* length of the output string (write offset in output buffer) */
141
0
    size_t size = 0; /* size of the output buffer */
142
0
    size_t u;
143
0
    uint64_t start_line;
144
145
0
    assert(jsonctx);
146
147
    /* init */
148
0
    start = in;
149
0
    start_line = jsonctx->in->line;
150
0
    offset = len = 0;
151
152
    /* parse */
153
0
    while (in[offset]) {
154
0
        if (in[offset] == '\\') {
155
            /* escape sequence */
156
0
            const char *slash = &in[offset];
157
0
            uint32_t value;
158
0
            uint8_t i = 1;
159
160
0
            if (!buf) {
161
                /* prepare output buffer */
162
0
                buf = malloc(BUFSIZE);
163
0
                LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
164
0
                size = BUFSIZE;
165
0
            }
166
167
            /* allocate enough for the offset and next character,
168
             * we will need 4 bytes at most since we support only the predefined
169
             * (one-char) entities and character references */
170
0
            if (len + offset + 4 >= size) {
171
0
                size_t increment;
172
0
                for (increment = BUFSIZE_STEP; len + offset + 4 >= size + increment; increment += BUFSIZE_STEP) {}
173
0
                buf = ly_realloc(buf, size + increment);
174
0
                LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
175
0
                size += BUFSIZE_STEP;
176
0
            }
177
178
0
            if (offset) {
179
                /* store what we have so far */
180
0
                memcpy(&buf[len], in, offset);
181
0
                len += offset;
182
0
                in += offset;
183
0
                offset = 0;
184
0
            }
185
186
0
            switch (in[++offset]) {
187
0
            case '"':
188
                /* quotation mark */
189
0
                value = 0x22;
190
0
                break;
191
0
            case '\\':
192
                /* reverse solidus */
193
0
                value = 0x5c;
194
0
                break;
195
0
            case '/':
196
                /* solidus */
197
0
                value = 0x2f;
198
0
                break;
199
0
            case 'b':
200
                /* backspace */
201
0
                value = 0x08;
202
0
                break;
203
0
            case 'f':
204
                /* form feed */
205
0
                value = 0x0c;
206
0
                break;
207
0
            case 'n':
208
                /* line feed */
209
0
                value = 0x0a;
210
0
                break;
211
0
            case 'r':
212
                /* carriage return */
213
0
                value = 0x0d;
214
0
                break;
215
0
            case 't':
216
                /* tab */
217
0
                value = 0x09;
218
0
                break;
219
0
            case 'u':
220
                /* Basic Multilingual Plane character \uXXXX */
221
0
                offset++;
222
0
                for (value = i = 0; i < 4; i++) {
223
0
                    if (!in[offset + i]) {
224
0
                        LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", slash);
225
0
                        goto error;
226
0
                    } else if (isdigit(in[offset + i])) {
227
0
                        u = (in[offset + i] - '0');
228
0
                    } else if (in[offset + i] > 'F') {
229
0
                        u = LY_BASE_DEC + (in[offset + i] - 'a');
230
0
                    } else {
231
0
                        u = LY_BASE_DEC + (in[offset + i] - 'A');
232
0
                    }
233
0
                    value = (LY_BASE_HEX * value) + u;
234
0
                }
235
0
                break;
236
0
            default:
237
                /* invalid escape sequence */
238
0
                LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character escape sequence \\%c.", in[offset]);
239
0
                goto error;
240
241
0
            }
242
243
0
            offset += i;   /* add read escaped characters */
244
0
            LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
245
0
                    LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).",
246
0
                    (int)(&in[offset] - slash), slash, value),
247
0
                    error);
248
0
            len += u;      /* update number of bytes in buffer */
249
0
            in += offset;  /* move the input by the processed bytes stored in the buffer ... */
250
0
            offset = 0;    /* ... and reset the offset index for future moving data into buffer */
251
252
0
        } else if (in[offset] == '"') {
253
            /* end of string */
254
0
            if (buf) {
255
                /* realloc exact size string */
256
0
                buf = ly_realloc(buf, len + offset + 1);
257
0
                LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
258
0
                size = len + offset + 1;
259
0
                memcpy(&buf[len], in, offset);
260
261
                /* set terminating NULL byte */
262
0
                buf[len + offset] = '\0';
263
0
            }
264
0
            len += offset;
265
0
            ++offset;
266
0
            in += offset;
267
0
            goto success;
268
0
        } else {
269
            /* get it as UTF-8 character for check */
270
0
            const char *c = &in[offset];
271
0
            uint32_t code = 0;
272
0
            size_t code_len = 0;
273
274
0
            LY_CHECK_ERR_GOTO(ly_getutf8(&c, &code, &code_len),
275
0
                    LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error);
276
277
0
            LY_CHECK_ERR_GOTO(!is_jsonstrchar(code),
278
0
                    LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08x).",
279
0
                    (int)(&in[offset] - start + code_len), start, code),
280
0
                    error);
281
282
            /* character is ok, continue */
283
0
            offset += code_len;
284
0
        }
285
0
    }
286
287
    /* EOF reached before endchar */
288
0
    LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
289
0
    LOGVAL_LINE(jsonctx->ctx, start_line, LYVE_SYNTAX, "Missing quotation-mark at the end of a JSON string.");
290
291
0
error:
292
0
    free(buf);
293
0
    return LY_EVALID;
294
295
0
success:
296
0
    jsonctx->in->current = in;
297
0
    if (buf) {
298
0
        lyjson_ctx_set_value(jsonctx, buf, len, 1);
299
0
    } else {
300
0
        lyjson_ctx_set_value(jsonctx, start, len, 0);
301
0
    }
302
303
0
    return LY_SUCCESS;
304
305
0
#undef BUFSIZE
306
0
#undef BUFSIZE_STEP
307
0
}
308
309
/*
310
 *
311
 * Wrapper around lyjson_string_() adding LYJSON_STRING status into context to allow using lyjson_string_() for parsing object's name.
312
 */
313
static LY_ERR
314
lyjson_string(struct lyjson_ctx *jsonctx)
315
0
{
316
0
    LY_CHECK_RET(lyjson_string_(jsonctx));
317
318
0
    JSON_PUSH_STATUS_RET(jsonctx, LYJSON_STRING);
319
0
    LY_CHECK_RET(lyjson_check_next(jsonctx));
320
321
0
    return LY_SUCCESS;
322
0
}
323
324
static LY_ERR
325
lyjson_number(struct lyjson_ctx *jsonctx)
326
0
{
327
0
    size_t offset = 0, exponent = 0;
328
0
    const char *in = jsonctx->in->current;
329
0
    uint8_t minus = 0;
330
331
0
    if (in[offset] == '-') {
332
0
        ++offset;
333
0
        minus = 1;
334
0
    }
335
336
0
    if (in[offset] == '0') {
337
0
        ++offset;
338
0
    } else if (isdigit(in[offset])) {
339
0
        ++offset;
340
0
        while (isdigit(in[offset])) {
341
0
            ++offset;
342
0
        }
343
0
    } else {
344
0
invalid_character:
345
0
        if (in[offset]) {
346
0
            LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON Number value (\"%c\").", in[offset]);
347
0
        } else {
348
0
            LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
349
0
        }
350
0
        return LY_EVALID;
351
0
    }
352
353
0
    if (in[offset] == '.') {
354
0
        ++offset;
355
0
        if (!isdigit(in[offset])) {
356
0
            goto invalid_character;
357
0
        }
358
0
        while (isdigit(in[offset])) {
359
0
            ++offset;
360
0
        }
361
0
    }
362
363
0
    if ((in[offset] == 'e') || (in[offset] == 'E')) {
364
0
        exponent = offset++;
365
0
        if ((in[offset] == '+') || (in[offset] == '-')) {
366
0
            ++offset;
367
0
        }
368
0
        if (!isdigit(in[offset])) {
369
0
            goto invalid_character;
370
0
        }
371
0
        while (isdigit(in[offset])) {
372
0
            ++offset;
373
0
        }
374
0
    }
375
376
0
    if (exponent) {
377
        /* convert JSON number with exponent into the representation used by YANG */
378
0
        long int  e_val;
379
0
        char *ptr, *dec_point, *num;
380
0
        const char *e_ptr = &in[exponent + 1];
381
0
        size_t num_len, i;
382
0
        int64_t dp_position; /* final position of the deciaml point */
383
384
0
        errno = 0;
385
0
        e_val = strtol(e_ptr, &ptr, LY_BASE_DEC);
386
0
        if (errno) {
387
0
            LOGVAL(jsonctx->ctx, LYVE_SEMANTICS, "Exponent out-of-bounds in a JSON Number value (%.*s).",
388
0
                    (int)(offset - minus - (e_ptr - in)), e_ptr);
389
0
            return LY_EVALID;
390
0
        }
391
392
0
        dec_point = ly_strnchr(in, '.', exponent);
393
0
        if (!dec_point) {
394
            /* value is integer, we are just ... */
395
0
            if (e_val >= 0) {
396
                /* adding zeros at the end */
397
0
                num_len = exponent + e_val;
398
0
                dp_position = num_len; /* decimal point is behind the actual value */
399
0
            } else if ((size_t)labs(e_val) < exponent) {
400
                /* adding decimal point between the integer's digits */
401
0
                num_len = exponent + 1;
402
0
                dp_position = exponent + e_val;
403
0
            } else {
404
                /* adding decimal point before the integer with adding leading zero(s) */
405
0
                num_len = labs(e_val) + 2 + minus;
406
0
                dp_position = exponent + e_val;
407
0
            }
408
0
            dp_position -= minus;
409
0
        } else {
410
            /* value is decimal, we are moving the decimal point */
411
0
            dp_position = dec_point - in + e_val - minus;
412
0
            if (dp_position > (ssize_t)exponent) {
413
                /* moving decimal point after the decimal value make the integer result */
414
0
                num_len = dp_position;
415
0
            } else if (dp_position < 0) {
416
                /* moving decimal point before the decimal value requires additional zero(s)
417
                 * (decimal point is already count in exponent value) */
418
0
                num_len = exponent + labs(dp_position) + 1;
419
0
            } else if (dp_position == 0) {
420
                /* moving the decimal point exactly to the beginning will cause a zero character to be added. */
421
0
                num_len = exponent + 1;
422
0
            } else {
423
                /* moving decimal point just inside the decimal value does not make any change in length */
424
0
                num_len = exponent;
425
0
            }
426
0
        }
427
428
0
        LY_CHECK_ERR_RET((num_len + 1) > LY_NUMBER_MAXLEN, LOGVAL(jsonctx->ctx, LYVE_SEMANTICS,
429
0
                "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit."), LY_EVALID);
430
431
        /* allocate buffer for the result (add terminating NULL-byte */
432
0
        num = malloc(num_len + 1);
433
0
        LY_CHECK_ERR_RET(!num, LOGMEM(jsonctx->ctx), LY_EMEM);
434
435
        /* compose the resulting vlaue */
436
0
        i = 0;
437
0
        if (minus) {
438
0
            num[i++] = '-';
439
0
        }
440
        /* add leading zeros */
441
0
        if (dp_position <= 0) {
442
0
            num[i++] = '0';
443
0
            num[i++] = '.';
444
0
            for ( ; dp_position; dp_position++) {
445
0
                num[i++] = '0';
446
0
            }
447
0
        }
448
        /* copy the value */
449
0
        ly_bool dp_placed;
450
0
        size_t j;
451
0
        for (dp_placed = dp_position ? 0 : 1, j = minus; j < exponent; j++) {
452
0
            if (in[j] == '.') {
453
0
                continue;
454
0
            }
455
0
            if (!dp_placed) {
456
0
                if (!dp_position) {
457
0
                    num[i++] = '.';
458
0
                    dp_placed = 1;
459
0
                } else {
460
0
                    dp_position--;
461
0
                    if (in[j] == '0') {
462
0
                        num_len--;
463
0
                        continue;
464
0
                    }
465
0
                }
466
0
            }
467
468
0
            num[i++] = in[j];
469
0
        }
470
        /* trailing zeros */
471
0
        while (dp_position--) {
472
0
            num[i++] = '0';
473
0
        }
474
        /* terminating NULL byte */
475
0
        num[i] = '\0';
476
477
        /* store the modified number */
478
0
        lyjson_ctx_set_value(jsonctx, num, num_len, 1);
479
0
    } else {
480
        /* store the number */
481
0
        lyjson_ctx_set_value(jsonctx, jsonctx->in->current, offset, 0);
482
0
    }
483
0
    ly_in_skip(jsonctx->in, offset);
484
485
0
    JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NUMBER);
486
0
    LY_CHECK_RET(lyjson_check_next(jsonctx));
487
488
0
    return LY_SUCCESS;
489
0
}
490
491
static LY_ERR
492
lyjson_object_name(struct lyjson_ctx *jsonctx)
493
0
{
494
0
    if (*jsonctx->in->current != '"') {
495
0
        LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
496
0
                jsonctx->in->current, "a JSON object's member");
497
0
        return LY_EVALID;
498
0
    }
499
0
    ly_in_skip(jsonctx->in, 1);
500
501
0
    LY_CHECK_RET(lyjson_string_(jsonctx));
502
0
    LY_CHECK_RET(skip_ws(jsonctx));
503
0
    if (*jsonctx->in->current != ':') {
504
0
        LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
505
0
                "a JSON object's name-separator ':'");
506
0
        return LY_EVALID;
507
0
    }
508
0
    ly_in_skip(jsonctx->in, 1);
509
0
    LY_CHECK_RET(skip_ws(jsonctx));
510
511
0
    return LY_SUCCESS;
512
0
}
513
514
static LY_ERR
515
lyjson_object(struct lyjson_ctx *jsonctx)
516
0
{
517
0
    LY_CHECK_RET(skip_ws(jsonctx));
518
519
0
    if (*jsonctx->in->current == '}') {
520
        /* empty object */
521
0
        ly_in_skip(jsonctx->in, 1);
522
0
        lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
523
0
        JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT_EMPTY);
524
0
        return LY_SUCCESS;
525
0
    }
526
527
0
    LY_CHECK_RET(lyjson_object_name(jsonctx));
528
529
    /* output data are set by lyjson_string_() */
530
0
    JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT);
531
532
0
    return LY_SUCCESS;
533
0
}
534
535
/*
536
 * @brief Process JSON array envelope
537
 *
538
 *
539
 *
540
 * @param[in] jsonctx JSON parser context
541
 * @return LY_SUCCESS or LY_EMEM
542
 */
543
static LY_ERR
544
lyjson_array(struct lyjson_ctx *jsonctx)
545
0
{
546
0
    LY_CHECK_RET(skip_ws(jsonctx));
547
548
0
    if (*jsonctx->in->current == ']') {
549
        /* empty array */
550
0
        ly_in_skip(jsonctx->in, 1);
551
0
        JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY_EMPTY);
552
0
    } else {
553
0
        JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY);
554
0
    }
555
556
    /* erase previous values, array has no value on its own */
557
0
    lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
558
559
0
    return LY_SUCCESS;
560
0
}
561
562
static LY_ERR
563
lyjson_value(struct lyjson_ctx *jsonctx)
564
0
{
565
0
    if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
566
0
        return LY_SUCCESS;
567
0
    }
568
569
0
    if ((*jsonctx->in->current == 'f') && !strncmp(jsonctx->in->current, "false", ly_strlen_const("false"))) {
570
        /* false */
571
0
        lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
572
0
        ly_in_skip(jsonctx->in, ly_strlen_const("false"));
573
0
        JSON_PUSH_STATUS_RET(jsonctx, LYJSON_FALSE);
574
0
        LY_CHECK_RET(lyjson_check_next(jsonctx));
575
576
0
    } else if ((*jsonctx->in->current == 't') && !strncmp(jsonctx->in->current, "true", ly_strlen_const("true"))) {
577
        /* true */
578
0
        lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
579
0
        ly_in_skip(jsonctx->in, ly_strlen_const("true"));
580
0
        JSON_PUSH_STATUS_RET(jsonctx, LYJSON_TRUE);
581
0
        LY_CHECK_RET(lyjson_check_next(jsonctx));
582
583
0
    } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) {
584
        /* none */
585
0
        lyjson_ctx_set_value(jsonctx, "", 0, 0);
586
0
        ly_in_skip(jsonctx->in, ly_strlen_const("null"));
587
0
        JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NULL);
588
0
        LY_CHECK_RET(lyjson_check_next(jsonctx));
589
590
0
    } else if (*jsonctx->in->current == '"') {
591
        /* string */
592
0
        ly_in_skip(jsonctx->in, 1);
593
0
        LY_CHECK_RET(lyjson_string(jsonctx));
594
595
0
    } else if (*jsonctx->in->current == '[') {
596
        /* array */
597
0
        ly_in_skip(jsonctx->in, 1);
598
0
        LY_CHECK_RET(lyjson_array(jsonctx));
599
600
0
    } else if (*jsonctx->in->current == '{') {
601
        /* object */
602
0
        ly_in_skip(jsonctx->in, 1);
603
0
        LY_CHECK_RET(lyjson_object(jsonctx));
604
605
0
    } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) {
606
        /* number */
607
0
        LY_CHECK_RET(lyjson_number(jsonctx));
608
609
0
    } else {
610
        /* unexpected value */
611
0
        LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
612
0
                jsonctx->in->current, "a JSON value");
613
0
        return LY_EVALID;
614
0
    }
615
616
0
    return LY_SUCCESS;
617
0
}
618
619
LY_ERR
620
lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
621
0
{
622
0
    LY_ERR ret = LY_SUCCESS;
623
0
    struct lyjson_ctx *jsonctx;
624
625
0
    assert(ctx);
626
0
    assert(in);
627
0
    assert(jsonctx_p);
628
629
    /* new context */
630
0
    jsonctx = calloc(1, sizeof *jsonctx);
631
0
    LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
632
0
    jsonctx->ctx = ctx;
633
0
    jsonctx->in = in;
634
635
0
    LOG_LOCINIT(NULL, NULL, NULL, in);
636
637
    /* parse JSON value, if any */
638
0
    LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup);
639
0
    if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) {
640
        /* empty data input */
641
0
        goto cleanup;
642
0
    }
643
644
0
    ret = lyjson_value(jsonctx);
645
646
0
    if ((jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
647
0
        LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
648
0
        ret = LY_EVALID;
649
0
    }
650
651
0
cleanup:
652
0
    if (ret) {
653
0
        lyjson_ctx_free(jsonctx);
654
0
    } else {
655
0
        *jsonctx_p = jsonctx;
656
0
    }
657
0
    return ret;
658
0
}
659
660
void
661
lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
662
0
{
663
0
    if (jsonctx->backup.dynamic) {
664
0
        free((char *)jsonctx->backup.value);
665
0
    }
666
0
    jsonctx->backup.status = lyjson_ctx_status(jsonctx, 0);
667
0
    jsonctx->backup.status_count = jsonctx->status.count;
668
0
    jsonctx->backup.value = jsonctx->value;
669
0
    jsonctx->backup.value_len = jsonctx->value_len;
670
0
    jsonctx->backup.input = jsonctx->in->current;
671
0
    jsonctx->backup.dynamic = jsonctx->dynamic;
672
0
    jsonctx->dynamic = 0;
673
0
}
674
675
void
676
lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
677
0
{
678
0
    if (jsonctx->dynamic) {
679
0
        free((char *)jsonctx->value);
680
0
    }
681
0
    jsonctx->status.count = jsonctx->backup.status_count;
682
0
    jsonctx->status.objs[jsonctx->backup.status_count - 1] = (void *)jsonctx->backup.status;
683
0
    jsonctx->value = jsonctx->backup.value;
684
0
    jsonctx->value_len = jsonctx->backup.value_len;
685
0
    jsonctx->in->current = jsonctx->backup.input;
686
0
    jsonctx->dynamic = jsonctx->backup.dynamic;
687
0
    jsonctx->backup.dynamic = 0;
688
0
}
689
690
LY_ERR
691
lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
692
0
{
693
0
    LY_ERR ret = LY_SUCCESS;
694
0
    ly_bool toplevel = 0;
695
0
    enum LYJSON_PARSER_STATUS prev;
696
697
0
    assert(jsonctx);
698
699
0
    prev = lyjson_ctx_status(jsonctx, 0);
700
701
0
    if ((prev == LYJSON_OBJECT) || (prev == LYJSON_ARRAY)) {
702
        /* get value for the object's member OR the first value in the array */
703
0
        ret = lyjson_value(jsonctx);
704
0
        goto result;
705
0
    } else {
706
        /* the previous token is closed and should be completely processed */
707
0
        JSON_POP_STATUS_RET(jsonctx);
708
0
        prev = lyjson_ctx_status(jsonctx, 0);
709
0
    }
710
711
0
    if (!jsonctx->status.count) {
712
        /* we are done with the top level value */
713
0
        toplevel = 1;
714
0
    }
715
0
    LY_CHECK_RET(skip_ws(jsonctx));
716
0
    if (toplevel && !jsonctx->status.count) {
717
        /* EOF expected, but there are some data after the top level token */
718
0
        LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Expecting end-of-input, but some data follows the top level JSON value.");
719
0
        return LY_EVALID;
720
0
    }
721
722
0
    if (toplevel) {
723
        /* we are done */
724
0
        return LY_SUCCESS;
725
0
    }
726
727
    /* continue with the next token */
728
0
    assert(prev == LYJSON_OBJECT || prev == LYJSON_ARRAY);
729
730
0
    if (*jsonctx->in->current == ',') {
731
        /* sibling item in the ... */
732
0
        ly_in_skip(jsonctx->in, 1);
733
0
        LY_CHECK_RET(skip_ws(jsonctx));
734
735
0
        if (prev == LYJSON_OBJECT) {
736
            /* ... object - get another object's member */
737
0
            ret = lyjson_object_name(jsonctx);
738
0
        } else { /* LYJSON_ARRAY */
739
            /* ... array - get another complete value */
740
0
            ret = lyjson_value(jsonctx);
741
0
        }
742
0
    } else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) || ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) {
743
0
        ly_in_skip(jsonctx->in, 1);
744
0
        JSON_POP_STATUS_RET(jsonctx);
745
0
        JSON_PUSH_STATUS_RET(jsonctx, prev + 1);
746
0
    } else {
747
        /* unexpected value */
748
0
        LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
749
0
                prev == LYJSON_ARRAY ? "another JSON value in array" : "another JSON object's member");
750
0
        return LY_EVALID;
751
0
    }
752
753
0
result:
754
0
    if ((ret == LY_SUCCESS) && (jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
755
0
        LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
756
0
        ret = LY_EVALID;
757
0
    }
758
759
0
    if ((ret == LY_SUCCESS) && status) {
760
0
        *status = lyjson_ctx_status(jsonctx, 0);
761
0
    }
762
763
0
    return ret;
764
0
}
765
766
enum LYJSON_PARSER_STATUS
767
lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index)
768
0
{
769
0
    assert(jsonctx);
770
771
0
    if (jsonctx->status.count < index) {
772
0
        return LYJSON_ERROR;
773
0
    } else if (jsonctx->status.count == index) {
774
0
        return LYJSON_ROOT;
775
0
    } else {
776
0
        return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - (index + 1)];
777
0
    }
778
0
}
779
780
void
781
lyjson_ctx_free(struct lyjson_ctx *jsonctx)
782
0
{
783
0
    if (!jsonctx) {
784
0
        return;
785
0
    }
786
787
0
    LOG_LOCBACK(0, 0, 0, 1);
788
789
0
    if (jsonctx->dynamic) {
790
0
        free((char *)jsonctx->value);
791
0
    }
792
0
    if (jsonctx->backup.dynamic) {
793
0
        free((char *)jsonctx->backup.value);
794
0
    }
795
796
0
    ly_set_erase(&jsonctx->status, NULL);
797
798
0
    free(jsonctx);
799
0
}