|
// retoor <retoor@molodetz.nl>
|
|
|
|
/*
|
|
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
#include <float.h>
|
|
|
|
#include "cJSON.h"
|
|
|
|
typedef struct {
|
|
const unsigned char *json;
|
|
size_t position;
|
|
} error;
|
|
static error global_error = { NULL, 0 };
|
|
|
|
const char *cJSON_GetErrorPtr(void) {
|
|
return (const char*)(global_error.json + global_error.position);
|
|
}
|
|
|
|
static void *(*global_malloc)(size_t sz) = malloc;
|
|
static void (*global_free)(void *ptr) = free;
|
|
|
|
void *cJSON_malloc(size_t size) {
|
|
return global_malloc(size);
|
|
}
|
|
|
|
void cJSON_free(void *object) {
|
|
global_free(object);
|
|
}
|
|
|
|
void cJSON_InitHooks(cJSON_Hooks* hooks) {
|
|
if (hooks == NULL) {
|
|
global_malloc = malloc;
|
|
global_free = free;
|
|
return;
|
|
}
|
|
global_malloc = (hooks->malloc_fn != NULL) ? hooks->malloc_fn : malloc;
|
|
global_free = (hooks->free_fn != NULL) ? hooks->free_fn : free;
|
|
}
|
|
|
|
static cJSON *cJSON_New_Item(void) {
|
|
cJSON* node = (cJSON*)global_malloc(sizeof(cJSON));
|
|
if (node) {
|
|
memset(node, 0, sizeof(cJSON));
|
|
}
|
|
return node;
|
|
}
|
|
|
|
void cJSON_Delete(cJSON *item) {
|
|
cJSON *next = NULL;
|
|
while (item != NULL) {
|
|
next = item->next;
|
|
if (!(item->type & cJSON_IsReference) && (item->child != NULL)) {
|
|
cJSON_Delete(item->child);
|
|
}
|
|
if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) {
|
|
global_free(item->valuestring);
|
|
}
|
|
if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) {
|
|
global_free(item->string);
|
|
}
|
|
global_free(item);
|
|
item = next;
|
|
}
|
|
}
|
|
|
|
static const unsigned char *skip_whitespace(const unsigned char *in) {
|
|
while (in && *in && (*in <= 32)) {
|
|
in++;
|
|
}
|
|
return in;
|
|
}
|
|
|
|
static cJSON *parse_value(const unsigned char **value);
|
|
static cJSON *parse_string(const unsigned char **value);
|
|
static cJSON *parse_number(const unsigned char **value);
|
|
static cJSON *parse_array(const unsigned char **value);
|
|
static cJSON *parse_object(const unsigned char **value);
|
|
|
|
static unsigned char *print_value(const cJSON *item, int depth, int fmt);
|
|
static unsigned char *print_string(const cJSON *item);
|
|
static unsigned char *print_number(const cJSON *item);
|
|
static unsigned char *print_array(const cJSON *item, int depth, int fmt);
|
|
static unsigned char *print_object(const cJSON *item, int depth, int fmt);
|
|
|
|
static cJSON *parse_string(const unsigned char **value) {
|
|
const unsigned char *ptr = *value + 1;
|
|
const unsigned char *end_ptr = ptr;
|
|
size_t len = 0;
|
|
cJSON *item = cJSON_New_Item();
|
|
|
|
if (item == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (**value != '\"') {
|
|
global_error.json = *value;
|
|
global_error.position = 0;
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
|
|
while (*end_ptr != '\"' && *end_ptr) {
|
|
if (*end_ptr == '\\') {
|
|
end_ptr++;
|
|
}
|
|
end_ptr++;
|
|
}
|
|
|
|
len = (size_t)(end_ptr - ptr);
|
|
item->valuestring = (char*)global_malloc(len + 1);
|
|
if (item->valuestring == NULL) {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
|
|
const unsigned char *src = ptr;
|
|
char *dst = item->valuestring;
|
|
while (src < end_ptr) {
|
|
if (*src != '\\') {
|
|
*dst++ = (char)*src++;
|
|
} else {
|
|
src++;
|
|
switch (*src) {
|
|
case 'b': *dst++ = '\b'; break;
|
|
case 'f': *dst++ = '\f'; break;
|
|
case 'n': *dst++ = '\n'; break;
|
|
case 'r': *dst++ = '\r'; break;
|
|
case 't': *dst++ = '\t'; break;
|
|
case '\"': *dst++ = '\"'; break;
|
|
case '\\': *dst++ = '\\'; break;
|
|
case '/': *dst++ = '/'; break;
|
|
case 'u': {
|
|
unsigned int uc = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
src++;
|
|
uc <<= 4;
|
|
if (*src >= '0' && *src <= '9') uc += *src - '0';
|
|
else if (*src >= 'a' && *src <= 'f') uc += 10 + *src - 'a';
|
|
else if (*src >= 'A' && *src <= 'F') uc += 10 + *src - 'A';
|
|
}
|
|
if (uc < 0x80) {
|
|
*dst++ = (char)uc;
|
|
} else if (uc < 0x800) {
|
|
*dst++ = (char)(0xC0 | (uc >> 6));
|
|
*dst++ = (char)(0x80 | (uc & 0x3F));
|
|
} else {
|
|
*dst++ = (char)(0xE0 | (uc >> 12));
|
|
*dst++ = (char)(0x80 | ((uc >> 6) & 0x3F));
|
|
*dst++ = (char)(0x80 | (uc & 0x3F));
|
|
}
|
|
break;
|
|
}
|
|
default: *dst++ = (char)*src; break;
|
|
}
|
|
src++;
|
|
}
|
|
}
|
|
*dst = '\0';
|
|
|
|
item->type = cJSON_String;
|
|
*value = end_ptr + 1;
|
|
return item;
|
|
}
|
|
|
|
static cJSON *parse_number(const unsigned char **value) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
double n = 0;
|
|
int sign = 1;
|
|
int scale = 0;
|
|
int signsubscale = 1;
|
|
int subscale = 0;
|
|
const unsigned char *ptr = *value;
|
|
|
|
if (*ptr == '-') {
|
|
sign = -1;
|
|
ptr++;
|
|
}
|
|
|
|
if (*ptr == '0') {
|
|
ptr++;
|
|
} else {
|
|
while (*ptr >= '0' && *ptr <= '9') {
|
|
n = (n * 10.0) + (*ptr++ - '0');
|
|
}
|
|
}
|
|
|
|
if (*ptr == '.') {
|
|
ptr++;
|
|
while (*ptr >= '0' && *ptr <= '9') {
|
|
n = (n * 10.0) + (*ptr++ - '0');
|
|
scale--;
|
|
}
|
|
}
|
|
|
|
if (*ptr == 'e' || *ptr == 'E') {
|
|
ptr++;
|
|
if (*ptr == '+') {
|
|
ptr++;
|
|
} else if (*ptr == '-') {
|
|
signsubscale = -1;
|
|
ptr++;
|
|
}
|
|
while (*ptr >= '0' && *ptr <= '9') {
|
|
subscale = (subscale * 10) + (*ptr++ - '0');
|
|
}
|
|
}
|
|
|
|
n = sign * n * pow(10.0, (double)(scale + signsubscale * subscale));
|
|
|
|
item->valuedouble = n;
|
|
item->valueint = (int)n;
|
|
item->type = cJSON_Number;
|
|
*value = ptr;
|
|
return item;
|
|
}
|
|
|
|
static cJSON *parse_array(const unsigned char **value) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item == NULL) {
|
|
return NULL;
|
|
}
|
|
item->type = cJSON_Array;
|
|
|
|
*value = skip_whitespace(*value + 1);
|
|
if (**value == ']') {
|
|
(*value)++;
|
|
return item;
|
|
}
|
|
|
|
cJSON *child = parse_value(value);
|
|
if (child == NULL) {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
item->child = child;
|
|
|
|
while (**value == ',') {
|
|
(*value)++;
|
|
cJSON *new_item = parse_value(value);
|
|
if (new_item == NULL) {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
child->next = new_item;
|
|
new_item->prev = child;
|
|
child = new_item;
|
|
}
|
|
|
|
*value = skip_whitespace(*value);
|
|
if (**value != ']') {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
(*value)++;
|
|
return item;
|
|
}
|
|
|
|
static cJSON *parse_object(const unsigned char **value) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item == NULL) {
|
|
return NULL;
|
|
}
|
|
item->type = cJSON_Object;
|
|
|
|
*value = skip_whitespace(*value + 1);
|
|
if (**value == '}') {
|
|
(*value)++;
|
|
return item;
|
|
}
|
|
|
|
cJSON *child = parse_string(value);
|
|
if (child == NULL) {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
item->child = child;
|
|
child->string = child->valuestring;
|
|
child->valuestring = NULL;
|
|
|
|
*value = skip_whitespace(*value);
|
|
if (**value != ':') {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
(*value)++;
|
|
|
|
cJSON *val = parse_value(value);
|
|
if (val == NULL) {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
child->type = val->type;
|
|
child->valuestring = val->valuestring;
|
|
child->valueint = val->valueint;
|
|
child->valuedouble = val->valuedouble;
|
|
child->child = val->child;
|
|
val->valuestring = NULL;
|
|
val->child = NULL;
|
|
cJSON_Delete(val);
|
|
|
|
while (**value == ',') {
|
|
(*value)++;
|
|
*value = skip_whitespace(*value);
|
|
|
|
cJSON *new_item = parse_string(value);
|
|
if (new_item == NULL) {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
child->next = new_item;
|
|
new_item->prev = child;
|
|
child = new_item;
|
|
child->string = child->valuestring;
|
|
child->valuestring = NULL;
|
|
|
|
*value = skip_whitespace(*value);
|
|
if (**value != ':') {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
(*value)++;
|
|
|
|
val = parse_value(value);
|
|
if (val == NULL) {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
child->type = val->type;
|
|
child->valuestring = val->valuestring;
|
|
child->valueint = val->valueint;
|
|
child->valuedouble = val->valuedouble;
|
|
child->child = val->child;
|
|
val->valuestring = NULL;
|
|
val->child = NULL;
|
|
cJSON_Delete(val);
|
|
}
|
|
|
|
*value = skip_whitespace(*value);
|
|
if (**value != '}') {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
(*value)++;
|
|
return item;
|
|
}
|
|
|
|
static cJSON *parse_value(const unsigned char **value) {
|
|
*value = skip_whitespace(*value);
|
|
if (*value == NULL || **value == '\0') {
|
|
return NULL;
|
|
}
|
|
|
|
if (**value == '\"') {
|
|
return parse_string(value);
|
|
}
|
|
if (**value == '-' || (**value >= '0' && **value <= '9')) {
|
|
return parse_number(value);
|
|
}
|
|
if (**value == '[') {
|
|
return parse_array(value);
|
|
}
|
|
if (**value == '{') {
|
|
return parse_object(value);
|
|
}
|
|
if (strncmp((const char*)*value, "null", 4) == 0) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item) item->type = cJSON_NULL;
|
|
*value += 4;
|
|
return item;
|
|
}
|
|
if (strncmp((const char*)*value, "false", 5) == 0) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item) item->type = cJSON_False;
|
|
*value += 5;
|
|
return item;
|
|
}
|
|
if (strncmp((const char*)*value, "true", 4) == 0) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item) item->type = cJSON_True;
|
|
*value += 4;
|
|
return item;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
cJSON *cJSON_Parse(const char *value) {
|
|
return cJSON_ParseWithLength(value, strlen(value));
|
|
}
|
|
|
|
cJSON *cJSON_ParseWithLength(const char *value, size_t buffer_length) {
|
|
if (value == NULL || buffer_length == 0) {
|
|
return NULL;
|
|
}
|
|
const unsigned char *ptr = (const unsigned char*)value;
|
|
return parse_value(&ptr);
|
|
}
|
|
|
|
static unsigned char *print_number(const cJSON *item) {
|
|
unsigned char *output = NULL;
|
|
double d = item->valuedouble;
|
|
|
|
if (d == 0) {
|
|
output = (unsigned char*)global_malloc(2);
|
|
if (output) strcpy((char*)output, "0");
|
|
} else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) {
|
|
output = (unsigned char*)global_malloc(21);
|
|
if (output) sprintf((char*)output, "%d", item->valueint);
|
|
} else {
|
|
output = (unsigned char*)global_malloc(64);
|
|
if (output) {
|
|
if (fabs(floor(d) - d) <= DBL_EPSILON && fabs(d) < 1.0e60) {
|
|
sprintf((char*)output, "%.0f", d);
|
|
} else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) {
|
|
sprintf((char*)output, "%e", d);
|
|
} else {
|
|
sprintf((char*)output, "%f", d);
|
|
}
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
static unsigned char *print_string_ptr(const char *str) {
|
|
if (str == NULL) {
|
|
unsigned char *out = (unsigned char*)global_malloc(3);
|
|
if (out) strcpy((char*)out, "\"\"");
|
|
return out;
|
|
}
|
|
|
|
size_t len = 0;
|
|
const char *ptr = str;
|
|
while (*ptr) {
|
|
if (*ptr == '\"' || *ptr == '\\' || *ptr == '\b' || *ptr == '\f' ||
|
|
*ptr == '\n' || *ptr == '\r' || *ptr == '\t') {
|
|
len += 2;
|
|
} else if ((unsigned char)*ptr < 32) {
|
|
len += 6;
|
|
} else {
|
|
len++;
|
|
}
|
|
ptr++;
|
|
}
|
|
|
|
unsigned char *out = (unsigned char*)global_malloc(len + 3);
|
|
if (out == NULL) return NULL;
|
|
|
|
unsigned char *dst = out;
|
|
*dst++ = '\"';
|
|
ptr = str;
|
|
while (*ptr) {
|
|
if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\') {
|
|
*dst++ = (unsigned char)*ptr++;
|
|
} else {
|
|
*dst++ = '\\';
|
|
switch (*ptr++) {
|
|
case '\\': *dst++ = '\\'; break;
|
|
case '\"': *dst++ = '\"'; break;
|
|
case '\b': *dst++ = 'b'; break;
|
|
case '\f': *dst++ = 'f'; break;
|
|
case '\n': *dst++ = 'n'; break;
|
|
case '\r': *dst++ = 'r'; break;
|
|
case '\t': *dst++ = 't'; break;
|
|
default:
|
|
sprintf((char*)dst, "u%04x", (unsigned char)*(ptr - 1));
|
|
dst += 5;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
*dst++ = '\"';
|
|
*dst = '\0';
|
|
return out;
|
|
}
|
|
|
|
static unsigned char *print_string(const cJSON *item) {
|
|
return print_string_ptr(item->valuestring);
|
|
}
|
|
|
|
static unsigned char *print_array(const cJSON *item, int depth, int fmt) {
|
|
size_t len = 3;
|
|
cJSON *child = item->child;
|
|
int numentries = 0;
|
|
|
|
while (child) {
|
|
numentries++;
|
|
child = child->next;
|
|
}
|
|
|
|
if (numentries == 0) {
|
|
unsigned char *out = (unsigned char*)global_malloc(3);
|
|
if (out) strcpy((char*)out, "[]");
|
|
return out;
|
|
}
|
|
|
|
unsigned char **entries = (unsigned char**)global_malloc(numentries * sizeof(unsigned char*));
|
|
if (entries == NULL) return NULL;
|
|
memset(entries, 0, numentries * sizeof(unsigned char*));
|
|
|
|
child = item->child;
|
|
for (int i = 0; i < numentries; i++) {
|
|
entries[i] = print_value(child, depth + 1, fmt);
|
|
if (entries[i]) {
|
|
len += strlen((char*)entries[i]) + 2 + (fmt ? (depth + 1) : 0);
|
|
}
|
|
child = child->next;
|
|
}
|
|
|
|
unsigned char *out = (unsigned char*)global_malloc(len + (fmt ? numentries : 0) + 1);
|
|
if (out == NULL) {
|
|
for (int i = 0; i < numentries; i++) {
|
|
if (entries[i]) global_free(entries[i]);
|
|
}
|
|
global_free(entries);
|
|
return NULL;
|
|
}
|
|
|
|
*out = '[';
|
|
unsigned char *ptr = out + 1;
|
|
if (fmt) *ptr++ = '\n';
|
|
|
|
for (int i = 0; i < numentries; i++) {
|
|
if (fmt) {
|
|
for (int j = 0; j <= depth; j++) *ptr++ = '\t';
|
|
}
|
|
if (entries[i]) {
|
|
size_t elen = strlen((char*)entries[i]);
|
|
memcpy(ptr, entries[i], elen);
|
|
ptr += elen;
|
|
global_free(entries[i]);
|
|
}
|
|
if (i < numentries - 1) *ptr++ = ',';
|
|
if (fmt) *ptr++ = '\n';
|
|
}
|
|
|
|
if (fmt) {
|
|
for (int i = 0; i < depth; i++) *ptr++ = '\t';
|
|
}
|
|
*ptr++ = ']';
|
|
*ptr = '\0';
|
|
|
|
global_free(entries);
|
|
return out;
|
|
}
|
|
|
|
static unsigned char *print_object(const cJSON *item, int depth, int fmt) {
|
|
cJSON *child = item->child;
|
|
int numentries = 0;
|
|
|
|
while (child) {
|
|
numentries++;
|
|
child = child->next;
|
|
}
|
|
|
|
if (numentries == 0) {
|
|
unsigned char *out = (unsigned char*)global_malloc(fmt ? depth + 4 : 3);
|
|
if (out) strcpy((char*)out, "{}");
|
|
return out;
|
|
}
|
|
|
|
unsigned char **names = (unsigned char**)global_malloc(numentries * sizeof(unsigned char*));
|
|
unsigned char **entries = (unsigned char**)global_malloc(numentries * sizeof(unsigned char*));
|
|
if (names == NULL || entries == NULL) {
|
|
if (names) global_free(names);
|
|
if (entries) global_free(entries);
|
|
return NULL;
|
|
}
|
|
memset(names, 0, numentries * sizeof(unsigned char*));
|
|
memset(entries, 0, numentries * sizeof(unsigned char*));
|
|
|
|
size_t len = 3;
|
|
child = item->child;
|
|
for (int i = 0; i < numentries; i++) {
|
|
names[i] = print_string_ptr(child->string);
|
|
entries[i] = print_value(child, depth + 1, fmt);
|
|
if (names[i] && entries[i]) {
|
|
len += strlen((char*)names[i]) + strlen((char*)entries[i]) + 4 + (fmt ? (depth + 2) : 0);
|
|
}
|
|
child = child->next;
|
|
}
|
|
|
|
unsigned char *out = (unsigned char*)global_malloc(len + (fmt ? numentries * 2 : 0) + 1);
|
|
if (out == NULL) {
|
|
for (int i = 0; i < numentries; i++) {
|
|
if (names[i]) global_free(names[i]);
|
|
if (entries[i]) global_free(entries[i]);
|
|
}
|
|
global_free(names);
|
|
global_free(entries);
|
|
return NULL;
|
|
}
|
|
|
|
*out = '{';
|
|
unsigned char *ptr = out + 1;
|
|
if (fmt) *ptr++ = '\n';
|
|
|
|
for (int i = 0; i < numentries; i++) {
|
|
if (fmt) {
|
|
for (int j = 0; j <= depth; j++) *ptr++ = '\t';
|
|
}
|
|
if (names[i]) {
|
|
size_t nlen = strlen((char*)names[i]);
|
|
memcpy(ptr, names[i], nlen);
|
|
ptr += nlen;
|
|
global_free(names[i]);
|
|
}
|
|
*ptr++ = ':';
|
|
if (fmt) *ptr++ = ' ';
|
|
if (entries[i]) {
|
|
size_t elen = strlen((char*)entries[i]);
|
|
memcpy(ptr, entries[i], elen);
|
|
ptr += elen;
|
|
global_free(entries[i]);
|
|
}
|
|
if (i < numentries - 1) *ptr++ = ',';
|
|
if (fmt) *ptr++ = '\n';
|
|
}
|
|
|
|
if (fmt) {
|
|
for (int i = 0; i < depth; i++) *ptr++ = '\t';
|
|
}
|
|
*ptr++ = '}';
|
|
*ptr = '\0';
|
|
|
|
global_free(names);
|
|
global_free(entries);
|
|
return out;
|
|
}
|
|
|
|
static unsigned char *print_value(const cJSON *item, int depth, int fmt) {
|
|
unsigned char *out = NULL;
|
|
if (item == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
switch (item->type & 0xFF) {
|
|
case cJSON_NULL: out = (unsigned char*)global_malloc(5); if (out) strcpy((char*)out, "null"); break;
|
|
case cJSON_False: out = (unsigned char*)global_malloc(6); if (out) strcpy((char*)out, "false"); break;
|
|
case cJSON_True: out = (unsigned char*)global_malloc(5); if (out) strcpy((char*)out, "true"); break;
|
|
case cJSON_Number: out = print_number(item); break;
|
|
case cJSON_String: out = print_string(item); break;
|
|
case cJSON_Array: out = print_array(item, depth, fmt); break;
|
|
case cJSON_Object: out = print_object(item, depth, fmt); break;
|
|
case cJSON_Raw:
|
|
if (item->valuestring) {
|
|
size_t len = strlen(item->valuestring);
|
|
out = (unsigned char*)global_malloc(len + 1);
|
|
if (out) memcpy(out, item->valuestring, len + 1);
|
|
}
|
|
break;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
char *cJSON_Print(const cJSON *item) {
|
|
return (char*)print_value(item, 0, 1);
|
|
}
|
|
|
|
char *cJSON_PrintUnformatted(const cJSON *item) {
|
|
return (char*)print_value(item, 0, 0);
|
|
}
|
|
|
|
char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, int fmt) {
|
|
(void)prebuffer;
|
|
return (char*)print_value(item, 0, fmt);
|
|
}
|
|
|
|
int cJSON_GetArraySize(const cJSON *array) {
|
|
cJSON *child = array ? array->child : NULL;
|
|
int size = 0;
|
|
while (child) {
|
|
size++;
|
|
child = child->next;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
cJSON *cJSON_GetArrayItem(const cJSON *array, int index) {
|
|
if (array == NULL || index < 0) return NULL;
|
|
cJSON *child = array->child;
|
|
while (child && index > 0) {
|
|
child = child->next;
|
|
index--;
|
|
}
|
|
return child;
|
|
}
|
|
|
|
cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string) {
|
|
if (object == NULL || string == NULL) return NULL;
|
|
cJSON *child = object->child;
|
|
while (child && child->string && strcasecmp(child->string, string) != 0) {
|
|
child = child->next;
|
|
}
|
|
return child;
|
|
}
|
|
|
|
cJSON *cJSON_GetObjectItemCaseSensitive(const cJSON *object, const char *string) {
|
|
if (object == NULL || string == NULL) return NULL;
|
|
cJSON *child = object->child;
|
|
while (child && child->string && strcmp(child->string, string) != 0) {
|
|
child = child->next;
|
|
}
|
|
return child;
|
|
}
|
|
|
|
int cJSON_HasObjectItem(const cJSON *object, const char *string) {
|
|
return cJSON_GetObjectItem(object, string) != NULL ? 1 : 0;
|
|
}
|
|
|
|
char *cJSON_GetStringValue(const cJSON *item) {
|
|
if (!cJSON_IsString(item)) return NULL;
|
|
return item->valuestring;
|
|
}
|
|
|
|
double cJSON_GetNumberValue(const cJSON *item) {
|
|
if (!cJSON_IsNumber(item)) return (double)NAN;
|
|
return item->valuedouble;
|
|
}
|
|
|
|
int cJSON_IsInvalid(const cJSON *item) { return (item == NULL) || ((item->type & 0xFF) == cJSON_Invalid); }
|
|
int cJSON_IsFalse(const cJSON *item) { return (item != NULL) && ((item->type & 0xFF) == cJSON_False); }
|
|
int cJSON_IsTrue(const cJSON *item) { return (item != NULL) && ((item->type & 0xFF) == cJSON_True); }
|
|
int cJSON_IsBool(const cJSON *item) { return (item != NULL) && (((item->type & 0xFF) == cJSON_True) || ((item->type & 0xFF) == cJSON_False)); }
|
|
int cJSON_IsNull(const cJSON *item) { return (item != NULL) && ((item->type & 0xFF) == cJSON_NULL); }
|
|
int cJSON_IsNumber(const cJSON *item) { return (item != NULL) && ((item->type & 0xFF) == cJSON_Number); }
|
|
int cJSON_IsString(const cJSON *item) { return (item != NULL) && ((item->type & 0xFF) == cJSON_String); }
|
|
int cJSON_IsArray(const cJSON *item) { return (item != NULL) && ((item->type & 0xFF) == cJSON_Array); }
|
|
int cJSON_IsObject(const cJSON *item) { return (item != NULL) && ((item->type & 0xFF) == cJSON_Object); }
|
|
int cJSON_IsRaw(const cJSON *item) { return (item != NULL) && ((item->type & 0xFF) == cJSON_Raw); }
|
|
|
|
cJSON *cJSON_CreateNull(void) { cJSON *item = cJSON_New_Item(); if (item) item->type = cJSON_NULL; return item; }
|
|
cJSON *cJSON_CreateTrue(void) { cJSON *item = cJSON_New_Item(); if (item) item->type = cJSON_True; return item; }
|
|
cJSON *cJSON_CreateFalse(void) { cJSON *item = cJSON_New_Item(); if (item) item->type = cJSON_False; return item; }
|
|
cJSON *cJSON_CreateBool(int b) { cJSON *item = cJSON_New_Item(); if (item) item->type = b ? cJSON_True : cJSON_False; return item; }
|
|
|
|
cJSON *cJSON_CreateNumber(double num) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item) {
|
|
item->type = cJSON_Number;
|
|
item->valuedouble = num;
|
|
item->valueint = (int)num;
|
|
}
|
|
return item;
|
|
}
|
|
|
|
cJSON *cJSON_CreateString(const char *string) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item) {
|
|
item->type = cJSON_String;
|
|
item->valuestring = (string != NULL) ? strdup(string) : NULL;
|
|
if (string != NULL && item->valuestring == NULL) {
|
|
cJSON_Delete(item);
|
|
return NULL;
|
|
}
|
|
}
|
|
return item;
|
|
}
|
|
|
|
cJSON *cJSON_CreateRaw(const char *raw) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item) {
|
|
item->type = cJSON_Raw;
|
|
item->valuestring = (raw != NULL) ? strdup(raw) : NULL;
|
|
}
|
|
return item;
|
|
}
|
|
|
|
cJSON *cJSON_CreateArray(void) { cJSON *item = cJSON_New_Item(); if (item) item->type = cJSON_Array; return item; }
|
|
cJSON *cJSON_CreateObject(void) { cJSON *item = cJSON_New_Item(); if (item) item->type = cJSON_Object; return item; }
|
|
|
|
cJSON *cJSON_CreateStringReference(const char *string) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item) {
|
|
item->type = cJSON_String | cJSON_IsReference;
|
|
item->valuestring = (char*)string;
|
|
}
|
|
return item;
|
|
}
|
|
|
|
cJSON *cJSON_CreateObjectReference(const cJSON *child) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item) {
|
|
item->type = cJSON_Object | cJSON_IsReference;
|
|
item->child = (cJSON*)child;
|
|
}
|
|
return item;
|
|
}
|
|
|
|
cJSON *cJSON_CreateArrayReference(const cJSON *child) {
|
|
cJSON *item = cJSON_New_Item();
|
|
if (item) {
|
|
item->type = cJSON_Array | cJSON_IsReference;
|
|
item->child = (cJSON*)child;
|
|
}
|
|
return item;
|
|
}
|
|
|
|
cJSON *cJSON_CreateIntArray(const int *numbers, int count) {
|
|
cJSON *a = cJSON_CreateArray();
|
|
if (a == NULL) return NULL;
|
|
for (int i = 0; i < count; i++) {
|
|
cJSON_AddItemToArray(a, cJSON_CreateNumber(numbers[i]));
|
|
}
|
|
return a;
|
|
}
|
|
|
|
cJSON *cJSON_CreateFloatArray(const float *numbers, int count) {
|
|
cJSON *a = cJSON_CreateArray();
|
|
if (a == NULL) return NULL;
|
|
for (int i = 0; i < count; i++) {
|
|
cJSON_AddItemToArray(a, cJSON_CreateNumber((double)numbers[i]));
|
|
}
|
|
return a;
|
|
}
|
|
|
|
cJSON *cJSON_CreateDoubleArray(const double *numbers, int count) {
|
|
cJSON *a = cJSON_CreateArray();
|
|
if (a == NULL) return NULL;
|
|
for (int i = 0; i < count; i++) {
|
|
cJSON_AddItemToArray(a, cJSON_CreateNumber(numbers[i]));
|
|
}
|
|
return a;
|
|
}
|
|
|
|
cJSON *cJSON_CreateStringArray(const char *const *strings, int count) {
|
|
cJSON *a = cJSON_CreateArray();
|
|
if (a == NULL) return NULL;
|
|
for (int i = 0; i < count; i++) {
|
|
cJSON_AddItemToArray(a, cJSON_CreateString(strings[i]));
|
|
}
|
|
return a;
|
|
}
|
|
|
|
static void suffix_object(cJSON *prev, cJSON *item) {
|
|
prev->next = item;
|
|
item->prev = prev;
|
|
}
|
|
|
|
static cJSON* get_array_tail(cJSON *array) {
|
|
cJSON *child = array->child;
|
|
if (child == NULL) return NULL;
|
|
while (child->next) {
|
|
child = child->next;
|
|
}
|
|
return child;
|
|
}
|
|
|
|
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {
|
|
if (array == NULL || item == NULL) return;
|
|
cJSON *tail = get_array_tail(array);
|
|
if (tail == NULL) {
|
|
array->child = item;
|
|
item->prev = item;
|
|
} else {
|
|
suffix_object(tail, item);
|
|
array->child->prev = item;
|
|
}
|
|
}
|
|
|
|
void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) {
|
|
if (object == NULL || string == NULL || item == NULL) return;
|
|
if (item->string) {
|
|
global_free(item->string);
|
|
}
|
|
item->string = strdup(string);
|
|
cJSON_AddItemToArray(object, item);
|
|
}
|
|
|
|
void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) {
|
|
if (object == NULL || string == NULL || item == NULL) return;
|
|
item->string = (char*)string;
|
|
item->type |= cJSON_StringIsConst;
|
|
cJSON_AddItemToArray(object, item);
|
|
}
|
|
|
|
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {
|
|
if (array == NULL || item == NULL) return;
|
|
cJSON_AddItemToArray(array, cJSON_CreateArrayReference(item));
|
|
}
|
|
|
|
void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) {
|
|
if (object == NULL || string == NULL || item == NULL) return;
|
|
cJSON_AddItemToObject(object, string, cJSON_CreateObjectReference(item));
|
|
}
|
|
|
|
cJSON *cJSON_DetachItemViaPointer(cJSON *parent, cJSON *item) {
|
|
if (parent == NULL || item == NULL) return NULL;
|
|
if (item != parent->child) {
|
|
item->prev->next = item->next;
|
|
}
|
|
if (item->next) {
|
|
item->next->prev = item->prev;
|
|
}
|
|
if (item == parent->child) {
|
|
parent->child = item->next;
|
|
}
|
|
if (parent->child) {
|
|
parent->child->prev = get_array_tail(parent);
|
|
}
|
|
item->prev = NULL;
|
|
item->next = NULL;
|
|
return item;
|
|
}
|
|
|
|
cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) {
|
|
if (which < 0) return NULL;
|
|
return cJSON_DetachItemViaPointer(array, cJSON_GetArrayItem(array, which));
|
|
}
|
|
|
|
void cJSON_DeleteItemFromArray(cJSON *array, int which) {
|
|
cJSON_Delete(cJSON_DetachItemFromArray(array, which));
|
|
}
|
|
|
|
cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) {
|
|
return cJSON_DetachItemViaPointer(object, cJSON_GetObjectItem(object, string));
|
|
}
|
|
|
|
cJSON *cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) {
|
|
return cJSON_DetachItemViaPointer(object, cJSON_GetObjectItemCaseSensitive(object, string));
|
|
}
|
|
|
|
void cJSON_DeleteItemFromObject(cJSON *object, const char *string) {
|
|
cJSON_Delete(cJSON_DetachItemFromObject(object, string));
|
|
}
|
|
|
|
void cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) {
|
|
cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
|
|
}
|
|
|
|
int cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) {
|
|
if (which < 0 || array == NULL || newitem == NULL) return 0;
|
|
cJSON *after = cJSON_GetArrayItem(array, which);
|
|
if (after == NULL) {
|
|
cJSON_AddItemToArray(array, newitem);
|
|
return 1;
|
|
}
|
|
newitem->next = after;
|
|
newitem->prev = after->prev;
|
|
after->prev = newitem;
|
|
if (after == array->child) {
|
|
array->child = newitem;
|
|
} else if (newitem->prev) {
|
|
newitem->prev->next = newitem;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int cJSON_ReplaceItemViaPointer(cJSON *parent, cJSON *item, cJSON *replacement) {
|
|
if (parent == NULL || item == NULL || replacement == NULL) return 0;
|
|
if (replacement == item) return 1;
|
|
replacement->next = item->next;
|
|
replacement->prev = item->prev;
|
|
if (replacement->next) {
|
|
replacement->next->prev = replacement;
|
|
}
|
|
if (parent->child == item) {
|
|
if (parent->child->prev == parent->child) {
|
|
replacement->prev = replacement;
|
|
}
|
|
parent->child = replacement;
|
|
} else {
|
|
if (replacement->prev) {
|
|
replacement->prev->next = replacement;
|
|
}
|
|
}
|
|
item->next = NULL;
|
|
item->prev = NULL;
|
|
cJSON_Delete(item);
|
|
return 1;
|
|
}
|
|
|
|
int cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) {
|
|
if (which < 0) return 0;
|
|
return cJSON_ReplaceItemViaPointer(array, cJSON_GetArrayItem(array, which), newitem);
|
|
}
|
|
|
|
int cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) {
|
|
if (newitem == NULL || string == NULL) return 0;
|
|
if (newitem->string) {
|
|
global_free(newitem->string);
|
|
}
|
|
newitem->string = strdup(string);
|
|
return cJSON_ReplaceItemViaPointer(object, cJSON_GetObjectItem(object, string), newitem);
|
|
}
|
|
|
|
int cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) {
|
|
if (newitem == NULL || string == NULL) return 0;
|
|
if (newitem->string) {
|
|
global_free(newitem->string);
|
|
}
|
|
newitem->string = strdup(string);
|
|
return cJSON_ReplaceItemViaPointer(object, cJSON_GetObjectItemCaseSensitive(object, string), newitem);
|
|
}
|
|
|
|
cJSON *cJSON_Duplicate(const cJSON *item, int recurse) {
|
|
if (item == NULL) return NULL;
|
|
cJSON *newitem = cJSON_New_Item();
|
|
if (newitem == NULL) return NULL;
|
|
|
|
newitem->type = item->type & (~cJSON_IsReference);
|
|
newitem->valueint = item->valueint;
|
|
newitem->valuedouble = item->valuedouble;
|
|
|
|
if (item->valuestring) {
|
|
newitem->valuestring = strdup(item->valuestring);
|
|
if (newitem->valuestring == NULL) {
|
|
cJSON_Delete(newitem);
|
|
return NULL;
|
|
}
|
|
}
|
|
if (item->string) {
|
|
newitem->string = strdup(item->string);
|
|
if (newitem->string == NULL) {
|
|
cJSON_Delete(newitem);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (recurse && item->child) {
|
|
newitem->child = cJSON_Duplicate(item->child, 1);
|
|
if (newitem->child == NULL) {
|
|
cJSON_Delete(newitem);
|
|
return NULL;
|
|
}
|
|
cJSON *next = NULL;
|
|
cJSON *newchild = newitem->child;
|
|
cJSON *child = item->child->next;
|
|
while (child) {
|
|
next = cJSON_Duplicate(child, 1);
|
|
if (next == NULL) {
|
|
cJSON_Delete(newitem);
|
|
return NULL;
|
|
}
|
|
newchild->next = next;
|
|
next->prev = newchild;
|
|
newchild = next;
|
|
child = child->next;
|
|
}
|
|
if (newitem->child) {
|
|
newitem->child->prev = newchild;
|
|
}
|
|
}
|
|
|
|
return newitem;
|
|
}
|
|
|
|
int cJSON_Compare(const cJSON *a, const cJSON *b, int case_sensitive) {
|
|
if (a == NULL || b == NULL || (a->type & 0xFF) != (b->type & 0xFF)) {
|
|
return 0;
|
|
}
|
|
|
|
switch (a->type & 0xFF) {
|
|
case cJSON_False:
|
|
case cJSON_True:
|
|
case cJSON_NULL:
|
|
return 1;
|
|
case cJSON_Number:
|
|
return (a->valuedouble == b->valuedouble) ? 1 : 0;
|
|
case cJSON_String:
|
|
case cJSON_Raw:
|
|
if (a->valuestring == NULL || b->valuestring == NULL) {
|
|
return (a->valuestring == b->valuestring) ? 1 : 0;
|
|
}
|
|
return (strcmp(a->valuestring, b->valuestring) == 0) ? 1 : 0;
|
|
case cJSON_Array: {
|
|
cJSON *a_child = a->child;
|
|
cJSON *b_child = b->child;
|
|
while (a_child && b_child) {
|
|
if (!cJSON_Compare(a_child, b_child, case_sensitive)) return 0;
|
|
a_child = a_child->next;
|
|
b_child = b_child->next;
|
|
}
|
|
return (a_child == NULL && b_child == NULL) ? 1 : 0;
|
|
}
|
|
case cJSON_Object: {
|
|
cJSON *a_child = a->child;
|
|
while (a_child) {
|
|
cJSON *b_child = case_sensitive ?
|
|
cJSON_GetObjectItemCaseSensitive(b, a_child->string) :
|
|
cJSON_GetObjectItem(b, a_child->string);
|
|
if (b_child == NULL || !cJSON_Compare(a_child, b_child, case_sensitive)) {
|
|
return 0;
|
|
}
|
|
a_child = a_child->next;
|
|
}
|
|
cJSON *b_child = b->child;
|
|
while (b_child) {
|
|
cJSON *a_elem = case_sensitive ?
|
|
cJSON_GetObjectItemCaseSensitive(a, b_child->string) :
|
|
cJSON_GetObjectItem(a, b_child->string);
|
|
if (a_elem == NULL) return 0;
|
|
b_child = b_child->next;
|
|
}
|
|
return 1;
|
|
}
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void cJSON_Minify(char *json) {
|
|
if (json == NULL) return;
|
|
char *into = json;
|
|
while (*json) {
|
|
if (*json == ' ' || *json == '\t' || *json == '\r' || *json == '\n') {
|
|
json++;
|
|
} else if (*json == '\"') {
|
|
*into++ = *json++;
|
|
while (*json && *json != '\"') {
|
|
if (*json == '\\') {
|
|
*into++ = *json++;
|
|
}
|
|
*into++ = *json++;
|
|
}
|
|
if (*json) *into++ = *json++;
|
|
} else {
|
|
*into++ = *json++;
|
|
}
|
|
}
|
|
*into = '\0';
|
|
}
|