blob: 8ef06c8606c14fe67471b14cb63f6f8046c202c9 [file] [log] [blame]
Jon Ashburn752e06e2015-06-29 11:25:34 -06001/*
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 const char *ep;
36
37const char *cJSON_GetErrorPtr(void) {return ep;}
38
Jon Ashburn752e06e2015-06-29 11:25:34 -060039static void *(*cJSON_malloc)(size_t sz) = malloc;
40static void (*cJSON_free)(void *ptr) = free;
41
42static char* cJSON_strdup(const char* str)
43{
44 size_t len;
45 char* copy;
46
47 len = strlen(str) + 1;
48 if (!(copy = (char*)cJSON_malloc(len))) return 0;
49 memcpy(copy,str,len);
50 return copy;
51}
52
53void cJSON_InitHooks(cJSON_Hooks* hooks)
54{
55 if (!hooks) { /* Reset hooks */
56 cJSON_malloc = malloc;
57 cJSON_free = free;
58 return;
59 }
60
61 cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
62 cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
63}
64
65/* Internal constructor. */
66static cJSON *cJSON_New_Item(void)
67{
68 cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
69 if (node) memset(node,0,sizeof(cJSON));
70 return node;
71}
72
73/* Delete a cJSON structure. */
74void cJSON_Delete(cJSON *c)
75{
76 cJSON *next;
77 while (c)
78 {
79 next=c->next;
80 if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
81 if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
82 if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
83 cJSON_free(c);
84 c=next;
85 }
86}
87
88/* Parse the input text to generate a number, and populate the result into item. */
89static const char *parse_number(cJSON *item,const char *num)
90{
91 double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
92
93 if (*num=='-') sign=-1,num++; /* Has sign? */
94 if (*num=='0') num++; /* is zero */
95 if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
96 if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
97 if (*num=='e' || *num=='E') /* Exponent? */
98 { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
99 while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
100 }
101
102 n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
103
104 item->valuedouble=n;
105 item->valueint=(int)n;
106 item->type=cJSON_Number;
107 return num;
108}
109
Tony Barbour3b37a422015-07-13 15:06:12 -0600110static size_t pow2gt (size_t x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; }
Jon Ashburn752e06e2015-06-29 11:25:34 -0600111
Tony Barbour3b37a422015-07-13 15:06:12 -0600112typedef struct {char *buffer; size_t length; size_t offset; } printbuffer;
Jon Ashburn752e06e2015-06-29 11:25:34 -0600113
Tony Barbour3b37a422015-07-13 15:06:12 -0600114static char* ensure(printbuffer *p,size_t needed)
Jon Ashburn752e06e2015-06-29 11:25:34 -0600115{
Tony Barbour3b37a422015-07-13 15:06:12 -0600116 char *newbuffer;size_t newsize;
Jon Ashburn752e06e2015-06-29 11:25:34 -0600117 if (!p || !p->buffer) return 0;
118 needed+=p->offset;
119 if (needed<=p->length) return p->buffer+p->offset;
120
121 newsize=pow2gt(needed);
122 newbuffer=(char*)cJSON_malloc(newsize);
123 if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;}
124 if (newbuffer) memcpy(newbuffer,p->buffer,p->length);
125 cJSON_free(p->buffer);
126 p->length=newsize;
127 p->buffer=newbuffer;
128 return newbuffer+p->offset;
129}
130
Tony Barbour2e73dfd2015-07-13 13:37:24 -0600131static size_t update(printbuffer *p)
Jon Ashburn752e06e2015-06-29 11:25:34 -0600132{
133 char *str;
134 if (!p || !p->buffer) return 0;
135 str=p->buffer+p->offset;
136 return p->offset+strlen(str);
137}
138
139/* Render the number nicely from the given item into a string. */
140static char *print_number(cJSON *item,printbuffer *p)
141{
142 char *str=0;
143 double d=item->valuedouble;
144 if (d==0)
145 {
146 if (p) str=ensure(p,2);
147 else str=(char*)cJSON_malloc(2); /* special case for 0. */
148 if (str) strcpy(str,"0");
149 }
150 else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
151 {
152 if (p) str=ensure(p,21);
153 else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
154 if (str) sprintf(str,"%d",item->valueint);
155 }
156 else
157 {
158 if (p) str=ensure(p,64);
159 else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
160 if (str)
161 {
162 if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
163 else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
164 else sprintf(str,"%f",d);
165 }
166 }
167 return str;
168}
169
170static unsigned parse_hex4(const char *str)
171{
172 unsigned h=0;
173 if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
174 h=h<<4;str++;
175 if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
176 h=h<<4;str++;
177 if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
178 h=h<<4;str++;
179 if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
180 return h;
181}
182
183/* Parse the input text into an unescaped cstring, and populate item. */
184static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
185static const char *parse_string(cJSON *item,const char *str)
186{
187 const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
188 if (*str!='\"') {ep=str;return 0;} /* not a string! */
189
190 while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
191
192 out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
193 if (!out) return 0;
194
195 ptr=str+1;ptr2=out;
196 while (*ptr!='\"' && *ptr)
197 {
198 if (*ptr!='\\') *ptr2++=*ptr++;
199 else
200 {
201 ptr++;
202 switch (*ptr)
203 {
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 case 'u': /* transcode utf16 to utf8. */
210 uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */
211
212 if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
213
214 if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
215 {
216 if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
217 uc2=parse_hex4(ptr+3);ptr+=6;
218 if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
219 uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
220 }
221
222 len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
223
224 switch (len) {
225 case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
226 case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
227 case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
228 case 1: *--ptr2 =(uc | firstByteMark[len]);
229 }
230 ptr2+=len;
231 break;
232 default: *ptr2++=*ptr; break;
233 }
234 ptr++;
235 }
236 }
237 *ptr2=0;
238 if (*ptr=='\"') ptr++;
239 item->valuestring=out;
240 item->type=cJSON_String;
241 return ptr;
242}
243
244/* Render the cstring provided to an escaped version that can be printed. */
245static char *print_string_ptr(const char *str,printbuffer *p)
246{
Tony Barbour3b37a422015-07-13 15:06:12 -0600247 const char *ptr; char *ptr2; char *out; size_t len = 0, flag = 0; unsigned char token;
Jon Ashburn752e06e2015-06-29 11:25:34 -0600248
249 for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0;
250 if (!flag)
251 {
252 len=ptr-str;
253 if (p) out=ensure(p,len+3);
254 else out=(char*)cJSON_malloc(len+3);
255 if (!out) return 0;
256 ptr2=out;*ptr2++='\"';
257 strcpy(ptr2,str);
258 ptr2[len]='\"';
259 ptr2[len+1]=0;
260 return out;
261 }
262
263 if (!str)
264 {
265 if (p) out=ensure(p,3);
266 else out=(char*)cJSON_malloc(3);
267 if (!out) return 0;
268 strcpy(out,"\"\"");
269 return out;
270 }
271 ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
272
273 if (p) out=ensure(p,len+3);
274 else out=(char*)cJSON_malloc(len+3);
275 if (!out) return 0;
276
277 ptr2=out;ptr=str;
278 *ptr2++='\"';
279 while (*ptr)
280 {
281 if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
282 else
283 {
284 *ptr2++='\\';
285 switch (token=*ptr++)
286 {
287 case '\\': *ptr2++='\\'; break;
288 case '\"': *ptr2++='\"'; break;
289 case '\b': *ptr2++='b'; break;
290 case '\f': *ptr2++='f'; break;
291 case '\n': *ptr2++='n'; break;
292 case '\r': *ptr2++='r'; break;
293 case '\t': *ptr2++='t'; break;
294 default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
295 }
296 }
297 }
298 *ptr2++='\"';*ptr2++=0;
299 return out;
300}
301/* Invote print_string_ptr (which is useful) on an item. */
302static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);}
303
304/* Predeclare these prototypes. */
305static const char *parse_value(cJSON *item,const char *value);
306static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p);
307static const char *parse_array(cJSON *item,const char *value);
308static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p);
309static const char *parse_object(cJSON *item,const char *value);
310static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p);
311
312/* Utility to jump whitespace and cr/lf */
313static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
314
315/* Parse an object - create a new root, and populate. */
316cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
317{
318 const char *end=0;
319 cJSON *c=cJSON_New_Item();
320 ep=0;
321 if (!c) return 0; /* memory fail */
322
323 end=parse_value(c,skip(value));
324 if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
325
326 /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
327 if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
328 if (return_parse_end) *return_parse_end=end;
329 return c;
330}
331/* Default options for cJSON_Parse */
332cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
333
334/* Render a cJSON item/entity/structure to text. */
335char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);}
336char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);}
337
338char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt)
339{
340 printbuffer p;
341 p.buffer=(char*)cJSON_malloc(prebuffer);
342 p.length=prebuffer;
343 p.offset=0;
344 return print_value(item,0,fmt,&p);
345 return p.buffer;
346}
347
348
349/* Parser core - when encountering text, process appropriately. */
350static const char *parse_value(cJSON *item,const char *value)
351{
352 if (!value) return 0; /* Fail on null. */
353 if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
354 if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
355 if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
356 if (*value=='\"') { return parse_string(item,value); }
357 if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
358 if (*value=='[') { return parse_array(item,value); }
359 if (*value=='{') { return parse_object(item,value); }
360
361 ep=value;return 0; /* failure. */
362}
363
364/* Render a value to text. */
365static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p)
366{
367 char *out=0;
368 if (!item) return 0;
369 if (p)
370 {
371 switch ((item->type)&255)
372 {
373 case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;}
374 case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;}
375 case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;}
376 case cJSON_Number: out=print_number(item,p);break;
377 case cJSON_String: out=print_string(item,p);break;
378 case cJSON_Array: out=print_array(item,depth,fmt,p);break;
379 case cJSON_Object: out=print_object(item,depth,fmt,p);break;
380 }
381 }
382 else
383 {
384 switch ((item->type)&255)
385 {
386 case cJSON_NULL: out=cJSON_strdup("null"); break;
387 case cJSON_False: out=cJSON_strdup("false");break;
388 case cJSON_True: out=cJSON_strdup("true"); break;
389 case cJSON_Number: out=print_number(item,0);break;
390 case cJSON_String: out=print_string(item,0);break;
391 case cJSON_Array: out=print_array(item,depth,fmt,0);break;
392 case cJSON_Object: out=print_object(item,depth,fmt,0);break;
393 }
394 }
395 return out;
396}
397
398/* Build an array from input text. */
399static const char *parse_array(cJSON *item,const char *value)
400{
401 cJSON *child;
402 if (*value!='[') {ep=value;return 0;} /* not an array! */
403
404 item->type=cJSON_Array;
405 value=skip(value+1);
406 if (*value==']') return value+1; /* empty array. */
407
408 item->child=child=cJSON_New_Item();
409 if (!item->child) return 0; /* memory fail */
410 value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
411 if (!value) return 0;
412
413 while (*value==',')
414 {
415 cJSON *new_item;
416 if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
417 child->next=new_item;new_item->prev=child;child=new_item;
418 value=skip(parse_value(child,skip(value+1)));
419 if (!value) return 0; /* memory fail */
420 }
421
422 if (*value==']') return value+1; /* end of array */
423 ep=value;return 0; /* malformed. */
424}
425
426/* Render an array to text */
427static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p)
428{
429 char **entries;
Tony Barbour3b37a422015-07-13 15:06:12 -0600430 char *out=0,*ptr,*ret;size_t len=5;
431 cJSON *child=item->child;
432 int numentries=0,fail=0, j=0;
433 size_t tmplen=0,i=0;
Jon Ashburn752e06e2015-06-29 11:25:34 -0600434
435 /* How many entries in the array? */
436 while (child) numentries++,child=child->next;
437 /* Explicitly handle numentries==0 */
438 if (!numentries)
439 {
440 if (p) out=ensure(p,3);
441 else out=(char*)cJSON_malloc(3);
442 if (out) strcpy(out,"[]");
443 return out;
444 }
445
446 if (p)
447 {
448 /* Compose the output array. */
449 i=p->offset;
450 ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++;
451 child=item->child;
452 while (child && !fail)
453 {
454 print_value(child,depth+1,fmt,p);
455 p->offset=update(p);
456 if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;}
457 child=child->next;
458 }
459 ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0;
460 out=(p->buffer)+i;
461 }
462 else
463 {
464 /* Allocate an array to hold the values for each */
465 entries=(char**)cJSON_malloc(numentries*sizeof(char*));
466 if (!entries) return 0;
467 memset(entries,0,numentries*sizeof(char*));
468 /* Retrieve all the results: */
469 child=item->child;
470 while (child && !fail)
471 {
472 ret=print_value(child,depth+1,fmt,0);
473 entries[i++]=ret;
474 if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
475 child=child->next;
476 }
477
478 /* If we didn't fail, try to malloc the output string */
479 if (!fail) out=(char*)cJSON_malloc(len);
480 /* If that fails, we fail. */
481 if (!out) fail=1;
482
483 /* Handle failure. */
484 if (fail)
485 {
Tony Barbour3b37a422015-07-13 15:06:12 -0600486 for (j=0;j<numentries;j++) if (entries[j]) cJSON_free(entries[j]);
Jon Ashburn752e06e2015-06-29 11:25:34 -0600487 cJSON_free(entries);
488 return 0;
489 }
490
491 /* Compose the output array. */
492 *out='[';
493 ptr=out+1;*ptr=0;
Tony Barbour3b37a422015-07-13 15:06:12 -0600494 for (j=0;j<numentries;j++)
Jon Ashburn752e06e2015-06-29 11:25:34 -0600495 {
Tony Barbour3b37a422015-07-13 15:06:12 -0600496 tmplen=strlen(entries[j]);memcpy(ptr,entries[j],tmplen);ptr+=tmplen;
497 if (j!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
498 cJSON_free(entries[j]);
Jon Ashburn752e06e2015-06-29 11:25:34 -0600499 }
500 cJSON_free(entries);
501 *ptr++=']';*ptr++=0;
502 }
503 return out;
504}
505
506/* Build an object from the text. */
507static const char *parse_object(cJSON *item,const char *value)
508{
509 cJSON *child;
510 if (*value!='{') {ep=value;return 0;} /* not an object! */
511
512 item->type=cJSON_Object;
513 value=skip(value+1);
514 if (*value=='}') return value+1; /* empty array. */
515
516 item->child=child=cJSON_New_Item();
517 if (!item->child) return 0;
518 value=skip(parse_string(child,skip(value)));
519 if (!value) return 0;
520 child->string=child->valuestring;child->valuestring=0;
521 if (*value!=':') {ep=value;return 0;} /* fail! */
522 value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
523 if (!value) return 0;
524
525 while (*value==',')
526 {
527 cJSON *new_item;
528 if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
529 child->next=new_item;new_item->prev=child;child=new_item;
530 value=skip(parse_string(child,skip(value+1)));
531 if (!value) return 0;
532 child->string=child->valuestring;child->valuestring=0;
533 if (*value!=':') {ep=value;return 0;} /* fail! */
534 value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
535 if (!value) return 0;
536 }
537
538 if (*value=='}') return value+1; /* end of array */
539 ep=value;return 0; /* malformed. */
540}
541
542/* Render an object to text. */
543static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
544{
545 char **entries=0,**names=0;
Tony Barbour3b37a422015-07-13 15:06:12 -0600546 char *out=0,*ptr,*ret,*str;int j;
Jon Ashburn752e06e2015-06-29 11:25:34 -0600547 cJSON *child=item->child;
Tony Barbour3b37a422015-07-13 15:06:12 -0600548 int numentries=0,fail=0,k;
549 size_t tmplen=0,i=0,len=7;
Jon Ashburn752e06e2015-06-29 11:25:34 -0600550 /* Count the number of entries. */
551 while (child) numentries++,child=child->next;
552 /* Explicitly handle empty object case */
553 if (!numentries)
554 {
555 if (p) out=ensure(p,fmt?depth+4:3);
556 else out=(char*)cJSON_malloc(fmt?depth+4:3);
557 if (!out) return 0;
558 ptr=out;*ptr++='{';
Tony Barbour3b37a422015-07-13 15:06:12 -0600559 if (fmt) {*ptr++='\n';for (j=0;j<depth-1;j++) *ptr++='\t';}
Jon Ashburn752e06e2015-06-29 11:25:34 -0600560 *ptr++='}';*ptr++=0;
561 return out;
562 }
563 if (p)
564 {
565 /* Compose the output: */
566 i=p->offset;
567 len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0;
568 *ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len;
569 child=item->child;depth++;
570 while (child)
571 {
572 if (fmt)
573 {
574 ptr=ensure(p,depth); if (!ptr) return 0;
575 for (j=0;j<depth;j++) *ptr++='\t';
576 p->offset+=depth;
577 }
578 print_string_ptr(child->string,p);
579 p->offset=update(p);
580
581 len=fmt?2:1;
582 ptr=ensure(p,len); if (!ptr) return 0;
583 *ptr++=':';if (fmt) *ptr++='\t';
584 p->offset+=len;
585
586 print_value(child,depth,fmt,p);
587 p->offset=update(p);
588
589 len=(fmt?1:0)+(child->next?1:0);
590 ptr=ensure(p,len+1); if (!ptr) return 0;
591 if (child->next) *ptr++=',';
592 if (fmt) *ptr++='\n';*ptr=0;
593 p->offset+=len;
594 child=child->next;
595 }
596 ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0;
Tony Barbour3b37a422015-07-13 15:06:12 -0600597 if (fmt) for (j=0;j<depth-1;j++) *ptr++='\t';
Jon Ashburn752e06e2015-06-29 11:25:34 -0600598 *ptr++='}';*ptr=0;
599 out=(p->buffer)+i;
600 }
601 else
602 {
603 /* Allocate space for the names and the objects */
604 entries=(char**)cJSON_malloc(numentries*sizeof(char*));
605 if (!entries) return 0;
606 names=(char**)cJSON_malloc(numentries*sizeof(char*));
607 if (!names) {cJSON_free(entries);return 0;}
608 memset(entries,0,sizeof(char*)*numentries);
609 memset(names,0,sizeof(char*)*numentries);
610
611 /* Collect all the results into our arrays: */
612 child=item->child;depth++;if (fmt) len+=depth;
613 while (child)
614 {
615 names[i]=str=print_string_ptr(child->string,0);
616 entries[i++]=ret=print_value(child,depth,fmt,0);
617 if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
618 child=child->next;
619 }
620
621 /* Try to allocate the output string */
622 if (!fail) out=(char*)cJSON_malloc(len);
623 if (!out) fail=1;
624
625 /* Handle failure */
626 if (fail)
627 {
Tony Barbour3b37a422015-07-13 15:06:12 -0600628 for (j=0;j<numentries;j++) {if (names[i]) cJSON_free(names[j]);if (entries[j]) cJSON_free(entries[j]);}
Jon Ashburn752e06e2015-06-29 11:25:34 -0600629 cJSON_free(names);cJSON_free(entries);
630 return 0;
631 }
632
633 /* Compose the output: */
634 *out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
Tony Barbour3b37a422015-07-13 15:06:12 -0600635 for (j=0;j<numentries;j++)
Jon Ashburn752e06e2015-06-29 11:25:34 -0600636 {
Tony Barbour3b37a422015-07-13 15:06:12 -0600637 if (fmt) for (k=0;k<depth;k++) *ptr++='\t';
638 tmplen=strlen(names[j]);memcpy(ptr,names[j],tmplen);ptr+=tmplen;
Jon Ashburn752e06e2015-06-29 11:25:34 -0600639 *ptr++=':';if (fmt) *ptr++='\t';
Tony Barbour3b37a422015-07-13 15:06:12 -0600640 strcpy(ptr,entries[j]);ptr+=strlen(entries[j]);
641 if (j!=numentries-1) *ptr++=',';
Jon Ashburn752e06e2015-06-29 11:25:34 -0600642 if (fmt) *ptr++='\n';*ptr=0;
Tony Barbour3b37a422015-07-13 15:06:12 -0600643 cJSON_free(names[j]);cJSON_free(entries[j]);
Jon Ashburn752e06e2015-06-29 11:25:34 -0600644 }
645
646 cJSON_free(names);cJSON_free(entries);
Tony Barbour3b37a422015-07-13 15:06:12 -0600647 if (fmt) for (j=0;j<depth-1;j++) *ptr++='\t';
Jon Ashburn752e06e2015-06-29 11:25:34 -0600648 *ptr++='}';*ptr++=0;
649 }
650 return out;
651}
652
653/* Get Array size/item / object item. */
654int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
655cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
Antoine Labourc3371a92016-01-20 16:32:17 -0800656cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && strcmp(c->string,string)) c=c->next; return c;}
Jon Ashburn752e06e2015-06-29 11:25:34 -0600657
658/* Utility for array list handling. */
659static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
660/* Utility for handling references. */
661static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
662
663/* Add item to array/object. */
664void 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);}}
665void 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);}
666void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);}
667void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
668void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
669
670cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
671 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;}
672void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
Antoine Labourc3371a92016-01-20 16:32:17 -0800673cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && strcmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
Jon Ashburn752e06e2015-06-29 11:25:34 -0600674void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
675
676/* Replace array/object items with new ones. */
677void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;}
678 newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;}
679void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
680 newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
681 if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
Antoine Labourc3371a92016-01-20 16:32:17 -0800682void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && strcmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
Jon Ashburn752e06e2015-06-29 11:25:34 -0600683
684/* Create basic types: */
685cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
686cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
687cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
688cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
689cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
690cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
691cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
692cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
693
694/* Create Arrays: */
695cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
696cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
697cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
698cJSON *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;}
699
700/* Duplication */
701cJSON *cJSON_Duplicate(cJSON *item,int recurse)
702{
703 cJSON *newitem,*cptr,*nptr=0,*newchild;
704 /* Bail on bad ptr */
705 if (!item) return 0;
706 /* Create new item */
707 newitem=cJSON_New_Item();
708 if (!newitem) return 0;
709 /* Copy over all vars */
710 newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
711 if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}
712 if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}
713 /* If non-recursive, then we're done! */
714 if (!recurse) return newitem;
715 /* Walk the ->next chain for the child. */
716 cptr=item->child;
717 while (cptr)
718 {
719 newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
720 if (!newchild) {cJSON_Delete(newitem);return 0;}
721 if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */
722 else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */
723 cptr=cptr->next;
724 }
725 return newitem;
726}
727
728void cJSON_Minify(char *json)
729{
730 char *into=json;
731 while (*json)
732 {
733 if (*json==' ') json++;
734 else if (*json=='\t') json++; /* Whitespace characters. */
735 else if (*json=='\r') json++;
736 else if (*json=='\n') json++;
737 else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */
738 else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */
739 else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */
740 else *into++=*json++; /* All other characters. */
741 }
742 *into=0; /* and null-terminate. */
743}