Coverage Report

Created: 2025-08-29 06:58

/src/libyang/src/json.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file json.c
3
 * @author Radek Krejci <rkrejci@cesnet.cz>
4
 * @author Michal Vasko <mvasko@cesnet.cz>
5
 * @brief Generic JSON format parser for libyang
6
 *
7
 * Copyright (c) 2020 - 2023 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
#include <assert.h>
17
#include <ctype.h>
18
#include <errno.h>
19
#include <stdlib.h>
20
#include <string.h>
21
#include <sys/types.h>
22
23
#include "in_internal.h"
24
#include "json.h"
25
#include "ly_common.h"
26
#include "tree_schema_internal.h"
27
28
const char *
29
lyjson_token2str(enum LYJSON_PARSER_STATUS status)
30
0
{
31
0
    switch (status) {
32
0
    case LYJSON_ERROR:
33
0
        return "error";
34
0
    case LYJSON_OBJECT:
35
0
        return "object";
36
0
    case LYJSON_OBJECT_NEXT:
37
0
        return "object next";
38
0
    case LYJSON_OBJECT_CLOSED:
39
0
        return "object closed";
40
0
    case LYJSON_ARRAY:
41
0
        return "array";
42
0
    case LYJSON_ARRAY_NEXT:
43
0
        return "array next";
44
0
    case LYJSON_ARRAY_CLOSED:
45
0
        return "array closed";
46
0
    case LYJSON_OBJECT_NAME:
47
0
        return "object name";
48
0
    case LYJSON_NUMBER:
49
0
        return "number";
50
0
    case LYJSON_STRING:
51
0
        return "string";
52
0
    case LYJSON_TRUE:
53
0
        return "true";
54
0
    case LYJSON_FALSE:
55
0
        return "false";
56
0
    case LYJSON_NULL:
57
0
        return "null";
58
0
    case LYJSON_END:
59
0
        return "end of input";
60
0
    }
61
62
0
    return "";
63
0
}
64
65
enum LYJSON_PARSER_STATUS
66
lyjson_ctx_status(struct lyjson_ctx *jsonctx)
67
0
{
68
0
    assert(jsonctx);
69
70
0
    if (!jsonctx->status.count) {
71
0
        return LYJSON_END;
72
0
    }
73
74
0
    return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - 1];
75
0
}
76
77
uint32_t
78
lyjson_ctx_depth(struct lyjson_ctx *jsonctx)
79
0
{
80
0
    return jsonctx->status.count;
81
0
}
82
83
/**
84
 * @brief Skip WS in the JSON context.
85
 *
86
 * @param[in] jsonctx JSON parser context.
87
 */
88
static void
89
lyjson_skip_ws(struct lyjson_ctx *jsonctx)
90
0
{
91
    /* skip whitespaces */
92
0
    while (is_jsonws(*jsonctx->in->current)) {
93
0
        if (*jsonctx->in->current == '\n') {
94
0
            LY_IN_NEW_LINE(jsonctx->in);
95
0
        }
96
0
        ly_in_skip(jsonctx->in, 1);
97
0
    }
98
0
}
99
100
/**
101
 * @brief Set value in the JSON context.
102
 *
103
 * @param[in] jsonctx JSON parser context.
104
 * @param[in] value Value to set.
105
 * @param[in] value_len Length of @p value.
106
 * @param[in] dynamic Whether @p value is dynamically-allocated.
107
 */
108
static void
109
lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic)
110
0
{
111
0
    assert(jsonctx);
112
113
0
    if (jsonctx->dynamic) {
114
0
        free((char *)jsonctx->value);
115
0
    }
116
0
    jsonctx->value = value;
117
0
    jsonctx->value_len = value_len;
118
0
    jsonctx->dynamic = dynamic;
119
0
}
120
121
/**
122
 * @brief Parse a JSON string (starting after double quotes) and store it in the context.
123
 *
124
 * @param[in] jsonctx JSON parser context.
125
 * @return LY_ERR value.
126
 */
