xref: /3.0.3-GA/platform/src/cJSON.c (revision 70415c53)
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3  Copyright (c) 2009 Dave Gamble
4
5  Permission is hereby granted, free of charge, to any person obtaining a copy
6  of this software and associated documentation files (the "Software"), to deal
7  in the Software without restriction, including without limitation the rights
8  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  copies of the Software, and to permit persons to whom the Software is
10  furnished to do so, subject to the following conditions:
11
12  The above copyright notice and this permission notice shall be included in
13  all copies or substantial portions of the Software.
14
15  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21  THE SOFTWARE.
22*/
23
24/* cJSON */
25/* JSON parser in C. */
26
27#include <string.h>
28#include <stdio.h>
29#include <math.h>
30#include <stdlib.h>
31#include <float.h>
32#include <limits.h>
33#include <ctype.h>
34#include "cJSON.h"
35
36static int cJSON_strcasecmp(const char *s1,const char *s2)
37{
38    if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
39    for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0)    return 0;
40    return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
41}
42
43static void *(*cJSON_malloc)(size_t sz) = malloc;
44static void (*cJSON_free)(void *ptr) = free;
45
46static char* cJSON_strdup(const char* str)
47{
48      size_t len;
49      char* copy;
50
51      len = strlen(str) + 1;
52      if (!(copy = (char*)cJSON_malloc(len))) return 0;
53      memcpy(copy,str,len);
54      return copy;
55}
56
57void cJSON_InitHooks(cJSON_Hooks* hooks)
58{
59    if (!hooks) { /* Reset hooks */
60        cJSON_malloc = malloc;
61        cJSON_free = free;
62        return;
63    }
64
65    cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
66    cJSON_free   = (hooks->free_fn)?hooks->free_fn:free;
67}
68
69/* Internal constructor. */
70static cJSON *cJSON_New_Item(void)
71{
72    cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
73    if (node) memset(node,0,sizeof(cJSON));
74    return node;
75}
76
77/* Delete a cJSON structure. */
78void cJSON_Delete(cJSON *c)
79{
80    cJSON *next;
81    while (c)
82    {
83        next=c->next;
84        if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
85        if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
86        if (c->string) cJSON_free(c->string);
87        cJSON_free(c);
88        c=next;
89    }
90}
91
92/* Parse the input text to generate a number, and populate the result into item. */
93static const char *parse_number(cJSON *item,const char *num)
94{
95    double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
96
97    /* Could use sscanf for this? */
98    if (*num=='-') sign=-1,num++;   /* Has sign? */
99    if (*num=='0') num++;           /* is zero */
100    if (*num>='1' && *num<='9') do  n=(n*10.0)+(*num++ -'0');   while (*num>='0' && *num<='9'); /* Number? */
101    if (*num=='.') {num++;      do  n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');}  /* Fractional part? */
102    if (*num=='e' || *num=='E')     /* Exponent? */
103    {   num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++;      /* With sign? */
104        while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');   /* Number? */
105    }
106
107    n=sign*n*pow(10.0,(scale+subscale*signsubscale));   /* number = +/- number.fraction * 10^+/- exponent */
108
109    item->valuedouble=n;
110    item->valueint=(int)n;
111    item->type=cJSON_Number;
112    return num;
113}
114
115/* Render the number nicely from the given item into a string. */
116static char *print_number(cJSON *item)
117{
118    char *str;
119    double d=item->valuedouble;
120    if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
121    {
122        str=(char*)cJSON_malloc(21);    /* 2^64+1 can be represented in 21 chars. */
123        sprintf(str,"%d",item->valueint);
124    }
125    else
126    {
127        str=(char*)cJSON_malloc(64);    /* This is a nice tradeoff. */
128        if (fabs(floor(d)-d)<=DBL_EPSILON)          sprintf(str,"%.0f",d);
129        else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9)   sprintf(str,"%e",d);
130        else                                        sprintf(str,"%f",d);
131    }
132    return str;
133}
134
135/* Parse the input text into an unescaped cstring, and populate item. */
136static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
137static const char *parse_string(cJSON *item,const char *str)
138{
139    const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc;
140    if (*str!='\"') return 0;   /* not a string! */
141
142    while (*ptr!='\"' && (unsigned char)*ptr>31 && ++len) if (*ptr++ == '\\') ptr++;    /* Skip escaped quotes. */
143
144    out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
145    if (!out) return 0;
146
147    ptr=str+1;ptr2=out;
148    while (*ptr!='\"' && (unsigned char)*ptr>31)
149    {
150        if (*ptr!='\\') *ptr2++=*ptr++;
151        else
152        {
153            ptr++;
154            switch (*ptr)
155            {
156                case 'b': *ptr2++='\b'; break;
157                case 'f': *ptr2++='\f'; break;
158                case 'n': *ptr2++='\n'; break;
159                case 'r': *ptr2++='\r'; break;
160                case 't': *ptr2++='\t'; break;
161                case 'u':    /* transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY. */
162                    sscanf(ptr+1,"%4x",&uc);    /* get the unicode char. */
163                    len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len;
164
165                    switch (len) {
166                        case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
167                        case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
168                        case 1: *--ptr2 =(uc | firstByteMark[len]);
169                    }
170                    ptr2+=len;ptr+=4;
171                    break;
172                default:  *ptr2++=*ptr; break;
173            }
174            ptr++;
175        }
176    }
177    *ptr2=0;
178    if (*ptr=='\"') ptr++;
179    item->valuestring=out;
180    item->type=cJSON_String;
181    return ptr;
182}
183
184/* Render the cstring provided to an escaped version that can be printed. */
185static char *print_string_ptr(const char *str)
186{
187    const char *ptr;char *ptr2,*out;int len=0;
188
189    if (!str) return cJSON_strdup("");
190    ptr=str;while (*ptr && ++len) {if ((unsigned char)*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;}
191
192    out=(char*)cJSON_malloc(len+3);
193    ptr2=out;ptr=str;
194    *ptr2++='\"';
195    while (*ptr)
196    {
197        if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
198        else
199        {
200            *ptr2++='\\';
201            switch (*ptr++)
202            {
203                case '\\':  *ptr2++='\\';   break;
204                case '\"':  *ptr2++='\"';   break;
205                case '\b':  *ptr2++='b';    break;
206                case '\f':  *ptr2++='f';    break;
207                case '\n':  *ptr2++='n';    break;
208                case '\r':  *ptr2++='r';    break;
209                case '\t':  *ptr2++='t';    break;
210                default: ptr2--;    break;  /* eviscerate with prejudice. */
211            }
212        }
213    }
214    *ptr2++='\"';*ptr2++=0;
215    return out;
216}
217/* Invote print_string_ptr (which is useful) on an item. */
218static char *print_string(cJSON *item)  {return print_string_ptr(item->valuestring);}
219
220/* Predeclare these prototypes. */
221static const char *parse_value(cJSON *item,const char *value);
222static char *print_value(cJSON *item,int depth,int fmt);
223static const char *parse_array(cJSON *item,const char *value);
224static char *print_array(cJSON *item,int depth,int fmt);
225static const char *parse_object(cJSON *item,const char *value);
226static char *print_object(cJSON *item,int depth,int fmt);
227
228/* Utility to jump whitespace and cr/lf */
229static const char *skip(const char *in) {while (in && (unsigned char)*in<=32) in++; return in;}
230
231/* Parse an object - create a new root, and populate. */
232cJSON *cJSON_Parse(const char *value)
233{
234    cJSON *c=cJSON_New_Item();
235    if (!c) return 0;       /* memory fail */
236
237    if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
238    return c;
239}
240
241/* Render a cJSON item/entity/structure to text. */
242char *cJSON_Print(cJSON *item)              {return print_value(item,0,1);}
243char *cJSON_PrintUnformatted(cJSON *item)   {return print_value(item,0,0);}
244
245void cJSON_Free(char *ptr) { free(ptr); }
246
247/* Parser core - when encountering text, process appropriately. */
248static const char *parse_value(cJSON *item,const char *value)
249{
250    if (!value)                     return 0;   /* Fail on null. */
251    if (!strncmp(value,"null",4))   { item->type=cJSON_NULL;  return value+4; }
252    if (!strncmp(value,"false",5))  { item->type=cJSON_False; return value+5; }
253    if (!strncmp(value,"true",4))   { item->type=cJSON_True; item->valueint=1;  return value+4; }
254    if (*value=='\"')               { return parse_string(item,value); }
255    if (*value=='-' || (*value>='0' && *value<='9'))    { return parse_number(item,value); }
256    if (*value=='[')                { return parse_array(item,value); }
257    if (*value=='{')                { return parse_object(item,value); }
258
259    return 0;   /* failure. */
260}
261
262/* Render a value to text. */
263static char *print_value(cJSON *item,int depth,int fmt)
264{
265    char *out=0;
266    if (!item) return 0;
267    switch ((item->type)&255)
268    {
269        case cJSON_NULL:    out=cJSON_strdup("null");   break;
270        case cJSON_False:   out=cJSON_strdup("false");break;
271        case cJSON_True:    out=cJSON_strdup("true"); break;
272        case cJSON_Number:  out=print_number(item);break;
273        case cJSON_String:  out=print_string(item);break;
274        case cJSON_Array:   out=print_array(item,depth,fmt);break;
275        case cJSON_Object:  out=print_object(item,depth,fmt);break;
276    }
277    return out;
278}
279
280/* Build an array from input text. */
281static const char *parse_array(cJSON *item,const char *value)
282{
283    cJSON *child;
284    if (*value!='[')    return 0;   /* not an array! */
285
286    item->type=cJSON_Array;
287    value=skip(value+1);
288    if (*value==']') return value+1;    /* empty array. */
289
290    item->child=child=cJSON_New_Item();
291    if (!item->child) return 0;      /* memory fail */
292    value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
293    if (!value) return 0;
294
295    while (*value==',')
296    {
297        cJSON *new_item;
298        if (!(new_item=cJSON_New_Item())) return 0;     /* memory fail */
299        child->next=new_item;new_item->prev=child;child=new_item;
300        value=skip(parse_value(child,skip(value+1)));
301        if (!value) return 0;   /* memory fail */
302    }
303
304    if (*value==']') return value+1;    /* end of array */
305    return 0;   /* malformed. */
306}
307
308/* Render an array to text */
309static char *print_array(cJSON *item,int depth,int fmt)
310{
311    char **entries;
312    char *out=0,*ptr,*ret;int len=5;
313    cJSON *child=item->child;
314    int numentries=0,i=0,fail=0;
315
316    /* How many entries in the array? */
317    while (child) numentries++,child=child->next;
318    /* Allocate an array to hold the values for each */
319    entries=(char**)cJSON_malloc(numentries*sizeof(char*));
320    if (!entries) return 0;
321    memset(entries,0,numentries*sizeof(char*));
322    /* Retrieve all the results: */
323    child=item->child;
324    while (child && !fail)
325    {
326        ret=print_value(child,depth+1,fmt);
327        entries[i++]=ret;
328        if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
329        child=child->next;
330    }
331
332    /* If we didn't fail, try to malloc the output string */
333    if (!fail) out=cJSON_malloc(len);
334    /* If that fails, we fail. */
335    if (!out) fail=1;
336
337    /* Handle failure. */
338    if (fail)
339    {
340        for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
341        cJSON_free(entries);
342        return 0;
343    }
344
345    /* Compose the output array. */
346    *out='[';
347    ptr=out+1;*ptr=0;
348    for (i=0;i<numentries;i++)
349    {
350        strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
351        if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
352        cJSON_free(entries[i]);
353    }
354    cJSON_free(entries);
355    *ptr++=']';*ptr++=0;
356    return out;
357}
358
359/* Build an object from the text. */
360static const char *parse_object(cJSON *item,const char *value)
361{
362    cJSON *child;
363    if (*value!='{')    return 0;   /* not an object! */
364
365    item->type=cJSON_Object;
366    value=skip(value+1);
367    if (*value=='}') return value+1;    /* empty array. */
368
369    item->child=child=cJSON_New_Item();
370    value=skip(parse_string(child,skip(value)));
371    if (!value) return 0;
372    child->string=child->valuestring;child->valuestring=0;
373    if (*value!=':') return 0;  /* fail! */
374    value=skip(parse_value(child,skip(value+1)));   /* skip any spacing, get the value. */
375    if (!value) return 0;
376
377    while (*value==',')
378    {
379        cJSON *new_item;
380        if (!(new_item=cJSON_New_Item()))   return 0; /* memory fail */
381        child->next=new_item;new_item->prev=child;child=new_item;
382        value=skip(parse_string(child,skip(value+1)));
383        if (!value) return 0;
384        child->string=child->valuestring;child->valuestring=0;
385        if (*value!=':') return 0;  /* fail! */
386        value=skip(parse_value(child,skip(value+1)));   /* skip any spacing, get the value. */
387        if (!value) return 0;
388    }
389
390    if (*value=='}') return value+1;    /* end of array */
391    return 0;   /* malformed. */
392}
393
394/* Render an object to text. */
395static char *print_object(cJSON *item,int depth,int fmt)
396{
397    char **entries=0,**names=0;
398    char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
399    cJSON *child=item->child;
400    int numentries=0,fail=0;
401    /* Count the number of entries. */
402    while (child) numentries++,child=child->next;
403    /* Allocate space for the names and the objects */
404    entries=(char**)cJSON_malloc(numentries*sizeof(char*));
405    if (!entries) return 0;
406    names=(char**)cJSON_malloc(numentries*sizeof(char*));
407    if (!names) {cJSON_free(entries);return 0;}
408    memset(entries,0,sizeof(char*)*numentries);
409    memset(names,0,sizeof(char*)*numentries);
410
411    /* Collect all the results into our arrays: */
412    child=item->child;depth++;if (fmt) len+=depth;
413    while (child)
414    {
415        names[i]=str=print_string_ptr(child->string);
416        entries[i++]=ret=print_value(child,depth,fmt);
417        if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
418        child=child->next;
419    }
420
421    /* Try to allocate the output string */
422    if (!fail) out=(char*)cJSON_malloc(len);
423    if (!out) fail=1;
424
425    /* Handle failure */
426    if (fail)
427    {
428        for (i=0;i<numentries;i++) {if (names[i]) free(names[i]);if (entries[i]) free(entries[i]);}
429        free(names);free(entries);
430        return 0;
431    }
432
433    /* Compose the output: */
434    *out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
435    for (i=0;i<numentries;i++)
436    {
437        if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
438        strcpy(ptr,names[i]);ptr+=strlen(names[i]);
439        *ptr++=':';if (fmt) *ptr++='\t';
440        strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
441        if (i!=numentries-1) *ptr++=',';
442        if (fmt) *ptr++='\n';*ptr=0;
443        cJSON_free(names[i]);cJSON_free(entries[i]);
444    }
445
446    cJSON_free(names);cJSON_free(entries);
447    if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
448    *ptr++='}';*ptr++=0;
449    return out;
450}
451
452/* Get Array size/item / object item. */
453int    cJSON_GetArraySize(cJSON *array)                         {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
454cJSON *cJSON_GetArrayItem(cJSON *array,int item)                {cJSON *c=array->child;  while (c && item>0) item--,c=c->next; return c;}
455cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)    {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
456
457/* Utility for array list handling. */
458static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
459/* Utility for handling references. */
460static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
461
462/* Add item to array/object. */
463void   cJSON_AddItemToArray(cJSON *array, cJSON *item)                      {cJSON *c=array->child;if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
464void   cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)  {if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
465void    cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)                        {cJSON_AddItemToArray(array,create_reference(item));}
466void    cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item)    {cJSON_AddItemToObject(object,string,create_reference(item));}
467
468cJSON *cJSON_DetachItemFromArray(cJSON *array,int which)            {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
469    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;}
470void   cJSON_DeleteItemFromArray(cJSON *array,int which)            {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
471cJSON *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;}
472void   cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
473
474/* Replace array/object items with new ones. */
475void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)      {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
476    newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
477    if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
478void   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);}}
479
480/* Create basic types: */
481cJSON *cJSON_CreateNull()                       {cJSON *item=cJSON_New_Item();item->type=cJSON_NULL;return item;}
482cJSON *cJSON_CreateTrue()                       {cJSON *item=cJSON_New_Item();item->type=cJSON_True;return item;}
483cJSON *cJSON_CreateFalse()                      {cJSON *item=cJSON_New_Item();item->type=cJSON_False;return item;}
484cJSON *cJSON_CreateNumber(double num)           {cJSON *item=cJSON_New_Item();item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;return item;}
485cJSON *cJSON_CreateString(const char *string)   {cJSON *item=cJSON_New_Item();item->type=cJSON_String;item->valuestring=cJSON_strdup(string);return item;}
486cJSON *cJSON_CreateArray()                      {cJSON *item=cJSON_New_Item();item->type=cJSON_Array;return item;}
487cJSON *cJSON_CreateObject()                     {cJSON *item=cJSON_New_Item();item->type=cJSON_Object;return item;}
488
489/* Create Arrays: */
490cJSON *cJSON_CreateIntArray(int *numbers,int count)             {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
491cJSON *cJSON_CreateFloatArray(float *numbers,int count)         {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
492cJSON *cJSON_CreateDoubleArray(double *numbers,int count)       {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
493cJSON *cJSON_CreateStringArray(const char **strings,int count)  {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
494