blob: eca191ef41d8db37082404f5d9a781fad414339d [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 Krejci50f0c6b2020-06-18 16:31:48 +020074 ly_in_skip(jsonctx->in, 1);
75 }
76 if (*jsonctx->in->current == '\0') {
77 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_END);
78 }
79
80 return LY_SUCCESS;
81}
82
83/*
84 * @brief Set value corresponding to the current context's status
85 */
86static void
Radek Krejci857189e2020-09-01 13:26:36 +020087lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic)
Radek Krejci50f0c6b2020-06-18 16:31:48 +020088{
89 assert(jsonctx);
90
91 if (dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +020092 free((char *)jsonctx->value);
Radek Krejci50f0c6b2020-06-18 16:31:48 +020093 }
94 jsonctx->value = value;
95 jsonctx->value_len = value_len;
96 jsonctx->dynamic = dynamic;
97}
98
99static LY_ERR
100lyjson_check_next(struct lyjson_ctx *jsonctx)
101{
102 if (jsonctx->status.count == 1) {
103 /* top level value (JSON-text), ws expected */
Michal Vasko69730152020-10-09 16:30:07 +0200104 if ((*jsonctx->in->current == '\0') || is_jsonws(*jsonctx->in->current)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200105 return LY_SUCCESS;
106 }
107 } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_OBJECT) {
108 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko69730152020-10-09 16:30:07 +0200109 if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == '}')) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200110 return LY_SUCCESS;
111 }
112 } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_ARRAY) {
113 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko69730152020-10-09 16:30:07 +0200114 if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == ']')) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200115 return LY_SUCCESS;
116 }
117 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100118 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Unexpected character \"%c\" after JSON %s.",
119 *jsonctx->in->current, lyjson_token2str(lyjson_ctx_status(jsonctx, 0)));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200120 }
121
122 return LY_EVALID;
123}
124
125/**
126 * Input is expected to start after the opening quotation-mark.
127 * When succeeds, input is moved after the closing quotation-mark.
128 */
129static LY_ERR
130lyjson_string_(struct lyjson_ctx *jsonctx)
131{
132#define BUFSIZE 24
133#define BUFSIZE_STEP 128
134
135 const char *in = jsonctx->in->current, *start;
136 char *buf = NULL;
137 size_t offset; /* read offset in input buffer */
138 size_t len; /* length of the output string (write offset in output buffer) */
139 size_t size = 0; /* size of the output buffer */
140 size_t u;
141 uint64_t start_line;
142
143 assert(jsonctx);
144
145 /* init */
146 start = in;
Radek Krejcid54412f2020-12-17 20:25:35 +0100147 start_line = jsonctx->in->line;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200148 offset = len = 0;
149
150 /* parse */
151 while (in[offset]) {
152 if (in[offset] == '\\') {
153 /* escape sequence */
154 size_t slash = offset;
155 uint32_t value;
156 uint8_t i = 1;
157
158 if (!buf) {
159 /* prepare output buffer */
160 buf = malloc(BUFSIZE);
161 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
162 size = BUFSIZE;
163 }
164
165 /* allocate enough for the offset and next character,
166 * we will need 4 bytes at most since we support only the predefined
167 * (one-char) entities and character references */
168 if (len + offset + 4 >= size) {
169 buf = ly_realloc(buf, size + BUFSIZE_STEP);
170 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
171 size += BUFSIZE_STEP;
172 }
173
174 if (offset) {
175 /* store what we have so far */
176 memcpy(&buf[len], in, offset);
177 len += offset;
178 in += offset;
179 offset = 0;
180 }
181
182 switch (in[++offset]) {
183 case '"':
184 /* quotation mark */
185 value = 0x22;
186 break;
187 case '\\':
188 /* reverse solidus */
189 value = 0x5c;
190 break;
191 case '/':
192 /* solidus */
193 value = 0x2f;
194 break;
195 case 'b':
196 /* backspace */
197 value = 0x08;
198 break;
199 case 'f':
200 /* form feed */
201 value = 0x0c;
202 break;
203 case 'n':
204 /* line feed */
205 value = 0x0a;
206 break;
207 case 'r':
208 /* carriage return */
209 value = 0x0d;
210 break;
211 case 't':
212 /* tab */
213 value = 0x09;
214 break;
215 case 'u':
216 /* Basic Multilingual Plane character \uXXXX */
217 offset++;
218 for (value = i = 0; i < 4; i++) {
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100219 if (!in[offset + i]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100220 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", &in[slash]);
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100221 goto error;
222 } else if (isdigit(in[offset + i])) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200223 u = (in[offset + i] - '0');
224 } else if (in[offset + i] > 'F') {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100225 u = LY_BASE_DEC + (in[offset + i] - 'a');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200226 } else {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100227 u = LY_BASE_DEC + (in[offset + i] - 'A');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200228 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100229 value = (LY_BASE_HEX * value) + u;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200230 }
231 break;
232 default:
233 /* invalid escape sequence */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100234 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character escape sequence \\%c.", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200235 goto error;
236
237 }
238
239 offset += i; /* add read escaped characters */
240 LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100241 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).",
242 offset - slash, &in[slash], value),
Michal Vasko69730152020-10-09 16:30:07 +0200243 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200244 len += u; /* update number of bytes in buffer */
245 in += offset; /* move the input by the processed bytes stored in the buffer ... */
246 offset = 0; /* ... and reset the offset index for future moving data into buffer */
247
248 } else if (in[offset] == '"') {
249 /* end of string */
250 if (buf) {
251 /* realloc exact size string */
252 buf = ly_realloc(buf, len + offset + 1);
253 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
254 size = len + offset + 1;
255 memcpy(&buf[len], in, offset);
256
257 /* set terminating NULL byte */
258 buf[len + offset] = '\0';
259 }
260 len += offset;
261 ++offset;
262 in += offset;
263 goto success;
264 } else {
265 /* get it as UTF-8 character for check */
266 const char *c = &in[offset];
267 uint32_t code = 0;
268 size_t code_len = 0;
269
270 LY_CHECK_ERR_GOTO(ly_getutf8(&c, &code, &code_len),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100271 LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200272
273 LY_CHECK_ERR_GOTO(!is_jsonstrchar(code),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100274 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08x).",
275 &in[offset] - start + code_len, start, code),
Michal Vasko69730152020-10-09 16:30:07 +0200276 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200277
278 /* character is ok, continue */
279 offset += code_len;
280 }
281 }
282
283 /* EOF reached before endchar */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100284 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
285 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 +0200286
287error:
288 free(buf);
289 return LY_EVALID;
290
291success:
Radek Krejcid54412f2020-12-17 20:25:35 +0100292 jsonctx->in->current = in;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200293 if (buf) {
294 lyjson_ctx_set_value(jsonctx, buf, len, 1);
295 } else {
296 lyjson_ctx_set_value(jsonctx, start, len, 0);
297 }
298
299 return LY_SUCCESS;
300
301#undef BUFSIZE
302#undef BUFSIZE_STEP
303}
304
305/*
306 *
307 * Wrapper around lyjson_string_() adding LYJSON_STRING status into context to allow using lyjson_string_() for parsing object's name.
308 */
309static LY_ERR
310lyjson_string(struct lyjson_ctx *jsonctx)
311{
312 LY_CHECK_RET(lyjson_string_(jsonctx));
313
314 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_STRING);
315 LY_CHECK_RET(lyjson_check_next(jsonctx));
316
317 return LY_SUCCESS;
318}
319
320static LY_ERR
321lyjson_number(struct lyjson_ctx *jsonctx)
322{
323 size_t offset = 0, exponent = 0;
324 const char *in = jsonctx->in->current;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200325 uint8_t minus = 0;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200326
327 if (in[offset] == '-') {
328 ++offset;
329 minus = 1;
330 }
331
332 if (in[offset] == '0') {
333 ++offset;
334 } else if (isdigit(in[offset])) {
335 ++offset;
336 while (isdigit(in[offset])) {
337 ++offset;
338 }
339 } else {
340invalid_character:
341 if (in[offset]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100342 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON Number value (\"%c\").", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200343 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100344 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200345 }
346 return LY_EVALID;
347 }
348
349 if (in[offset] == '.') {
350 ++offset;
351 if (!isdigit(in[offset])) {
352 goto invalid_character;
353 }
354 while (isdigit(in[offset])) {
355 ++offset;
356 }
357 }
358
359 if ((in[offset] == 'e') || (in[offset] == 'E')) {
360 exponent = offset++;
361 if ((in[offset] == '+') || (in[offset] == '-')) {
362 ++offset;
363 }
364 if (!isdigit(in[offset])) {
365 goto invalid_character;
366 }
367 while (isdigit(in[offset])) {
368 ++offset;
369 }
370 }
371
372 if (exponent) {
373 /* convert JSON number with exponent into the representation used by YANG */
374 long int e_val;
375 char *ptr, *dec_point, *num;
376 const char *e_ptr = &in[exponent + 1];
377 size_t num_len, i;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200378 int64_t dp_position; /* final position of the deciaml point */
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200379
380 errno = 0;
Radek Krejcif13b87b2020-12-01 22:02:17 +0100381 e_val = strtol(e_ptr, &ptr, LY_BASE_DEC);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200382 if (errno) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100383 LOGVAL(jsonctx->ctx, LYVE_SEMANTICS, "Exponent out-of-bounds in a JSON Number value (%.*s).",
384 offset - minus - (e_ptr - in), e_ptr);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200385 return LY_EVALID;
386 }
387
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200388 dec_point = ly_strnchr(in, '.', exponent);
389 if (!dec_point) {
390 /* value is integer, we are just ... */
391 if (e_val >= 0) {
392 /* adding zeros at the end */
393 num_len = exponent + e_val;
394 dp_position = num_len; /* decimal point is behind the actual value */
395 } else if ((size_t)labs(e_val) < exponent) {
396 /* adding decimal point between the integer's digits */
397 num_len = exponent + 1;
398 dp_position = exponent + e_val;
399 } else {
400 /* adding decimal point before the integer with adding leading zero(s) */
401 num_len = labs(e_val) + 2;
402 dp_position = exponent + e_val;
403 }
404 dp_position -= minus;
405 } else {
406 /* value is decimal, we are moving the decimal point */
407 dp_position = dec_point - in + e_val - minus;
408 if (dp_position > (ssize_t)exponent) {
409 /* moving decimal point after the decimal value make the integer result */
410 num_len = dp_position;
411 } else if (dp_position < 0) {
412 /* moving decimal point before the decimal value requires additional zero(s)
413 * (decimal point is already count in exponent value) */
414 num_len = exponent + labs(dp_position) + 1;
415 } else {
416 /* moving decimal point just inside the decimal value does not make any change in length */
417 num_len = exponent;
418 }
419 }
420
421 /* allocate buffer for the result (add terminating NULL-byte */
422 num = malloc(num_len + 1);
423 LY_CHECK_ERR_RET(!num, LOGMEM(jsonctx->ctx), LY_EMEM);
424
425 /* compose the resulting vlaue */
426 i = 0;
427 if (minus) {
428 num[i++] = '-';
429 }
430 /* add leading zeros */
431 if (dp_position <= 0) {
432 num[i++] = '0';
433 num[i++] = '.';
Michal Vaskod989ba02020-08-24 10:59:24 +0200434 for ( ; dp_position; dp_position++) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200435 num[i++] = '0';
436 }
437 }
438 /* copy the value */
Radek Krejci857189e2020-09-01 13:26:36 +0200439 ly_bool dp_placed;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200440 size_t j;
441 for (dp_placed = dp_position ? 0 : 1, j = minus; j < exponent; j++) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200442 if (in[j] == '.') {
443 continue;
444 }
445 if (!dp_placed) {
446 if (!dp_position) {
447 num[i++] = '.';
448 dp_placed = 1;
449 } else {
450 dp_position--;
451 if (in[j] == '0') {
452 num_len--;
453 continue;
454 }
455 }
456 }
457
458 num[i++] = in[j];
459 }
460 /* trailing zeros */
461 while (dp_position--) {
462 num[i++] = '0';
463 }
464 /* terminating NULL byte */
465 num[i] = '\0';
466
467 /* store the modified number */
468 lyjson_ctx_set_value(jsonctx, num, num_len, 1);
469 } else {
470 /* store the number */
471 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, offset, 0);
472 }
473 ly_in_skip(jsonctx->in, offset);
474
475 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NUMBER);
476 LY_CHECK_RET(lyjson_check_next(jsonctx));
477
478 return LY_SUCCESS;
479}
480
481static LY_ERR
482lyjson_object_name(struct lyjson_ctx *jsonctx)
483{
484 if (*jsonctx->in->current != '"') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100485 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200486 jsonctx->in->current, "a JSON object's member");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200487 return LY_EVALID;
488 }
489 ly_in_skip(jsonctx->in, 1);
490
491 LY_CHECK_RET(lyjson_string_(jsonctx));
492 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko08dc70b2020-10-07 13:58:47 +0200493 if (*jsonctx->in->current != ':') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100494 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
495 "a JSON object's name-separator ':'");
Michal Vasko08dc70b2020-10-07 13:58:47 +0200496 return LY_EVALID;
497 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200498 ly_in_skip(jsonctx->in, 1);
499 LY_CHECK_RET(skip_ws(jsonctx));
500
501 return LY_SUCCESS;
502}
503
504static LY_ERR
505lyjson_object(struct lyjson_ctx *jsonctx)
506{
507 LY_CHECK_RET(skip_ws(jsonctx));
508
509 if (*jsonctx->in->current == '}') {
510 /* empty object */
511 ly_in_skip(jsonctx->in, 1);
512 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
513 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT_EMPTY);
514 return LY_SUCCESS;
515 }
516
517 LY_CHECK_RET(lyjson_object_name(jsonctx));
518
519 /* output data are set by lyjson_string_() */
520 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT);
521
522 return LY_SUCCESS;
523}
524
525/*
526 * @brief Process JSON array envelope
527 *
528 *
529 *
530 * @param[in] jsonctx JSON parser context
531 * @return LY_SUCCESS or LY_EMEM
532 */
533static LY_ERR
534lyjson_array(struct lyjson_ctx *jsonctx)
535{
536 LY_CHECK_RET(skip_ws(jsonctx));
537
538 if (*jsonctx->in->current == ']') {
539 /* empty array */
540 ly_in_skip(jsonctx->in, 1);
541 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY_EMPTY);
542 } else {
543 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY);
544 }
545
546 /* erase previous values, array has no value on its own */
547 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
548
549 return LY_SUCCESS;
550}
551
552static LY_ERR
553lyjson_value(struct lyjson_ctx *jsonctx)
554{
Michal Vasko69730152020-10-09 16:30:07 +0200555 if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200556 return LY_SUCCESS;
557 }
558
Radek Krejcif13b87b2020-12-01 22:02:17 +0100559 if ((*jsonctx->in->current == 'f') && !strncmp(jsonctx->in->current, "false", ly_strlen_const("false"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200560 /* false */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100561 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
562 ly_in_skip(jsonctx->in, ly_strlen_const("false"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200563 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_FALSE);
564 LY_CHECK_RET(lyjson_check_next(jsonctx));
565
Radek Krejcif13b87b2020-12-01 22:02:17 +0100566 } else if ((*jsonctx->in->current == 't') && !strncmp(jsonctx->in->current, "true", ly_strlen_const("true"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200567 /* true */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100568 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
569 ly_in_skip(jsonctx->in, ly_strlen_const("true"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200570 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_TRUE);
571 LY_CHECK_RET(lyjson_check_next(jsonctx));
572
Radek Krejcif13b87b2020-12-01 22:02:17 +0100573 } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200574 /* none */
Radek Krejci201963a2020-12-03 11:43:40 +0100575 lyjson_ctx_set_value(jsonctx, "", 0, 0);
Radek Krejcif13b87b2020-12-01 22:02:17 +0100576 ly_in_skip(jsonctx->in, ly_strlen_const("null"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200577 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NULL);
578 LY_CHECK_RET(lyjson_check_next(jsonctx));
579
580 } else if (*jsonctx->in->current == '"') {
581 /* string */
582 ly_in_skip(jsonctx->in, 1);
583 LY_CHECK_RET(lyjson_string(jsonctx));
584
585 } else if (*jsonctx->in->current == '[') {
586 /* array */
587 ly_in_skip(jsonctx->in, 1);
588 LY_CHECK_RET(lyjson_array(jsonctx));
589
590 } else if (*jsonctx->in->current == '{') {
591 /* object */
592 ly_in_skip(jsonctx->in, 1);
593 LY_CHECK_RET(lyjson_object(jsonctx));
594
Michal Vasko69730152020-10-09 16:30:07 +0200595 } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200596 /* number */
597 LY_CHECK_RET(lyjson_number(jsonctx));
598
599 } else {
600 /* unexpected value */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100601 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200602 jsonctx->in->current, "a JSON value");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200603 return LY_EVALID;
604 }
605
606 return LY_SUCCESS;
607}
608
609LY_ERR
610lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
611{
612 LY_ERR ret = LY_SUCCESS;
613 struct lyjson_ctx *jsonctx;
614
615 assert(ctx);
616 assert(in);
617 assert(jsonctx_p);
618
619 /* new context */
620 jsonctx = calloc(1, sizeof *jsonctx);
621 LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
622 jsonctx->ctx = ctx;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200623 jsonctx->in = in;
624
Radek Krejci2efc45b2020-12-22 16:25:44 +0100625 LOG_LOCINIT(ctx, NULL, NULL, NULL, in);
626
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200627 /* parse JSON value, if any */
628 LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup);
629 if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) {
630 /* empty data input */
631 goto cleanup;
632 }
633
634 ret = lyjson_value(jsonctx);
635
Michal Vasko69730152020-10-09 16:30:07 +0200636 if ((jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100637 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200638 ret = LY_EVALID;
639 }
640
641cleanup:
642 if (ret) {
643 lyjson_ctx_free(jsonctx);
644 } else {
645 *jsonctx_p = jsonctx;
646 }
647 return ret;
648}
649
650void
651lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
652{
653 if (jsonctx->backup.dynamic) {
654 free((char *)jsonctx->backup.value);
655 }
656 jsonctx->backup.status = lyjson_ctx_status(jsonctx, 0);
657 jsonctx->backup.status_count = jsonctx->status.count;
658 jsonctx->backup.value = jsonctx->value;
659 jsonctx->backup.value_len = jsonctx->value_len;
660 jsonctx->backup.input = jsonctx->in->current;
661 jsonctx->backup.dynamic = jsonctx->dynamic;
662 jsonctx->dynamic = 0;
663}
664
665void
666lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
667{
668 if (jsonctx->dynamic) {
669 free((char *)jsonctx->value);
670 }
671 jsonctx->status.count = jsonctx->backup.status_count;
Michal Vasko22df3f02020-08-24 13:29:22 +0200672 jsonctx->status.objs[jsonctx->backup.status_count - 1] = (void *)jsonctx->backup.status;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200673 jsonctx->value = jsonctx->backup.value;
674 jsonctx->value_len = jsonctx->backup.value_len;
675 jsonctx->in->current = jsonctx->backup.input;
676 jsonctx->dynamic = jsonctx->backup.dynamic;
677 jsonctx->backup.dynamic = 0;
678}
679
680LY_ERR
681lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
682{
683 LY_ERR ret = LY_SUCCESS;
Radek Krejci857189e2020-09-01 13:26:36 +0200684 ly_bool toplevel = 0;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200685 enum LYJSON_PARSER_STATUS prev;
686
687 assert(jsonctx);
688
689 prev = lyjson_ctx_status(jsonctx, 0);
690
Michal Vasko69730152020-10-09 16:30:07 +0200691 if ((prev == LYJSON_OBJECT) || (prev == LYJSON_ARRAY)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200692 /* get value for the object's member OR the first value in the array */
693 ret = lyjson_value(jsonctx);
694 goto result;
695 } else {
696 /* the previous token is closed and should be completely processed */
697 JSON_POP_STATUS_RET(jsonctx);
698 prev = lyjson_ctx_status(jsonctx, 0);
699 }
700
701 if (!jsonctx->status.count) {
702 /* we are done with the top level value */
703 toplevel = 1;
704 }
705 LY_CHECK_RET(skip_ws(jsonctx));
706 if (toplevel && !jsonctx->status.count) {
707 /* EOF expected, but there are some data after the top level token */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100708 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 +0200709 return LY_EVALID;
710 }
711
712 if (toplevel) {
713 /* we are done */
714 return LY_SUCCESS;
715 }
716
717 /* continue with the next token */
718 assert(prev == LYJSON_OBJECT || prev == LYJSON_ARRAY);
719
720 if (*jsonctx->in->current == ',') {
721 /* sibling item in the ... */
722 ly_in_skip(jsonctx->in, 1);
723 LY_CHECK_RET(skip_ws(jsonctx));
724
725 if (prev == LYJSON_OBJECT) {
726 /* ... object - get another object's member */
727 ret = lyjson_object_name(jsonctx);
728 } else { /* LYJSON_ARRAY */
729 /* ... array - get another complete value */
730 ret = lyjson_value(jsonctx);
731 }
Michal Vasko69730152020-10-09 16:30:07 +0200732 } else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) || ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200733 ly_in_skip(jsonctx->in, 1);
734 JSON_POP_STATUS_RET(jsonctx);
735 JSON_PUSH_STATUS_RET(jsonctx, prev + 1);
736 } else {
737 /* unexpected value */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100738 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
739 prev == LYJSON_ARRAY ? "another JSON value in array" : "another JSON object's member");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200740 return LY_EVALID;
741 }
742
743result:
Michal Vasko69730152020-10-09 16:30:07 +0200744 if ((ret == LY_SUCCESS) && (jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100745 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200746 ret = LY_EVALID;
747 }
748
Michal Vasko69730152020-10-09 16:30:07 +0200749 if ((ret == LY_SUCCESS) && status) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200750 *status = lyjson_ctx_status(jsonctx, 0);
751 }
752
753 return ret;
754}
755
756enum LYJSON_PARSER_STATUS
757lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index)
758{
759 assert(jsonctx);
760
761 if (jsonctx->status.count < index) {
762 return LYJSON_ERROR;
763 } else if (jsonctx->status.count == index) {
764 return LYJSON_ROOT;
765 } else {
Michal Vasko27915722020-08-31 14:54:42 +0200766 return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - (index + 1)];
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200767 }
768}
769
770void
771lyjson_ctx_free(struct lyjson_ctx *jsonctx)
772{
773 if (!jsonctx) {
774 return;
775 }
776
Radek Krejci2efc45b2020-12-22 16:25:44 +0100777 LOG_LOCBACK(jsonctx->ctx, 0, 0, 0, 1);
778
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200779 if (jsonctx->dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200780 free((char *)jsonctx->value);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200781 }
782 if (jsonctx->backup.dynamic) {
783 free((char *)jsonctx->backup.value);
784 }
785
786 ly_set_erase(&jsonctx->status, NULL);
787
788 free(jsonctx);
789}