127
static LY_ERR
128
lyjson_string(struct lyjson_ctx *jsonctx)
129
0
{
130
0
    const char *in = jsonctx->in->current, *start, *c;
131
0
    char *buf = NULL;
132
0
    size_t offset;   /* read offset in input buffer */
133
0
    size_t len;      /* length of the output string (write offset in output buffer) */
134
0
    size_t size = 0; /* size of the output buffer */
135
0
    size_t u;
136
0
    uint64_t start_line, orig_line;
137
0
    uint32_t value;
138
0
    uint8_t i;
139
140
0
    assert(jsonctx);
141
142
    /* init */
143
0
    start = in;
144
0
    start_line = jsonctx->in->line;
145
0
    offset = len = 0;
146
147
    /* parse */
148
0
    while (in[offset]) {
149
0
        switch (in[offset]) {
150
0
        case '\\':
151
            /* escape sequence */
152
0
            c = &in[offset];
153
0
            if (!buf) {
154
                /* prepare output buffer */
155
0
                buf = malloc(LYJSON_STRING_BUF_START);
156
0
                LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
157
0
                size = LYJSON_STRING_BUF_START;
158
0
            }
159
160
            /* allocate enough for the offset and next character,
161
             * we will need 4 bytes at most since we support only the predefined
162
             * (one-char) entities and character references */
163
0
            if (len + offset + 4 >= size) {
164
0
                size_t increment;
165
166
0
                for (increment = LYJSON_STRING_BUF_STEP; len + offset + 4 >= size + increment; increment += LYJSON_STRING_BUF_STEP) {}
167
0
                buf = ly_realloc(buf, size + increment);
168
0
                LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
169
0
                size += LYJSON_STRING_BUF_STEP;
170
0
            }
171
172
0
            if (offset) {
173
                /* store what we have so far */
174
0
                memcpy(&buf[len], in, offset);
175
0
                len += offset;
176
0
                in += offset;
177
0
                offset = 0;
178
0
            }
179
180
0
            i = 1;
181
0
            switch (in[++offset]) {
182
0
            case '"':
183
                /* quotation mark */
184
0
                value = 0x22;
185
0
                break;
186
0
            case '\\':
187
                /* reverse solidus */
188
0
                value = 0x5c;
189
0
                break;
190
0
            case '/':
191
                /* solidus */
192
0
                value = 0x2f;
193
0
                break;
194
0
            case 'b':
195
                /* backspace */
196
0
                value = 0x08;
197
0
                break;
198
0
            case 'f':
199
                /* form feed */
200
0
                value = 0x0c;
201
0
                break;
202
0
            case 'n':
203
                /* line feed */
204
0
                value = 0x0a;
205
0
                break;
206
0
            case 'r':
207
                /* carriage return */
208
0
                value = 0x0d;
209
0
                break;
210
0
            case 't':
211
                /* tab */
212
0
                value = 0x09;
213
0
                break;
214
0
            case 'u':
215
                /* Basic Multilingual Plane character \uXXXX */
216
0
                offset++;
217
0
                for (value = i = 0; i < 4; i++) {
218
0
                    if (!in[offset + i]) {
219
0
                        LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", c);
220
0
                        goto error;
221
0
                    } else if (isdigit(in[offset + i])) {
222
0
                        u = (in[offset + i] - '0');
223
0
                    } else if (in[offset + i] > 'F') {
224
0
                        u = LY_BASE_DEC + (in[offset + i] - 'a');
225
0
                    } else {
226
0
                        u = LY_BASE_DEC + (in[offset + i] - 'A');
227
0
                    }
228
0
                    value = (LY_BASE_HEX * value) + u;
229
0
                }
230
0
                break;
231
0
            default:
232
                /* invalid escape sequence */
233
0
                LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character escape sequence \\%c.", in[offset]);
234
0
                goto error;
235
236
0
            }
237
238
0
            offset += i;   /* add read escaped characters */
239
0
            LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
240
0
                    LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08" PRIx32 ").",
241
0
                    (int)(&in[offset] - c), c, value),
242
0
                    error);
243
0
            len += u;      /* update number of bytes in buffer */
244
0
            in += offset;  /* move the input by the processed bytes stored in the buffer ... */
245
0
            offset = 0;    /* ... and reset the offset index for future moving data into buffer */
246
0
            break;
247
248
0
        case '"':
249
            /* end of string */
250
0
            if (buf) {
251
                /* realloc exact size string */
252
0
                buf = ly_realloc(buf, len + offset + 1);
253
0
                LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
254
0
                size = len + offset + 1;
255
0
                if (offset) {
256
0
                    memcpy(&buf[len], in, offset);
257
0
                }
258
259
                /* set terminating NULL byte */
260
0
                buf[len + offset] = '\0';
261
0
            }
262
0
            len += offset;
263
0
            ++offset;
264
0
            in += offset;
265
0
            goto success;
266
267
0
        default:
268
            /* get it as UTF-8 character for check */
269
0
            c = &in[offset];
270
0
            LY_CHECK_ERR_GOTO(ly_getutf8(&c, &value, &u),
271
0
                    LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error);
272
273
0
            LY_CHECK_ERR_GOTO(!is_jsonstrchar(value),
274
0
                    LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08" PRIx32 ").",
275
0
                    (int)(&in[offset] - start + u), start, value),
276
0
                    error);
277
278
            /* character is ok, continue */
279
0
            offset += u;
280
0
            break;
281
0
        }
282
0
    }
283
284
    /* EOF reached before endchar */
285
0
    LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
286
0
    orig_line = jsonctx->in->line;
287
0
    jsonctx->in->line = start_line;
288
0
    LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Missing quotation-mark at the end of a JSON string.");
