| /* |
| Copyright (c) 2009 Dave Gamble |
| |
| 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. |
| */ |
| |
| /* cJSON */ |
| /* JSON parser in C. */ |
| |
| #include <string.h> |
| #include <stdio.h> |
| #include <math.h> |
| #include <stdlib.h> |
| #include <float.h> |
| #include <limits.h> |
| #include <ctype.h> |
| #ifdef HAVE_STDINT_H |
| #include <stdint.h> |
| #endif |
| #include <sys/types.h> |
| #include "cjson.h" |
| |
| #ifndef LLONG_MAX |
| #define LLONG_MAX 9223372036854775807LL |
| #endif |
| #ifndef LLONG_MIN |
| #define LLONG_MIN (-LLONG_MAX - 1LL) |
| #endif |
| |
| |
| static const char *ep; |
| |
| const char *cJSON_GetErrorPtr( void ) |
| { |
| return ep; |
| } |
| |
| |
| static int cJSON_strcasecmp( const char *s1, const char *s2 ) |
| { |
| if ( ! s1 ) |
| return ( s1 == s2 ) ? 0 : 1; |
| if ( ! s2 ) |
| return 1; |
| for ( ; tolower((u_char)*s1) == tolower((u_char)*s2); ++s1, ++s2) |
| if( *s1 == 0 ) |
| return 0; |
| return tolower((u_char)*s1) - tolower((u_char)*s2); |
| } |
| |
| |
| static void *(*cJSON_malloc)( size_t ) = malloc; |
| static void (*cJSON_free)( void * ) = free; |
| |
| void cJSON_InitHooks(cJSON_Hooks* hooks) |
| { |
| if ( ! hooks ) { |
| /* Reset hooks. */ |
| cJSON_malloc = malloc; |
| cJSON_free = free; |
| return; |
| } |
| cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc; |
| cJSON_free = (hooks->free_fn) ? hooks->free_fn : free; |
| } |
| |
| |
| static char* cJSON_strdup( const char* str ) |
| { |
| size_t len; |
| char* copy; |
| |
| len = strlen( str ) + 1; |
| if ( ! ( copy = (char*) cJSON_malloc( len ) ) ) |
| return 0; |
| memcpy( copy, str, len ); |
| return copy; |
| } |
| |
| |
| /* Internal constructor. */ |
| static cJSON *cJSON_New_Item( void ) |
| { |
| cJSON* node = (cJSON*) cJSON_malloc( sizeof(cJSON) ); |
| if ( node ) |
| memset( node, 0, sizeof(cJSON) ); |
| return node; |
| } |
| |
| |
| /* Delete a cJSON structure. */ |
| void cJSON_Delete( cJSON *c ) |
| { |
| cJSON *next; |
| |
| while ( c ) { |
| next = c->next; |
| if ( ! ( c->type & cJSON_IsReference ) && c->child ) |
| cJSON_Delete( c->child ); |
| if ( ! ( c->type & cJSON_IsReference ) && c->valuestring ) |
| cJSON_free( c->valuestring ); |
| if ( c->string ) |
| cJSON_free( c->string ); |
| cJSON_free( c ); |
| c = next; |
| } |
| } |
| |
| |
| static double ipow( double n, int exp ) |
| { |
| double r; |
| |
| if ( exp < 0 ) |
| return 1.0 / ipow( n, -exp ); |
| r = 1; |
| while ( exp > 0 ) { |
| if ( exp & 1 ) |
| r *= n; |
| exp >>= 1; |
| n *= n; |
| } |
| return r; |
| } |
| |
| |
| /* Parse the input text to generate a number, and populate the result into item. */ |
| static const char *parse_number( cJSON *item, const char *num ) |
| { |
| int64_t i = 0; |
| double f = 0; |
| int isint = 1; |
| int sign = 1, scale = 0, subscale = 0, signsubscale = 1; |
| |
| /* Could use sscanf for this? */ |
| if ( *num == '-' ) { |
| /* Has sign. */ |
| sign = -1; |
| ++num; |
| } |
| if ( *num == '0' ) |
| /* Is zero. */ |
| ++num; |
| if ( *num >= '1' && *num<='9' ) { |
| /* Number. */ |
| do { |
| i = ( i * 10 ) + ( *num - '0' ); |
| f = ( f * 10.0 ) + ( *num - '0' ); |
| ++num; |
| } while ( *num >= '0' && *num <= '9' ); |
| } |
| if ( *num == '.' && num[1] >= '0' && num[1] <= '9' ) { |
| /* Fractional part. */ |
| isint = 0; |
| ++num; |
| do { |
| f = ( f * 10.0 ) + ( *num++ - '0' ); |
| scale--; |
| } while ( *num >= '0' && *num <= '9' ); |
| } |
| if ( *num == 'e' || *num == 'E' ) { |
| /* Exponent. */ |
| isint = 0; |
| ++num; |
| if ( *num == '+' ) |
| ++num; |
| else if ( *num == '-' ) { |
| /* With sign. */ |
| signsubscale = -1; |
| ++num; |
| } |
| while ( *num >= '0' && *num <= '9' ) |
| subscale = ( subscale * 10 ) + ( *num++ - '0' ); |
| } |
| |
| /* Put it together. */ |
| if ( isint ) { |
| /* Int: number = +/- number */ |
| i = sign * i; |
| item->valueint = i; |
| item->valuefloat = i; |
| } else { |
| /* Float: number = +/- number.fraction * 10^+/- exponent */ |
| f = sign * f * ipow( 10.0, scale + subscale * signsubscale ); |
| item->valueint = f; |
| item->valuefloat = f; |
| } |
| |
| item->type = cJSON_Number; |
| return num; |
| } |
| |
| |
| /* Render the number nicely from the given item into a string. */ |
| static char *print_number( cJSON *item ) |
| { |
| char *str; |
| double f, f2; |
| int64_t i; |
| |
| str = (char*) cJSON_malloc( 64 ); |
| if ( str ) { |
| f = item->valuefloat; |
| i = f; |
| f2 = i; |
| if ( f2 == f && item->valueint >= LLONG_MIN && item->valueint <= LLONG_MAX ) |
| sprintf( str, "%lld", (long long) item->valueint ); |
| else |
| sprintf( str, "%g", item->valuefloat ); |
| } |
| return str; |
| } |
| |
| |
| /* Parse the input text into an unescaped cstring, and populate item. */ |
| static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; |
| |
| static const char *parse_string( cJSON *item, const char *str ) |
| { |
| const char *ptr = str + 1; |
| char *ptr2; |
| char *out; |
| int len = 0; |
| unsigned uc, uc2; |
| |
| if ( *str != '\"' ) { |
| /* Not a string! */ |
| ep = str; |
| return 0; |
| } |
| |
| /* Skip escaped quotes. */ |
| while ( *ptr != '\"' && *ptr && ++len ) |
| if ( *ptr++ == '\\' ) |
| ptr++; |
| |
| if ( ! ( out = (char*) cJSON_malloc( len + 1 ) ) ) |
| return 0; |
| |
| ptr = str + 1; |
| ptr2 = out; |
| while ( *ptr != '\"' && *ptr ) { |
| if ( *ptr != '\\' ) |
| *ptr2++ = *ptr++; |
| else { |
| ptr++; |
| switch ( *ptr ) { |
| case 'b': *ptr2++ ='\b'; break; |
| case 'f': *ptr2++ ='\f'; break; |
| case 'n': *ptr2++ ='\n'; break; |
| case 'r': *ptr2++ ='\r'; break; |
| case 't': *ptr2++ ='\t'; break; |
| case 'u': |
| /* Transcode utf16 to utf8. */ |
| /* Get the unicode char. */ |
| sscanf( ptr + 1,"%4x", &uc ); |
| ptr += 4; |
| /* Check for invalid. */ |
| if ( ( uc >= 0xDC00 && uc <= 0xDFFF ) || uc == 0 ) |
| break; |
| |
| /* UTF16 surrogate pairs. */ |
| if ( uc >= 0xD800 && uc <= 0xDBFF ) { |
| if ( ptr[1] != '\\' || ptr[2] != 'u' ) |
| /* Missing second-half of surrogate. */ |
| break; |
| sscanf( ptr + 3, "%4x", &uc2 ); |
| ptr += 6; |
| if ( uc2 < 0xDC00 || uc2 > 0xDFFF ) |
| /* Invalid second-half of surrogate. */ |
| break; |
| uc = 0x10000 | ( ( uc & 0x3FF ) << 10 ) | ( uc2 & 0x3FF ); |
| } |
| |
| len = 4; |
| if ( uc < 0x80 ) |
| len = 1; |
| else if ( uc < 0x800 ) |
| len = 2; |
| else if ( uc < 0x10000 ) |
| len = 3; |
| ptr2 += len; |
| |
| switch ( len ) { |
| case 4: *--ptr2 = ( ( uc | 0x80) & 0xBF ); uc >>= 6; |
| case 3: *--ptr2 = ( ( uc | 0x80) & 0xBF ); uc >>= 6; |
| case 2: *--ptr2 = ( ( uc | 0x80) & 0xBF ); uc >>= 6; |
| case 1: *--ptr2 = ( uc | firstByteMark[len] ); |
| } |
| ptr2 += len; |
| break; |
| default: *ptr2++ = *ptr; break; |
| } |
| ++ptr; |
| } |
| } |
| *ptr2 = 0; |
| if ( *ptr == '\"' ) |
| ++ptr; |
| item->valuestring = out; |
| item->type = cJSON_String; |
| return ptr; |
| } |
| |
| |
| /* Render the cstring provided to an escaped version that can be printed. */ |
| static char *print_string_ptr( const char *str ) |
| { |
| const char *ptr; |
| char *ptr2, *out; |
| int len = 0; |
| unsigned char token; |
| |
| if ( ! str ) |
| return cJSON_strdup( "" ); |
| ptr = str; |
| while ( ( token = *ptr ) && ++len ) { |
| if ( strchr( "\"\\\b\f\n\r\t", token ) ) |
| ++len; |
| else if ( token < 32 ) |
| len += 5; |
| ++ptr; |
| } |
| |
| if ( ! ( out = (char*) cJSON_malloc( len + 3 ) ) ) |
| return 0; |
| |
| ptr2 = out; |
| ptr = str; |
| *ptr2++ = '\"'; |
| while ( *ptr ) { |
| if ( (unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\' ) |
| *ptr2++ = *ptr++; |
| else { |
| *ptr2++ = '\\'; |
| switch ( token = *ptr++ ) { |
| case '\\': *ptr2++ = '\\'; break; |
| case '\"': *ptr2++ = '\"'; break; |
| case '\b': *ptr2++ = 'b'; break; |
| case '\f': *ptr2++ = 'f'; break; |
| case '\n': *ptr2++ = 'n'; break; |
| case '\r': *ptr2++ = 'r'; break; |
| case '\t': *ptr2++ = 't'; break; |
| default: |
| /* Escape and print. */ |
| sprintf( ptr2, "u%04x", token ); |
| ptr2 += 5; |
| break; |
| } |
| } |
| } |
| *ptr2++ = '\"'; |
| *ptr2++ = 0; |
| return out; |
| } |
| |
| |
| /* Invote print_string_ptr (which is useful) on an item. */ |
| static char *print_string( cJSON *item ) |
| { |
| return print_string_ptr( item->valuestring ); |
| } |
| |
| |
| /* Predeclare these prototypes. */ |
| static const char *parse_value( cJSON *item, const char *value ); |
| static char *print_value( cJSON *item, int depth, int fmt ); |
| static const char *parse_array( cJSON *item, const char *value ); |
| static char *print_array( cJSON *item, int depth, int fmt ); |
| static const char *parse_object( cJSON *item, const char *value ); |
| static char *print_object( cJSON *item, int depth, int fmt ); |
| |
| /* Utility to jump whitespace and cr/lf. */ |
| static const char *skip( const char *in ) |
| { |
| while ( in && *in && (unsigned char) *in <= 32 ) |
| in++; |
| return in; |
| } |
| |
| |
| /* Parse an object - create a new root, and populate. */ |
| cJSON *cJSON_Parse( const char *value ) |
| { |
| cJSON *c; |
| ep = 0; |
| if ( ! ( c = cJSON_New_Item() ) ) |
| return 0; /* memory fail */ |
| |
| if ( ! parse_value( c, skip( value ) ) ) { |
| cJSON_Delete( c ); |
| return 0; |
| } |
| return c; |
| } |
| |
| |
| /* Render a cJSON item/entity/structure to text. */ |
| char *cJSON_Print( cJSON *item ) |
| { |
| return print_value( item, 0, 1 ); |
| } |
| char *cJSON_PrintUnformatted( cJSON *item ) |
| { |
| return print_value( item, 0, 0 ); |
| } |
| |
| |
| /* Parser core - when encountering text, process appropriately. */ |
| static const char *parse_value( cJSON *item, const char *value ) |
| { |
| if ( ! value ) |
| return 0; /* Fail on null. */ |
| if ( ! strncmp( value, "null", 4 ) ) { |
| item->type = cJSON_NULL; |
| return value + 4; |
| } |
| if ( ! strncmp( value, "false", 5 ) ) { |
| item->type = cJSON_False; |
| return value + 5; |
| } |
| if ( ! strncmp( value, "true", 4 ) ) { |
| item->type = cJSON_True; |
| item->valueint = 1; |
| return value + 4; |
| } |
| if ( *value == '\"' ) |
| return parse_string( item, value ); |
| if ( *value == '-' || ( *value >= '0' && *value <= '9' ) ) |
| return parse_number( item, value ); |
| if ( *value == '[' ) |
| return parse_array( item, value ); |
| if ( *value == '{' ) |
| return parse_object( item, value ); |
| |
| /* Fail. */ |
| ep = value; |
| return 0; |
| } |
| |
| |
| /* Render a value to text. */ |
| static char *print_value( cJSON *item, int depth, int fmt ) |
| { |
| char *out = 0; |
| |
| if ( ! item ) |
| return 0; |
| switch ( ( item->type ) & 255 ) { |
| case cJSON_NULL: out = cJSON_strdup( "null" ); break; |
| case cJSON_False: out = cJSON_strdup( "false" ); break; |
| case cJSON_True: out = cJSON_strdup( "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; |
| } |
| return out; |
| } |
| |
| |
| /* Build an array from input text. */ |
| static const char *parse_array( cJSON *item, const char *value ) |
| { |
| cJSON *child; |
| |
| if ( *value != '[' ) { |
| /* Not an array! */ |
| ep = value; |
| return 0; |
| } |
| |
| item->type = cJSON_Array; |
| value = skip( value + 1 ); |
| if ( *value == ']' ) |
| return value + 1; /* empty array. */ |
| |
| if ( ! ( item->child = child = cJSON_New_Item() ) ) |
| return 0; /* memory fail */ |
| if ( ! ( value = skip( parse_value( child, skip( value ) ) ) ) ) |
| return 0; |
| |
| while ( *value == ',' ) { |
| cJSON *new_item; |
| if ( ! ( new_item = cJSON_New_Item() ) ) |
| return 0; /* memory fail */ |
| child->next = new_item; |
| new_item->prev = child; |
| child = new_item; |
| if ( ! ( value = skip( parse_value( child, skip( value+1 ) ) ) ) ) |
| return 0; /* memory fail */ |
| } |
| |
| if ( *value == ']' ) |
| return value + 1; /* end of array */ |
| /* Malformed. */ |
| ep = value; |
| return 0; |
| } |
| |
| |
| /* Render an array to text */ |
| static char *print_array( cJSON *item, int depth, int fmt ) |
| { |
| char **entries; |
| char *out = 0, *ptr, *ret; |
| int len = 5; |
| cJSON *child = item->child; |
| int numentries = 0, i = 0, fail = 0; |
| |
| /* How many entries in the array? */ |
| while ( child ) { |
| ++numentries; |
| child = child->next; |
| } |
| /* Allocate an array to hold the values for each. */ |
| if ( ! ( entries = (char**) cJSON_malloc( numentries * sizeof(char*) ) ) ) |
| return 0; |
| memset( entries, 0, numentries * sizeof(char*) ); |
| /* Retrieve all the results. */ |
| child = item->child; |
| while ( child && ! fail ) { |
| ret = print_value( child, depth + 1, fmt ); |
| entries[i++] = ret; |
| if ( ret ) |
| len += strlen( ret ) + 2 + ( fmt ? 1 : 0 ); |
| else |
| fail = 1; |
| child = child -> next; |
| } |
| |
| /* If we didn't fail, try to malloc the output string. */ |
| if ( ! fail ) { |
| out = (char*) cJSON_malloc( len ); |
| if ( ! out ) |
| fail = 1; |
| } |
| |
| /* Handle failure. */ |
| if ( fail ) { |
| for ( i = 0; i < numentries; ++i ) |
| if ( entries[i] ) |
| cJSON_free( entries[i] ); |
| cJSON_free( entries ); |
| return 0; |
| } |
| |
| /* Compose the output array. */ |
| *out = '['; |
| ptr = out + 1; |
| *ptr = 0; |
| for ( i = 0; i < numentries; ++i ) { |
| strcpy( ptr, entries[i] ); |
| ptr += strlen( entries[i] ); |
| if ( i != numentries - 1 ) { |
| *ptr++ = ','; |
| if ( fmt ) |
| *ptr++ = ' '; |
| *ptr = 0; |
| } |
| cJSON_free( entries[i] ); |
| } |
| cJSON_free( entries ); |
| *ptr++ = ']'; |
| *ptr++ = 0; |
| return out; |
| } |
| |
| |
| /* Build an object from the text. */ |
| static const char *parse_object( cJSON *item, const char *value ) |
| { |
| cJSON *child; |
| |
| if ( *value != '{' ) { |
| /* Not an object! */ |
| ep = value; |
| return 0; |
| } |
| |
| item->type = cJSON_Object; |
| value =skip( value + 1 ); |
| if ( *value == '}' ) |
| return value + 1; /* empty array. */ |
| |
| if ( ! ( item->child = child = cJSON_New_Item() ) ) |
| return 0; |
| if ( ! ( value = skip( parse_string( child, skip( value ) ) ) ) ) |
| return 0; |
| child->string = child->valuestring; |
| child->valuestring = 0; |
| if ( *value != ':' ) { |
| /* Fail! */ |
| ep = value; |
| return 0; |
| } |
| if ( ! ( value = skip( parse_value( child, skip( value + 1 ) ) ) ) ) |
| return 0; |
| |
| while ( *value == ',' ) { |
| cJSON *new_item; |
| if ( ! ( new_item = cJSON_New_Item() ) ) |
| return 0; /* memory fail */ |
| child->next = new_item; |
| new_item->prev = child; |
| child = new_item; |
| if ( ! ( value = skip( parse_string( child, skip( value + 1 ) ) ) ) ) |
| return 0; |
| child->string = child->valuestring; |
| child->valuestring = 0; |
| if ( *value != ':' ) { |
| /* Fail! */ |
| ep = value; |
| return 0; |
| } |
| if ( ! ( value = skip( parse_value( child, skip( value + 1 ) ) ) ) ) |
| return 0; |
| } |
| |
| if ( *value == '}' ) |
| return value + 1; /* end of array */ |
| /* Malformed. */ |
| ep = value; |
| return 0; |
| } |
| |
| |
| /* Render an object to text. */ |
| static char *print_object( cJSON *item, int depth, int fmt ) |
| { |
| char **entries = 0, **names = 0; |
| char *out = 0, *ptr, *ret, *str; |
| int len = 7, i = 0, j; |
| cJSON *child = item->child; |
| int numentries = 0, fail = 0; |
| |
| /* Count the number of entries. */ |
| while ( child ) { |
| ++numentries; |
| child = child->next; |
| } |
| /* Allocate space for the names and the objects. */ |
| if ( ! ( entries = (char**) cJSON_malloc( numentries * sizeof(char*) ) ) ) |
| return 0; |
| if ( ! ( names = (char**) cJSON_malloc( numentries * sizeof(char*) ) ) ) { |
| cJSON_free( entries ); |
| return 0; |
| } |
| memset( entries, 0, sizeof(char*) * numentries ); |
| memset( names, 0, sizeof(char*) * numentries ); |
| |
| /* Collect all the results into our arrays. */ |
| child = item->child; |
| ++depth; |
| if ( fmt ) |
| len += depth; |
| while ( child ) { |
| names[i] = str = print_string_ptr( child->string ); |
| entries[i++] = ret = print_value( child, depth, fmt ); |
| if ( str && ret ) |
| len += strlen( ret ) + strlen( str ) + 2 + ( fmt ? 2 + depth : 0 ); |
| else |
| fail = 1; |
| child = child->next; |
| } |
| |
| /* Try to allocate the output string. */ |
| if ( ! fail ) { |
| out = (char*) cJSON_malloc( len ); |
| if ( ! out ) |
| fail = 1; |
| } |
| |
| /* Handle failure. */ |
| if ( fail ) { |
| for ( i = 0; i < numentries; ++i ) { |
| if ( names[i] ) |
| cJSON_free( names[i] ); |
| if ( entries[i] ) |
| cJSON_free( entries[i] ); |
| } |
| cJSON_free( names ); |
| cJSON_free( entries ); |
| return 0; |
| } |
| |
| /* Compose the output. */ |
| *out = '{'; |
| ptr = out + 1; |
| if ( fmt ) |
| *ptr++ = '\n'; |
| *ptr = 0; |
| for ( i = 0; i < numentries; ++i ) { |
| if ( fmt ) |
| for ( j = 0; j < depth; ++j ) |
| *ptr++ = '\t'; |
| strcpy( ptr, names[i] ); |
| ptr += strlen( names[i] ); |
| *ptr++ = ':'; |
| if ( fmt ) |
| *ptr++ = '\t'; |
| strcpy( ptr, entries[i] ); |
| ptr += strlen( entries[i] ); |
| if ( i != numentries - 1 ) |
| *ptr++ = ','; |
| if ( fmt ) |
| *ptr++ = '\n'; |
| *ptr = 0; |
| cJSON_free( names[i] ); |
| cJSON_free( entries[i] ); |
| } |
| |
| cJSON_free( names ); |
| cJSON_free( entries ); |
| if ( fmt ) |
| for ( i = 0; i < depth - 1; ++i ) |
| *ptr++ = '\t'; |
| *ptr++ = '}'; |
| *ptr++ = 0; |
| return out; |
| } |
| |
| |
| int cJSON_GetArraySize( cJSON *array ) |
| { |
| cJSON *c = array->child; |
| int i = 0; |
| while ( c ) { |
| ++i; |
| c = c->next; |
| } |
| return i; |
| } |
| |
| |
| cJSON *cJSON_GetArrayItem( cJSON *array, int item ) |
| { |
| cJSON *c = array->child; |
| while ( c && item > 0 ) { |
| --item; |
| c = c->next; |
| } |
| return c; |
| } |
| |
| |
| cJSON *cJSON_GetObjectItem( cJSON *object, const char *string ) |
| { |
| cJSON *c = object->child; |
| while ( c && cJSON_strcasecmp( c->string, string ) ) |
| c = c->next; |
| return c; |
| } |
| |
| |
| /* Utility for array list handling. */ |
| static void suffix_object( cJSON *prev, cJSON *item ) |
| { |
| prev->next = item; |
| item->prev = prev; |
| } |
| |
| |
| /* Utility for handling references. */ |
| static cJSON *create_reference( cJSON *item ) |
| { |
| cJSON *ref; |
| if ( ! ( ref = cJSON_New_Item() ) ) |
| return 0; |
| memcpy( ref, item, sizeof(cJSON) ); |
| ref->string = 0; |
| ref->type |= cJSON_IsReference; |
| ref->next = ref->prev = 0; |
| return ref; |
| } |
| |
| |
| /* Add item to array/object. */ |
| void cJSON_AddItemToArray( cJSON *array, cJSON *item ) |
| { |
| cJSON *c = array->child; |
| if ( ! item ) |
| return; |
| if ( ! c ) { |
| array->child = item; |
| } else { |
| while ( c && c->next ) |
| c = c->next; |
| suffix_object( c, item ); |
| } |
| } |
| |
| void cJSON_AddItemToObject( cJSON *object, const char *string, cJSON *item ) |
| { |
| if ( ! item ) |
| return; |
| if ( item->string ) |
| cJSON_free( item->string ); |
| item->string = cJSON_strdup( string ); |
| cJSON_AddItemToArray( object, item ); |
| } |
| |
| void cJSON_AddItemReferenceToArray( cJSON *array, cJSON *item ) |
| { |
| cJSON_AddItemToArray( array, create_reference( item ) ); |
| } |
| |
| void cJSON_AddItemReferenceToObject( cJSON *object, const char *string, cJSON *item ) |
| { |
| cJSON_AddItemToObject( object, string, create_reference( item ) ); |
| } |
| |
| cJSON *cJSON_DetachItemFromArray( cJSON *array, int which ) |
| { |
| cJSON *c = array->child; |
| while ( c && which > 0 ) { |
| c = c->next; |
| --which; |
| } |
| if ( ! c ) |
| return 0; |
| if ( c->prev ) |
| c->prev->next = c->next; |
| if ( c->next ) c->next->prev = c->prev; |
| if ( c == array->child ) |
| array->child = c->next; |
| c->prev = c->next = 0; |
| return c; |
| } |
| |
| void cJSON_DeleteItemFromArray( cJSON *array, int which ) |
| { |
| cJSON_Delete( cJSON_DetachItemFromArray( array, which ) ); |
| } |
| |
| cJSON *cJSON_DetachItemFromObject( cJSON *object, const char *string ) |
| { |
| int i = 0; |
| cJSON *c = object->child; |
| while ( c && cJSON_strcasecmp( c->string, string ) ) { |
| ++i; |
| c = c->next; |
| } |
| if ( c ) |
| return cJSON_DetachItemFromArray( object, i ); |
| return 0; |
| } |
| |
| void cJSON_DeleteItemFromObject( cJSON *object, const char *string ) |
| { |
| cJSON_Delete( cJSON_DetachItemFromObject( object, string ) ); |
| } |
| |
| /* Replace array/object items with new ones. */ |
| void cJSON_ReplaceItemInArray( cJSON *array, int which, cJSON *newitem ) |
| { |
| cJSON *c = array->child; |
| while ( c && which > 0 ) { |
| c = c->next; |
| --which; |
| } |
| if ( ! c ) |
| return; |
| newitem->next = c->next; |
| newitem->prev = c->prev; |
| if ( newitem->next ) |
| newitem->next->prev = newitem; |
| if ( c == array->child ) |
| array->child = newitem; |
| else |
| newitem->prev->next = newitem; |
| c->next = c->prev = 0; |
| cJSON_Delete( c ); |
| } |
| |
| void cJSON_ReplaceItemInObject( cJSON *object, const char *string, cJSON *newitem ) |
| { |
| int i = 0; |
| cJSON *c = object->child; |
| while ( c && cJSON_strcasecmp( c->string, string ) ) { |
| ++i; |
| c = c->next; |
| } |
| if ( c ) { |
| newitem->string = cJSON_strdup( string ); |
| cJSON_ReplaceItemInArray( object, i, newitem ); |
| } |
| } |
| |
| |
| /* Create basic types: */ |
| |
| 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_CreateInt( int64_t num ) |
| { |
| cJSON *item = cJSON_New_Item(); |
| if ( item ) { |
| item->type = cJSON_Number; |
| item->valuefloat = num; |
| item->valueint = num; |
| } |
| return item; |
| } |
| |
| cJSON *cJSON_CreateFloat( double num ) |
| { |
| cJSON *item = cJSON_New_Item(); |
| if ( item ) { |
| item->type = cJSON_Number; |
| item->valuefloat = num; |
| item->valueint = num; |
| } |
| return item; |
| } |
| |
| cJSON *cJSON_CreateString( const char *string ) |
| { |
| cJSON *item = cJSON_New_Item(); |
| if ( item ) { |
| item->type = cJSON_String; |
| item->valuestring = cJSON_strdup( string ); |
| } |
| 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; |
| } |
| |
| |
| /* Create Arrays. */ |
| |
| cJSON *cJSON_CreateIntArray( int64_t *numbers, int count ) |
| { |
| int i; |
| cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); |
| for ( i = 0; a && i < count; ++i ) { |
| n = cJSON_CreateInt( numbers[i] ); |
| if ( ! i ) |
| a->child = n; |
| else |
| suffix_object( p, n ); |
| p = n; |
| } |
| return a; |
| } |
| |
| cJSON *cJSON_CreateFloatArray( double *numbers, int count ) |
| { |
| int i; |
| cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); |
| for ( i = 0; a && i < count; ++i ) { |
| n = cJSON_CreateFloat( numbers[i] ); |
| if ( ! i ) |
| a->child = n; |
| else |
| suffix_object( p, n ); |
| p = n; |
| } |
| return a; |
| } |
| |
| cJSON *cJSON_CreateStringArray( const char **strings, int count ) |
| { |
| int i; |
| cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); |
| for ( i = 0; a && i < count; ++i ) { |
| n = cJSON_CreateString( strings[i] ); |
| if ( ! i ) |
| a->child = n; |
| else |
| suffix_object( p, n ); |
| p = n; |
| } |
| return a; |
| } |