blob: 95c5bdaa347a9d23986fdc131b0272d86b776fdf [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>
Radek Krejci50f0c6b2020-06-18 16:31:48 +020018#include <stdlib.h>
Radek Krejci47fab892020-11-05 17:02:41 +010019#include <string.h>
Radek Krejci50f0c6b2020-06-18 16:31:48 +020020#include <sys/types.h>
21
22#include "common.h"
Michal Vaskoafac7822020-10-20 14:22:26 +020023#include "in_internal.h"
Radek Krejci47fab892020-11-05 17:02:41 +010024#include "json.h"
Radek Krejci50f0c6b2020-06-18 16:31:48 +020025
26#define JSON_PUSH_STATUS_RET(CTX, STATUS) \
Radek Krejci3d92e442020-10-12 12:48:13 +020027 LY_CHECK_RET(ly_set_add(&CTX->status, (void*)STATUS, 1, NULL))
Radek Krejci50f0c6b2020-06-18 16:31:48 +020028
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)) {
Radek Krejcidd713ce2021-01-04 23:12:12 +010074 if (*jsonctx->in->current == '\n') {
75 LY_IN_NEW_LINE(jsonctx->in);
76 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +020077 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
Radek Krejci857189e2020-09-01 13:26:36 +020090lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic)
Radek Krejci50f0c6b2020-06-18 16:31:48 +020091{
92 assert(jsonctx);
93
Juraj Vijtiukec285cd2021-01-14 11:41:20 +010094 if (jsonctx->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 */
Michal Vasko69730152020-10-09 16:30:07 +0200107 if ((*jsonctx->in->current == '\0') || is_jsonws(*jsonctx->in->current)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200108 return LY_SUCCESS;
109 }
110 } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_OBJECT) {
111 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko69730152020-10-09 16:30:07 +0200112 if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == '}')) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200113 return LY_SUCCESS;
114 }
115 } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_ARRAY) {
116 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko69730152020-10-09 16:30:07 +0200117 if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == ']')) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200118 return LY_SUCCESS;
119 }
120 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100121 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Unexpected character \"%c\" after JSON %s.",
122 *jsonctx->in->current, lyjson_token2str(lyjson_ctx_status(jsonctx, 0)));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200123 }
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;
Radek Krejcid54412f2020-12-17 20:25:35 +0100150 start_line = jsonctx->in->line;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200151 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++) {
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100222 if (!in[offset + i]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100223 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", &in[slash]);
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100224 goto error;
225 } else if (isdigit(in[offset + i])) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200226 u = (in[offset + i] - '0');
227 } else if (in[offset + i] > 'F') {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100228 u = LY_BASE_DEC + (in[offset + i] - 'a');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200229 } else {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100230 u = LY_BASE_DEC + (in[offset + i] - 'A');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200231 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100232 value = (LY_BASE_HEX * value) + u;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200233 }
234 break;
235 default:
236 /* invalid escape sequence */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100237 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character escape sequence \\%c.", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200238 goto error;
239
240 }
241
242 offset += i; /* add read escaped characters */
243 LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100244 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).",
245 offset - slash, &in[slash], value),
Michal Vasko69730152020-10-09 16:30:07 +0200246 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200247 len += u; /* update number of bytes in buffer */
248 in += offset; /* move the input by the processed bytes stored in the buffer ... */
249 offset = 0; /* ... and reset the offset index for future moving data into buffer */
250
251 } else if (in[offset] == '"') {
252 /* end of string */
253 if (buf) {
254 /* realloc exact size string */
255 buf = ly_realloc(buf, len + offset + 1);
256 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
257 size = len + offset + 1;
258 memcpy(&buf[len], in, offset);
259
260 /* set terminating NULL byte */
261 buf[len + offset] = '\0';
262 }
263 len += offset;
264 ++offset;
265 in += offset;
266 goto success;
267 } else {
268 /* get it as UTF-8 character for check */
269 const char *c = &in[offset];
270 uint32_t code = 0;
271 size_t code_len = 0;
272
273 LY_CHECK_ERR_GOTO(ly_getutf8(&c, &code, &code_len),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100274 LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200275
276 LY_CHECK_ERR_GOTO(!is_jsonstrchar(code),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100277 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08x).",
278 &in[offset] - start + code_len, start, code),
Michal Vasko69730152020-10-09 16:30:07 +0200279 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200280
281 /* character is ok, continue */
282 offset += code_len;
283 }
284 }
285
286 /* EOF reached before endchar */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100287 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
288 LOGVAL_LINE(jsonctx->ctx, start_line, LYVE_SYNTAX, "Missing quotation-mark at the end of a JSON string.");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200289
290error:
291 free(buf);
292 return LY_EVALID;
293
294success:
Radek Krejcid54412f2020-12-17 20:25:35 +0100295 jsonctx->in->current = in;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200296 if (buf) {
297 lyjson_ctx_set_value(jsonctx, buf, len, 1);
298 } else {
299 lyjson_ctx_set_value(jsonctx, start, len, 0);
300 }
301
302 return LY_SUCCESS;
303
304#undef BUFSIZE
305#undef BUFSIZE_STEP
306}
307
308/*
309 *
310 * Wrapper around lyjson_string_() adding LYJSON_STRING status into context to allow using lyjson_string_() for parsing object's name.
311 */
312static LY_ERR
313lyjson_string(struct lyjson_ctx *jsonctx)
314{
315 LY_CHECK_RET(lyjson_string_(jsonctx));
316
317 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_STRING);
318 LY_CHECK_RET(lyjson_check_next(jsonctx));
319
320 return LY_SUCCESS;
321}
322
323static LY_ERR
324lyjson_number(struct lyjson_ctx *jsonctx)
325{
326 size_t offset = 0, exponent = 0;
327 const char *in = jsonctx->in->current;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200328 uint8_t minus = 0;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200329
330 if (in[offset] == '-') {
331 ++offset;
332 minus = 1;
333 }
334
335 if (in[offset] == '0') {
336 ++offset;
337 } else if (isdigit(in[offset])) {
338 ++offset;
339 while (isdigit(in[offset])) {
340 ++offset;
341 }
342 } else {
343invalid_character:
344 if (in[offset]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100345 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON Number value (\"%c\").", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200346 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100347 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200348 }
349 return LY_EVALID;
350 }
351
352 if (in[offset] == '.') {
353 ++offset;
354 if (!isdigit(in[offset])) {
355 goto invalid_character;
356 }
357 while (isdigit(in[offset])) {
358 ++offset;
359 }
360 }
361
362 if ((in[offset] == 'e') || (in[offset] == 'E')) {
363 exponent = offset++;
364 if ((in[offset] == '+') || (in[offset] == '-')) {
365 ++offset;
366 }
367 if (!isdigit(in[offset])) {
368 goto invalid_character;
369 }
370 while (isdigit(in[offset])) {
371 ++offset;
372 }
373 }
374
375 if (exponent) {
376 /* convert JSON number with exponent into the representation used by YANG */
377 long int e_val;
378 char *ptr, *dec_point, *num;
379 const char *e_ptr = &in[exponent + 1];
380 size_t num_len, i;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200381 int64_t dp_position; /* final position of the deciaml point */
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200382
383 errno = 0;
Radek Krejcif13b87b2020-12-01 22:02:17 +0100384 e_val = strtol(e_ptr, &ptr, LY_BASE_DEC);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200385 if (errno) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100386 LOGVAL(jsonctx->ctx, LYVE_SEMANTICS, "Exponent out-of-bounds in a JSON Number value (%.*s).",
387 offset - minus - (e_ptr - in), e_ptr);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200388 return LY_EVALID;
389 }
390
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200391 dec_point = ly_strnchr(in, '.', exponent);
392 if (!dec_point) {
393 /* value is integer, we are just ... */
394 if (e_val >= 0) {
395 /* adding zeros at the end */
396 num_len = exponent + e_val;
397 dp_position = num_len; /* decimal point is behind the actual value */
398 } else if ((size_t)labs(e_val) < exponent) {
399 /* adding decimal point between the integer's digits */
400 num_len = exponent + 1;
401 dp_position = exponent + e_val;
402 } else {
403 /* adding decimal point before the integer with adding leading zero(s) */
404 num_len = labs(e_val) + 2;
405 dp_position = exponent + e_val;
406 }
407 dp_position -= minus;
408 } else {
409 /* value is decimal, we are moving the decimal point */
410 dp_position = dec_point - in + e_val - minus;
411 if (dp_position > (ssize_t)exponent) {
412 /* moving decimal point after the decimal value make the integer result */
413 num_len = dp_position;
414 } else if (dp_position < 0) {
415 /* moving decimal point before the decimal value requires additional zero(s)
416 * (decimal point is already count in exponent value) */
417 num_len = exponent + labs(dp_position) + 1;
418 } else {
419 /* moving decimal point just inside the decimal value does not make any change in length */
420 num_len = exponent;
421 }
422 }
423
424 /* allocate buffer for the result (add terminating NULL-byte */
425 num = malloc(num_len + 1);
426 LY_CHECK_ERR_RET(!num, LOGMEM(jsonctx->ctx), LY_EMEM);
427
428 /* compose the resulting vlaue */
429 i = 0;
430 if (minus) {
431 num[i++] = '-';
432 }
433 /* add leading zeros */
434 if (dp_position <= 0) {
435 num[i++] = '0';
436 num[i++] = '.';
Michal Vaskod989ba02020-08-24 10:59:24 +0200437 for ( ; dp_position; dp_position++) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200438 num[i++] = '0';
439 }
440 }
441 /* copy the value */
Radek Krejci857189e2020-09-01 13:26:36 +0200442 ly_bool dp_placed;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200443 size_t j;
444 for (dp_placed = dp_position ? 0 : 1, j = minus; j < exponent; j++) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200445 if (in[j] == '.') {
446 continue;
447 }
448 if (!dp_placed) {
449 if (!dp_position) {
450 num[i++] = '.';
451 dp_placed = 1;
452 } else {
453 dp_position--;
454 if (in[j] == '0') {
455 num_len--;
456 continue;
457 }
458 }
459 }
460
461 num[i++] = in[j];
462 }
463 /* trailing zeros */
464 while (dp_position--) {
465 num[i++] = '0';
466 }
467 /* terminating NULL byte */
468 num[i] = '\0';
469
470 /* store the modified number */
471 lyjson_ctx_set_value(jsonctx, num, num_len, 1);
472 } else {
473 /* store the number */
474 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, offset, 0);
475 }
476 ly_in_skip(jsonctx->in, offset);
477
478 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NUMBER);
479 LY_CHECK_RET(lyjson_check_next(jsonctx));
480
481 return LY_SUCCESS;
482}
483
484static LY_ERR
485lyjson_object_name(struct lyjson_ctx *jsonctx)
486{
487 if (*jsonctx->in->current != '"') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100488 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200489 jsonctx->in->current, "a JSON object's member");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200490 return LY_EVALID;
491 }
492 ly_in_skip(jsonctx->in, 1);
493
494 LY_CHECK_RET(lyjson_string_(jsonctx));
495 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko08dc70b2020-10-07 13:58:47 +0200496 if (*jsonctx->in->current != ':') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100497 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
498 "a JSON object's name-separator ':'");
Michal Vasko08dc70b2020-10-07 13:58:47 +0200499 return LY_EVALID;
500 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200501 ly_in_skip(jsonctx->in, 1);
502 LY_CHECK_RET(skip_ws(jsonctx));
503
504 return LY_SUCCESS;
505}
506
507static LY_ERR
508lyjson_object(struct lyjson_ctx *jsonctx)
509{
510 LY_CHECK_RET(skip_ws(jsonctx));
511
512 if (*jsonctx->in->current == '}') {
513 /* empty object */
514 ly_in_skip(jsonctx->in, 1);
515 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
516 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT_EMPTY);
517 return LY_SUCCESS;
518 }
519
520 LY_CHECK_RET(lyjson_object_name(jsonctx));
521
522 /* output data are set by lyjson_string_() */
523 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT);
524
525 return LY_SUCCESS;
526}
527
528/*
529 * @brief Process JSON array envelope
530 *
531 *
532 *
533 * @param[in] jsonctx JSON parser context
534 * @return LY_SUCCESS or LY_EMEM
535 */
536static LY_ERR
537lyjson_array(struct lyjson_ctx *jsonctx)
538{
539 LY_CHECK_RET(skip_ws(jsonctx));
540
541 if (*jsonctx->in->current == ']') {
542 /* empty array */
543 ly_in_skip(jsonctx->in, 1);
544 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY_EMPTY);
545 } else {
546 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY);
547 }
548
549 /* erase previous values, array has no value on its own */
550 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
551
552 return LY_SUCCESS;
553}
554
555static LY_ERR
556lyjson_value(struct lyjson_ctx *jsonctx)
557{
Michal Vasko69730152020-10-09 16:30:07 +0200558 if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200559 return LY_SUCCESS;
560 }
561
Radek Krejcif13b87b2020-12-01 22:02:17 +0100562 if ((*jsonctx->in->current == 'f') && !strncmp(jsonctx->in->current, "false", ly_strlen_const("false"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200563 /* false */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100564 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
565 ly_in_skip(jsonctx->in, ly_strlen_const("false"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200566 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_FALSE);
567 LY_CHECK_RET(lyjson_check_next(jsonctx));
568
Radek Krejcif13b87b2020-12-01 22:02:17 +0100569 } else if ((*jsonctx->in->current == 't') && !strncmp(jsonctx->in->current, "true", ly_strlen_const("true"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200570 /* true */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100571 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
572 ly_in_skip(jsonctx->in, ly_strlen_const("true"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200573 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_TRUE);
574 LY_CHECK_RET(lyjson_check_next(jsonctx));
575
Radek Krejcif13b87b2020-12-01 22:02:17 +0100576 } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200577 /* none */
Radek Krejci201963a2020-12-03 11:43:40 +0100578 lyjson_ctx_set_value(jsonctx, "", 0, 0);
Radek Krejcif13b87b2020-12-01 22:02:17 +0100579 ly_in_skip(jsonctx->in, ly_strlen_const("null"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200580 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NULL);
581 LY_CHECK_RET(lyjson_check_next(jsonctx));
582
583 } else if (*jsonctx->in->current == '"') {
584 /* string */
585 ly_in_skip(jsonctx->in, 1);
586 LY_CHECK_RET(lyjson_string(jsonctx));
587
588 } else if (*jsonctx->in->current == '[') {
589 /* array */
590 ly_in_skip(jsonctx->in, 1);
591 LY_CHECK_RET(lyjson_array(jsonctx));
592
593 } else if (*jsonctx->in->current == '{') {
594 /* object */
595 ly_in_skip(jsonctx->in, 1);
596 LY_CHECK_RET(lyjson_object(jsonctx));
597
Michal Vasko69730152020-10-09 16:30:07 +0200598 } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200599 /* number */
600 LY_CHECK_RET(lyjson_number(jsonctx));
601
602 } else {
603 /* unexpected value */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100604 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200605 jsonctx->in->current, "a JSON value");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200606 return LY_EVALID;
607 }
608
609 return LY_SUCCESS;
610}
611
612LY_ERR
613lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
614{
615 LY_ERR ret = LY_SUCCESS;
616 struct lyjson_ctx *jsonctx;
617
618 assert(ctx);
619 assert(in);
620 assert(jsonctx_p);
621
622 /* new context */
623 jsonctx = calloc(1, sizeof *jsonctx);
624 LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
625 jsonctx->ctx = ctx;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200626 jsonctx->in = in;
627
Radek Krejciddace2c2021-01-08 11:30:56 +0100628 LOG_LOCINIT(NULL, NULL, NULL, in);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100629
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200630 /* parse JSON value, if any */
631 LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup);
632 if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) {
633 /* empty data input */
634 goto cleanup;
635 }
636
637 ret = lyjson_value(jsonctx);
638
Michal Vasko69730152020-10-09 16:30:07 +0200639 if ((jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100640 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200641 ret = LY_EVALID;
642 }
643
644cleanup:
645 if (ret) {
646 lyjson_ctx_free(jsonctx);
647 } else {
648 *jsonctx_p = jsonctx;
649 }
650 return ret;
651}
652
653void
654lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
655{
656 if (jsonctx->backup.dynamic) {
657 free((char *)jsonctx->backup.value);
658 }
659 jsonctx->backup.status = lyjson_ctx_status(jsonctx, 0);
660 jsonctx->backup.status_count = jsonctx->status.count;
661 jsonctx->backup.value = jsonctx->value;
662 jsonctx->backup.value_len = jsonctx->value_len;
663 jsonctx->backup.input = jsonctx->in->current;
664 jsonctx->backup.dynamic = jsonctx->dynamic;
665 jsonctx->dynamic = 0;
666}
667
668void
669lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
670{
671 if (jsonctx->dynamic) {
672 free((char *)jsonctx->value);
673 }
674 jsonctx->status.count = jsonctx->backup.status_count;
Michal Vasko22df3f02020-08-24 13:29:22 +0200675 jsonctx->status.objs[jsonctx->backup.status_count - 1] = (void *)jsonctx->backup.status;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200676 jsonctx->value = jsonctx->backup.value;
677 jsonctx->value_len = jsonctx->backup.value_len;
678 jsonctx->in->current = jsonctx->backup.input;
679 jsonctx->dynamic = jsonctx->backup.dynamic;
680 jsonctx->backup.dynamic = 0;
681}
682
683LY_ERR
684lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
685{
686 LY_ERR ret = LY_SUCCESS;
Radek Krejci857189e2020-09-01 13:26:36 +0200687 ly_bool toplevel = 0;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200688 enum LYJSON_PARSER_STATUS prev;
689
690 assert(jsonctx);
691
692 prev = lyjson_ctx_status(jsonctx, 0);
693
Michal Vasko69730152020-10-09 16:30:07 +0200694 if ((prev == LYJSON_OBJECT) || (prev == LYJSON_ARRAY)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200695 /* get value for the object's member OR the first value in the array */
696 ret = lyjson_value(jsonctx);
697 goto result;
698 } else {
699 /* the previous token is closed and should be completely processed */
700 JSON_POP_STATUS_RET(jsonctx);
701 prev = lyjson_ctx_status(jsonctx, 0);
702 }
703
704 if (!jsonctx->status.count) {
705 /* we are done with the top level value */
706 toplevel = 1;
707 }
708 LY_CHECK_RET(skip_ws(jsonctx));
709 if (toplevel && !jsonctx->status.count) {
710 /* EOF expected, but there are some data after the top level token */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100711 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Expecting end-of-input, but some data follows the top level JSON value.");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200712 return LY_EVALID;
713 }
714
715 if (toplevel) {
716 /* we are done */
717 return LY_SUCCESS;
718 }
719
720 /* continue with the next token */
721 assert(prev == LYJSON_OBJECT || prev == LYJSON_ARRAY);
722
723 if (*jsonctx->in->current == ',') {
724 /* sibling item in the ... */
725 ly_in_skip(jsonctx->in, 1);
726 LY_CHECK_RET(skip_ws(jsonctx));
727
728 if (prev == LYJSON_OBJECT) {
729 /* ... object - get another object's member */
730 ret = lyjson_object_name(jsonctx);
731 } else { /* LYJSON_ARRAY */
732 /* ... array - get another complete value */
733 ret = lyjson_value(jsonctx);
734 }
Michal Vasko69730152020-10-09 16:30:07 +0200735 } else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) || ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200736 ly_in_skip(jsonctx->in, 1);
737 JSON_POP_STATUS_RET(jsonctx);
738 JSON_PUSH_STATUS_RET(jsonctx, prev + 1);
739 } else {
740 /* unexpected value */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100741 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
742 prev == LYJSON_ARRAY ? "another JSON value in array" : "another JSON object's member");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200743 return LY_EVALID;
744 }
745
746result:
Michal Vasko69730152020-10-09 16:30:07 +0200747 if ((ret == LY_SUCCESS) && (jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100748 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200749 ret = LY_EVALID;
750 }
751
Michal Vasko69730152020-10-09 16:30:07 +0200752 if ((ret == LY_SUCCESS) && status) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200753 *status = lyjson_ctx_status(jsonctx, 0);
754 }
755
756 return ret;
757}
758
759enum LYJSON_PARSER_STATUS
760lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index)
761{
762 assert(jsonctx);
763
764 if (jsonctx->status.count < index) {
765 return LYJSON_ERROR;
766 } else if (jsonctx->status.count == index) {
767 return LYJSON_ROOT;
768 } else {
Michal Vasko27915722020-08-31 14:54:42 +0200769 return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - (index + 1)];
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200770 }
771}
772
773void
774lyjson_ctx_free(struct lyjson_ctx *jsonctx)
775{
776 if (!jsonctx) {
777 return;
778 }
779
Radek Krejciddace2c2021-01-08 11:30:56 +0100780 LOG_LOCBACK(0, 0, 0, 1);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100781
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200782 if (jsonctx->dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200783 free((char *)jsonctx->value);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200784 }
785 if (jsonctx->backup.dynamic) {
786 free((char *)jsonctx->backup.value);
787 }
788
789 ly_set_erase(&jsonctx->status, NULL);
790
791 free(jsonctx);
792}