289
0
    jsonctx->in->line = orig_line;
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
0
}
305
306
/**
307
 * @brief Calculate how many @p c characters there are in a row.
308
 *
309
 * @param[in] str Count from this position.
310
 * @param[in] end Position after the last checked character.
311
 * @param[in] c Checked character.
312
 * @param[in] backwards Set to 1, if to proceed from end-1 to str.
313
 * @return Number of characters in a row.
314
 */
315
static uint32_t
316
lyjson_count_in_row(const char *str, const char *end, char c, ly_bool backwards)
317
0
{
318
0
    uint32_t cnt;
319
320
0
    assert(str && end);
321
322
0
    if (str >= end) {
323
0
        return 0;
324
0
    }
325
326
0
    if (!backwards) {
327
0
        for (cnt = 0; (str != end) && (*str == c); ++str, ++cnt) {}
328
0
    } else {
329
0
        --end;
330
0
        --str;
331
0
        for (cnt = 0; (str != end) && (*end == c); --end, ++cnt) {}
332
0
    }
333
334
0
    return cnt;
335
0
}
336
337
/**
338
 * @brief Check if the number can be shortened to zero.
339
 *
340
 * @param[in] in Start of input string;
341
 * @param[in] end End of input string;
342
 * @return 1 if number is zero, otherwise 0.
343
 */
344
static ly_bool
345
lyjson_number_is_zero(const char *in, const char *end)
346
0
{
347
0
    assert(in < end);
348
349
0
    if ((in[0] == '-') || (in[0] == '+')) {
350
0
        in++;
351
0
        assert(in < end);
352
0
    }
353
0
    if ((in[0] == '0') && (in[1] == '.')) {
354
0
        in += 2;
355
0
        if (!(in < end)) {
356
0
            return 1;
357
0
        }
358
0
    }
359
360
0
    return lyjson_count_in_row(in, end, '0', 0) == (uint32_t)(end - in);
361
0
}
362
363
/**
364
 * @brief Allocate buffer for number in string format.
365
 *
366
 * @param[in] jsonctx JSON context.
367
 * @param[in] num_len Required space in bytes for a number.
368
 * Terminating null byte is added by default.
369
 * @param[out] buffer Output allocated buffer.
370
 * @return LY_ERR value.
371
 */
372
static LY_ERR
373
lyjson_get_buffer_for_number(const struct ly_ctx *ctx, uint64_t num_len, char **buffer)
374
0
{
375
0
    *buffer = NULL;
376
377
0
    LY_CHECK_ERR_RET((num_len + 1) > LY_NUMBER_MAXLEN, LOGVAL(ctx, LYVE_SEMANTICS,
378
0
            "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit."), LY_EVALID);
379
380
    /* allocate buffer for the result (add NULL-byte) */
381
0
    *buffer = malloc(num_len + 1);
382
0
    LY_CHECK_ERR_RET(!(*buffer), LOGMEM(ctx), LY_EMEM);
383
0
    return LY_SUCCESS;
384
0
}
385
386
/**
387
 * @brief Copy the 'numeric part' (@p num) except its decimal point
388
 * (@p dec_point) and insert the new decimal point (@p dp_position)
389
 * only if it is to be placed in the 'numeric part' range (@p num).
390
 *
391
 * @param[in] num Begin of the 'numeric part'.
392
 * @param[in] num_len Length of the 'numeric part'.
393
 * @param[in] dec_point Pointer to the old decimal point.
394
 * If it has a NULL value, it is ignored.
395
 * @param[in] dp_position Position of the new decimal point.
396
 * If it has a negative value, it is ignored.
397
 * @param[out] dst Memory into which the copied result is written.
398
 * @return Number of characters written to the @p dst.
399
 */
400
static uint32_t
401
lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len, char *dec_point, int32_t dp_position, char *dst)
402
0
{
403
0
    int32_t dec_point_idx;
404
0
    int32_t n, d;
405
406
0
    assert(num && dst);
407
408
0
    dec_point_idx = dec_point ? dec_point - num : INT32_MAX;
409
0
    assert((dec_point_idx >= 0) && (dec_point_idx != dp_position));
410
411
0
    for (n = 0, d = 0; (uint32_t)n < num_len; n++) {
412
0
        if (n == dec_point_idx) {
413
0
            continue;
414
0
        } else if (d == dp_position) {
415
0
            dst[d++] = '.';
416
0
            dst[d++] = num[n];
417
0
        } else {
418
0
            dst[d++] = num[n];
419
0
        }
420
0
    }
421
422
0
    return d;
423
0
}
424
425
/**
426
 * @brief Convert JSON number with exponent into the representation
427
 * used by YANG.
428
 *
429
 * The input numeric string must be syntactically valid. Also, before
430
 * calling this function, checks should be performed using the
431
 * ::lyjson_number_is_zero().
432
 *
433
 * @param[in] ctx Context for the error message.
434
 * @param[in] in Beginning of the string containing the number.
435
 * @param[in] exponent Pointer to the letter E/e.
436
 * @param[in] total_len Total size of the input number.
437
 * @param[out] res Conversion result.
438
 * @param[out] res_len Length of the result.
439
 * @return LY_ERR value.
440
 */
