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