#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "in_internal.h"
#include "json.h"
#include "ly_common.h"
#include "tree_schema_internal.h"
const char *
lyjson_token2str(enum LYJSON_PARSER_STATUS status)
{
switch (status) {
case LYJSON_ERROR:
return "error";
case LYJSON_OBJECT:
return "object";
case LYJSON_OBJECT_NEXT:
return "object next";
case LYJSON_OBJECT_CLOSED:
return "object closed";
case LYJSON_ARRAY:
return "array";
case LYJSON_ARRAY_NEXT:
return "array next";
case LYJSON_ARRAY_CLOSED:
return "array closed";
case LYJSON_OBJECT_NAME:
return "object name";
case LYJSON_NUMBER:
return "number";
case LYJSON_STRING:
return "string";
case LYJSON_TRUE:
return "true";
case LYJSON_FALSE:
return "false";
case LYJSON_NULL:
return "null";
case LYJSON_END:
return "end of input";
}
return "";
}
enum LYJSON_PARSER_STATUS
lyjson_ctx_status(struct lyjson_ctx *jsonctx)
{
assert(jsonctx);
if (!jsonctx->status.count) {
return LYJSON_END;
}
return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - 1];
}
uint32_t
lyjson_ctx_depth(struct lyjson_ctx *jsonctx)
{
return jsonctx->status.count;
}
static void
lyjson_skip_ws(struct lyjson_ctx *jsonctx)
{
while (is_jsonws(*jsonctx->in->current)) {
if (*jsonctx->in->current == '\n') {
LY_IN_NEW_LINE(jsonctx->in);
}
ly_in_skip(jsonctx->in, 1);
}
}
static void
lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, uint32_t value_len, ly_bool dynamic)
{
assert(jsonctx);
if (jsonctx->dynamic) {
free((char *)jsonctx->value);
}
jsonctx->value = value;
jsonctx->value_len = value_len;
jsonctx->dynamic = dynamic;
}
static LY_ERR
lyjson_string(struct lyjson_ctx *jsonctx)
{
#define ADD_CHECK_OVERFLOW_GOTO(var, num, err_label) \
if (var + num < var) { \
LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "JSON value too long."); \
goto err_label; \
} \
var += num;
const char *in = jsonctx->in->current, *start, *c;
char *buf = NULL;
uint32_t offset;
uint32_t len;
uint32_t size = 0;
uint64_t start_line, orig_line;
uint32_t u, value;
uint8_t i;
assert(jsonctx);
start = in;
start_line = jsonctx->in->line;
offset = len = 0;
while (in[offset]) {
switch (in[offset]) {
case '\\':
c = &in[offset];
if (!buf) {
buf = malloc(LYJSON_STRING_BUF_START);
LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
size = LYJSON_STRING_BUF_START;
}
if (len + offset + 4 >= size) {
uint32_t increment;
for (increment = LYJSON_STRING_BUF_STEP; len + offset + 4 >= size + increment; increment += LYJSON_STRING_BUF_STEP) {}
buf = ly_realloc(buf, size + increment);
LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
size += LYJSON_STRING_BUF_STEP;
}
if (offset) {
memcpy(&buf[len], in, offset);
len += offset;
in += offset;
offset = 0;
}
i = 1;
switch (in[++offset]) {
case '"':
value = 0x22;
break;
case '\\':
value = 0x5c;
break;
case '/':
value = 0x2f;
break;
case 'b':
value = 0x08;
break;
case 'f':
value = 0x0c;
break;
case 'n':
value = 0x0a;
break;
case 'r':
value = 0x0d;
break;
case 't':
value = 0x09;
break;
case 'u':
ADD_CHECK_OVERFLOW_GOTO(offset, 1, error);
for (value = i = 0; i < 4; i++) {
if (!in[offset + i]) {
LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", c);
goto error;
} else if (isdigit(in[offset + i])) {
u = (in[offset + i] - '0');
} else if (in[offset + i] > 'F') {
u = LY_BASE_DEC + (in[offset + i] - 'a');
} else {
u = LY_BASE_DEC + (in[offset + i] - 'A');
}
value = (LY_BASE_HEX * value) + u;
}
break;
default:
LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "Invalid character escape sequence \\%c.", in[offset]);
goto error;
}
ADD_CHECK_OVERFLOW_GOTO(offset, i, error);
LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08" PRIx32 ").",
(int)(&in[offset] - c), c, value),
error);
len += u;
in += offset;
offset = 0;
break;
case '"':
if (buf) {
buf = ly_realloc(buf, len + offset + 1);
LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
size = len + offset + 1;
if (offset) {
memcpy(&buf[len], in, offset);
}
buf[len + offset] = '\0';
}
len += offset;
ADD_CHECK_OVERFLOW_GOTO(offset, 1, error);
in += offset;
goto success;
default:
c = &in[offset];
LY_CHECK_ERR_GOTO(ly_getutf8(&c, &value, &u),
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_INCHAR, in[offset]), error);
LY_CHECK_ERR_GOTO(!is_jsonstrchar(value),
LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08" PRIx32 ").",
(int)(&in[offset] - start + u), start, value),
error);
ADD_CHECK_OVERFLOW_GOTO(offset, u, error);
break;
}
}
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_EOF);
orig_line = jsonctx->in->line;
jsonctx->in->line = start_line;
LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "Missing quotation-mark at the end of a JSON string.");
jsonctx->in->line = orig_line;
error:
free(buf);
return LY_EVALID;
success:
jsonctx->in->current = in;
if (buf) {
lyjson_ctx_set_value(jsonctx, buf, len, 1);
} else {
lyjson_ctx_set_value(jsonctx, start, len, 0);
}
return LY_SUCCESS;
}
static uint32_t
lyjson_count_in_row(const char *str, const char *end, char c, ly_bool backwards)
{
uint32_t cnt;
assert(str && end);
if (str >= end) {
return 0;
}
if (!backwards) {
for (cnt = 0; (str != end) && (*str == c); ++str, ++cnt) {}
} else {
--end;
--str;
for (cnt = 0; (str != end) && (*end == c); --end, ++cnt) {}
}
return cnt;
}
static ly_bool
lyjson_number_is_zero(const char *in, const char *end)
{
assert(in < end);
if ((in[0] == '-') || (in[0] == '+')) {
in++;
assert(in < end);
}
if ((in[0] == '0') && (in[1] == '.')) {
in += 2;
if (!(in < end)) {
return 1;
}
}
return lyjson_count_in_row(in, end, '0', 0) == (uint32_t)(end - in);
}
static LY_ERR
lyjson_get_buffer_for_number(const struct ly_ctx *ctx, uint64_t num_len, char **buffer)
{
*buffer = NULL;
LY_CHECK_ERR_RET((num_len + 1) > LY_NUMBER_MAXLEN, LOGVAL(ctx, NULL, LYVE_SEMANTICS,
"Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit."), LY_EVALID);
*buffer = malloc(num_len + 1);
LY_CHECK_ERR_RET(!(*buffer), LOGMEM(ctx), LY_EMEM);
return LY_SUCCESS;
}
static uint32_t
lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len, char *dec_point, int32_t dp_position, char *dst)
{
int32_t dec_point_idx;
int32_t n, d;
assert(num && dst);
dec_point_idx = dec_point ? dec_point - num : INT32_MAX;
assert((dec_point_idx >= 0) && (dec_point_idx != dp_position));
for (n = 0, d = 0; (uint32_t)n < num_len; n++) {
if (n == dec_point_idx) {
continue;
} else if (d == dp_position) {
dst[d++] = '.';
dst[d++] = num[n];
} else {
dst[d++] = num[n];
}
}
return d;
}
static LY_ERR
lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent, uint64_t total_len, char **res,
uint32_t *res_len)
{
#define MAYBE_WRITE_MINUS(ARRAY, INDEX, FLAG) \
if (FLAG) { \
ARRAY[INDEX++] = '-'; \
}
#define LEADING_ZERO 1
#define FORWARD 0
#define BACKWARD 1
char *buf;
uint64_t buf_len;
uint32_t i = 0;
const char *num;
uint16_t num_len;
char *dec_point;
int32_t dp_position;
long long e_val;
int8_t dot;
uint8_t minus;
long zeros;
ly_bool leading_zero;
assert(ctx && in && exponent && res && res_len && (total_len > 2));
assert((in < exponent) && ((*exponent == 'e') || (*exponent == 'E')));
if ((exponent - in) > UINT16_MAX) {
LOGVAL(ctx, NULL, LYVE_SEMANTICS, "JSON number is too long.");
return LY_EVALID;
}
errno = 0;
e_val = strtoll(exponent + 1, NULL, LY_BASE_DEC);
if (errno || (e_val > UINT16_MAX) || (e_val < -UINT16_MAX)) {
LOGVAL(ctx, NULL, LYVE_SEMANTICS, "Exponent out-of-bounds in a JSON Number value (%.*s).", (int)total_len, in);
return LY_EVALID;
}
minus = in[0] == '-';
if (in[minus] == '0') {
assert(in[minus + 1] == '.');
leading_zero = 1;
num = &in[minus + 1];
} else {
leading_zero = 0;
num = &in[minus];
}
num_len = exponent - num;
dec_point = ly_strnchr(num, '.', num_len);
dp_position = dec_point ?
dec_point - num + e_val :
num_len + e_val;
num_len -= dp_position > 0 ?
lyjson_count_in_row(num + dp_position - 1, exponent, '0', BACKWARD) :
lyjson_count_in_row(num, exponent, '0', BACKWARD);
if (dec_point && ((int32_t)(num_len - 1) == dp_position)) {
dot = -1;
} else if (dec_point) {
dot = 0;
} else {
dot = 1;
}
if (dp_position <= 0) {
zeros = labs(dp_position);
buf_len = minus + LEADING_ZERO + dot + zeros + num_len;
LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
MAYBE_WRITE_MINUS(buf, i, minus);
buf[i++] = '0';
buf[i++] = '.';
memset(buf + i, '0', zeros);
i += zeros;
dp_position = -1;
lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
} else if (leading_zero && (dp_position < (ssize_t)num_len)) {
num++;
num_len--;
dp_position--;
zeros = lyjson_count_in_row(num, num + dp_position + 1, '0', FORWARD);
if (zeros == (dp_position + 1)) {
zeros--;
dp_position = 1;
dot = 1;
} else {
dot = 0;
}
buf_len = minus + dot + (num_len - zeros);
LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
MAYBE_WRITE_MINUS(buf, i, minus);
lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
} else if (dp_position < (ssize_t)num_len) {
buf_len = minus + dot + num_len;
LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
MAYBE_WRITE_MINUS(buf, i, minus);
lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
} else if (leading_zero) {
num++;
num_len--;
zeros = lyjson_count_in_row(num, num + num_len, '0', FORWARD);
buf_len = minus + dp_position - zeros;
LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
MAYBE_WRITE_MINUS(buf, i, minus);
i += lyjson_exp_number_copy_num_part(num + zeros, num_len - zeros, NULL, dp_position, buf + i);
memset(buf + i, '0', buf_len - i);
} else {
buf_len = minus + dp_position;
LY_CHECK_RET(lyjson_get_buffer_for_number(ctx, buf_len, &buf));
MAYBE_WRITE_MINUS(buf, i, minus);
i += lyjson_exp_number_copy_num_part(num, num_len, dec_point, dp_position, buf + i);
memset(buf + i, '0', buf_len - i);
}
buf[buf_len] = '\0';
*res = buf;
*res_len = buf_len;
#undef MAYBE_WRITE_MINUS
#undef LEADING_ZERO
#undef FORWARD
#undef BACKWARD
return LY_SUCCESS;
}
static LY_ERR
lyjson_number(struct lyjson_ctx *jsonctx)
{
#define ADD_CHECK_OVERFLOW_RET(var, num, ret) \
if (var + num < var) { \
LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "JSON value too long."); \
return ret; \
} \
var += num;
uint32_t offset = 0, num_len;
const char *in = jsonctx->in->current, *exponent = NULL;
uint8_t minus = 0;
char *num;
if (in[offset] == '-') {
++offset;
minus = 1;
}
if (in[offset] == '0') {
++offset;
} else if (isdigit(in[offset])) {
++offset;
while (isdigit(in[offset])) {
ADD_CHECK_OVERFLOW_RET(offset, 1, LY_EINVAL);
}
} else {
invalid_character:
if (in[offset]) {
LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "Invalid character in JSON Number value (\"%c\").", in[offset]);
} else {
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_EOF);
}
return LY_EVALID;
}
if (in[offset] == '.') {
ADD_CHECK_OVERFLOW_RET(offset, 1, LY_EINVAL);
if (!isdigit(in[offset])) {
goto invalid_character;
}
while (isdigit(in[offset])) {
ADD_CHECK_OVERFLOW_RET(offset, 1, LY_EINVAL);
}
}
if ((in[offset] == 'e') || (in[offset] == 'E')) {
exponent = &in[offset];
ADD_CHECK_OVERFLOW_RET(offset, 1, LY_EINVAL);
if ((in[offset] == '+') || (in[offset] == '-')) {
ADD_CHECK_OVERFLOW_RET(offset, 1, LY_EINVAL);
}
if (!isdigit(in[offset])) {
goto invalid_character;
}
while (isdigit(in[offset])) {
ADD_CHECK_OVERFLOW_RET(offset, 1, LY_EINVAL);
}
}
if (lyjson_number_is_zero(in, exponent ? exponent : &in[offset])) {
lyjson_ctx_set_value(jsonctx, in, minus + 1, 0);
} else if (exponent && lyjson_number_is_zero(exponent + 1, &in[offset])) {
lyjson_ctx_set_value(jsonctx, in, exponent - in, 0);
} else if (exponent) {
LY_CHECK_RET(lyjson_exp_number(jsonctx->ctx, in, exponent, offset, &num, &num_len));
lyjson_ctx_set_value(jsonctx, num, num_len, 1);
} else {
if (offset > LY_NUMBER_MAXLEN) {
LOGVAL(jsonctx->ctx, NULL, LYVE_SEMANTICS,
"Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.");
return LY_EVALID;
}
lyjson_ctx_set_value(jsonctx, in, offset, 0);
}
ly_in_skip(jsonctx->in, offset);
return LY_SUCCESS;
}
LY_ERR
lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
{
LY_ERR ret = LY_SUCCESS;
struct lyjson_ctx *jsonctx;
assert(ctx && in && jsonctx_p);
jsonctx = calloc(1, sizeof *jsonctx);
LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
jsonctx->ctx = ctx;
jsonctx->in = in;
ly_log_location(NULL, NULL, in);
lyjson_skip_ws(jsonctx);
if (jsonctx->in->current[0] == '\0') {
LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "Empty JSON file.");
ret = LY_EVALID;
goto cleanup;
}
LY_CHECK_GOTO(ret = lyjson_ctx_next(jsonctx, NULL), cleanup);
cleanup:
if (ret) {
lyjson_ctx_free(jsonctx);
} else {
*jsonctx_p = jsonctx;
}
return ret;
}
static LY_ERR
lyjson_next_object_name(struct lyjson_ctx *jsonctx)
{
switch (*jsonctx->in->current) {
case '\0':
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_EOF);
return LY_EVALID;
case '"':
ly_in_skip(jsonctx->in, 1);
LY_CHECK_RET(lyjson_string(jsonctx));
lyjson_skip_ws(jsonctx);
if (*jsonctx->in->current != ':') {
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
"a JSON value name-separator ':'");
return LY_EVALID;
}
ly_in_skip(jsonctx->in, 1);
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NAME);
break;
case '}':
ly_in_skip(jsonctx->in, 1);
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
break;
default:
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
jsonctx->in->current, "a JSON object name");
return LY_EVALID;
}
return LY_SUCCESS;
}
static LY_ERR
lyjson_next_value(struct lyjson_ctx *jsonctx, ly_bool array_end)
{
switch (*jsonctx->in->current) {
case '\0':
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_EOF);
return LY_EVALID;
case '"':
ly_in_skip(jsonctx->in, 1);
LY_CHECK_RET(lyjson_string(jsonctx));
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_STRING);
break;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
LY_CHECK_RET(lyjson_number(jsonctx));
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NUMBER);
break;
case '{':
ly_in_skip(jsonctx->in, 1);
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT);
break;
case '[':
ly_in_skip(jsonctx->in, 1);
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY);
break;
case 't':
if (strncmp(jsonctx->in->current + 1, "rue", ly_strlen_const("rue"))) {
goto unexpected_value;
}
lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
ly_in_skip(jsonctx->in, ly_strlen_const("true"));
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_TRUE);
break;
case 'f':
if (strncmp(jsonctx->in->current + 1, "alse", ly_strlen_const("alse"))) {
goto unexpected_value;
}
lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
ly_in_skip(jsonctx->in, ly_strlen_const("false"));
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_FALSE);
break;
case 'n':
if (strncmp(jsonctx->in->current + 1, "ull", ly_strlen_const("ull"))) {
goto unexpected_value;
}
lyjson_ctx_set_value(jsonctx, "", 0, 0);
ly_in_skip(jsonctx->in, ly_strlen_const("null"));
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NULL);
break;
case ']':
if (!array_end) {
goto unexpected_value;
}
ly_in_skip(jsonctx->in, 1);
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
break;
default:
unexpected_value:
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
jsonctx->in->current, "a JSON value");
return LY_EVALID;
}
if (jsonctx->status.count > LY_MAX_BLOCK_DEPTH * 10) {
LOGERR(jsonctx->ctx, LY_EINVAL, "Maximum number %d of nestings has been exceeded.", LY_MAX_BLOCK_DEPTH * 10);
return LY_EINVAL;
}
return LY_SUCCESS;
}
static LY_ERR
lyjson_next_object_item(struct lyjson_ctx *jsonctx)
{
switch (*jsonctx->in->current) {
case '\0':
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_EOF);
return LY_EVALID;
case '}':
ly_in_skip(jsonctx->in, 1);
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
break;
case ',':
ly_in_skip(jsonctx->in, 1);
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NEXT);
break;
default:
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
jsonctx->in->current, "a JSON object-end or next item");
return LY_EVALID;
}
return LY_SUCCESS;
}
static LY_ERR
lyjson_next_array_item(struct lyjson_ctx *jsonctx)
{
switch (*jsonctx->in->current) {
case '\0':
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_EOF);
return LY_EVALID;
case ']':
ly_in_skip(jsonctx->in, 1);
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
break;
case ',':
ly_in_skip(jsonctx->in, 1);
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_NEXT);
break;
default:
LOGVAL(jsonctx->ctx, NULL, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
jsonctx->in->current, "a JSON array-end or next item");
return LY_EVALID;
}
return LY_SUCCESS;
}
LY_ERR
lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
{
LY_ERR ret = LY_SUCCESS;
enum LYJSON_PARSER_STATUS cur;
assert(jsonctx);
cur = lyjson_ctx_status(jsonctx);
switch (cur) {
case LYJSON_OBJECT:
LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup);
break;
case LYJSON_ARRAY:
LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 1), cleanup);
break;
case LYJSON_OBJECT_NEXT:
LYJSON_STATUS_POP(jsonctx);
LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup);
break;
case LYJSON_ARRAY_NEXT:
LYJSON_STATUS_POP(jsonctx);
LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
break;
case LYJSON_OBJECT_NAME:
lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
LYJSON_STATUS_POP(jsonctx);
LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
break;
case LYJSON_OBJECT_CLOSED:
case LYJSON_ARRAY_CLOSED:
LYJSON_STATUS_POP(jsonctx);
case LYJSON_NUMBER:
case LYJSON_STRING:
case LYJSON_TRUE:
case LYJSON_FALSE:
case LYJSON_NULL:
lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
LYJSON_STATUS_POP(jsonctx);
cur = lyjson_ctx_status(jsonctx);
if (cur == LYJSON_OBJECT) {
LY_CHECK_GOTO(ret = lyjson_next_object_item(jsonctx), cleanup);
break;
} else if (cur == LYJSON_ARRAY) {
LY_CHECK_GOTO(ret = lyjson_next_array_item(jsonctx), cleanup);
break;
}
assert(cur == LYJSON_END);
goto cleanup;
case LYJSON_END:
LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
break;
case LYJSON_ERROR:
LOGINT(jsonctx->ctx);
ret = LY_EINT;
goto cleanup;
}
lyjson_skip_ws(jsonctx);
cleanup:
if (!ret && status) {
*status = lyjson_ctx_status(jsonctx);
}
return ret;
}
void
lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
{
if (jsonctx->backup.dynamic) {
free((char *)jsonctx->backup.value);
}
jsonctx->backup.status = lyjson_ctx_status(jsonctx);
jsonctx->backup.status_count = jsonctx->status.count;
jsonctx->backup.value = jsonctx->value;
jsonctx->backup.value_len = jsonctx->value_len;
jsonctx->backup.input = jsonctx->in->current;
jsonctx->backup.dynamic = jsonctx->dynamic;
jsonctx->dynamic = 0;
}
void
lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
{
if (jsonctx->dynamic) {
free((char *)jsonctx->value);
}
jsonctx->status.count = jsonctx->backup.status_count;
jsonctx->status.objs[jsonctx->backup.status_count - 1] = (void *)jsonctx->backup.status;
jsonctx->value = jsonctx->backup.value;
jsonctx->value_len = jsonctx->backup.value_len;
jsonctx->in->current = jsonctx->backup.input;
jsonctx->dynamic = jsonctx->backup.dynamic;
jsonctx->backup.dynamic = 0;
}
void
lyjson_ctx_free(struct lyjson_ctx *jsonctx)
{
if (!jsonctx) {
return;
}
ly_log_location_revert(0, 0, 1);
if (jsonctx->dynamic) {
free((char *)jsonctx->value);
}
if (jsonctx->backup.dynamic) {
free((char *)jsonctx->backup.value);
}
ly_set_erase(&jsonctx->status, NULL);
free(jsonctx);
}