441
static LY_ERR
442
lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent, uint64_t total_len, char **res,
443
        size_t *res_len)
444
0
{
445
446
0
#define MAYBE_WRITE_MINUS(ARRAY, INDEX, FLAG) \
447
0
        if (FLAG) { \
448
0
            ARRAY[INDEX++] = '-'; \
449
0
        }
450
451
/* Length of leading zero followed by the decimal point. */
452
0
#define LEADING_ZERO 1
453
454
/* Flags for the ::lyjson_count_in_row() */
455
0
#define FORWARD 0
456
0
#define BACKWARD 1
457
458
    /* Buffer where the result is stored. */
459
0
    char *buf;
460
    /* Size without space for terminating NULL-byte. */
461
0
    uint64_t buf_len;
462
    /* Index to buf. */
463
0
    uint32_t i = 0;
464
    /* A 'numeric part' doesn't contain a minus sign or an leading zero.
465
     * For example, in 0.45, there is the leading zero.
466
     */
467
0
    const char *num;
468
    /* Length of the 'numeric part' ends before E/e. */
469
0
    uint16_t num_len;
470
    /* Position of decimal point in the num. */
471
0
    char *dec_point;
472
    /* Final position of decimal point in the buf. */
473
0
    int32_t dp_position;
474
    /* Exponent as integer. */
475
0
    long long e_val;
476
    /* Byte for the decimal point. */
477
0
    int8_t dot;
478
    /* Required additional byte for the minus sign. */
479
0
    uint8_t minus;
480
    /* The number of zeros. */
481
0
    long zeros;
482
    /* If the number starts with leading zero followed by the decimal point. */
483
0
    ly_bool leading_zero;
484
485
0
    assert(ctx && in && exponent && res && res_len && (total_len > 2));
486
0
    assert((in < exponent) && ((*exponent == 'e') || (*exponent == 'E')));
487
488
0
    if ((exponent - in) > UINT16_MAX) {
489
0
        LOGVAL(ctx, LYVE_SEMANTICS, "JSON number is too long.");
490
0
        return LY_EVALID;
491
0
    }
492
493
    /* Convert exponent. */
494
0
    errno = 0;
495
0
    e_val = strtoll(exponent + 1, NULL, LY_BASE_DEC);
496
0
    if (errno || (e_val > UINT16_MAX) || (e_val < -UINT16_MAX)) {
497
0
        LOGVAL(ctx, LYVE_SEMANTICS,
498
0
                "Exponent out-of-bounds in a JSON Number value (%.*s).",
499
0
                (int)total_len, in);
500
0
        return LY_EVALID;
501
0
    }
502
503
0
    minus = in[0] == '-';
504
0
    if (in[minus] == '0') {
505
0
        assert(in[minus + 1] == '.');
506
0
        leading_zero = 1;
507
        /* The leading zero has been found, it will be skipped. */
508
0
        num = &in[minus + 1];
509
0
    } else {
510
0
        leading_zero = 0;
511
        /* Set to the first number. */
512
0
        num = &in[minus];
513
0
    }
514
0
    num_len = exponent - num;
515
516
    /* Find the location of the decimal points. */
517
0
    dec_point = ly_strnchr(num, '.', num_len);
518
0
    dp_position = dec_point ?
519
0
            dec_point - num + e_val :
520
0
            num_len + e_val;
521
522
    /* Remove zeros after the decimal point from the end of
523
     * the 'numeric part' because these are useless.
524
     * (For example, in 40.001000 these are the last 3).
525
     */
526
0
    num_len -= dp_position > 0 ?
527
0
            lyjson_count_in_row(num + dp_position - 1, exponent, '0', BACKWARD) :
528
0
            lyjson_count_in_row(num, exponent, '0', BACKWARD);
529
530
    /* Decide what to do with the dot from the 'numeric part'. */
531
0
    if (dec_point && ((int32_t)(num_len - 1) == dp_position)) {
532
        /* Decimal point in the last place is useless. */
533
0
        dot = -1;
534
0
    } else if (dec_point) {
535
        /* Decimal point is shifted. */
536
0
        dot = 0;
537
0
    } else {
538
        /* Additional byte for the decimal point is requred. */
539
0
        dot = 1;
540
0
    }
541
542
    /* Final composition of the result. */
543
0
    if (dp_position <= 0) {
544
        /* Adding decimal point before the integer with adding additional zero(s). */
545
546
0
        zeros = labs(dp_position);
547
0
        buf_len = minus + LEADING_ZERO + dot + zeros + num_len;
548
0
        LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
549
0
        MAYBE_WRITE_MINUS(buf, i, minus);
550
0
        buf[i++] = '0';
551
0
        buf[i++] = '.';
552
0
        memset(buf + i, '0', zeros);
553
0
        i += zeros;
554
0
        dp_position = -1;
555
0
        lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
556
0
    } else if (leading_zero && (dp_position < (ssize_t)num_len)) {
557
        /* Insert decimal point between the integer's digits. */
558
559
        /* Set a new range of 'numeric part'. Old decimal point is skipped. */
560
0
        num++;
561
0
        num_len--;
562
0
        dp_position--;
563
        /* Get the number of useless zeros between the old
564
         * and new decimal point. For example, in the number 0.005E1,
565
         * there is one useless zero.
566
         */
567
0
        zeros = lyjson_count_in_row(num, num + dp_position + 1, '0', FORWARD);
568
        /* If the new decimal point will be in the place of the first non-zero subnumber. */
569
0
        if (zeros == (dp_position + 1)) {
570
            /* keep one zero as leading zero */
571
0
            zeros--;
572
            /* new decimal point will be behind the leading zero */
573
0
            dp_position = 1;
574
0
            dot = 1;
575
0
        } else {
576
0
            dot = 0;
577
0
        }
578
0
        buf_len = minus + dot + (num_len - zeros);
579
0
        LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
580
0
        MAYBE_WRITE_MINUS(buf, i, minus);
581
        /* Skip useless zeros and copy. */
582
0
        lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
583
0
    } else if (dp_position < (ssize_t)num_len) {
584
        /* Insert decimal point between the integer's digits. */
585
586
0
        buf_len = minus + dot + num_len;
587
0
        LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
588
0
        MAYBE_WRITE_MINUS(buf, i, minus);
589
0
        lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
590
0
    } else if (leading_zero) {
591
        /* Adding decimal point after the decimal value make the integer result. */
592
593
        /* Set a new range of 'numeric part'. Old decimal point is skipped. */
594
0
        num++;
595
0
        num_len--;
596
        /* Get the number of useless zeros. */
597
0
        zeros = lyjson_count_in_row(num, num + num_len, '0', FORWARD);
598
0
        buf_len = minus + dp_position - zeros;
599
0
        LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
600
0
        MAYBE_WRITE_MINUS(buf, i, minus);
601
        /* Skip useless zeros and copy. */
602
0
        i += lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
603
        /* Add multiples of ten behind the 'numeric part'. */
604
0
        memset(buf + i, '0', buf_len - i);
605
0
    } else {
606
        /* Adding decimal point after the decimal value make the integer result. */
607
608
0
        buf_len = minus + dp_position;
609
0
        LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
610
0
        MAYBE_WRITE_MINUS(buf, i, minus);
611
0
        i += lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
612
        /* Add multiples of ten behind the 'numeric part'. */
613
0
        memset(buf + i, '0', buf_len - i);
614
0
    }
615
616
0
    buf[buf_len] = '\0';
617
0
    *res = buf;
618
0
    *res_len = buf_len;
619
620
0
#undef MAYBE_WRITE_MINUS
621
0
#undef LEADING_ZERO
622
0
#undef FORWARD
623
0
#undef BACKWARD
624
625
0
    return LY_SUCCESS;
626
0
}
627
628
/**
629
 * @brief Parse a JSON number and store it in the context.
630
 *
631
 * @param[in] jsonctx JSON parser context.
632
 * @return LY_ERR value.
633
 */
