/* 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: */ #include #include #include #include #include #include #include #include "cJSON.h" /* define our own boolean type */ #ifdef true #undef true #endif #define true ((cJSON_bool)1) #ifdef false #undef false #endif #define false ((cJSON_bool)0) typedef struct { const unsigned char *content; size_t length; size_t offset; size_t depth; } parse_buffer; /* case insensitive strcmp */ CJSON_PUBLIC(int) cJSON_Strcasecmp(const char *a, const char *b) { if (!a && !b) return 0; if (!a || !b) return 1; for (; tolower(*a) == tolower(*b); a++, b++) { if (*a == '\0') return 0; } return tolower(*a) - tolower(*b); } static cJSON_bool parse_number(cJSON *item, parse_buffer *input) { double number = 0; unsigned char *after_end = NULL; unsigned char number_c_string[64]; size_t i = 0; if (input->length == 0) return false; if (input->length > (sizeof(number_c_string) - 1)) return false; for (i = 0; (i < input->length) && (input->content[input->offset + i] != '\0'); i++) { number_c_string[i] = input->content[input->offset + i]; } number_c_string[i] = '\0'; number = strtod((char *)number_c_string, (char **)&after_end); if (number_c_string == after_end) return false; item->valuedouble = number; item->valueint = (int)number; item->type = cJSON_Number; input->offset += (size_t)(after_end - number_c_string); return true; } static cJSON_bool parse_string(cJSON *item, parse_buffer *input) { const unsigned char *input_pointer = input->content + input->offset; size_t i = 0; if (input->length < 2) return false; if (input_pointer[0] != '\"') return false; input_pointer++; while ((i < input->length) && (input_pointer[0] != '\0')) { if (input_pointer[0] == '\"') { char *output = (char *)malloc(i + 1); if (!output) return false; memcpy(output, input_pointer - i, i); output[i] = '\0'; item->valuestring = output; item->type = cJSON_String; input->offset += i + 2; return true; } i++; input_pointer++; } return false; } static parse_buffer *skip_whitespace(parse_buffer *buffer) { if (buffer == NULL || buffer->content == NULL) return NULL; while ((buffer->offset < buffer->length) && isspace(buffer->content[buffer->offset])) { buffer->offset++; } return buffer; } static cJSON_bool parse_value(cJSON *item, parse_buffer *input); static cJSON_bool parse_array(cJSON *item, parse_buffer *input) { cJSON *child = NULL; if (input->content[input->offset] != '[') return false; input->offset++; skip_whitespace(input); if (input->content[input->offset] == ']') { item->type = cJSON_Array; input->offset++; return true; } input->offset--; child = cJSON_CreateNull(); if (child == NULL) return false; input->offset++; if (!parse_value(child, skip_whitespace(input))) { cJSON_Delete(child); return false; } item->child = child; skip_whitespace(input); while (input->content[input->offset] == ',') { cJSON *new_item = cJSON_CreateNull(); if (new_item == NULL) { cJSON_Delete(child); return false; } input->offset++; if (!parse_value(new_item, skip_whitespace(input))) { cJSON_Delete(new_item); cJSON_Delete(child); return false; } child->next = new_item; new_item->prev = child; child = new_item; skip_whitespace(input); } if (input->content[input->offset] != ']') { cJSON_Delete(child); return false; } input->offset++; item->type = cJSON_Array; return true; } static cJSON_bool parse_object(cJSON *item, parse_buffer *input) { cJSON *child = NULL; if (input->content[input->offset] != '{') return false; input->offset++; skip_whitespace(input); if (input->content[input->offset] == '}') { item->type = cJSON_Object; input->offset++; return true; } child = cJSON_CreateNull(); if (child == NULL) return false; /* Parse key */ if (!parse_string(child, input)) { cJSON_Delete(child); return false; } char *key = child->valuestring; child->valuestring = NULL; /* clear before parse_value overwrites it */ /* Parse value */ skip_whitespace(input); if (input->content[input->offset] != ':') { cJSON_Delete(child); return false; } input->offset++; if (!parse_value(child, skip_whitespace(input))) { cJSON_Delete(child); return false; } item->child = child; child->string = key; skip_whitespace(input); while (input->content[input->offset] == ',') { cJSON *new_item = cJSON_CreateNull(); if (new_item == NULL) { cJSON_Delete(child); return false; } input->offset++; /* Parse key */ if (!parse_string(new_item, input)) { cJSON_Delete(new_item); cJSON_Delete(child); return false; } char *new_key = new_item->valuestring; new_item->valuestring = NULL; /* Parse value */ skip_whitespace(input); if (input->content[input->offset] != ':') { cJSON_Delete(new_item); cJSON_Delete(child); return false; } input->offset++; if (!parse_value(new_item, skip_whitespace(input))) { cJSON_Delete(new_item); cJSON_Delete(child); return false; } new_item->string = new_key; child->next = new_item; new_item->prev = child; child = new_item; skip_whitespace(input); } if (input->content[input->offset] != '}') { cJSON_Delete(child); return false; } input->offset++; item->type = cJSON_Object; return true; } static cJSON_bool parse_value(cJSON *item, parse_buffer *input) { if (input == NULL || item == NULL) return false; skip_whitespace(input); if (input->length == 0) return false; switch (input->content[input->offset]) { case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return parse_number(item, input); case 't': case 'f': if (strncmp((const char *)input->content + input->offset, "true", 4) == 0) { item->type = cJSON_True; input->offset += 4; return true; } if (strncmp((const char *)input->content + input->offset, "false", 5) == 0) { item->type = cJSON_False; input->offset += 5; return true; } return false; case 'n': if (strncmp((const char *)input->content + input->offset, "null", 4) == 0) { item->type = cJSON_NULL; input->offset += 4; return true; } return false; case '\"': return parse_string(item, input); case '[': return parse_array(item, input); case '{': return parse_object(item, input); default: return false; } } /* Custom hooks */ static cJSON_Hooks hooks = { malloc, free }; CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *h) { if (h != NULL) { hooks.malloc_fn = h->malloc_fn; hooks.free_fn = h->free_fn; } } CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) { parse_buffer buffer = {0}; cJSON *item = NULL; if (value == NULL) return NULL; buffer.length = strlen(value); buffer.content = (const unsigned char *)value; buffer.offset = 0; item = (cJSON *)hooks.malloc_fn(sizeof(cJSON)); if (item == NULL) return NULL; memset(item, 0, sizeof(cJSON)); if (!parse_value(item, skip_whitespace(&buffer))) { cJSON_Delete(item); return NULL; } return item; } CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) { cJSON *next = NULL; while (item != NULL) { next = item->next; if (item->child != NULL) cJSON_Delete(item->child); if (item->valuestring != NULL) hooks.free_fn(item->valuestring); if (item->string != NULL) hooks.free_fn(item->string); hooks.free_fn(item); item = next; } } /* --- Print functions (unformatted) --- */ static size_t print_value_ptr(const cJSON *item, char *buffer); static size_t print_string_to(const char *str, char *buf) { size_t len = strlen(str) + 2; if (buf) { buf[0] = '\"'; memcpy(buf + 1, str, len - 2); buf[len - 1] = '\"'; } return len; } static size_t print_number_to(const cJSON *item, char *buf) { char tmp[32]; size_t len; if (fabs(floor(item->valuedouble) - item->valuedouble) <= DBL_EPSILON && fabs(item->valuedouble) < 1.0e15) len = (size_t)sprintf(tmp, "%d", item->valueint); else len = (size_t)sprintf(tmp, "%g", item->valuedouble); if (buf) memcpy(buf, tmp, len); return len; } static size_t print_array_to(const cJSON *item, char *buf) { size_t pos = 0; if (buf) buf[pos++] = '['; else pos++; cJSON *child = item->child; while (child != NULL) { pos += print_value_ptr(child, buf ? buf + pos : NULL); if (child->next != NULL) { if (buf) buf[pos++] = ','; else pos++; } child = child->next; } if (buf) buf[pos++] = ']'; else pos++; return pos; } static size_t print_object_to(const cJSON *item, char *buf) { size_t pos = 0; if (buf) buf[pos++] = '{'; else pos++; cJSON *child = item->child; while (child != NULL) { pos += print_string_to(child->string, buf ? buf + pos : NULL); if (buf) buf[pos++] = ':'; else pos++; pos += print_value_ptr(child, buf ? buf + pos : NULL); if (child->next != NULL) { if (buf) buf[pos++] = ','; else pos++; } child = child->next; } if (buf) buf[pos++] = '}'; else pos++; return pos; } static size_t print_value_ptr(const cJSON *item, char *buffer) { switch (item->type & 0xFF) { case cJSON_NULL: if (buffer) { memcpy(buffer, "null", 4); } return 4; case cJSON_False: if (buffer) { memcpy(buffer, "false", 5); } return 5; case cJSON_True: if (buffer) { memcpy(buffer, "true", 4); } return 4; case cJSON_Number: return print_number_to(item, buffer); case cJSON_String: return print_string_to(item->valuestring, buffer); case cJSON_Array: return print_array_to(item, buffer); case cJSON_Object: return print_object_to(item, buffer); default: return 0; } } CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) { return cJSON_PrintUnformatted(item); } CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) { size_t length = 0; char *output = NULL; length = print_value_ptr(item, NULL); output = (char *)hooks.malloc_fn(length + 1); if (output == NULL) return NULL; print_value_ptr(item, output); output[length] = '\0'; return output; } /* --- Item access --- */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) { cJSON *child = NULL; size_t size = 0; if (array == NULL || !cJSON_IsArray(array)) return 0; child = array->child; while (child != NULL) { size++; child = child->next; } return (int)size; } CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) { cJSON *child = NULL; int i = 0; if (array == NULL || !cJSON_IsArray(array)) return NULL; child = array->child; while (child != NULL && i < index) { i++; child = child->next; } return child; } CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *object, const char *string) { cJSON *current = NULL; if (object == NULL || string == NULL) return NULL; current = object->child; while (current != NULL) { if (current->string != NULL && strcmp(current->string, string) == 0) return current; current = current->next; } return NULL; } CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string) { cJSON *current = NULL; if (object == NULL || string == NULL) return NULL; current = object->child; while (current != NULL) { if (current->string != NULL && cJSON_Strcasecmp(current->string, string) == 0) return current; current = current->next; } return NULL; } CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) { return cJSON_GetObjectItem(object, string) != NULL; } static const char *global_error_ptr = NULL; CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) { return global_error_ptr; } /* --- Create items --- */ static cJSON *cJSON_New_Item(void) { cJSON *item = (cJSON *)hooks.malloc_fn(sizeof(cJSON)); if (item != NULL) memset(item, 0, sizeof(cJSON)); return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) { cJSON *item = cJSON_New_Item(); if(item) item->type = cJSON_NULL; return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) { cJSON *item = cJSON_New_Item(); if(item) item->type = cJSON_True; return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) { cJSON *item = cJSON_New_Item(); if(item) item->type = cJSON_False; return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) { cJSON *item = cJSON_New_Item(); if(item) item->type = b ? cJSON_True : cJSON_False; return item; } CJSON_PUBLIC(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_PUBLIC(cJSON *) cJSON_CreateString(const char *str) { cJSON *item = cJSON_New_Item(); if (item && str) { item->type = cJSON_String; item->valuestring = (char *)hooks.malloc_fn(strlen(str) + 1); if (item->valuestring) strcpy(item->valuestring, str); } return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) { return cJSON_CreateString(raw); } CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) { cJSON *item = cJSON_New_Item(); if(item) item->type = cJSON_Array; return item; } CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) { cJSON *item = cJSON_New_Item(); if(item) item->type = cJSON_Object; return item; } /* --- Add items --- */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) { cJSON *child = NULL; if (array == NULL || item == NULL) return false; child = array->child; if (child == NULL) { array->child = item; } else { while (child->next) child = child->next; child->next = item; item->prev = child; } return true; } CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *name, cJSON *item) { if (object == NULL || name == NULL || item == NULL) return false; if (item->string != NULL) hooks.free_fn(item->string); item->string = (char *)hooks.malloc_fn(strlen(name) + 1); if (item->string) strcpy(item->string, name); return cJSON_AddItemToArray(object, item); } /* --- Type checks --- */ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *item) { return (item != NULL) && (item->type == cJSON_Invalid); } CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *item) { return (item != NULL) && (item->type == cJSON_False); } CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *item) { return (item != NULL) && (item->type == cJSON_True); } CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *item) { return (item != NULL) && (item->type & cJSON_Bool); } CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *item) { return (item != NULL) && (item->type == cJSON_Number); } CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *item) { return (item != NULL) && (item->type == cJSON_String); } CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *item) { return (item != NULL) && (item->type == cJSON_Array); } CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *item) { return (item != NULL) && (item->type == cJSON_Object); } CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *item) { return (item != NULL) && (item->type == cJSON_Raw); } /* --- Add helpers --- */ CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON *object, const char *name) { cJSON *item = cJSON_CreateNull(); if(item) cJSON_AddItemToObject(object, name, item); return item; } CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON *object, const char *name) { cJSON *item = cJSON_CreateTrue(); if(item) cJSON_AddItemToObject(object, name, item); return item; } CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON *object, const char *name) { cJSON *item = cJSON_CreateFalse(); if(item) cJSON_AddItemToObject(object, name, item); return item; } CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON *object, const char *name, cJSON_bool b) { cJSON *item = cJSON_CreateBool(b); if(item) cJSON_AddItemToObject(object, name, item); return item; } CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON *object, const char *name, double n) { cJSON *item = cJSON_CreateNumber(n); if(item) cJSON_AddItemToObject(object, name, item); return item; } CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON *object, const char *name, const char *s) { cJSON *item = cJSON_CreateString(s); if(item) cJSON_AddItemToObject(object, name, item); return item; } CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON *object, const char *name, const char *raw) { cJSON *item = cJSON_CreateRaw(raw); if(item) cJSON_AddItemToObject(object, name, item); return item; } CJSON_PUBLIC(void) cJSON_SetValuestring(cJSON *object, const char *valuestring) { if (object == NULL || !cJSON_IsString(object) || valuestring == NULL) return; if (object->valuestring != NULL) hooks.free_fn(object->valuestring); object->valuestring = (char *)hooks.malloc_fn(strlen(valuestring) + 1); if (object->valuestring) strcpy(object->valuestring, valuestring); } CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) { (void)numbers; (void)count; return NULL; } CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) { (void)numbers; (void)count; return NULL; } CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) { (void)numbers; (void)count; return NULL; } CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) { (void)strings; (void)count; return NULL; } CJSON_PUBLIC(void) cJSON_free(void *ptr) { if(ptr) hooks.free_fn(ptr); }