1 /* Copyright 2018, UCAR/Unidata.
2 See the COPYRIGHT file for more information.
3 */
4
5 /*
6 TODO: make utf8 safe
7 */
8
9 #define NCJSON_INTERNAL
10
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <assert.h>
15 #include "ncjson.h"
16
17 #ifdef _WIN32
18 #define strcasecmp _stricmp
19 #else
20 #include <strings.h>
21 #endif
22
23 #undef DEBUG
24
25 #define NCJ_OK 0
26 #define NCJ_ERR 1
27
28 #define NCJ_EOF -1
29
30 #define NCJ_LBRACKET '['
31 #define NCJ_RBRACKET ']'
32 #define NCJ_LBRACE '{'
33 #define NCJ_RBRACE '}'
34 #define NCJ_COLON ':'
35 #define NCJ_COMMA ','
36 #define NCJ_QUOTE '"'
37 #define NCJ_ESCAPE '\\'
38 #define NCJ_TAG_TRUE "true"
39 #define NCJ_TAG_FALSE "false"
40 #define NCJ_TAG_NULL "null"
41
42 /* WORD Subsumes Number also */
43 #define WORD "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$+-."
44
45 /*//////////////////////////////////////////////////*/
46
47 typedef struct NCJparser {
48 char* text;
49 char* pos;
50 size_t yylen; /* |yytext| */
51 char* yytext; /* string or word */
52 long long num;
53 int tf;
54 int err;
55 } NCJparser;
56
57 typedef struct NCJbuf {
58 int len; /* |text|; does not include nul terminator */
59 char* text; /* NULL || nul terminated */
60 } NCJbuf;
61
62 /**************************************************/
63 /* Forward */
64 static int NCJparseR(NCJparser* parser, NCjson**);
65 static int NCJparseArray(NCJparser* parser, struct NCjlist* array);
66 static int NCJparseDict(NCJparser* parser, struct NCjlist* dict);
67 static int testbool(const char* word);
68 static int testint(const char* word);
69 static int testdouble(const char* word);
70 static int testnull(const char* word);
71 static int NCJlex(NCJparser* parser);
72 static int NCJyytext(NCJparser*, char* start, size_t pdlen);
73 static void NCJreclaimArray(struct NCjlist*);
74 static void NCJreclaimDict(struct NCjlist*);
75 static int NCJunparseR(const NCjson* json, NCJbuf* buf, unsigned flags);
76 static int NCJunescape(NCJparser* parser);
77 static int listappend(struct NCjlist* list, NCjson* element);
78 static int bytesappendquoted(NCJbuf* buf, const char*);
79 static int bytesappend(NCJbuf* buf, const char* s);
80 static int bytesappendc(NCJbuf* buf, const char c);
81 static int escape(const char* text, NCJbuf* buf);
82 static int NCJcloneArray(const NCjson* array, NCjson** clonep);
83 static int NCJcloneDict(const NCjson* dict, NCjson** clonep);
84
85 #ifndef nullfree
86 #define nullfree(x) {if(x)free(x);}
87 #endif
88 #ifndef nulldup
89 #define nulldup(x) ((x)?strdup(x):(x))
90 #endif
91
92 #ifdef DEBUG
93 static char* tokenname(int token);
94 #endif
95 /**************************************************/
96 int
NCJparse(const char * text,unsigned flags,NCjson ** jsonp)97 NCJparse(const char* text, unsigned flags, NCjson** jsonp)
98 {
99 int stat = NCJ_OK;
100 size_t len;
101 NCJparser* parser = NULL;
102 NCjson* json = NULL;
103
104 /* Need at least 1 character of input */
105 if(text == NULL || text[0] == '\0')
106 {stat = NCJ_ERR; goto done;}
107 if(jsonp == NULL) goto done;
108 parser = calloc(1,sizeof(NCJparser));
109 if(parser == NULL)
110 {stat = NCJ_ERR; goto done;}
111 len = strlen(text);
112 parser->text = (char*)malloc(len+1+1);
113 if(parser->text == NULL)
114 {stat = NCJ_ERR; goto done;}
115 strcpy(parser->text,text);
116 parser->text[len] = '\0';
117 parser->text[len+1] = '\0';
118 parser->pos = &parser->text[0];
119 #ifdef DEBUG
120 fprintf(stderr,"json: |%s|\n",parser->text);
121 #endif
122 if((stat=NCJparseR(parser,&json))) goto done;
123 *jsonp = json;
124 json = NULL;
125
126 done:
127 if(parser != NULL) {
128 nullfree(parser->text);
129 nullfree(parser->yytext);
130 free(parser);
131 }
132 (void)NCJreclaim(json);
133 return (stat);
134 }
135
136 /*
137 Simple recursive descent
138 intertwined with dict and list parsers.
139
140 Invariants:
141 1. The json argument is provided by caller and filled in by NCJparseR.
142 2. Each call pushed back last unconsumed token
143 */
144
145 static int
NCJparseR(NCJparser * parser,NCjson ** jsonp)146 NCJparseR(NCJparser* parser, NCjson** jsonp)
147 {
148 int stat = NCJ_OK;
149 int token = NCJ_UNDEF;
150 NCjson* json = NULL;
151
152 if(jsonp == NULL)
153 {stat = NCJ_ERR; goto done;}
154 if((token = NCJlex(parser)) == NCJ_UNDEF)
155 {stat = NCJ_ERR; goto done;}
156 switch (token) {
157 case NCJ_EOF:
158 break;
159 case NCJ_NULL:
160 if((stat = NCJnew(NCJ_NULL,&json))) goto done;
161 break;
162 case NCJ_BOOLEAN:
163 if((stat = NCJnew(NCJ_BOOLEAN,&json))) goto done;
164 json->string = strdup(parser->yytext);
165 break;
166 case NCJ_INT:
167 if((stat = NCJnew(NCJ_INT,&json))) goto done;
168 json->string = strdup(parser->yytext);
169 break;
170 case NCJ_DOUBLE:
171 if((stat = NCJnew(NCJ_DOUBLE,&json))) goto done;
172 json->string = strdup(parser->yytext);
173 break;
174 case NCJ_STRING:
175 if((stat = NCJnew(NCJ_STRING,&json))) goto done;
176 json->string = strdup(parser->yytext);
177 break;
178 case NCJ_LBRACE:
179 if((stat = NCJnew(NCJ_DICT,&json))) goto done;
180 if((stat = NCJparseDict(parser, &json->list))) goto done;
181 break;
182 case NCJ_LBRACKET:
183 if((stat = NCJnew(NCJ_ARRAY,&json))) goto done;
184 if((stat = NCJparseArray(parser, &json->list))) goto done;
185 break;
186 case NCJ_RBRACE: /* We hit end of the dict we are parsing */
187 parser->pos--; /* pushback so NCJparseArray will catch */
188 json = NULL;
189 break;
190 case NCJ_RBRACKET:
191 parser->pos--; /* pushback so NCJparseDict will catch */
192 json = NULL;
193 break;
194 default:
195 stat = NCJ_ERR;
196 break;
197 }
198 if(jsonp && json) {*jsonp = json; json = NULL;}
199
200 done:
201 NCJreclaim(json);
202 return (stat);
203 }
204
205 static int
NCJparseArray(NCJparser * parser,struct NCjlist * arrayp)206 NCJparseArray(NCJparser* parser, struct NCjlist* arrayp)
207 {
208 int stat = NCJ_OK;
209 int token = NCJ_UNDEF;
210 NCjson* element = NULL;
211 int stop = 0;
212
213 /* [ ^e1,e2, ...en] */
214
215 while(!stop) {
216 /* Recurse to get the value ei (might be null) */
217 if((stat = NCJparseR(parser,&element))) goto done;
218 token = NCJlex(parser); /* Get next token */
219 /* Next token should be comma or rbracket */
220 switch(token) {
221 case NCJ_RBRACKET:
222 if(element != NULL) listappend(arrayp,element);
223 element = NULL;
224 stop = 1;
225 break;
226 case NCJ_COMMA:
227 /* Append the ei to the list */
228 if(element == NULL) {stat = NCJ_ERR; goto done;} /* error */
229 listappend(arrayp,element);
230 element = NULL;
231 break;
232 case NCJ_EOF:
233 case NCJ_UNDEF:
234 default:
235 stat = NCJ_ERR;
236 goto done;
237 }
238 }
239
240 done:
241 if(element != NULL)
242 NCJreclaim(element);
243 return (stat);
244 }
245
246 static int
NCJparseDict(NCJparser * parser,struct NCjlist * dictp)247 NCJparseDict(NCJparser* parser, struct NCjlist* dictp)
248 {
249 int stat = NCJ_OK;
250 int token = NCJ_UNDEF;
251 NCjson* value = NULL;
252 NCjson* key = NULL;
253 int stop = 0;
254
255 /* { ^k1:v1,k2:v2, ...kn:vn] */
256
257 while(!stop) {
258 /* Get the key, which must be a word of some sort */
259 token = NCJlex(parser);
260 switch(token) {
261 case NCJ_STRING:
262 case NCJ_BOOLEAN:
263 case NCJ_INT: case NCJ_DOUBLE: {
264 if((stat=NCJnewstring(token,parser->yytext,&key))) goto done;
265 } break;
266 case NCJ_RBRACE: /* End of containing Dict */
267 stop = 1;
268 continue; /* leave loop */
269 case NCJ_EOF: case NCJ_UNDEF:
270 default:
271 stat = NCJ_ERR;
272 goto done;
273 }
274 /* Next token must be colon*/
275 switch((token = NCJlex(parser))) {
276 case NCJ_COLON: break;
277 case NCJ_UNDEF: case NCJ_EOF:
278 default: stat = NCJ_ERR; goto done;
279 }
280 /* Get the value */
281 if((stat = NCJparseR(parser,&value))) goto done;
282 /* Next token must be comma or RBRACE */
283 switch((token = NCJlex(parser))) {
284 case NCJ_RBRACE:
285 stop = 1;
286 /* fall thru */
287 case NCJ_COMMA:
288 /* Insert key value into dict: key first, then value */
289 listappend(dictp,key);
290 key = NULL;
291 listappend(dictp,value);
292 value = NULL;
293 break;
294 case NCJ_EOF:
295 case NCJ_UNDEF:
296 default:
297 stat = NCJ_ERR;
298 goto done;
299 }
300 }
301
302 done:
303 if(key != NULL)
304 NCJreclaim(key);
305 if(value != NULL)
306 NCJreclaim(value);
307 return (stat);
308 }
309
310 static int
NCJlex(NCJparser * parser)311 NCJlex(NCJparser* parser)
312 {
313 int c;
314 int token = 0;
315 char* start;
316 size_t count;
317
318 while(token == 0) { /* avoid need to goto when retrying */
319 c = *parser->pos;
320 if(c == '\0') {
321 token = NCJ_EOF;
322 } else if(c <= ' ' || c == '\177') {
323 parser->pos++;
324 continue; /* ignore whitespace */
325 } else if(strchr(WORD, c) != NULL) {
326 start = parser->pos;
327 for(;;) {
328 c = *parser->pos++;
329 if(c == '\0' || strchr(WORD,c) == NULL) break; /* end of word */
330 }
331 /* Pushback c if not whitespace */
332 parser->pos--;
333 count = ((parser->pos) - start);
334 if(NCJyytext(parser,start,count)) goto done;
335 /* Discriminate the word string to get the proper sort */
336 if(testbool(parser->yytext) == NCJ_OK)
337 token = NCJ_BOOLEAN;
338 /* do int test first since double subsumes int */
339 else if(testint(parser->yytext) == NCJ_OK)
340 token = NCJ_INT;
341 else if(testdouble(parser->yytext) == NCJ_OK)
342 token = NCJ_DOUBLE;
343 else if(testnull(parser->yytext) == NCJ_OK)
344 token = NCJ_NULL;
345 else
346 token = NCJ_STRING;
347 } else if(c == NCJ_QUOTE) {
348 parser->pos++;
349 start = parser->pos;
350 for(;;) {
351 c = *parser->pos++;
352 if(c == NCJ_ESCAPE) parser->pos++;
353 else if(c == NCJ_QUOTE || c == '\0') break;
354 }
355 if(c == '\0') {
356 parser->err = NCJ_ERR;
357 token = NCJ_UNDEF;
358 goto done;
359 }
360 count = ((parser->pos) - start) - 1; /* -1 for trailing quote */
361 if(NCJyytext(parser,start,count)) goto done;
362 if(NCJunescape(parser)) goto done;
363 token = NCJ_STRING;
364 } else { /* single char token */
365 if(NCJyytext(parser,parser->pos,1)) goto done;
366 token = *parser->pos++;
367 }
368 #ifdef DEBUG
369 fprintf(stderr,"%s(%d): |%s|\n",tokenname(token),token,parser->yytext);
370 #endif
371 } /*for(;;)*/
372 done:
373 if(parser->err) token = NCJ_UNDEF;
374 return token;
375 }
376
377 static int
testnull(const char * word)378 testnull(const char* word)
379 {
380 if(strcasecmp(word,NCJ_TAG_NULL)==0)
381 return NCJ_OK;
382 return NCJ_ERR;
383 }
384
385 static int
testbool(const char * word)386 testbool(const char* word)
387 {
388 if(strcasecmp(word,NCJ_TAG_TRUE)==0
389 || strcasecmp(word,NCJ_TAG_FALSE)==0)
390 return NCJ_OK;
391 return NCJ_ERR;
392 }
393
394 static int
testint(const char * word)395 testint(const char* word)
396 {
397 int ncvt;
398 long long i;
399 int count = 0;
400 /* Try to convert to number */
401 ncvt = sscanf(word,"%lld%n",&i,&count);
402 return (ncvt == 1 && strlen(word)==count ? NCJ_OK : NCJ_ERR);
403 }
404
405 static int
testdouble(const char * word)406 testdouble(const char* word)
407 {
408 int ncvt;
409 double d;
410 int count = 0;
411 /* Check for Nan and Infinity */
412 if(strcasecmp("nan",word)==0) return NCJ_OK;
413 if(strcasecmp("infinity",word)==0) return NCJ_OK;
414 if(strcasecmp("-infinity",word)==0) return NCJ_OK;
415 /* Allow the XXXf versions as well */
416 if(strcasecmp("nanf",word)==0) return NCJ_OK;
417 if(strcasecmp("infinityf",word)==0) return NCJ_OK;
418 if(strcasecmp("-infinityf",word)==0) return NCJ_OK;
419 /* Try to convert to number */
420 ncvt = sscanf(word,"%lg%n",&d,&count);
421 return (ncvt == 1 && strlen(word)==count ? NCJ_OK : NCJ_ERR);
422 }
423
424 static int
NCJyytext(NCJparser * parser,char * start,size_t pdlen)425 NCJyytext(NCJparser* parser, char* start, size_t pdlen)
426 {
427 size_t len = (size_t)pdlen;
428 if(parser->yytext == NULL) {
429 parser->yytext = (char*)malloc(len+1);
430 parser->yylen = len;
431 } else if(parser->yylen <= len) {
432 parser->yytext = (char*) realloc(parser->yytext,len+1);
433 parser->yylen = len;
434 }
435 if(parser->yytext == NULL) return NCJ_ERR;
436 memcpy(parser->yytext,start,len);
437 parser->yytext[len] = '\0';
438 return NCJ_OK;
439 }
440
441 /**************************************************/
442
443 void
NCJreclaim(NCjson * json)444 NCJreclaim(NCjson* json)
445 {
446 if(json == NULL) return;
447 switch(json->sort) {
448 case NCJ_INT:
449 case NCJ_DOUBLE:
450 case NCJ_BOOLEAN:
451 case NCJ_STRING:
452 nullfree(json->string);
453 break;
454 case NCJ_DICT:
455 NCJreclaimDict(&json->list);
456 break;
457 case NCJ_ARRAY:
458 NCJreclaimArray(&json->list);
459 break;
460 default: break; /* nothing to reclaim */
461 }
462 free(json);
463 }
464
465 static void
NCJreclaimArray(struct NCjlist * array)466 NCJreclaimArray(struct NCjlist* array)
467 {
468 int i;
469 for(i=0;i<array->len;i++) {
470 NCJreclaim(array->contents[i]);
471 }
472 nullfree(array->contents);
473 array->contents = NULL;
474 }
475
476 static void
NCJreclaimDict(struct NCjlist * dict)477 NCJreclaimDict(struct NCjlist* dict)
478 {
479 return NCJreclaimArray(dict);
480 }
481
482 int
NCJclone(const NCjson * json,NCjson ** clonep)483 NCJclone(const NCjson* json, NCjson** clonep)
484 {
485 int stat = NCJ_OK;
486 NCjson* clone = NULL;
487 if(json == NULL) goto done;
488 switch(NCJsort(json)) {
489 case NCJ_INT:
490 case NCJ_DOUBLE:
491 case NCJ_BOOLEAN:
492 case NCJ_STRING:
493 if((stat=NCJnew(NCJsort(json),&clone))) goto done;
494 if((NCJstring(clone) = strdup(NCJstring(json))) == NULL)
495 {stat = NCJ_ERR; goto done;}
496 break;
497 case NCJ_NULL:
498 if((stat=NCJnew(NCJsort(json),&clone))) goto done;
499 break;
500 case NCJ_DICT:
501 if((stat=NCJcloneDict(json,&clone))) goto done;
502 break;
503 case NCJ_ARRAY:
504 if((stat=NCJcloneArray(json,&clone))) goto done;
505 break;
506 default: break; /* nothing to clone */
507 }
508 done:
509 if(stat == NCJ_OK && clonep) {*clonep = clone; clone = NULL;}
510 NCJreclaim(clone);
511 return stat;
512 }
513
514 static int
NCJcloneArray(const NCjson * array,NCjson ** clonep)515 NCJcloneArray(const NCjson* array, NCjson** clonep)
516 {
517 int i, stat=NCJ_OK;
518 NCjson* clone = NULL;
519 if((stat=NCJnew(NCJ_ARRAY,&clone))) goto done;
520 for(i=0;i<NCJlength(array);i++) {
521 NCjson* elem = NCJith(array,i);
522 NCjson* elemclone = NULL;
523 if((stat=NCJclone(elem,&elemclone))) goto done;
524 NCJappend(clone,elemclone);
525 }
526 done:
527 if(stat == NCJ_OK && clonep) {*clonep = clone; clone = NULL;}
528 NCJreclaim(clone);
529 return stat;
530 }
531
532 static int
NCJcloneDict(const NCjson * dict,NCjson ** clonep)533 NCJcloneDict(const NCjson* dict, NCjson** clonep)
534 {
535 int i, stat=NCJ_OK;
536 NCjson* clone = NULL;
537 if((stat=NCJnew(NCJ_DICT,&clone))) goto done;
538 for(i=0;i<NCJlength(dict);i++) {
539 NCjson* elem = NCJith(dict,i);
540 NCjson* elemclone = NULL;
541 if((stat=NCJclone(elem,&elemclone))) goto done;
542 NCJappend(clone,elemclone);
543 }
544 done:
545 if(stat == NCJ_OK && clonep) {*clonep = clone; clone = NULL;}
546 NCJreclaim(clone);
547 return stat;
548 }
549
550 /**************************************************/
551 /* Build Functions */
552
553 int
NCJnew(int sort,NCjson ** objectp)554 NCJnew(int sort, NCjson** objectp)
555 {
556 int stat = NCJ_OK;
557 NCjson* object = NULL;
558
559 if((object = (NCjson*)calloc(1,sizeof(NCjson))) == NULL)
560 {stat = NCJ_ERR; goto done;}
561 NCJsetsort(object,sort);
562 switch (sort) {
563 case NCJ_INT:
564 case NCJ_DOUBLE:
565 case NCJ_BOOLEAN:
566 case NCJ_STRING:
567 case NCJ_NULL:
568 break;
569 case NCJ_DICT:
570 case NCJ_ARRAY:
571 break;
572 default:
573 stat = NCJ_ERR;
574 goto done;
575 }
576 if(objectp) {*objectp = object; object = NULL;}
577
578 done:
579 if(stat) NCJreclaim(object);
580 return (stat);
581 }
582
583 int
NCJnewstring(int sort,const char * value,NCjson ** jsonp)584 NCJnewstring(int sort, const char* value, NCjson** jsonp)
585 {
586 return NCJnewstringn(sort,strlen(value),value,jsonp);
587 }
588
589 int
NCJnewstringn(int sort,size_t len,const char * value,NCjson ** jsonp)590 NCJnewstringn(int sort, size_t len, const char* value, NCjson** jsonp)
591 {
592 int stat = NCJ_OK;
593 NCjson* json = NULL;
594
595 if(jsonp) *jsonp = NULL;
596 if(value == NULL)
597 {stat = NCJ_ERR; goto done;}
598 if((stat = NCJnew(sort,&json)))
599 goto done;
600 if((json->string = (char*)malloc(len+1))==NULL)
601 {stat = NCJ_ERR; goto done;}
602 memcpy(json->string,value,len);
603 json->string[len] = '\0';
604 if(jsonp) *jsonp = json;
605 json = NULL; /* avoid memory errors */
606 done:
607 NCJreclaim(json);
608 return (stat);
609 }
610
611 int
NCJaddstring(NCjson * json,int sort,const char * s)612 NCJaddstring(NCjson* json, int sort, const char* s)
613 {
614 int stat = NCJ_OK;
615 NCjson* jtmp = NULL;
616
617 if(NCJsort(json) != NCJ_DICT && NCJsort(json) != NCJ_ARRAY)
618 {stat = NCJ_ERR; goto done;}
619 if((stat = NCJnewstring(sort, s, &jtmp))) goto done;
620 if((stat = NCJappend(json,jtmp))) goto done;
621 jtmp = NULL;
622
623 done:
624 NCJreclaim(jtmp);
625 return stat;
626 }
627
628 int
NCJdictget(const NCjson * dict,const char * key,NCjson ** valuep)629 NCJdictget(const NCjson* dict, const char* key, NCjson** valuep)
630 {
631 int i,stat = NCJ_OK;
632
633 if(dict == NULL || dict->sort != NCJ_DICT)
634 {stat = NCJ_ERR; goto done;}
635 if(valuep) {*valuep = NULL;}
636 for(i=0;i<NCJlength(dict);i+=2) {
637 NCjson* jkey = NCJith(dict,i);
638 if(jkey->string != NULL && strcmp(jkey->string,key)==0) {
639 if(valuep) {*valuep = NCJith(dict,i+1); break;}
640 }
641 }
642
643 done:
644 return stat;
645 }
646
647 /* Insert key-value pair into a dict object. key will be strdup'd */
648 int
NCJinsert(NCjson * object,char * key,NCjson * jvalue)649 NCJinsert(NCjson* object, char* key, NCjson* jvalue)
650 {
651 int stat = NCJ_OK;
652 NCjson* jkey = NULL;
653 if(object == NULL || object->sort != NCJ_DICT || key == NULL || jvalue == NULL)
654 {stat = NCJ_ERR; goto done;}
655 if((stat = NCJnewstring(NCJ_STRING,key,&jkey))) goto done;
656 if((stat = NCJappend(object,jkey))) goto done;
657 if((stat = NCJappend(object,jvalue))) goto done;
658 done:
659 return stat;
660 }
661
662 /* Append value to an array or dict object. */
663 int
NCJappend(NCjson * object,NCjson * value)664 NCJappend(NCjson* object, NCjson* value)
665 {
666 if(object == NULL || value == NULL)
667 return NCJ_ERR;
668 switch (object->sort) {
669 case NCJ_ARRAY:
670 case NCJ_DICT:
671 listappend(&object->list,value);
672 break;
673 default:
674 return NCJ_ERR;
675 }
676 return NCJ_OK;
677 }
678
679 /* Unescape the text in parser->yytext; can
680 do in place because unescaped string will
681 always be shorter */
682 static int
NCJunescape(NCJparser * parser)683 NCJunescape(NCJparser* parser)
684 {
685 char* p = parser->yytext;
686 char* q = p;
687 int c;
688 for(;(c=*p++);) {
689 if(c == NCJ_ESCAPE) {
690 c = *p++;
691 switch (c) {
692 case 'b': c = '\b'; break;
693 case 'f': c = '\f'; break;
694 case 'n': c = '\n'; break;
695 case 'r': c = '\r'; break;
696 case 't': c = '\t'; break;
697 case NCJ_QUOTE: c = c; break;
698 case NCJ_ESCAPE: c = c; break;
699 default: c = c; break;/* technically not Json conformant */
700 }
701 }
702 *q++ = c;
703 }
704 *q = '\0';
705 return NCJ_OK;
706 }
707
708 /**************************************************/
709 /* Unparser to convert NCjson object to text in buffer */
710
711 int
NCJunparse(const NCjson * json,unsigned flags,char ** textp)712 NCJunparse(const NCjson* json, unsigned flags, char** textp)
713 {
714 int stat = NCJ_OK;
715 NCJbuf buf = {0,NULL};
716 if((stat = NCJunparseR(json,&buf,flags)))
717 goto done;
718 if(textp) {*textp = buf.text; buf.text = NULL; buf.len = 0;}
719 done:
720 nullfree(buf.text);
721 return (stat);
722 }
723
724 static int
NCJunparseR(const NCjson * json,NCJbuf * buf,unsigned flags)725 NCJunparseR(const NCjson* json, NCJbuf* buf, unsigned flags)
726 {
727 int stat = NCJ_OK;
728 int i;
729
730 switch (NCJsort(json)) {
731 case NCJ_STRING:
732 bytesappendquoted(buf,json->string);
733 break;
734 case NCJ_INT:
735 case NCJ_DOUBLE:
736 case NCJ_BOOLEAN:
737 bytesappend(buf,json->string);
738 break;
739 case NCJ_DICT:
740 bytesappendc(buf,NCJ_LBRACE);
741 if(json->list.len > 0 && json->list.contents != NULL) {
742 int shortlist = 0;
743 for(i=0;!shortlist && i < json->list.len;i+=2) {
744 if(i > 0) bytesappendc(buf,NCJ_COMMA);
745 NCJunparseR(json->list.contents[i],buf,flags); /* key */
746 bytesappendc(buf,NCJ_COLON);
747 bytesappendc(buf,' ');
748 /* Allow for the possibility of a short dict entry */
749 if(json->list.contents[i+1] == NULL) { /* short */
750 bytesappendc(buf,'?');
751 shortlist = 1;
752 } else {
753 NCJunparseR(json->list.contents[i+1],buf,flags);
754 }
755 }
756 }
757 bytesappendc(buf,NCJ_RBRACE);
758 break;
759 case NCJ_ARRAY:
760 bytesappendc(buf,NCJ_LBRACKET);
761 if(json->list.len > 0 && json->list.contents != NULL) {
762 for(i=0;i < json->list.len;i++) {
763 if(i > 0) bytesappendc(buf,NCJ_COMMA);
764 NCJunparseR(json->list.contents[i],buf,flags);
765 }
766 }
767 bytesappendc(buf,NCJ_RBRACKET);
768 break;
769 case NCJ_NULL:
770 bytesappend(buf,"null");
771 break;
772 default:
773 stat = NCJ_ERR; goto done;
774 }
775 done:
776 return (stat);
777 }
778
779 /* Escape a string and append to buf */
780 static int
escape(const char * text,NCJbuf * buf)781 escape(const char* text, NCJbuf* buf)
782 {
783 const char* p = text;
784 int c;
785 for(;(c=*p++);) {
786 char replace = 0;
787 switch (c) {
788 case '\b': replace = 'b'; break;
789 case '\f': replace = 'f'; break;
790 case '\n': replace = 'n'; break;
791 case '\r': replace = 'r'; break;
792 case '\t': replace = 't'; break;
793 case NCJ_QUOTE: replace = '\''; break;
794 case NCJ_ESCAPE: replace = '\\'; break;
795 default: break;
796 }
797 if(replace) {
798 bytesappendc(buf,NCJ_ESCAPE);
799 bytesappendc(buf,replace);
800 } else
801 bytesappendc(buf,c);
802 }
803 return NCJ_OK;
804 }
805
806 static int
bytesappendquoted(NCJbuf * buf,const char * s)807 bytesappendquoted(NCJbuf* buf, const char* s)
808 {
809 bytesappend(buf,"\"");
810 escape(s,buf);
811 bytesappend(buf,"\"");
812 return NCJ_OK;
813 }
814
815 void
NCJdump(const NCjson * json,unsigned flags,FILE * out)816 NCJdump(const NCjson* json, unsigned flags, FILE* out)
817 {
818 char* text = NULL;
819 (void)NCJunparse(json,0,&text);
820 if(out == NULL) out = stderr;
821 fprintf(out,"%s\n",text);
822 fflush(out);
823 nullfree(text);
824 }
825
826 #ifdef DEBUG
827 static char*
tokenname(int token)828 tokenname(int token)
829 {
830 switch (token) {
831 case NCJ_STRING: return "NCJ_STRING";
832 case NCJ_INT: return "NCJ_INT";
833 case NCJ_DOUBLE: return "NCJ_DOUBLE";
834 case NCJ_BOOLEAN: return "NCJ_BOOLEAN";
835 case NCJ_DICT: return "NCJ_DICT";
836 case NCJ_ARRAY: return "NCJ_ARRAY";
837 case NCJ_NULL: return "NCJ_NULL";
838 default:
839 if(token > ' ' && token <= 127) {
840 static char s[4];
841 s[0] = '\'';
842 s[1] = (char)token;
843 s[2] = '\'';
844 s[3] = '\0';
845 return s;
846 } else
847 break;
848 }
849 return "NCJ_UNDEF";
850 }
851 #endif
852
853
854 /* Convert a JSON value to an equivalent value of a specified sort */
855 int
NCJcvt(const NCjson * jvalue,int outsort,struct NCJconst * output)856 NCJcvt(const NCjson* jvalue, int outsort, struct NCJconst* output)
857 {
858 int stat = NCJ_OK;
859
860 if(output == NULL) goto done;
861
862 #undef CASE
863 #define CASE(t1,t2) ((t1)<<4 | (t2)) /* the shift constant must be larger than log2(NCJ_NSORTS) */
864 switch (CASE(jvalue->sort,outsort)) {
865
866 case CASE(NCJ_BOOLEAN,NCJ_BOOLEAN):
867 if(strcasecmp(jvalue->string,NCJ_TAG_FALSE)==0) output->bval = 0; else output->bval = 1;
868 break;
869 case CASE(NCJ_BOOLEAN,NCJ_INT):
870 if(strcasecmp(jvalue->string,NCJ_TAG_FALSE)==0) output->ival = 0; else output->ival = 1;
871 break;
872 case CASE(NCJ_BOOLEAN,NCJ_DOUBLE):
873 if(strcasecmp(jvalue->string,NCJ_TAG_FALSE)==0) output->dval = 0.0; else output->dval = 1.0;
874 break;
875 case CASE(NCJ_BOOLEAN,NCJ_STRING):
876 output->sval = nulldup(jvalue->string);
877 break;
878
879 case CASE(NCJ_INT,NCJ_BOOLEAN):
880 sscanf(jvalue->string,"%lldd",&output->ival);
881 output->bval = (output->ival?1:0);
882 break;
883 case CASE(NCJ_INT,NCJ_INT):
884 sscanf(jvalue->string,"%lld",&output->ival);
885 break;
886 case CASE(NCJ_INT,NCJ_DOUBLE):
887 sscanf(jvalue->string,"%lld",&output->ival);
888 output->dval = (double)output->ival;
889 break;
890 case CASE(NCJ_INT,NCJ_STRING):
891 output->sval = nulldup(jvalue->string);
892 break;
893
894 case CASE(NCJ_DOUBLE,NCJ_BOOLEAN):
895 sscanf(jvalue->string,"%lf",&output->dval);
896 output->bval = (output->dval == 0?0:1);
897 break;
898 case CASE(NCJ_DOUBLE,NCJ_INT):
899 sscanf(jvalue->string,"%lf",&output->dval);
900 output->ival = (long long)output->dval;
901 break;
902 case CASE(NCJ_DOUBLE,NCJ_DOUBLE):
903 sscanf(jvalue->string,"%lf",&output->dval);
904 break;
905 case CASE(NCJ_DOUBLE,NCJ_STRING):
906 output->sval = nulldup(jvalue->string);
907 break;
908
909 case CASE(NCJ_STRING,NCJ_BOOLEAN):
910 if(strcasecmp(jvalue->string,NCJ_TAG_FALSE)==0) output->bval = 0; else output->bval = 1;
911 break;
912 case CASE(NCJ_STRING,NCJ_INT):
913 sscanf(jvalue->string,"%lld",&output->ival);
914 break;
915 case CASE(NCJ_STRING,NCJ_DOUBLE):
916 sscanf(jvalue->string,"%lf",&output->dval);
917 break;
918 case CASE(NCJ_STRING,NCJ_STRING):
919 output->sval = nulldup(jvalue->string);
920 break;
921
922 default:
923 stat = NCJ_ERR;
924 break;
925 }
926
927 done:
928 return stat;
929 }
930
931 static int
listappend(struct NCjlist * list,NCjson * json)932 listappend(struct NCjlist* list, NCjson* json)
933 {
934 int stat = NCJ_OK;
935 NCjson** newcontents = NULL;
936
937 assert(list->len == 0 || list->contents != NULL);
938 if(json == NULL)
939 {stat = NCJ_ERR; goto done;}
940 if(list->len == 0) {
941 nullfree(list->contents);
942 list->contents = (NCjson**)calloc(2,sizeof(NCjson*));
943 if(list->contents == NULL)
944 {stat = NCJ_ERR; goto done;}
945 list->contents[0] = json;
946 list->len++;
947 } else {
948 if((newcontents = (NCjson**)calloc((2*list->len)+1,sizeof(NCjson*)))==NULL)
949 {stat = NCJ_ERR; goto done;}
950 memcpy(newcontents,list->contents,list->len*sizeof(NCjson*));
951 newcontents[list->len] = json;
952 list->len++;
953 free(list->contents);
954 list->contents = newcontents; newcontents = NULL;
955 }
956
957 done:
958 nullfree(newcontents);
959 return stat;
960 }
961
962 static int
bytesappend(NCJbuf * buf,const char * s)963 bytesappend(NCJbuf* buf, const char* s)
964 {
965 int stat = NCJ_OK;
966 char* newtext = NULL;
967 if(buf == NULL)
968 {stat = NCJ_ERR; goto done;}
969 if(s == NULL) s = "";
970 if(buf->len == 0) {
971 assert(buf->text == NULL);
972 buf->text = strdup(s);
973 if(buf->text == NULL)
974 {stat = NCJ_ERR; goto done;}
975 buf->len = strlen(s);
976 } else {
977 size_t slen = strlen(s);
978 size_t newlen = buf->len + slen + 1;
979 if((newtext = (char*)malloc(newlen))==NULL)
980 {stat = NCJ_ERR; goto done;}
981 strcpy(newtext,buf->text);
982 strcat(newtext,s);
983 free(buf->text); buf->text = NULL;
984 buf->text = newtext; newtext = NULL;
985 buf->len = newlen;
986 }
987
988 done:
989 nullfree(newtext);
990 return stat;
991 }
992
993 static int
bytesappendc(NCJbuf * bufp,const char c)994 bytesappendc(NCJbuf* bufp, const char c)
995 {
996 char s[2];
997 s[0] = c;
998 s[1] = '\0';
999 return bytesappend(bufp,s);
1000 }
1001