634
static LY_ERR
635
lyjson_number(struct lyjson_ctx *jsonctx)
636
0
{
637
0
    size_t offset = 0, num_len;
638
0
    const char *in = jsonctx->in->current, *exponent = NULL;
639
0
    uint8_t minus = 0;
640
0
    char *num;
641
642
0
    if (in[offset] == '-') {
643
0
        ++offset;
644
0
        minus = 1;
645
0
    }
646
647
0
    if (in[offset] == '0') {
648
0
        ++offset;
649
0
    } else if (isdigit(in[offset])) {
650
0
        ++offset;
651
0
        while (isdigit(in[offset])) {
652
0
            ++offset;
653
0
        }
654
0
    } else {
655
0
invalid_character:
656
0
        if (in[offset]) {
657
0
            LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON Number value (\"%c\").", in[offset]);
658
0
        } else {
659
0
            LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
660
0
        }
661
0
        return LY_EVALID;
662
0
    }
663
664
0
    if (in[offset] == '.') {
665
0
        ++offset;
666
0
        if (!isdigit(in[offset])) {
667
0
            goto invalid_character;
668
0
        }
669
0
        while (isdigit(in[offset])) {
670
0
            ++offset;
671
0
        }
672
0
    }
673
674
0
    if ((in[offset] == 'e') || (in[offset] == 'E')) {
675
0
        exponent = &in[offset];
676
0
        ++offset;
677
0
        if ((in[offset] == '+') || (in[offset] == '-')) {
678
0
            ++offset;
679
0
        }
680
0
        if (!isdigit(in[offset])) {
681
0
            goto invalid_character;
682
0
        }
683
0
        while (isdigit(in[offset])) {
684
0
            ++offset;
685
0
        }
686
0
    }
687
688
0
    if (lyjson_number_is_zero(in, exponent ? exponent : &in[offset])) {
689
0
        lyjson_ctx_set_value(jsonctx, in, minus + 1, 0);
690
0
    } else if (exponent && lyjson_number_is_zero(exponent + 1, &in[offset])) {
691
0
        lyjson_ctx_set_value(jsonctx, in, exponent - in, 0);
692
0
    } else if (exponent) {
693
0
        LY_CHECK_RET(lyjson_exp_number(jsonctx->ctx, in, exponent, offset, &num, &num_len));
694
0
        lyjson_ctx_set_value(jsonctx, num, num_len, 1);
695
0
    } else {
696
0
        if (offset > LY_NUMBER_MAXLEN) {
697
0
            LOGVAL(jsonctx->ctx, LYVE_SEMANTICS,
698
0
                    "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.");
699
0
            return LY_EVALID;
700
0
        }
701
0
        lyjson_ctx_set_value(jsonctx, in, offset, 0);
702
0
    }
703
0
    ly_in_skip(jsonctx->in, offset);
704
705
0
    return LY_SUCCESS;
706
0
}
707
708
LY_ERR
709
lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
710
0
{
711
0
    LY_ERR ret = LY_SUCCESS;
712
0
    struct lyjson_ctx *jsonctx;
713
714
0
    assert(ctx && in && jsonctx_p);
715
716
    /* new context */
717
0
    jsonctx = calloc(1, sizeof *jsonctx);
718
0
    LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
719
0
    jsonctx->ctx = ctx;
720
0
    jsonctx->in = in;
721
722
    /* input line logging */
723
0
    ly_log_location(NULL, NULL, NULL, in);
724
725
    /* WS are always expected to be skipped */
726
0
    lyjson_skip_ws(jsonctx);
727
728
0
    if (jsonctx->in->current[0] == '\0') {
729
        /* empty file, invalid */
730
0
        LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON file.");
731
0
        ret = LY_EVALID;
732
0
        goto cleanup;
733
0
    }
734
735
    /* start JSON parsing */
736
0
    LY_CHECK_GOTO(ret = lyjson_ctx_next(jsonctx, NULL), cleanup);
737
738
0
cleanup:
739
0
    if (ret) {
740
0
        lyjson_ctx_free(jsonctx);
741
0
    } else {
742
0
        *jsonctx_p = jsonctx;
743
0
    }
744
0
    return ret;
745
0
}
746
747
/**
748
 * @brief Parse next JSON token, object-name is expected.
749
 *
750
 * @param[in] jsonctx JSON parser context.
751
 * @return LY_ERR value.
752
 */
