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