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