753
static LY_ERR
754
lyjson_next_object_name(struct lyjson_ctx *jsonctx)
755
0
{
756
0
    switch (*jsonctx->in->current) {
757
0
    case '\0':
758
        /* EOF */
759
0
        LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
760
0
        return LY_EVALID;
761
762
0
    case '"':
763
        /* object name */
764
0
        ly_in_skip(jsonctx->in, 1);
765
0
        LY_CHECK_RET(lyjson_string(jsonctx));
766
0
        lyjson_skip_ws(jsonctx);
767
768
0
        if (*jsonctx->in->current != ':') {
769
0
            LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
770
0
                    "a JSON value name-separator ':'");
771
0
            return LY_EVALID;
772
0
        }
773
0
        ly_in_skip(jsonctx->in, 1);
774
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NAME);
775
0
        break;
776
777
0
    case '}':
778
        /* object end */
779
0
        ly_in_skip(jsonctx->in, 1);
780
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
781
0
        break;
782
783
0
    default:
784
        /* unexpected value */
785
0
        LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
786
0
                jsonctx->in->current, "a JSON object name");
787
0
        return LY_EVALID;
788
0
    }
789
790
0
    return LY_SUCCESS;
791
0
}
792
793
/**
794
 * @brief Parse next JSON token, value is expected.
795
 *
796
 * @param[in] jsonctx JSON parser context.
797
 * @param[in] array_end Whether array-end is accepted or not.
798
 * @return LY_ERR value.
799
 */
800
static LY_ERR
801
lyjson_next_value(struct lyjson_ctx *jsonctx, ly_bool array_end)
802
0
{
803
0
    switch (*jsonctx->in->current) {
804
0
    case '\0':
805
        /* EOF */
806
0
        LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
807
0
        return LY_EVALID;
808
809
0
    case '"':
810
        /* string */
811
0
        ly_in_skip(jsonctx->in, 1);
812
0
        LY_CHECK_RET(lyjson_string(jsonctx));
813
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_STRING);
814
0
        break;
815
816
0
    case '-':
817
0
    case '0':
818
0
    case '1':
819
0
    case '2':
820
0
    case '3':
821
0
    case '4':
822
0
    case '5':
823
0
    case '6':
824
0
    case '7':
825
0
    case '8':
826
0
    case '9':
827
        /* number */
828
0
        LY_CHECK_RET(lyjson_number(jsonctx));
829
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NUMBER);
830
0
        break;
