blob: d4fe20b3f5d2e7086cb3968a801ddc4ce7bf8b38 [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 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200120 }
121
Radek Krejcie7010dc2021-03-04 15:54:24 +0100122 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Unexpected character \"%c\" after JSON %s.",
123 *jsonctx->in->current, lyjson_token2str(lyjson_ctx_status(jsonctx, 0)));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200124 return LY_EVALID;
125}
126
127/**
128 * Input is expected to start after the opening quotation-mark.
129 * When succeeds, input is moved after the closing quotation-mark.
130 */
131static LY_ERR
132lyjson_string_(struct lyjson_ctx *jsonctx)
133{
134#define BUFSIZE 24
135#define BUFSIZE_STEP 128
136
137 const char *in = jsonctx->in->current, *start;
138 char *buf = NULL;
139 size_t offset; /* read offset in input buffer */
140 size_t len; /* length of the output string (write offset in output buffer) */
141 size_t size = 0; /* size of the output buffer */
142 size_t u;
143 uint64_t start_line;
144
145 assert(jsonctx);
146
147 /* init */
148 start = in;
Radek Krejcid54412f2020-12-17 20:25:35 +0100149 start_line = jsonctx->in->line;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200150 offset = len = 0;
151
152 /* parse */
153 while (in[offset]) {
154 if (in[offset] == '\\') {
155 /* escape sequence */
Michal Vasko2be1d762021-03-11 16:53:15 +0100156 const char *slash = &in[offset];
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200157 uint32_t value;
158 uint8_t i = 1;
159
160 if (!buf) {
161 /* prepare output buffer */
162 buf = malloc(BUFSIZE);
163 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
164 size = BUFSIZE;
165 }
166
167 /* allocate enough for the offset and next character,
168 * we will need 4 bytes at most since we support only the predefined
169 * (one-char) entities and character references */
170 if (len + offset + 4 >= size) {
Juraj Vijtiukd746a352021-01-15 11:33:33 +0100171 size_t increment;
Radek Krejcidf549132021-01-21 10:32:32 +0100172 for (increment = BUFSIZE_STEP; len + offset + 4 >= size + increment; increment += BUFSIZE_STEP) {}
Juraj Vijtiukd746a352021-01-15 11:33:33 +0100173 buf = ly_realloc(buf, size + increment);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200174 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
175 size += BUFSIZE_STEP;
176 }
177
178 if (offset) {
179 /* store what we have so far */
180 memcpy(&buf[len], in, offset);
181 len += offset;
182 in += offset;
183 offset = 0;
184 }
185
186 switch (in[++offset]) {
187 case '"':
188 /* quotation mark */
189 value = 0x22;
190 break;
191 case '\\':
192 /* reverse solidus */
193 value = 0x5c;
194 break;
195 case '/':
196 /* solidus */
197 value = 0x2f;
198 break;
199 case 'b':
200 /* backspace */
201 value = 0x08;
202 break;
203 case 'f':
204 /* form feed */
205 value = 0x0c;
206 break;
207 case 'n':
208 /* line feed */
209 value = 0x0a;
210 break;
211 case 'r':
212 /* carriage return */
213 value = 0x0d;
214 break;
215 case 't':
216 /* tab */
217 value = 0x09;
218 break;
219 case 'u':
220 /* Basic Multilingual Plane character \uXXXX */
221 offset++;
222 for (value = i = 0; i < 4; i++) {
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100223 if (!in[offset + i]) {
Michal Vasko2be1d762021-03-11 16:53:15 +0100224 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", slash);
Juraj Vijtiuk2b94e4b2020-11-16 23:52:07 +0100225 goto error;
226 } else if (isdigit(in[offset + i])) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200227 u = (in[offset + i] - '0');
228 } else if (in[offset + i] > 'F') {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100229 u = LY_BASE_DEC + (in[offset + i] - 'a');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200230 } else {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100231 u = LY_BASE_DEC + (in[offset + i] - 'A');
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200232 }
Radek Krejcif13b87b2020-12-01 22:02:17 +0100233 value = (LY_BASE_HEX * value) + u;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200234 }
235 break;
236 default:
237 /* invalid escape sequence */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100238 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character escape sequence \\%c.", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200239 goto error;
240
241 }
242
243 offset += i; /* add read escaped characters */
244 LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100245 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).",
Michal Vasko2be1d762021-03-11 16:53:15 +0100246 (int)(&in[offset] - slash), slash, value),
Michal Vasko69730152020-10-09 16:30:07 +0200247 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200248 len += u; /* update number of bytes in buffer */
249 in += offset; /* move the input by the processed bytes stored in the buffer ... */
250 offset = 0; /* ... and reset the offset index for future moving data into buffer */
251
252 } else if (in[offset] == '"') {
253 /* end of string */
254 if (buf) {
255 /* realloc exact size string */
256 buf = ly_realloc(buf, len + offset + 1);
257 LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
258 size = len + offset + 1;
Michal Vasko08e9b112021-06-11 15:41:17 +0200259 if (offset) {
260 memcpy(&buf[len], in, offset);
261 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200262
263 /* set terminating NULL byte */
264 buf[len + offset] = '\0';
265 }
266 len += offset;
267 ++offset;
268 in += offset;
269 goto success;
270 } else {
271 /* get it as UTF-8 character for check */
272 const char *c = &in[offset];
273 uint32_t code = 0;
274 size_t code_len = 0;
275
276 LY_CHECK_ERR_GOTO(ly_getutf8(&c, &code, &code_len),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100277 LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200278
279 LY_CHECK_ERR_GOTO(!is_jsonstrchar(code),
Radek Krejci2efc45b2020-12-22 16:25:44 +0100280 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08x).",
Radek Krejci422afb12021-03-04 16:38:16 +0100281 (int)(&in[offset] - start + code_len), start, code),
Michal Vasko69730152020-10-09 16:30:07 +0200282 error);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200283
284 /* character is ok, continue */
285 offset += code_len;
286 }
287 }
288
289 /* EOF reached before endchar */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100290 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
291 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 +0200292
293error:
294 free(buf);
295 return LY_EVALID;
296
297success:
Radek Krejcid54412f2020-12-17 20:25:35 +0100298 jsonctx->in->current = in;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200299 if (buf) {
300 lyjson_ctx_set_value(jsonctx, buf, len, 1);
301 } else {
302 lyjson_ctx_set_value(jsonctx, start, len, 0);
303 }
304
305 return LY_SUCCESS;
306
307#undef BUFSIZE
308#undef BUFSIZE_STEP
309}
310
311/*
312 *
313 * Wrapper around lyjson_string_() adding LYJSON_STRING status into context to allow using lyjson_string_() for parsing object's name.
314 */
315static LY_ERR
316lyjson_string(struct lyjson_ctx *jsonctx)
317{
318 LY_CHECK_RET(lyjson_string_(jsonctx));
319
320 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_STRING);
321 LY_CHECK_RET(lyjson_check_next(jsonctx));
322
323 return LY_SUCCESS;
324}
325
aPieceke87c0a12021-05-13 15:43:26 +0200326/**
aPiecek76034c32021-06-08 15:03:11 +0200327 * @brief Calculate how many @p c characters there are in a row.
328 *
329 * @param[in] str Count from this position.
330 * @param[in] end Position after the last checked character.
331 * @param[in] c Checked character.
332 * @param[in] backwards Set to 1, if to proceed from end-1 to str.
333 * @return Number of characters in a row.
334 */
335static uint32_t
336lyjson_count_in_row(const char *str, const char *end, char c, ly_bool backwards)
337{
338 uint32_t cnt;
339
340 assert(str && end);
341
342 if (str >= end) {
343 return 0;
344 }
345
346 if (!backwards) {
347 for (cnt = 0; (str != end) && (*str == c); ++str, ++cnt) {}
348 } else {
349 --end;
350 --str;
351 for (cnt = 0; (str != end) && (*end == c); --end, ++cnt) {}
352 }
353
354 return cnt;
355}
356
357/**
358 * @brief Check if the number can be shortened to zero.
359 *
aPiecek76034c32021-06-08 15:03:11 +0200360 * @param[in] in Start of input string;
361 * @param[in] end End of input string;
362 * @return 1 if number is zero, otherwise 0.
363 */
364static ly_bool
365lyjson_number_is_zero(const char *in, const char *end)
366{
aPiecek28e101a2021-06-10 09:09:31 +0200367 assert(in < end);
aPiecek76034c32021-06-08 15:03:11 +0200368
369 if ((in[0] == '-') || (in[0] == '+')) {
370 in++;
aPiecek28e101a2021-06-10 09:09:31 +0200371 assert(in < end);
aPiecek76034c32021-06-08 15:03:11 +0200372 }
373 if ((in[0] == '0') && (in[1] == '.')) {
374 in += 2;
aPiecek28e101a2021-06-10 09:09:31 +0200375 if (!(in < end)) {
376 return 1;
377 }
aPiecek76034c32021-06-08 15:03:11 +0200378 }
379
380 return lyjson_count_in_row(in, end, '0', 0) == end - in;
381}
382
383/**
aPieceke87c0a12021-05-13 15:43:26 +0200384 * @brief Allocate buffer for number in string format.
385 *
386 * @param[in] jsonctx JSON context.
387 * @param[in] num_len Required space in bytes for a number.
388 * Terminating null byte is added by default.
389 * @param[out] buffer Output allocated buffer.
390 * @return LY_ERR value.
391 */
392static LY_ERR
aPiecek0ba088e2021-06-15 12:53:17 +0200393lyjson_get_buffer_for_number(const struct ly_ctx *ctx, uint64_t num_len, char **buffer)
aPieceke87c0a12021-05-13 15:43:26 +0200394{
395 *buffer = NULL;
396
aPiecek76034c32021-06-08 15:03:11 +0200397 LY_CHECK_ERR_RET((num_len + 1) > LY_NUMBER_MAXLEN, LOGVAL(ctx, LYVE_SEMANTICS,
aPieceke87c0a12021-05-13 15:43:26 +0200398 "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit."), LY_EVALID);
399
aPiecek76034c32021-06-08 15:03:11 +0200400 /* allocate buffer for the result (add NULL-byte) */
aPieceke87c0a12021-05-13 15:43:26 +0200401 *buffer = malloc(num_len + 1);
aPiecek76034c32021-06-08 15:03:11 +0200402 LY_CHECK_ERR_RET(!(*buffer), LOGMEM(ctx), LY_EMEM);
403 return LY_SUCCESS;
404}
405
406/**
407 * @brief Copy the 'numeric part' (@p num) except its decimal point
408 * (@p dec_point) and insert the new decimal point (@p dp_position)
409 * only if it is to be placed in the 'numeric part' range (@p num).
410 *
411 * @param[in] num Begin of the 'numeric part'.
412 * @param[in] num_len Length of the 'numeric part'.
413 * @param[in] dec_point Pointer to the old decimal point.
414 * If it has a NULL value, it is ignored.
415 * @param[in] dp_position Position of the new decimal point.
416 * If it has a negative value, it is ignored.
417 * @param[out] dst Memory into which the copied result is written.
418 * @return Number of characters written to the @p dst.
419 */
420static uint32_t
421lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len,
422 char *dec_point, int32_t dp_position, char *dst)
423{
424 int32_t dec_point_idx;
425 int32_t n, d;
426
427 assert(num && dst);
428
429 dec_point_idx = dec_point ? dec_point - num : INT32_MAX;
430 assert((dec_point_idx >= 0) && (dec_point_idx != dp_position));
431
432 for (n = 0, d = 0; (uint32_t)n < num_len; n++) {
433 if (n == dec_point_idx) {
434 continue;
435 } else if (d == dp_position) {
436 dst[d++] = '.';
437 dst[d++] = num[n];
438 } else {
439 dst[d++] = num[n];
440 }
441 }
442
443 return d;
444}
445
446/**
447 * @brief Convert JSON number with exponent into the representation
448 * used by YANG.
449 *
450 * The input numeric string must be syntactically valid. Also, before
451 * calling this function, checks should be performed using the
452 * ::lyjson_number_is_zero().
453 *
454 * @param[in] ctx Context for the error message.
455 * @param[in] in Beginning of the string containing the number.
456 * @param[in] exponent Pointer to the letter E/e.
457 * @param[in] total_len Total size of the input number.
458 * @param[out] res Conversion result.
459 * @param[out] res_len Length of the result.
460 * @return LY_ERR value.
461 */
462static LY_ERR
463lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent,
aPiecek0ba088e2021-06-15 12:53:17 +0200464 uint64_t total_len, char **res, uint64_t *res_len)
aPiecek76034c32021-06-08 15:03:11 +0200465{
466
467#define MAYBE_WRITE_MINUS(ARRAY, INDEX, FLAG) \
468 if (FLAG) { \
469 ARRAY[INDEX++] = '-'; \
470 }
471
472/* Length of leading zero followed by the decimal point. */
473#define LEADING_ZERO 1
474
475/* Flags for the ::lyjson_count_in_row() */
476#define FORWARD 0
477#define BACKWARD 1
478
479 /* Buffer where the result is stored. */
480 char *buf;
481 /* Size without space for terminating NULL-byte. */
aPiecek0ba088e2021-06-15 12:53:17 +0200482 uint64_t buf_len;
aPiecek76034c32021-06-08 15:03:11 +0200483 /* Index to buf. */
484 uint32_t i = 0;
485 /* A 'numeric part' doesn't contain a minus sign or an leading zero.
486 * For example, in 0.45, there is the leading zero.
487 */
488 const char *num;
489 /* Length of the 'numeric part' ends before E/e. */
aPiecek0ba088e2021-06-15 12:53:17 +0200490 uint16_t num_len;
aPiecek76034c32021-06-08 15:03:11 +0200491 /* Position of decimal point in the num. */
492 char *dec_point;
493 /* Final position of decimal point in the buf. */
494 int32_t dp_position;
495 /* Exponent as integer. */
496 long int e_val;
497 /* Byte for the decimal point. */
498 int8_t dot;
499 /* Required additional byte for the minus sign. */
500 uint8_t minus;
501 /* The number of zeros. */
502 long zeros;
503 /* If the number starts with leading zero followed by the decimal point. */
504 ly_bool leading_zero;
505
506 assert(ctx && in && exponent && res && res_len && (total_len > 2));
507 assert((in < exponent) && ((*exponent == 'e') || (*exponent == 'E')));
508
aPiecek0ba088e2021-06-15 12:53:17 +0200509 if ((exponent - in) > UINT16_MAX) {
510 LOGVAL(ctx, LYVE_SEMANTICS, "JSON number is too long.");
511 return LY_EVALID;
512 }
513
aPiecek76034c32021-06-08 15:03:11 +0200514 /* Convert exponent. */
515 errno = 0;
516 e_val = strtol(exponent + 1, NULL, LY_BASE_DEC);
aPieceke4e0d6c2021-07-01 12:02:54 +0200517 if (errno || (e_val > UINT16_MAX) || (e_val < -UINT16_MAX)) {
aPiecek76034c32021-06-08 15:03:11 +0200518 LOGVAL(ctx, LYVE_SEMANTICS,
519 "Exponent out-of-bounds in a JSON Number value (%.*s).",
520 total_len, in);
521 return LY_EVALID;
522 }
523
524 minus = in[0] == '-';
525 if (in[minus] == '0') {
526 assert(in[minus + 1] == '.');
527 leading_zero = 1;
528 /* The leading zero has been found, it will be skipped. */
529 num = &in[minus + 1];
530 } else {
531 leading_zero = 0;
532 /* Set to the first number. */
533 num = &in[minus];
534 }
535 num_len = exponent - num;
536
537 /* Find the location of the decimal points. */
538 dec_point = ly_strnchr(num, '.', num_len);
539 dp_position = dec_point ?
540 dec_point - num + e_val :
541 num_len + e_val;
542
543 /* Remove zeros after the decimal point from the end of
544 * the 'numeric part' because these are useless.
545 * (For example, in 40.001000 these are the last 3).
546 */
547 num_len -= dp_position > 0 ?
548 lyjson_count_in_row(num + dp_position - 1, exponent, '0', BACKWARD) :
549 lyjson_count_in_row(num, exponent, '0', BACKWARD);
550
551 /* Decide what to do with the dot from the 'numeric part'. */
552 if (dec_point && ((int32_t)(num_len - 1) == dp_position)) {
553 /* Decimal point in the last place is useless. */
554 dot = -1;
555 } else if (dec_point) {
556 /* Decimal point is shifted. */
557 dot = 0;
558 } else {
559 /* Additional byte for the decimal point is requred. */
560 dot = 1;
561 }
562
563 /* Final composition of the result. */
564 if (dp_position <= 0) {
565 /* Adding decimal point before the integer with adding additional zero(s). */
566
567 zeros = labs(dp_position);
568 buf_len = minus + LEADING_ZERO + dot + zeros + num_len;
569 LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
570 MAYBE_WRITE_MINUS(buf, i, minus);
571 buf[i++] = '0';
572 buf[i++] = '.';
573 memset(buf + i, '0', zeros);
574 i += zeros;
575 dp_position = -1;
576 lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
577 } else if (leading_zero && (dp_position < (ssize_t)num_len)) {
578 /* Insert decimal point between the integer's digits. */
579
580 /* Set a new range of 'numeric part'. Old decimal point is skipped. */
581 num++;
582 num_len--;
583 dp_position--;
584 /* Get the number of useless zeros between the old
585 * and new decimal point. For example, in the number 0.005E1,
586 * there is one useless zero.
587 */
588 zeros = lyjson_count_in_row(num, num + dp_position + 1, '0', FORWARD);
589 /* If the new decimal point will be in the place of the first non-zero subnumber. */
590 if (zeros == (dp_position + 1)) {
591 /* keep one zero as leading zero */
592 zeros--;
593 /* new decimal point will be behind the leading zero */
594 dp_position = 1;
595 dot = 1;
596 } else {
597 dot = 0;
598 }
599 buf_len = minus + dot + (num_len - zeros);
600 LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
601 MAYBE_WRITE_MINUS(buf, i, minus);
602 /* Skip useless zeros and copy. */
603 lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
604 } else if (dp_position < (ssize_t)num_len) {
605 /* Insert decimal point between the integer's digits. */
606
607 buf_len = minus + dot + num_len;
608 LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
609 MAYBE_WRITE_MINUS(buf, i, minus);
610 lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
611 } else if (leading_zero) {
612 /* Adding decimal point after the decimal value make the integer result. */
613
614 /* Set a new range of 'numeric part'. Old decimal point is skipped. */
615 num++;
616 num_len--;
617 /* Get the number of useless zeros. */
618 zeros = lyjson_count_in_row(num, num + num_len, '0', FORWARD);
619 buf_len = minus + dp_position - zeros;
620 LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
621 MAYBE_WRITE_MINUS(buf, i, minus);
622 /* Skip useless zeros and copy. */
623 i += lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
624 /* Add multiples of ten behind the 'numeric part'. */
625 memset(buf + i, '0', buf_len - i);
626 } else {
627 /* Adding decimal point after the decimal value make the integer result. */
628
629 buf_len = minus + dp_position;
630 LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
631 MAYBE_WRITE_MINUS(buf, i, minus);
632 i += lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
633 /* Add multiples of ten behind the 'numeric part'. */
634 memset(buf + i, '0', buf_len - i);
635 }
636
637 buf[buf_len] = '\0';
638 *res = buf;
639 *res_len = buf_len;
640
641#undef MAYBE_WRITE_MINUS
642#undef LEADING_ZERO
643#undef FORWARD
644#undef BACKWARD
645
aPieceke87c0a12021-05-13 15:43:26 +0200646 return LY_SUCCESS;
647}
648
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200649static LY_ERR
650lyjson_number(struct lyjson_ctx *jsonctx)
651{
aPiecek76034c32021-06-08 15:03:11 +0200652 size_t offset = 0, num_len;
653 const char *in = jsonctx->in->current, *exponent = NULL;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200654 uint8_t minus = 0;
aPiecek76034c32021-06-08 15:03:11 +0200655 char *num;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200656
657 if (in[offset] == '-') {
658 ++offset;
659 minus = 1;
660 }
661
662 if (in[offset] == '0') {
663 ++offset;
664 } else if (isdigit(in[offset])) {
665 ++offset;
666 while (isdigit(in[offset])) {
667 ++offset;
668 }
669 } else {
670invalid_character:
671 if (in[offset]) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100672 LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON Number value (\"%c\").", in[offset]);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200673 } else {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100674 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200675 }
676 return LY_EVALID;
677 }
678
679 if (in[offset] == '.') {
680 ++offset;
681 if (!isdigit(in[offset])) {
682 goto invalid_character;
683 }
684 while (isdigit(in[offset])) {
685 ++offset;
686 }
687 }
688
689 if ((in[offset] == 'e') || (in[offset] == 'E')) {
aPiecek76034c32021-06-08 15:03:11 +0200690 exponent = &in[offset];
691 ++offset;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200692 if ((in[offset] == '+') || (in[offset] == '-')) {
693 ++offset;
694 }
695 if (!isdigit(in[offset])) {
696 goto invalid_character;
697 }
698 while (isdigit(in[offset])) {
699 ++offset;
700 }
701 }
702
aPiecek76034c32021-06-08 15:03:11 +0200703 if (lyjson_number_is_zero(in, exponent ? exponent : &in[offset])) {
704 lyjson_ctx_set_value(jsonctx, in, minus + 1, 0);
705 } else if (exponent && lyjson_number_is_zero(exponent + 1, &in[offset])) {
706 lyjson_ctx_set_value(jsonctx, in, exponent - in, 0);
707 } else if (exponent) {
708 LY_CHECK_RET(lyjson_exp_number(jsonctx->ctx, in, exponent, offset, &num, &num_len));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200709 lyjson_ctx_set_value(jsonctx, num, num_len, 1);
710 } else {
aPiecek5b6dd182021-06-10 09:11:58 +0200711 if (offset > LY_NUMBER_MAXLEN) {
712 LOGVAL(jsonctx->ctx, LYVE_SEMANTICS,
713 "Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.");
714 return LY_EVALID;
715 }
aPiecek76034c32021-06-08 15:03:11 +0200716 lyjson_ctx_set_value(jsonctx, in, offset, 0);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200717 }
718 ly_in_skip(jsonctx->in, offset);
719
720 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NUMBER);
721 LY_CHECK_RET(lyjson_check_next(jsonctx));
722
723 return LY_SUCCESS;
724}
725
726static LY_ERR
727lyjson_object_name(struct lyjson_ctx *jsonctx)
728{
729 if (*jsonctx->in->current != '"') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100730 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200731 jsonctx->in->current, "a JSON object's member");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200732 return LY_EVALID;
733 }
734 ly_in_skip(jsonctx->in, 1);
735
736 LY_CHECK_RET(lyjson_string_(jsonctx));
737 LY_CHECK_RET(skip_ws(jsonctx));
Michal Vasko08dc70b2020-10-07 13:58:47 +0200738 if (*jsonctx->in->current != ':') {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100739 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
740 "a JSON object's name-separator ':'");
Michal Vasko08dc70b2020-10-07 13:58:47 +0200741 return LY_EVALID;
742 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200743 ly_in_skip(jsonctx->in, 1);
744 LY_CHECK_RET(skip_ws(jsonctx));
745
746 return LY_SUCCESS;
747}
748
749static LY_ERR
750lyjson_object(struct lyjson_ctx *jsonctx)
751{
752 LY_CHECK_RET(skip_ws(jsonctx));
753
754 if (*jsonctx->in->current == '}') {
aPiecek93582ed2021-05-25 14:49:06 +0200755 assert(jsonctx->depth);
756 jsonctx->depth--;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200757 /* empty object */
758 ly_in_skip(jsonctx->in, 1);
759 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
760 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT_EMPTY);
761 return LY_SUCCESS;
762 }
763
764 LY_CHECK_RET(lyjson_object_name(jsonctx));
765
766 /* output data are set by lyjson_string_() */
767 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_OBJECT);
768
769 return LY_SUCCESS;
770}
771
772/*
773 * @brief Process JSON array envelope
774 *
775 *
776 *
777 * @param[in] jsonctx JSON parser context
778 * @return LY_SUCCESS or LY_EMEM
779 */
780static LY_ERR
781lyjson_array(struct lyjson_ctx *jsonctx)
782{
783 LY_CHECK_RET(skip_ws(jsonctx));
784
785 if (*jsonctx->in->current == ']') {
786 /* empty array */
787 ly_in_skip(jsonctx->in, 1);
788 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY_EMPTY);
789 } else {
790 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_ARRAY);
791 }
792
793 /* erase previous values, array has no value on its own */
794 lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
795
796 return LY_SUCCESS;
797}
798
799static LY_ERR
800lyjson_value(struct lyjson_ctx *jsonctx)
801{
Michal Vasko69730152020-10-09 16:30:07 +0200802 if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200803 return LY_SUCCESS;
804 }
805
Radek Krejcif13b87b2020-12-01 22:02:17 +0100806 if ((*jsonctx->in->current == 'f') && !strncmp(jsonctx->in->current, "false", ly_strlen_const("false"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200807 /* false */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100808 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
809 ly_in_skip(jsonctx->in, ly_strlen_const("false"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200810 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_FALSE);
811 LY_CHECK_RET(lyjson_check_next(jsonctx));
812
Radek Krejcif13b87b2020-12-01 22:02:17 +0100813 } else if ((*jsonctx->in->current == 't') && !strncmp(jsonctx->in->current, "true", ly_strlen_const("true"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200814 /* true */
Radek Krejcif13b87b2020-12-01 22:02:17 +0100815 lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
816 ly_in_skip(jsonctx->in, ly_strlen_const("true"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200817 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_TRUE);
818 LY_CHECK_RET(lyjson_check_next(jsonctx));
819
Radek Krejcif13b87b2020-12-01 22:02:17 +0100820 } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200821 /* none */
Radek Krejci201963a2020-12-03 11:43:40 +0100822 lyjson_ctx_set_value(jsonctx, "", 0, 0);
Radek Krejcif13b87b2020-12-01 22:02:17 +0100823 ly_in_skip(jsonctx->in, ly_strlen_const("null"));
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200824 JSON_PUSH_STATUS_RET(jsonctx, LYJSON_NULL);
825 LY_CHECK_RET(lyjson_check_next(jsonctx));
826
827 } else if (*jsonctx->in->current == '"') {
828 /* string */
829 ly_in_skip(jsonctx->in, 1);
830 LY_CHECK_RET(lyjson_string(jsonctx));
831
832 } else if (*jsonctx->in->current == '[') {
833 /* array */
834 ly_in_skip(jsonctx->in, 1);
835 LY_CHECK_RET(lyjson_array(jsonctx));
836
837 } else if (*jsonctx->in->current == '{') {
aPiecek93582ed2021-05-25 14:49:06 +0200838 jsonctx->depth++;
839 if (jsonctx->depth > LY_MAX_BLOCK_DEPTH) {
840 LOGERR(jsonctx->ctx, LY_EINVAL,
841 "The maximum number of block nestings has been exceeded.");
842 return LY_EINVAL;
843 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200844 /* object */
845 ly_in_skip(jsonctx->in, 1);
846 LY_CHECK_RET(lyjson_object(jsonctx));
847
Michal Vasko69730152020-10-09 16:30:07 +0200848 } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200849 /* number */
850 LY_CHECK_RET(lyjson_number(jsonctx));
851
852 } else {
853 /* unexpected value */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100854 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
Michal Vasko69730152020-10-09 16:30:07 +0200855 jsonctx->in->current, "a JSON value");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200856 return LY_EVALID;
857 }
858
859 return LY_SUCCESS;
860}
861
862LY_ERR
863lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
864{
865 LY_ERR ret = LY_SUCCESS;
866 struct lyjson_ctx *jsonctx;
867
868 assert(ctx);
869 assert(in);
870 assert(jsonctx_p);
871
872 /* new context */
873 jsonctx = calloc(1, sizeof *jsonctx);
874 LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
875 jsonctx->ctx = ctx;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200876 jsonctx->in = in;
877
Radek Krejciddace2c2021-01-08 11:30:56 +0100878 LOG_LOCINIT(NULL, NULL, NULL, in);
Radek Krejci2efc45b2020-12-22 16:25:44 +0100879
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200880 /* parse JSON value, if any */
881 LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup);
882 if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) {
883 /* empty data input */
884 goto cleanup;
885 }
886
887 ret = lyjson_value(jsonctx);
888
Michal Vasko69730152020-10-09 16:30:07 +0200889 if ((jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +0100890 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200891 ret = LY_EVALID;
892 }
893
894cleanup:
895 if (ret) {
896 lyjson_ctx_free(jsonctx);
897 } else {
898 *jsonctx_p = jsonctx;
899 }
900 return ret;
901}
902
903void
904lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
905{
906 if (jsonctx->backup.dynamic) {
907 free((char *)jsonctx->backup.value);
908 }
909 jsonctx->backup.status = lyjson_ctx_status(jsonctx, 0);
910 jsonctx->backup.status_count = jsonctx->status.count;
911 jsonctx->backup.value = jsonctx->value;
912 jsonctx->backup.value_len = jsonctx->value_len;
913 jsonctx->backup.input = jsonctx->in->current;
914 jsonctx->backup.dynamic = jsonctx->dynamic;
aPiecek93582ed2021-05-25 14:49:06 +0200915 jsonctx->backup.depth = jsonctx->depth;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200916 jsonctx->dynamic = 0;
917}
918
919void
920lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
921{
922 if (jsonctx->dynamic) {
923 free((char *)jsonctx->value);
924 }
925 jsonctx->status.count = jsonctx->backup.status_count;
Michal Vasko22df3f02020-08-24 13:29:22 +0200926 jsonctx->status.objs[jsonctx->backup.status_count - 1] = (void *)jsonctx->backup.status;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200927 jsonctx->value = jsonctx->backup.value;
928 jsonctx->value_len = jsonctx->backup.value_len;
929 jsonctx->in->current = jsonctx->backup.input;
930 jsonctx->dynamic = jsonctx->backup.dynamic;
aPiecek93582ed2021-05-25 14:49:06 +0200931 jsonctx->depth = jsonctx->backup.depth;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200932 jsonctx->backup.dynamic = 0;
933}
934
935LY_ERR
936lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
937{
938 LY_ERR ret = LY_SUCCESS;
Radek Krejci857189e2020-09-01 13:26:36 +0200939 ly_bool toplevel = 0;
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200940 enum LYJSON_PARSER_STATUS prev;
941
942 assert(jsonctx);
943
944 prev = lyjson_ctx_status(jsonctx, 0);
945
Michal Vasko69730152020-10-09 16:30:07 +0200946 if ((prev == LYJSON_OBJECT) || (prev == LYJSON_ARRAY)) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200947 /* get value for the object's member OR the first value in the array */
948 ret = lyjson_value(jsonctx);
949 goto result;
950 } else {
951 /* the previous token is closed and should be completely processed */
952 JSON_POP_STATUS_RET(jsonctx);
953 prev = lyjson_ctx_status(jsonctx, 0);
954 }
955
956 if (!jsonctx->status.count) {
957 /* we are done with the top level value */
958 toplevel = 1;
959 }
960 LY_CHECK_RET(skip_ws(jsonctx));
961 if (toplevel && !jsonctx->status.count) {
962 /* EOF expected, but there are some data after the top level token */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100963 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 +0200964 return LY_EVALID;
965 }
966
967 if (toplevel) {
968 /* we are done */
969 return LY_SUCCESS;
970 }
971
972 /* continue with the next token */
973 assert(prev == LYJSON_OBJECT || prev == LYJSON_ARRAY);
974
975 if (*jsonctx->in->current == ',') {
976 /* sibling item in the ... */
977 ly_in_skip(jsonctx->in, 1);
978 LY_CHECK_RET(skip_ws(jsonctx));
979
980 if (prev == LYJSON_OBJECT) {
981 /* ... object - get another object's member */
982 ret = lyjson_object_name(jsonctx);
983 } else { /* LYJSON_ARRAY */
984 /* ... array - get another complete value */
985 ret = lyjson_value(jsonctx);
986 }
Michal Vasko69730152020-10-09 16:30:07 +0200987 } else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) || ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) {
aPiecek93582ed2021-05-25 14:49:06 +0200988 if (*jsonctx->in->current == '}') {
989 assert(jsonctx->depth);
990 jsonctx->depth--;
991 }
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200992 ly_in_skip(jsonctx->in, 1);
993 JSON_POP_STATUS_RET(jsonctx);
994 JSON_PUSH_STATUS_RET(jsonctx, prev + 1);
995 } else {
996 /* unexpected value */
Radek Krejci2efc45b2020-12-22 16:25:44 +0100997 LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
998 prev == LYJSON_ARRAY ? "another JSON value in array" : "another JSON object's member");
Radek Krejci50f0c6b2020-06-18 16:31:48 +0200999 return LY_EVALID;
1000 }
1001
1002result:
Michal Vasko69730152020-10-09 16:30:07 +02001003 if ((ret == LY_SUCCESS) && (jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
Radek Krejci2efc45b2020-12-22 16:25:44 +01001004 LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001005 ret = LY_EVALID;
1006 }
1007
Michal Vasko69730152020-10-09 16:30:07 +02001008 if ((ret == LY_SUCCESS) && status) {
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001009 *status = lyjson_ctx_status(jsonctx, 0);
1010 }
1011
1012 return ret;
1013}
1014
1015enum LYJSON_PARSER_STATUS
1016lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index)
1017{
1018 assert(jsonctx);
1019
1020 if (jsonctx->status.count < index) {
1021 return LYJSON_ERROR;
1022 } else if (jsonctx->status.count == index) {
1023 return LYJSON_ROOT;
1024 } else {
Michal Vasko27915722020-08-31 14:54:42 +02001025 return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - (index + 1)];
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001026 }
1027}
1028
1029void
1030lyjson_ctx_free(struct lyjson_ctx *jsonctx)
1031{
1032 if (!jsonctx) {
1033 return;
1034 }
1035
Radek Krejciddace2c2021-01-08 11:30:56 +01001036 LOG_LOCBACK(0, 0, 0, 1);
Radek Krejci2efc45b2020-12-22 16:25:44 +01001037
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001038 if (jsonctx->dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +02001039 free((char *)jsonctx->value);
Radek Krejci50f0c6b2020-06-18 16:31:48 +02001040 }
1041 if (jsonctx->backup.dynamic) {
1042 free((char *)jsonctx->backup.value);
1043 }
1044
1045 ly_set_erase(&jsonctx->status, NULL);
1046
1047 free(jsonctx);
1048}