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