831
832
0
    case '{':
833
        /* object */
834
0
        ly_in_skip(jsonctx->in, 1);
835
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT);
836
0
        break;
837
838
0
    case '[':
839
        /* array */
840
0
        ly_in_skip(jsonctx->in, 1);
841
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY);
842
0
        break;
843
844
0
    case 't':
845
0
        if (strncmp(jsonctx->in->current + 1, "rue", ly_strlen_const("rue"))) {
846
0
            goto unexpected_value;
847
0
        }
848
849
        /* true */
850
0
        lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
851
0
        ly_in_skip(jsonctx->in, ly_strlen_const("true"));
852
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_TRUE);
853
0
        break;
854
855
0
    case 'f':
856
0
        if (strncmp(jsonctx->in->current + 1, "alse", ly_strlen_const("alse"))) {
857
0
            goto unexpected_value;
858
0
        }
859
860
        /* false */
861
0
        lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
862
0
        ly_in_skip(jsonctx->in, ly_strlen_const("false"));
863
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_FALSE);
864
0
        break;
865
866
0
    case 'n':
867
0
        if (strncmp(jsonctx->in->current + 1, "ull", ly_strlen_const("ull"))) {
868
0
            goto unexpected_value;
869
0
        }
870
871
        /* null */
872
0
        lyjson_ctx_set_value(jsonctx, "", 0, 0);
873
0
        ly_in_skip(jsonctx->in, ly_strlen_const("null"));
874
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NULL);
875
0
        break;
876
877
0
    case ']':
878
0
        if (!array_end) {
879
0
            goto unexpected_value;
880
0
        }
881
882
        /* array end */
883
0
        ly_in_skip(jsonctx->in, 1);
884
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
885
0
        break;
886
887
0
    default:
888
0
unexpected_value:
889
0
        LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
890
0
                jsonctx->in->current, "a JSON value");
891
0
        return LY_EVALID;
892
0
    }
893
894
0
    if (jsonctx->status.count > LY_MAX_BLOCK_DEPTH * 10) {
895
0
        LOGERR(jsonctx->ctx, LY_EINVAL, "Maximum number %d of nestings has been exceeded.", LY_MAX_BLOCK_DEPTH * 10);
896
0
        return LY_EINVAL;
897
0
    }
898
899
0
    return LY_SUCCESS;
900
0
}
901
902
/**
903
 * @brief Parse next JSON token, object-next-item is expected.
904
 *
905
 * @param[in] jsonctx JSON parser context.
906
 * @return LY_ERR value.
907
 */
908
static LY_ERR
909
lyjson_next_object_item(struct lyjson_ctx *jsonctx)
910
0
{
911
0
    switch (*jsonctx->in->current) {
912
0
    case '\0':
913
        /* EOF */
914
0
        LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
915
0
        return LY_EVALID;
916
917
0
    case '}':
918
        /* object end */
919
0
        ly_in_skip(jsonctx->in, 1);
920
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
921
0
        break;
922
923
0
    case ',':
924
        /* next object item */
925
0
        ly_in_skip(jsonctx->in, 1);
926
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NEXT);
927
0
        break;
928
929
0
    default:
930
        /* unexpected value */
931
0
        LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
932
0
                jsonctx->in->current, "a JSON object-end or next item");
933
0
        return LY_EVALID;
934
0
    }
935
936
0
    return LY_SUCCESS;
937
0
}
938
939
/**
940
 * @brief Parse next JSON token, array-next-item is expected.
941
 *
942
 * @param[in] jsonctx JSON parser context.
943
 * @return LY_ERR value.
944
 */
945
static LY_ERR
946
lyjson_next_array_item(struct lyjson_ctx *jsonctx)
947
0
{
948
0
    switch (*jsonctx->in->current) {
949
0
    case '\0':
950
        /* EOF */
951
0
        LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
952
0
        return LY_EVALID;
953
954
0
    case ']':
955
        /* array end */
956
0
        ly_in_skip(jsonctx->in, 1);
957
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
958
0
        break;
959
960
0
    case ',':
961
        /* next array item */
962
0
        ly_in_skip(jsonctx->in, 1);
963
0
        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_NEXT);
964
0
        break;
965
966
0
    default:
967
        /* unexpected value */
968
0
        LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
969
0
                jsonctx->in->current, "a JSON array-end or next item");
970
0
        return LY_EVALID;
971
0
    }
972
973
0
    return LY_SUCCESS;
