#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <inttypes.h>
#include <ctype.h>
#include <math.h>
#include <limits.h>
#include <node.h>
#include "plist.h"
#include "strbuf.h"
#include "jsmn.h"
#include "hashtable.h"
#include "base64.h"
#include "time64.h"
#include "common.h"
#ifdef DEBUG
static int plist_json_debug = 0;
#define PLIST_JSON_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonparser] ERROR: " __VA_ARGS__); }
#define PLIST_JSON_WRITE_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonwriter] ERROR: " __VA_ARGS__); }
#else
#define PLIST_JSON_ERR(...)
#define PLIST_JSON_WRITE_ERR(...)
#endif
void plist_json_init(void)
{
#ifdef DEBUG
char *env_debug = getenv("PLIST_JSON_DEBUG");
if (env_debug && !strcmp(env_debug, "1")) {
plist_json_debug = 1;
}
#endif
}
void plist_json_deinit(void)
{
}
void plist_json_set_debug(int debug)
{
#ifdef DEBUG
plist_json_debug = debug;
#endif
}
#ifndef HAVE_STRNDUP
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
static char* strndup(const char* str, size_t len)
{
char *newstr = (char *)malloc(len+1);
if (newstr) {
strncpy(newstr, str, len);
newstr[len]= '\0';
}
return newstr;
}
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
#endif
static plist_err_t node_to_json(node_t node, bytearray_t **outbuf, uint32_t depth, int prettify, int coerce)
{
plist_data_t node_data = NULL;
char *val = NULL;
size_t val_len = 0;
uint32_t i = 0;
if (!node)
return PLIST_ERR_INVALID_ARG;
node_data = plist_get_data(node);
switch (node_data->type)
{
case PLIST_BOOLEAN:
{
if (node_data->boolval) {
str_buf_append(*outbuf, "true", 4);
} else {
str_buf_append(*outbuf, "false", 5);
}
}
break;
case PLIST_NULL:
str_buf_append(*outbuf, "null", 4);
break;
case PLIST_INT:
val = (char*)malloc(64);
if (node_data->length == 16) {
val_len = snprintf(val, 64, "%" PRIu64, node_data->intval);
} else {
val_len = snprintf(val, 64, "%" PRIi64, node_data->intval);
}
str_buf_append(*outbuf, val, val_len);
free(val);
break;
case PLIST_REAL:
val = (char*)malloc(64);
val_len = dtostr(val, 64, node_data->realval);
str_buf_append(*outbuf, val, val_len);
free(val);
break;
case PLIST_STRING:
case PLIST_KEY: {
const char *charmap[32] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007",
"\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f",
"\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017",
"\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f",
};
size_t j = 0;
size_t len = 0;
off_t start = 0;
off_t cur = 0;
str_buf_append(*outbuf, "\"", 1);
len = node_data->length;
for (j = 0; j < len; j++) {
unsigned char ch = (unsigned char)node_data->strval[j];
if (ch < 0x20) {
str_buf_append(*outbuf, node_data->strval + start, cur - start);
str_buf_append(*outbuf, charmap[ch], (charmap[ch][1] == 'u') ? 6 : 2);
start = cur+1;
} else if (ch == '"') {
str_buf_append(*outbuf, node_data->strval + start, cur - start);
str_buf_append(*outbuf, "\\\"", 2);
start = cur+1;
}
cur++;
}
str_buf_append(*outbuf, node_data->strval + start, cur - start);
str_buf_append(*outbuf, "\"", 1);
} break;
case PLIST_ARRAY: {
str_buf_append(*outbuf, "[", 1);
node_t ch;
uint32_t cnt = 0;
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
if (cnt > 0) {
str_buf_append(*outbuf, ",", 1);
}
if (prettify) {
str_buf_append(*outbuf, "\n", 1);
for (i = 0; i <= depth; i++) {
str_buf_append(*outbuf, " ", 2);
}
}
plist_err_t res = node_to_json(ch, outbuf, depth+1, prettify, coerce);
if (res < 0) {
return res;
}
cnt++;
}
if (cnt > 0 && prettify) {
str_buf_append(*outbuf, "\n", 1);
for (i = 0; i < depth; i++) {
str_buf_append(*outbuf, " ", 2);
}
}
str_buf_append(*outbuf, "]", 1);
} break;
case PLIST_DICT: {
str_buf_append(*outbuf, "{", 1);
node_t ch;
uint32_t cnt = 0;
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
if (cnt > 0 && cnt % 2 == 0) {
str_buf_append(*outbuf, ",", 1);
}
if (cnt % 2 == 0 && prettify) {
str_buf_append(*outbuf, "\n", 1);
for (i = 0; i <= depth; i++) {
str_buf_append(*outbuf, " ", 2);
}
}
plist_err_t res = node_to_json(ch, outbuf, depth+1, prettify, coerce);
if (res < 0) {
return res;
}
if (cnt % 2 == 0) {
str_buf_append(*outbuf, ":", 1);
if (prettify) {
str_buf_append(*outbuf, " ", 1);
}
}
cnt++;
}
if (cnt > 0 && prettify) {
str_buf_append(*outbuf, "\n", 1);
for (i = 0; i < depth; i++) {
str_buf_append(*outbuf, " ", 2);
}
}
str_buf_append(*outbuf, "}", 1);
} break;
case PLIST_DATA:
if (coerce) {
size_t b64_len = ((node_data->length + 2) / 3) * 4;
char *b64_buf = (char*)malloc(b64_len + 1);
if (!b64_buf) {
return PLIST_ERR_NO_MEM;
}
size_t actual_len = base64encode(b64_buf, node_data->buff, node_data->length);
str_buf_append(*outbuf, "\"", 1);
str_buf_append(*outbuf, b64_buf, actual_len);
str_buf_append(*outbuf, "\"", 1);
free(b64_buf);
} else {
PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n");
return PLIST_ERR_FORMAT;
}
break;
case PLIST_DATE:
if (coerce) {
Time64_T timev;
if (plist_real_to_time64(node_data->realval, &timev) < 0) {
PLIST_JSON_WRITE_ERR("Encountered invalid date value %f\n", node_data->realval);
return PLIST_ERR_INVALID_ARG;
}
struct TM _btime;
struct TM *btime = gmtime64_r(&timev, &_btime);
char datebuf[32];
size_t datelen = 0;
if (btime) {
struct tm _tmcopy;
copy_TM64_to_tm(btime, &_tmcopy);
datelen = strftime(datebuf, sizeof(datebuf), "%Y-%m-%dT%H:%M:%SZ", &_tmcopy);
}
if (datelen <= 0) {
datelen = snprintf(datebuf, sizeof(datebuf), "1970-01-01T00:00:00Z");
}
str_buf_append(*outbuf, "\"", 1);
str_buf_append(*outbuf, datebuf, datelen);
str_buf_append(*outbuf, "\"", 1);
} else {
PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n");
return PLIST_ERR_FORMAT;
}
break;
case PLIST_UID:
if (coerce) {
val = (char*)malloc(64);
if (node_data->length == 16) {
val_len = snprintf(val, 64, "%" PRIu64, node_data->intval);
} else {
val_len = snprintf(val, 64, "%" PRIi64, node_data->intval);
}
str_buf_append(*outbuf, val, val_len);
free(val);
} else {
PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n");
return PLIST_ERR_FORMAT;
}
break;
default:
return PLIST_ERR_UNKNOWN;
}
return PLIST_ERR_SUCCESS;
}
static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, int coerce, hashtable_t *visited)
{
plist_data_t data;
if (!node) {
return PLIST_ERR_INVALID_ARG;
}
if (depth > PLIST_MAX_NESTING_DEPTH) {
PLIST_JSON_WRITE_ERR("maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH);
return PLIST_ERR_MAX_NESTING;
}
if (hash_table_lookup(visited, node)) {
PLIST_JSON_WRITE_ERR("circular reference detected\n");
return PLIST_ERR_CIRCULAR_REF;
}
hash_table_insert(visited, node, (void*)1);
data = plist_get_data(node);
if (node->children) {
node_t ch;
unsigned int n_children = node_n_children(node);
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
plist_err_t res = _node_estimate_size(ch, size, depth + 1, prettify, coerce, visited);
if (res < 0) {
return res;
}
}
switch (data->type) {
case PLIST_DICT:
*size += 2; *size += n_children-1; if (prettify) {
*size += n_children; *size += (uint64_t)n_children * (depth+1); *size += 1; }
break;
case PLIST_ARRAY:
*size += 2; *size += n_children-1; if (prettify) {
*size += n_children; *size += (uint64_t)n_children * ((depth+1)<<1); *size += 1; }
break;
default:
break;
}
if (prettify)
*size += (depth << 1); } else {
switch (data->type) {
case PLIST_STRING:
case PLIST_KEY:
*size += data->length;
*size += 2;
break;
case PLIST_INT:
if (data->length == 16) {
*size += num_digits_u(data->intval);
} else {
*size += num_digits_i((int64_t)data->intval);
}
break;
case PLIST_REAL:
*size += dtostr(NULL, 0, data->realval);
break;
case PLIST_BOOLEAN:
*size += ((data->boolval) ? 4 : 5);
break;
case PLIST_NULL:
*size += 4;
break;
case PLIST_DICT:
case PLIST_ARRAY:
*size += 2;
break;
case PLIST_DATA:
if (coerce) {
*size += 2 + ((data->length + 2) / 3) * 4;
} else {
PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n");
return PLIST_ERR_FORMAT;
}
break;
case PLIST_DATE:
if (coerce) {
*size += 24;
} else {
PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n");
return PLIST_ERR_FORMAT;
}
break;
case PLIST_UID:
if (coerce) {
if (data->length == 16) {
*size += num_digits_u(data->intval);
} else {
*size += num_digits_i((int64_t)data->intval);
}
} else {
PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n");
return PLIST_ERR_FORMAT;
}
break;
default:
PLIST_JSON_WRITE_ERR("invalid node type encountered\n");
return PLIST_ERR_UNKNOWN;
}
}
return PLIST_ERR_SUCCESS;
}
static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, int coerce)
{
hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
if (!visited) return PLIST_ERR_NO_MEM;
plist_err_t err = _node_estimate_size(node, size, depth, prettify, coerce, visited);
hash_table_destroy(visited);
return err;
}
plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify)
{
plist_write_options_t opts = prettify ? PLIST_OPT_NONE : PLIST_OPT_COMPACT;
return plist_to_json_with_options(plist, plist_json, length, opts);
}
plist_err_t plist_to_json_with_options(plist_t plist, char **plist_json, uint32_t* length, plist_write_options_t options)
{
uint64_t size = 0;
plist_err_t res;
if (!plist || !plist_json || !length) {
return PLIST_ERR_INVALID_ARG;
}
if (!PLIST_IS_DICT(plist) && !PLIST_IS_ARRAY(plist)) {
PLIST_JSON_WRITE_ERR("plist data is not valid for JSON format\n");
return PLIST_ERR_FORMAT;
}
int prettify = !(options & PLIST_OPT_COMPACT);
int coerce = options & PLIST_OPT_COERCE;
res = node_estimate_size((node_t)plist, &size, 0, prettify, coerce);
if (res < 0) {
return res;
}
strbuf_t *outbuf = str_buf_new(size);
if (!outbuf) {
PLIST_JSON_WRITE_ERR("Could not allocate output buffer\n");
return PLIST_ERR_NO_MEM;
}
res = node_to_json((node_t)plist, &outbuf, 0, prettify, coerce);
if (res < 0) {
str_buf_free(outbuf);
*plist_json = NULL;
*length = 0;
return res;
}
if (prettify) {
str_buf_append(outbuf, "\n", 1);
}
str_buf_append(outbuf, "\0", 1);
*plist_json = (char*)outbuf->data;
*length = outbuf->len - 1;
outbuf->data = NULL;
str_buf_free(outbuf);
return PLIST_ERR_SUCCESS;
}
typedef struct {
jsmntok_t* tokens;
int count;
plist_err_t err;
} jsmntok_info_t;
static int64_t parse_decimal(const char* str, const char* str_end, char** endp)
{
const uint64_t po10i_limit = INT64_MAX / 10;
uint64_t MAX = INT64_MAX;
uint64_t x = 0;
int is_neg = 0;
*endp = (char*)str;
if (str[0] == '-') {
is_neg = 1;
(*endp)++;
} else if (str[0] == '+') {
(*endp)++;
}
if (is_neg) {
MAX++;
}
while (*endp < str_end && isdigit(**endp)) {
if (x > po10i_limit) {
x = MAX;
break;
}
x = x * 10;
unsigned int add = (**endp - '0');
if (x + add > MAX) {
x = MAX;
break;
}
x += add;
(*endp)++;
}
while (*endp < str_end && isdigit(**endp)) (*endp)++;
int64_t result = x;
if (is_neg) {
if (x == MAX) {
result = INT64_MIN;
} else {
result = -(int64_t)x;
}
}
return result;
}
static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index)
{
if (ti->tokens[*index].type != JSMN_PRIMITIVE) {
PLIST_JSON_ERR("%s: token type != JSMN_PRIMITIVE\n", __func__);
return NULL;
}
plist_t val = NULL;
const char* str_val = js + ti->tokens[*index].start;
const char* str_end = js + ti->tokens[*index].end;
size_t str_len = ti->tokens[*index].end - ti->tokens[*index].start;
if (!strncmp("false", str_val, str_len)) {
val = plist_new_bool(0);
} else if (!strncmp("true", str_val, str_len)) {
val = plist_new_bool(1);
} else if (!strncmp("null", str_val, str_len)) {
plist_data_t data = plist_new_plist_data();
if (!data) {
PLIST_JSON_ERR("%s: failed to allocate plist data\n", __func__);
return NULL;
}
data->type = PLIST_NULL;
val = plist_new_node(data);
} else if (isdigit(str_val[0]) || (str_val[0] == '-' && str_val+1 < str_end && isdigit(str_val[1]))) {
char* endp = (char*)str_val;
int is_neg = (str_val[0] == '-');
int64_t intpart = parse_decimal(str_val, str_end, &endp);
if (endp >= str_end) {
if (is_neg || intpart <= INT64_MAX) {
val = plist_new_int(intpart);
} else {
val = plist_new_uint((uint64_t)intpart);
}
} else if ((*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) || ((*endp == 'e' || *endp == 'E') && endp+1 < str_end && (isdigit(*(endp+1)) || (((*(endp+1) == '-') || (*(endp+1) == '+')) && endp+2 < str_end && isdigit(*(endp+2)))))) {
double dval = (double)intpart;
char* fendp = endp;
int err = 0;
do {
if (*endp == '.') {
fendp++;
double frac = 0;
double p = 0.1;
while (fendp < str_end && isdigit(*fendp)) {
frac = frac + (*fendp - '0') * p;
p *= 0.1;
fendp++;
}
if (is_neg) {
dval -= frac;
} else {
dval += frac;
}
}
if (fendp >= str_end) {
break;
}
if (fendp+1 < str_end && (*fendp == 'e' || *fendp == 'E') && (isdigit(*(fendp+1)) || (((*(fendp+1) == '-') || (*(fendp+1) == '+')) && fendp+2 < str_end && isdigit(*(fendp+2))))) {
int64_t exp = parse_decimal(fendp+1, str_end, &fendp);
dval = dval * pow(10, (double)exp);
} else {
PLIST_JSON_ERR("%s: invalid character at offset %d when parsing floating point value\n", __func__, (int)(fendp - js));
err++;
}
} while (0);
if (!err) {
if (isinf(dval) || isnan(dval)) {
PLIST_JSON_ERR("%s: unrepresentable floating point value at offset %d when parsing numerical value\n", __func__, (int)(str_val - js));
} else {
val = plist_new_real(dval);
}
}
} else {
PLIST_JSON_ERR("%s: invalid character at offset %d when parsing numerical value\n", __func__, (int)(endp - js));
}
} else {
PLIST_JSON_ERR("%s: invalid primitive value '%.*s' encountered\n", __func__, (int)str_len, str_val);
}
if (!val) {
PLIST_JSON_ERR("%s: failed to create node\n", __func__);
return NULL;
}
(*index)++;
return val;
}
static char* unescape_string(const char* str_val, size_t str_len, size_t *new_len)
{
char* strval = strndup(str_val, str_len);
if (!strval) return NULL;
size_t i = 0;
while (i < str_len) {
if (strval[i] == '\\' && i < str_len-1) {
switch (strval[i+1]) {
case '\"': case '/' : case '\\' : case 'b' :
case 'f' : case 'r' : case 'n' : case 't' :
memmove(strval+i, strval+i+1, str_len - (i+1));
str_len--;
switch (strval[i]) {
case 'b':
strval[i] = '\b';
break;
case 'f':
strval[i] = '\f';
break;
case 'r':
strval[i] = '\r';
break;
case 'n':
strval[i] = '\n';
break;
case 't':
strval[i] = '\t';
break;
default:
break;
}
break;
case 'u': {
unsigned int val = 0;
if (str_len-(i+2) < 4) {
PLIST_JSON_ERR("%s: invalid escape sequence '%s' (too short)\n", __func__, strval+i);
free(strval);
return NULL;
}
if (!(isxdigit(strval[i+2]) && isxdigit(strval[i+3]) && isxdigit(strval[i+4]) && isxdigit(strval[i+5])) || sscanf(strval+i+2, "%04x", &val) != 1) {
PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 6, strval+i);
free(strval);
return NULL;
}
int bytelen = 0;
if (val >= 0x800) {
strval[i] = (char)(0xE0 + ((val >> 12) & 0xF));
strval[i+1] = (char)(0x80 + ((val >> 6) & 0x3F));
strval[i+2] = (char)(0x80 + (val & 0x3F));
bytelen = 3;
} else if (val >= 0x80) {
strval[i] = (char)(0xC0 + ((val >> 6) & 0x1F));
strval[i+1] = (char)(0x80 + (val & 0x3F));
bytelen = 2;
} else {
strval[i] = (char)(val & 0x7F);
bytelen = 1;
}
memmove(strval+i+bytelen, strval+i+6, str_len - (i+5));
str_len -= (6-bytelen);
} break;
default:
PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 2, strval+i);
free(strval);
return NULL;
}
}
i++;
}
strval[str_len] = '\0';
if (new_len) {
*new_len = str_len;
}
return strval;
}
static plist_t parse_string(const char* js, jsmntok_info_t* ti, int* index)
{
if (ti->tokens[*index].type != JSMN_STRING) {
PLIST_JSON_ERR("%s: token type != JSMN_STRING\n", __func__);
return NULL;
}
size_t str_len = 0; ;
char* strval = unescape_string(js + ti->tokens[*index].start, ti->tokens[*index].end - ti->tokens[*index].start, &str_len);
if (!strval) {
return NULL;
}
plist_t node;
plist_data_t data = plist_new_plist_data();
if (!data) {
free(strval);
PLIST_JSON_ERR("%s: failed to allocate plist data\n", __func__);
return NULL;
}
data->type = PLIST_STRING;
data->strval = strval;
data->length = str_len;
node = plist_new_node(data);
if (!node) {
plist_free_data(data);
PLIST_JSON_ERR("%s: failed to create node\n", __func__);
return NULL;
}
(*index)++;
return node;
}
static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth);
static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth)
{
if (ti->tokens[*index].type != JSMN_ARRAY) {
PLIST_JSON_ERR("%s: token type != JSMN_ARRAY\n", __func__);
ti->err = PLIST_ERR_PARSE;
return NULL;
}
if (depth > PLIST_MAX_NESTING_DEPTH) {
PLIST_JSON_ERR("%s: maximum nesting depth (%u) exceeded\n", __func__, (unsigned)PLIST_MAX_NESTING_DEPTH);
ti->err = PLIST_ERR_MAX_NESTING;
return NULL;
}
plist_t arr = plist_new_array();
if (!arr) {
PLIST_JSON_ERR("%s: failed to create array node\n", __func__);
ti->err = PLIST_ERR_NO_MEM;
return NULL;
}
size_t num_tokens = ti->tokens[*index].size;
size_t num;
int j = (*index)+1;
for (num = 0; num < num_tokens; num++) {
if (j >= ti->count) {
PLIST_JSON_ERR("%s: token index out of valid range\n", __func__);
plist_free(arr);
ti->err = PLIST_ERR_PARSE;
return NULL;
}
plist_t val = NULL;
switch (ti->tokens[j].type) {
case JSMN_OBJECT:
val = parse_object(js, ti, &j, depth+1);
break;
case JSMN_ARRAY:
val = parse_array(js, ti, &j, depth+1);
break;
case JSMN_STRING:
val = parse_string(js, ti, &j);
break;
case JSMN_PRIMITIVE:
val = parse_primitive(js, ti, &j);
break;
default:
break;
}
if (val) {
plist_array_append_item(arr, val);
if (((node_t)val)->parent == NULL) {
plist_free(val);
plist_free(arr);
ti->err = PLIST_ERR_NO_MEM;
return NULL;
}
} else {
plist_free(arr);
ti->err = PLIST_ERR_PARSE;
return NULL;
}
}
*(index) = j;
return arr;
}
static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth)
{
if (ti->tokens[*index].type != JSMN_OBJECT) {
PLIST_JSON_ERR("%s: token type != JSMN_OBJECT\n", __func__);
ti->err = PLIST_ERR_PARSE;
return NULL;
}
if (depth > PLIST_MAX_NESTING_DEPTH) {
PLIST_JSON_ERR("%s: maximum nesting depth (%u) exceeded\n", __func__, (unsigned)PLIST_MAX_NESTING_DEPTH);
ti->err = PLIST_ERR_MAX_NESTING;
return NULL;
}
size_t num_tokens = ti->tokens[*index].size;
size_t num;
int j = (*index)+1;
if (num_tokens % 2 != 0) {
PLIST_JSON_ERR("%s: number of children must be even\n", __func__);
ti->err = PLIST_ERR_PARSE;
return NULL;
}
plist_t obj = plist_new_dict();
if (!obj) {
PLIST_JSON_ERR("%s: failed to create dict node\n", __func__);
ti->err = PLIST_ERR_NO_MEM;
return NULL;
}
for (num = 0; num < num_tokens; num++) {
if (j+1 >= ti->count) {
PLIST_JSON_ERR("%s: token index out of valid range\n", __func__);
plist_free(obj);
ti->err = PLIST_ERR_PARSE;
return NULL;
}
if (ti->tokens[j].type == JSMN_STRING) {
char* key = unescape_string(js + ti->tokens[j].start, ti->tokens[j].end - ti->tokens[j].start, NULL);
if (!key) {
plist_free(obj);
ti->err = PLIST_ERR_PARSE;
return NULL;
}
plist_t val = NULL;
j++;
num++;
switch (ti->tokens[j].type) {
case JSMN_OBJECT:
val = parse_object(js, ti, &j, depth+1);
break;
case JSMN_ARRAY:
val = parse_array(js, ti, &j, depth+1);
break;
case JSMN_STRING:
val = parse_string(js, ti, &j);
break;
case JSMN_PRIMITIVE:
val = parse_primitive(js, ti, &j);
break;
default:
break;
}
if (val) {
plist_dict_set_item(obj, key, val);
if (((node_t)val)->parent == NULL) {
plist_free(val);
free(key);
plist_free(obj);
ti->err = PLIST_ERR_NO_MEM;
return NULL;
}
} else {
free(key);
plist_free(obj);
ti->err = PLIST_ERR_PARSE;
return NULL;
}
free(key);
} else {
PLIST_JSON_ERR("%s: keys must be of type STRING\n", __func__);
plist_free(obj);
ti->err = PLIST_ERR_PARSE;
return NULL;
}
}
(*index) = j;
return obj;
}
plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist)
{
if (!plist) {
return PLIST_ERR_INVALID_ARG;
}
*plist = NULL;
if (!json || (length == 0)) {
return PLIST_ERR_INVALID_ARG;
}
jsmn_parser parser;
jsmn_init(&parser);
unsigned int maxtoks = 256;
unsigned int curtoks = 0;
int r = 0;
jsmntok_t *tokens = NULL;
do {
jsmntok_t* newtokens = (jsmntok_t*)realloc(tokens, sizeof(jsmntok_t)*maxtoks);
if (!newtokens) {
free(tokens);
PLIST_JSON_ERR("%s: Out of memory\n", __func__);
return PLIST_ERR_NO_MEM;
}
memset((unsigned char*)newtokens + sizeof(jsmntok_t)*curtoks, '\0', sizeof(jsmntok_t)*(maxtoks-curtoks));
tokens = newtokens;
curtoks = maxtoks;
r = jsmn_parse(&parser, json, length, tokens, maxtoks);
if (r == JSMN_ERROR_NOMEM) {
if (maxtoks > (unsigned int)INT_MAX - 16) {
free(tokens);
return PLIST_ERR_NO_MEM;
}
maxtoks+=16;
continue;
} else if (r < 0) {
break;
}
} while (r == JSMN_ERROR_NOMEM);
switch(r) {
case JSMN_ERROR_NOMEM:
PLIST_JSON_ERR("%s: Out of memory...\n", __func__);
free(tokens);
return PLIST_ERR_NO_MEM;
case JSMN_ERROR_INVAL:
PLIST_JSON_ERR("%s: Invalid character inside JSON string\n", __func__);
free(tokens);
return PLIST_ERR_PARSE;
case JSMN_ERROR_PART:
PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__);
free(tokens);
return PLIST_ERR_PARSE;
case JSMN_ERROR_LIMIT:
PLIST_JSON_ERR("%s: Input data too large\n", __func__);
free(tokens);
return PLIST_ERR_PARSE;
default:
break;
}
int startindex = 0;
jsmntok_info_t ti = { tokens, parser.toknext, PLIST_ERR_SUCCESS };
switch (tokens[startindex].type) {
case JSMN_PRIMITIVE:
*plist = parse_primitive(json, &ti, &startindex);
break;
case JSMN_STRING:
*plist = parse_string(json, &ti, &startindex);
break;
case JSMN_ARRAY:
*plist = parse_array(json, &ti, &startindex, 0);
break;
case JSMN_OBJECT:
*plist = parse_object(json, &ti, &startindex, 0);
break;
default:
break;
}
free(tokens);
if (!*plist) {
return (ti.err != PLIST_ERR_SUCCESS) ? ti.err : PLIST_ERR_PARSE;
}
return PLIST_ERR_SUCCESS;
}