974
0
}
975
976
LY_ERR
977
lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
978
0
{
979
0
    LY_ERR ret = LY_SUCCESS;
980
0
    enum LYJSON_PARSER_STATUS cur;
981
982
0
    assert(jsonctx);
983
984
0
    cur = lyjson_ctx_status(jsonctx);
985
0
    switch (cur) {
986
0
    case LYJSON_OBJECT:
987
0
        LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup);
988
0
        break;
989
0
    case LYJSON_ARRAY:
990
0
        LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 1), cleanup);
991
0
        break;
992
0
    case LYJSON_OBJECT_NEXT:
993
0
        LYJSON_STATUS_POP(jsonctx);
994
0
        LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup);
995
0
        break;
996
0
    case LYJSON_ARRAY_NEXT:
997
0
        LYJSON_STATUS_POP(jsonctx);
998
0
        LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
999
0
        break;
1000
0
    case LYJSON_OBJECT_NAME:
1001
0
        lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
1002
0
        LYJSON_STATUS_POP(jsonctx);
1003
0
        LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
1004
0
        break;
1005
0
    case LYJSON_OBJECT_CLOSED:
1006
0
    case LYJSON_ARRAY_CLOSED:
1007
0
        LYJSON_STATUS_POP(jsonctx);
1008
    /* fallthrough */
1009
0
    case LYJSON_NUMBER:
1010
0
    case LYJSON_STRING:
1011
0
    case LYJSON_TRUE:
1012
0
    case LYJSON_FALSE:
1013
0
    case LYJSON_NULL:
1014
0
        lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
1015
0
        LYJSON_STATUS_POP(jsonctx);
1016
0
        cur = lyjson_ctx_status(jsonctx);
1017
1018
0
        if (cur == LYJSON_OBJECT) {
1019
0
            LY_CHECK_GOTO(ret = lyjson_next_object_item(jsonctx), cleanup);
1020
0
            break;
1021
0
        } else if (cur == LYJSON_ARRAY) {
1022
0
            LY_CHECK_GOTO(ret = lyjson_next_array_item(jsonctx), cleanup);
1023
0
            break;
1024
0
        }
1025
1026
0
        assert(cur == LYJSON_END);
1027
0
        goto cleanup;
1028
0
    case LYJSON_END:
1029
0
        LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
1030
0
        break;
1031
0
    case LYJSON_ERROR:
1032
0
        LOGINT(jsonctx->ctx);
1033
0
        ret = LY_EINT;
1034
0
        goto cleanup;
1035
0
    }
1036
1037
    /* skip WS */
1038
0
    lyjson_skip_ws(jsonctx);
1039
1040
0
cleanup:
1041
0
    if (!ret && status) {
1042
0
        *status = lyjson_ctx_status(jsonctx);
1043
0
    }
1044
0
    return ret;
1045
0
}
1046
1047
void
1048
lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
1049
0
{
1050
0
    if (jsonctx->backup.dynamic) {
1051
0
        free((char *)jsonctx->backup.value);
1052
0
    }
1053
0
    jsonctx->backup.status = lyjson_ctx_status(jsonctx);
1054
0
    jsonctx->backup.status_count = jsonctx->status.count;
1055
0
    jsonctx->backup.value = jsonctx->value;
1056
0
    jsonctx->backup.value_len = jsonctx->value_len;
1057
0
    jsonctx->backup.input = jsonctx->in->current;
1058
0
    jsonctx->backup.dynamic = jsonctx->dynamic;
1059
0
    jsonctx->dynamic = 0;
1060
0
}
1061
1062
void
1063
lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
1064
0
{
1065
0
    if (jsonctx->dynamic) {
1066
0
        free((char *)jsonctx->value);
1067
0
    }
1068
0
    jsonctx->status.count = jsonctx->backup.status_count;
1069
0
    jsonctx->status.objs[jsonctx->backup.status_count - 1] = (void *)jsonctx->backup.status;
1070
0
    jsonctx->value = jsonctx->backup.value;
1071
0
    jsonctx->value_len = jsonctx->backup.value_len;
1072
0
    jsonctx->in->current = jsonctx->backup.input;
1073
0
    jsonctx->dynamic = jsonctx->backup.dynamic;
1074
0
    jsonctx->backup.dynamic = 0;
1075
0
}
1076
1077
void
1078
lyjson_ctx_free(struct lyjson_ctx *jsonctx)
1079
0
{
1080
0
    if (!jsonctx) {
1081
0
        return;
1082
0
    }
1083
1084
0
    ly_log_location_revert(0, 0, 0, 1);
1085
1086
0
    if (jsonctx->dynamic) {
1087
0
        free((char *)jsonctx->value);
1088
0
    }
1089
0
    if (jsonctx->backup.dynamic) {
1090
0
        free((char *)jsonctx->backup.value);
1091
0
    }
1092
1093
0
    ly_set_erase(&jsonctx->status, NULL);
1094
1095
0
    free(jsonctx);
1096
0
}