Coverage Report

Created: 2025-10-28 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/netcdf-c/libdispatch/ncjson.c
Line
Count
Source
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 go to
13
the include/ directory
14
and do the command:
15
    make netcdf_json.h
16
*/
17
18
#ifdef HAVE_CONFIG_H
19
#include "config.h"
20
#endif /*HAVE_CONFIG_H*/
21
#include <stdlib.h>
22
#include <stdio.h>
23
#include <string.h>
24
#include <assert.h>
25
26
#include "ncjson.h"
27
28
#undef NCJCATCH
29
#undef NCJDEBUG
30
#undef NCJTRACE
31
32
#ifdef NCJCATCH
33
/* Warning: do not evaluate err more than once */
34
#define NCJTHROW(err) ncjbreakpoint(err)
35
static int ncjbreakpoint(int err) {return err;}
36
#else /*!NCJCATCH*/
37
0
#define NCJTHROW(err) (err)
38
#endif /*NCJCATCH*/
39
40
/**************************************************/
41
42
0
#define NCJ_EOF -2
43
44
0
#define NCJ_LBRACKET '['
45
0
#define NCJ_RBRACKET ']'
46
0
#define NCJ_LBRACE '{'
47
0
#define NCJ_RBRACE '}'
48
0
#define NCJ_COLON ':'
49
0
#define NCJ_COMMA ','
50
0
#define NCJ_QUOTE '"'
51
0
#define NCJ_ESCAPE '\\'
52
0
#define NCJ_TAG_TRUE "true"
53
0
#define NCJ_TAG_FALSE "false"
54
0
#define NCJ_TAG_NULL "null"
55
56
/* JSON_WORD Subsumes Number also */
57
0
#define JSON_WORD "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$+-."
58
59
#define NCJ_DEFAULTALLOC 16
60
61
/**************************************************/
62
typedef struct NCJparser {
63
    char* text;
64
    char* pos;
65
    size_t yylen; /* |yytext| */
66
    char* yytext; /* string or word */
67
    long long num;
68
    int tf;
69
    int status; /* NCJ_ERR|NCJ_OK */
70
    unsigned flags;
71
#     define NCJ_TRACE 1
72
} NCJparser;
73
74
/* This is used only by the unparser */
75
typedef struct NCJbuf {
76
    size_t len; /* |text|; does not include nul terminator */
77
    char* text; /* NULL || nul terminated */
78
} NCJbuf;
79
80
/**************************************************/
81
82
#if defined(_WIN32) && !defined(__MINGW32__)
83
#define strdup _strdup
84
#define strcasecmp _stricmp
85
#else /*!WIN32 || __MINGW32*/
86
#include <strings.h>
87
#endif /*defined(_WIN32) && !defined(__MINGW32__)*/
88
89
#ifndef nullfree
90
#define nullfree(x) {if(x)free(x);}
91
#endif /*nullfree*/
92
#ifndef nulldup
93
#define nulldup(x) ((x)?strdup(x):(x))
94
#endif /*nulldup*/
95
96
#if defined NCJDEBUG || defined NCJTRACE
97
static char* tokenname(int token);
98
#endif /*defined NCJDEBUG || defined NCJTRACE*/
99
100
/**************************************************/
101
/* Forward */
102
static int NCJparseR(NCJparser* parser, NCjson**);
103
static int NCJparseArray(NCJparser* parser, struct NCjlist* array);
104
static int NCJparseDict(NCJparser* parser, struct NCjlist* dict);
105
static int testbool(const char* word);
106
static int testint(const char* word);
107
static int testdouble(const char* word);
108
static int testnull(const char* word);
109
static int NCJlex(NCJparser* parser);
110
static int NCJyytext(NCJparser*, char* start, size_t pdlen);
111
static void NCJreclaimArray(struct NCjlist*);
112
static void NCJreclaimDict(struct NCjlist*);
113
static int NCJunescape(NCJparser* parser);
114
static char unescape1(char c);
115
116
static int listappend(struct NCjlist* list, NCjson* element);
117
static int listsetalloc(struct NCjlist* list, size_t sz);
118
static int listlookup(const struct NCjlist* list, const char* key, size_t* indexp);
119
120
static int NCJcloneArray(const NCjson* array, NCjson** clonep);
121
static int NCJcloneDict(const NCjson* dict, NCjson** clonep);
122
123
/* These are used only by the unparser */
124
static int NCJunparseR(const NCjson* json, NCJbuf* buf, unsigned flags);
125
static int bytesappendquoted(NCJbuf* buf, const char* s);
126
static int bytesappend(NCJbuf* buf, const char* s);
127
static int bytesappendc(NCJbuf* bufp, char c);
128
129
/* Hide everything for plugins */
130
#ifdef NETCDF_JSON_H
131
#define OPTSTATIC static
132
#else /*!NETCDF_JSON_H*/
133
#define OPTSTATIC
134
#endif /*NETCDF_JSON_H*/
135
136
/* List legal nan and infinity names (lower case); keep in strcasecmp sorted order */
137
static const char* NANINF[] = {"-infinity","infinity","-infinityf","infinityf","nan","nanf"};
138
static const size_t NNANINF = 6;
139
140
/**************************************************/
141
142
OPTSTATIC int
143
NCJparse(const char* text, unsigned flags, NCjson** jsonp)
144
0
{
145
0
    size_t textlen = strlen(text);
146
0
    return NCJparsen(textlen,text,flags,jsonp);
147
0
}
148
149
OPTSTATIC int
150
NCJparsen(size_t len, const char* text, unsigned flags, NCjson** jsonp)
151
0
{
152
0
    int stat = NCJ_OK;
153
0
    NCJparser* parser = NULL;
154
0
    NCjson* json = NULL;
155
156
0
    parser = calloc(1,sizeof(NCJparser));
157
0
    if(parser == NULL)
158
0
  {stat = NCJTHROW(NCJ_ERR); goto done;}
159
0
    parser->flags = flags;
160
0
    parser->text = (char*)malloc(len+1+1);
161
0
    if(parser->text == NULL)
162
0
  {stat = NCJTHROW(NCJ_ERR); goto done;}
163
0
    memcpy(parser->text,text,len);
164
    /* trim trailing whitespace */
165
0
    if(len > 0) {
166
0
  char* p;
167
0
        for(p=parser->text+(len-1);p >= parser->text;p--) {
168
0
     if(*p > ' ') break;
169
0
  }
170
0
  len = (size_t)((p - parser->text) + 1);
171
0
    }
172
0
    if(len == 0) 
173
0
  {stat = NCJTHROW(NCJ_ERR); goto done;}
174
0
    parser->text[len] = '\0';
175
0
    parser->text[len+1] = '\0';
176
0
    parser->pos = &parser->text[0];
177
0
    parser->status = NCJ_OK;
178
#ifdef NCJDEBUG
179
fprintf(stderr,"json: |%s|\n",parser->text);
180
#endif /*NCJDEBUG*/
181
0
    if((stat=NCJparseR(parser,&json))==NCJ_ERR) goto done;
182
    /* Must consume all of the input */
183
0
    if(parser->pos != (parser->text+len)) {stat = NCJ_ERR; goto done;}
184
0
    *jsonp = json;
185
0
    json = NULL;
186
187
0
done:
188
0
    if(parser != NULL) {
189
0
  nullfree(parser->text);
190
0
  nullfree(parser->yytext);
191
0
  free(parser);
192
0
    }
193
0
    (void)NCJreclaim(json);
194
0
    return NCJTHROW(stat);
195
0
}
196
197
/*
198
Simple recursive descent
199
intertwined with dict and list parsers.
200
201
Invariants:
202
1. The json argument is provided by caller and filled in by NCJparseR.
203
2. Each call pushed back last unconsumed token
204
*/
205
206
static int
207
NCJparseR(NCJparser* parser, NCjson** jsonp)
208
0
{
209
0
    int stat = NCJ_OK;
210
0
    int token = NCJ_UNDEF;
211
0
    NCjson* json = NULL;
212
213
0
    if(jsonp == NULL)
214
0
  {stat = NCJTHROW(NCJ_ERR); goto done;}
215
0
    if((token = NCJlex(parser)) == NCJ_UNDEF)
216
0
  {stat = NCJTHROW(NCJ_ERR); goto done;}
217
0
    switch (token) {
218
0
    case NCJ_EOF:
219
0
  break;
220
0
    case NCJ_NULL:
221
0
        if((stat = NCJnew(NCJ_NULL,&json))==NCJ_ERR) goto done;
222
0
  break;
223
0
    case NCJ_BOOLEAN:
224
0
        if((stat = NCJnew(NCJ_BOOLEAN,&json))==NCJ_ERR) goto done;
225
0
  json->string = strdup(parser->yytext);
226
0
  break;
227
0
    case NCJ_INT:
228
0
        if((stat = NCJnew(NCJ_INT,&json))==NCJ_ERR) goto done;
229
0
  json->string = strdup(parser->yytext);
230
0
  break;
231
0
    case NCJ_DOUBLE:
232
0
        if((stat = NCJnew(NCJ_DOUBLE,&json))==NCJ_ERR) goto done;
233
0
  json->string = strdup(parser->yytext);
234
0
  break;
235
0
    case NCJ_STRING:
236
0
        if((stat = NCJnew(NCJ_STRING,&json))==NCJ_ERR) goto done;
237
0
  json->string = strdup(parser->yytext);
238
0
  break;
239
0
    case NCJ_LBRACE:
240
0
        if((stat = NCJnew(NCJ_DICT,&json))==NCJ_ERR) goto done;
241
0
  if((stat = NCJparseDict(parser, &json->list))==NCJ_ERR) goto done;
242
0
  break;
243
0
    case NCJ_LBRACKET:
244
0
        if((stat = NCJnew(NCJ_ARRAY,&json))==NCJ_ERR) goto done;
245
0
  if((stat = NCJparseArray(parser, &json->list))==NCJ_ERR) goto done;
246
0
  break;
247
0
    case NCJ_RBRACE: /* We hit end of the dict we are parsing */
248
0
  parser->pos--; /* pushback so NCJparseArray will catch */
249
0
  json = NULL;
250
0
  break;
251
0
    case NCJ_RBRACKET:
252
0
  parser->pos--; /* pushback so NCJparseDict will catch */
253
0
  json = NULL;
254
0
  break;
255
0
    default:
256
0
  stat = NCJTHROW(NCJ_ERR);
257
0
  break;
258
0
    }
259
0
    if(jsonp && json) {*jsonp = json; json = NULL;}
260
261
0
done:
262
0
    NCJreclaim(json);
263
0
    return NCJTHROW(stat);
264
0
}
265
266
static int
267
NCJparseArray(NCJparser* parser, struct NCjlist* arrayp)
268
0
{
269
0
    int stat = NCJ_OK;
270
0
    int token = NCJ_UNDEF;
271
0
    NCjson* element = NULL;
272
0
    int stop = 0;
273
274
    /* [ ^e1,e2, ...en] */
275
276
0
    while(!stop) {
277
  /* Recurse to get the value ei (might be null) */
278
0
  if((stat = NCJparseR(parser,&element))==NCJ_ERR) goto done;
279
0
  token = NCJlex(parser); /* Get next token */
280
  /* Next token should be comma or rbracket */
281
0
  switch(token) {
282
0
  case NCJ_RBRACKET:
283
0
      if(element != NULL) listappend(arrayp,element);
284
0
      element = NULL;
285
0
      stop = 1;
286
0
      break;
287
0
  case NCJ_COMMA:
288
      /* Append the ei to the list */
289
0
      if(element == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;} /* error */
290
0
      listappend(arrayp,element);
291
0
      element = NULL;
292
0
      break;
293
0
  case NCJ_EOF:
294
0
  case NCJ_UNDEF:
295
0
  default:
296
0
      stat = NCJTHROW(NCJ_ERR);
297
0
      goto done;
298
0
  } 
299
0
    } 
300
301
0
done:
302
0
    if(element != NULL)
303
0
  NCJreclaim(element);
304
0
    return NCJTHROW(stat);
305
0
}
306
307
static int
308
NCJparseDict(NCJparser* parser, struct NCjlist* dictp)
309
0
{
310
0
    int stat = NCJ_OK;
311
0
    int token = NCJ_UNDEF;
312
0
    NCjson* value = NULL;
313
0
    NCjson* key = NULL;
314
0
    int stop = 0;
315
316
    /* { ^k1:v1,k2:v2, ...kn:vn] */
317
318
0
    while(!stop) {
319
  /* Get the key, which must be a word of some sort */
320
0
  token = NCJlex(parser);
321
0
  switch(token) {
322
0
  case NCJ_STRING:
323
0
  case NCJ_BOOLEAN:
324
0
  case NCJ_INT: case NCJ_DOUBLE: {
325
0
      if((stat=NCJnewstring(token,parser->yytext,&key))==NCJ_ERR) goto done;
326
0
      } break;
327
0
  case NCJ_RBRACE: /* End of containing Dict */
328
0
      stop = 1;
329
0
      continue; /* leave loop */
330
0
  case NCJ_EOF: case NCJ_UNDEF:
331
0
  default:
332
0
      stat = NCJTHROW(NCJ_ERR);
333
0
      goto done;
334
0
  }
335
  /* Next token must be colon*/
336
0
    switch((token = NCJlex(parser))) {
337
0
  case NCJ_COLON: break;
338
0
  case NCJ_UNDEF: case NCJ_EOF:
339
0
  default: stat = NCJTHROW(NCJ_ERR); goto done;
340
0
  }    
341
  /* Get the value */
342
0
  if((stat = NCJparseR(parser,&value))==NCJ_ERR) goto done;
343
        /* Next token must be comma or RBRACE */
344
0
  switch((token = NCJlex(parser))) {
345
0
  case NCJ_RBRACE:
346
0
      stop = 1;
347
      /* fall thru */
348
0
  case NCJ_COMMA:
349
      /* Insert key value into dict: key first, then value */
350
0
      listappend(dictp,key);
351
0
      key = NULL;
352
0
      listappend(dictp,value);
353
0
      value = NULL;
354
0
      break;
355
0
  case NCJ_EOF:
356
0
  case NCJ_UNDEF:
357
0
  default:
358
0
      stat = NCJTHROW(NCJ_ERR);
359
0
      goto done;
360
0
  } 
361
0
    } 
362
363
0
done:
364
0
    if(key != NULL)
365
0
  NCJreclaim(key);
366
0
    if(value != NULL)
367
0
  NCJreclaim(value);
368
0
    return NCJTHROW(stat);
369
0
}
370
371
static int
372
NCJlex(NCJparser* parser)
373
0
{
374
0
    int token = NCJ_UNDEF;
375
0
    char* start;
376
0
    size_t count;
377
378
0
    while(token == 0) { /* avoid need to goto when retrying */
379
0
  char c = *parser->pos;
380
0
  if(c == '\0') {
381
0
      token = NCJ_EOF;
382
0
  } else if(c <= ' ' || c == '\177') {/* ignore whitespace */
383
0
      parser->pos++;
384
0
      continue;
385
0
  } else if(c == NCJ_ESCAPE) {
386
0
      parser->pos++;
387
0
      c = *parser->pos;
388
0
      *parser->pos = (char)unescape1(c);
389
0
      continue;
390
0
  } else if(strchr(JSON_WORD, c) != NULL) {
391
0
      start = parser->pos;
392
0
      for(;;) {
393
0
    c = *parser->pos++;
394
0
    if(c == '\0' || strchr(JSON_WORD,c) == NULL) break; /* end of word */
395
0
      }
396
      /* Pushback c */
397
0
      parser->pos--;
398
0
      count = (size_t)((parser->pos) - start);
399
0
      if(NCJyytext(parser,start,count)) goto done;
400
      /* Discriminate the word string to get the proper sort */
401
0
      if(testbool(parser->yytext))
402
0
    token = NCJ_BOOLEAN;
403
      /* do int test first since double subsumes int */
404
0
      else if(testint(parser->yytext))
405
0
    token = NCJ_INT;
406
0
      else if(testdouble(parser->yytext))
407
0
    token = NCJ_DOUBLE;
408
0
      else if(testnull(parser->yytext))
409
0
    token = NCJ_NULL;
410
0
      else
411
0
    token = NCJ_STRING;
412
0
  } else if(c == NCJ_QUOTE) {
413
0
      parser->pos++;
414
0
      start = parser->pos;
415
0
      for(;;) {
416
0
    c = *parser->pos++;
417
0
    if(c == NCJ_ESCAPE) parser->pos++;
418
0
    else if(c == NCJ_QUOTE || c == '\0') break;
419
0
      }
420
0
      if(c == '\0') {
421
0
    parser->status = NCJ_ERR;
422
0
    token = NCJ_UNDEF;
423
0
    goto done;
424
0
      }
425
0
      count = (size_t)((parser->pos) - start) - 1; /* -1 for trailing quote */
426
0
      if(NCJyytext(parser,start,count)==NCJ_ERR) goto done;
427
0
      if(NCJunescape(parser)==NCJ_ERR) goto done;
428
0
      token = NCJ_STRING;
429
0
  } else { /* single char token */
430
0
      if(NCJyytext(parser,parser->pos,1)==NCJ_ERR) goto done;
431
0
      token = *parser->pos++;
432
0
  }
433
#ifdef NCJDEBUG
434
fprintf(stderr,"%s(%d): |%s|\n",tokenname(token),token,parser->yytext);
435
#endif /*NCJDEBUG*/
436
0
    } /*for(;;)*/
437
0
done:
438
0
    if(parser->status == NCJ_ERR)
439
0
        token = NCJ_UNDEF;
440
#ifdef NCJTRACE
441
    if(parser->flags & NCJ_TRACE) {
442
  const char* txt = NULL;
443
  switch(token) {
444
  case NCJ_STRING: case NCJ_INT: case NCJ_DOUBLE: case NCJ_BOOLEAN: txt = parser->yytext; break;
445
        default: break;
446
  }
447
        fprintf(stderr,">>>> token=%s:'%s'\n",tokenname(token),(txt?txt:""));
448
    }
449
#endif /*NCJTRACE*/
450
0
    return token;
451
0
}
452
453
static int
454
testnull(const char* word)
455
0
{
456
0
    if(strcasecmp(word,NCJ_TAG_NULL)==0) return 1;
457
0
    return 0;
458
0
}
459
460
static int
461
testbool(const char* word)
462
0
{
463
0
    if(strcasecmp(word,NCJ_TAG_TRUE)==0
464
0
       || strcasecmp(word,NCJ_TAG_FALSE)==0)
465
0
  return 1;
466
0
    return 0;
467
0
}
468
469
static int
470
testint(const char* word)
471
0
{
472
0
    int ncvt;
473
0
    long long i;
474
0
    int count = 0;
475
    /* Try to convert to number */
476
0
    ncvt = sscanf(word,"%lld%n",&i,&count);
477
0
    return (ncvt == 1 && strlen(word)==((size_t)count) ? 1 : 0);
478
0
}
479
480
static int
481
nancmp(const void* keyp, const void* membpp)
482
0
{
483
0
    int cmp;    
484
0
    const char* key = (const char*)keyp;
485
0
    const char** membp = (const char**)membpp;
486
0
    cmp = strcasecmp(key,*membp);
487
0
    return cmp;
488
0
}
489
490
static int
491
testdouble(const char* word)
492
0
{
493
0
    int ncvt;
494
0
    double d;
495
0
    int count = 0;
496
0
    void* pos = NULL;
497
498
    /* Check for Nan and Infinity */
499
0
    pos = bsearch(word, NANINF, NNANINF, sizeof(char*), nancmp);
500
0
    if(pos != NULL) return 1;
501
    /* Try to convert to number */
502
0
    ncvt = sscanf(word,"%lg%n",&d,&count); 
503
0
    return (ncvt == 1 && strlen(word)==((size_t)count) ? 1 : 0);
504
0
}
505
506
static int
507
NCJyytext(NCJparser* parser, char* start, size_t pdlen)
508
0
{
509
0
    size_t len = (size_t)pdlen;
510
0
    if(parser->yytext == NULL) {
511
0
  parser->yytext = (char*)malloc(len+1);
512
0
  parser->yylen = len;
513
0
    } else if(parser->yylen <= len) {
514
0
  parser->yytext = (char*) realloc(parser->yytext,len+1);
515
0
  parser->yylen = len;
516
0
    }
517
0
    if(parser->yytext == NULL) return NCJTHROW(NCJ_ERR);
518
0
    memcpy(parser->yytext,start,len);
519
0
    parser->yytext[len] = '\0';
520
0
    return NCJTHROW(NCJ_OK);
521
0
}
522
523
/**************************************************/
524
525
OPTSTATIC void
526
NCJreclaim(NCjson* json)
527
0
{
528
0
    if(json == NULL) return;
529
0
    switch(json->sort) {
530
0
    case NCJ_INT:
531
0
    case NCJ_DOUBLE:
532
0
    case NCJ_BOOLEAN:
533
0
    case NCJ_STRING: 
534
0
  nullfree(json->string);
535
0
  break;
536
0
    case NCJ_DICT:
537
0
  NCJreclaimDict(&json->list);
538
0
  break;
539
0
    case NCJ_ARRAY:
540
0
  NCJreclaimArray(&json->list);
541
0
  break;
542
0
    default: break; /* nothing to reclaim */
543
0
    }
544
0
    free(json);
545
0
}
546
547
static void
548
NCJreclaimArray(struct NCjlist* array)
549
0
{
550
0
    size_t i;
551
0
    for(i=0;i<array->len;i++) {
552
0
  NCJreclaim(array->contents[i]);
553
0
    }
554
0
    nullfree(array->contents);
555
0
    array->contents = NULL;
556
0
    array->len = 0;
557
0
}
558
559
static void
560
NCJreclaimDict(struct NCjlist* dict)
561
0
{
562
0
   NCJreclaimArray(dict);
563
0
}
564
565
/**************************************************/
566
/* Build Functions */
567
568
OPTSTATIC int
569
NCJnew(int sort, NCjson** objectp)
570
0
{
571
0
    int stat = NCJ_OK;
572
0
    NCjson* object = NULL;
573
574
0
    if((object = (NCjson*)calloc(1,sizeof(NCjson))) == NULL)
575
0
  {stat = NCJTHROW(NCJ_ERR); goto done;}
576
0
    NCJsetsort(object,sort);
577
0
    switch (sort) {
578
0
    case NCJ_INT:
579
0
    case NCJ_DOUBLE:
580
0
    case NCJ_BOOLEAN:
581
0
    case NCJ_STRING:
582
0
    case NCJ_NULL:
583
0
  break;
584
0
    case NCJ_DICT:
585
0
    case NCJ_ARRAY:
586
0
  break;
587
0
    default: 
588
0
  stat = NCJTHROW(NCJ_ERR);
589
0
  goto done;
590
0
    }
591
0
    if(objectp) {*objectp = object; object = NULL;}
592
593
0
done:
594
0
    if(stat) NCJreclaim(object);
595
0
    return NCJTHROW(stat);
596
0
}
597
598
OPTSTATIC int
599
NCJnewstring(int sort, const char* value, NCjson** jsonp)
600
0
{
601
0
    return NCJTHROW(NCJnewstringn(sort,strlen(value),value,jsonp));
602
0
}
603
604
OPTSTATIC int
605
NCJnewstringn(int sort, size_t len, const char* value, NCjson** jsonp)
606
0
{
607
0
    int stat = NCJ_OK;
608
0
    NCjson* json = NULL;
609
610
0
    if(jsonp) *jsonp = NULL;
611
0
    if(value == NULL)
612
0
        {stat = NCJTHROW(NCJ_ERR); goto done;}
613
0
    if((stat = NCJnew(sort,&json))==NCJ_ERR) goto done;
614
0
    if((json->string = (char*)malloc(len+1))==NULL)
615
0
        {stat = NCJTHROW(NCJ_ERR); goto done;}
616
0
    memcpy(json->string,value,len);
617
0
    json->string[len] = '\0';
618
0
    if(jsonp) *jsonp = json;
619
0
    json = NULL; /* avoid memory errors */
620
0
done:
621
0
    NCJreclaim(json);
622
0
    return NCJTHROW(stat);
623
0
}
624
625
OPTSTATIC int
626
NCJdictget(const NCjson* dict, const char* key, const NCjson** jvaluep)
627
0
{
628
0
    int stat = NCJ_OK;
629
0
    size_t i;
630
631
0
    if(dict == NULL || dict->sort != NCJ_DICT)
632
0
        {stat = NCJTHROW(NCJ_ERR); goto done;}
633
0
    if(jvaluep) {*jvaluep = NULL;}
634
0
    for(i=0;i<NCJdictlength(dict);i++) {
635
0
  NCjson* jkey = NCJdictkey(dict,i);
636
0
  NCjson* jvalue  = NCJdictvalue(dict,i);
637
0
  if(jkey->string != NULL && strcmp(jkey->string,key)==0) {
638
0
      if(jvaluep) {*jvaluep = jvalue;}
639
0
      break;
640
0
  }     
641
0
    }
642
643
0
done:
644
0
    return NCJTHROW(stat);
645
0
}
646
647
/* Functional version of NCJdictget */
648
OPTSTATIC NCjson*
649
NCJdictlookup(const NCjson* dict, const char* key)
650
0
{
651
0
    int stat;
652
0
    const NCjson* jvalue = NULL;
653
0
    stat = NCJdictget(dict,key,&jvalue);
654
0
    if(stat != NCJ_OK) jvalue = NULL;
655
0
    return jvalue;
656
0
}
657
658
/* Unescape the text in parser->yytext; can
659
   do in place because unescaped string will
660
   always be shorter */
661
static int
662
NCJunescape(NCJparser* parser)
663
0
{
664
0
    char* p = parser->yytext;
665
0
    char* q = p;
666
0
    char c;
667
0
    for(;(c=*p++);) {
668
0
  if(c == NCJ_ESCAPE) {
669
0
      c = *p++;
670
0
      switch (c) {
671
0
      case 'b': c = '\b'; break;
672
0
      case 'f': c = '\f'; break;
673
0
      case 'n': c = '\n'; break;
674
0
      case 'r': c = '\r'; break;
675
0
      case 't': c = '\t'; break;
676
0
      case NCJ_QUOTE: break;
677
0
      case NCJ_ESCAPE: break;
678
0
      default: break;/* technically not Json conformant */
679
0
      }
680
0
  }
681
0
  *q++ = (char)c;
682
0
    }
683
0
    *q = '\0';
684
0
    return NCJTHROW(NCJ_OK);    
685
0
}
686
687
/* Unescape a single character */
688
static char
689
unescape1(char c)
690
0
{
691
0
    switch (c) {
692
0
    case 'b': c = '\b'; break;
693
0
    case 'f': c = '\f'; break;
694
0
    case 'n': c = '\n'; break;
695
0
    case 'r': c = '\r'; break;
696
0
    case 't': c = '\t'; break;
697
0
    default: break;/* technically not Json conformant */
698
0
    }
699
0
    return c;
700
0
}
701
702
#if defined NCJDEBUG || defined NCJTRACE
703
static char*
704
tokenname(int token)
705
{
706
    switch (token) {
707
    case NCJ_STRING: return ("NCJ_STRING");
708
    case NCJ_INT: return ("NCJ_INT");
709
    case NCJ_DOUBLE: return ("NCJ_DOUBLE");
710
    case NCJ_BOOLEAN: return ("NCJ_BOOLEAN");
711
    case NCJ_DICT: return ("NCJ_DICT");
712
    case NCJ_ARRAY: return ("NCJ_ARRAY");
713
    case NCJ_NULL: return ("NCJ_NULL");
714
    default:
715
  if(token > ' ' && token <= 127) {
716
      static char s[4];
717
      s[0] = '\'';
718
      s[1] = (char)token;
719
      s[2] = '\'';
720
      s[3] = '\0';
721
      return (s);
722
  } else
723
      break;
724
    }
725
    return ("NCJ_UNDEF");
726
}
727
#endif /*defined NCJDEBUG || defined NCJTRACE*/
728
729
/* Convert a JSON value to an equivalent value of a specified sort */
730
OPTSTATIC int
731
NCJcvt(const NCjson* jvalue, int outsort, struct NCJconst* output)
732
0
{
733
0
    int stat = NCJ_OK;
734
735
0
    if(output == NULL) goto done;
736
737
0
#undef CASE
738
0
#define CASE(t1,t2) ((t1)<<4 | (t2)) /* the shift constant must be larger than log2(NCJ_NSORTS) */
739
0
    switch (CASE(jvalue->sort,outsort)) {
740
741
0
    case CASE(NCJ_BOOLEAN,NCJ_BOOLEAN):
742
0
  if(strcasecmp(jvalue->string,NCJ_TAG_FALSE)==0) output->bval = 0; else output->bval = 1;
743
0
  break;
744
0
    case CASE(NCJ_BOOLEAN,NCJ_INT):
745
0
  if(strcasecmp(jvalue->string,NCJ_TAG_FALSE)==0) output->ival = 0; else output->ival = 1;
746
0
  break;  
747
0
    case CASE(NCJ_BOOLEAN,NCJ_DOUBLE):
748
0
  if(strcasecmp(jvalue->string,NCJ_TAG_FALSE)==0) output->dval = 0.0; else output->dval = 1.0;
749
0
  break;  
750
0
    case CASE(NCJ_BOOLEAN,NCJ_STRING):
751
0
        output->sval = nulldup(jvalue->string);
752
0
  break;  
753
754
0
    case CASE(NCJ_INT,NCJ_BOOLEAN):
755
0
  sscanf(jvalue->string,"%lld",&output->ival);
756
0
  output->bval = (output->ival?1:0);
757
0
  break;  
758
0
    case CASE(NCJ_INT,NCJ_INT):
759
0
  sscanf(jvalue->string,"%lld",&output->ival);
760
0
  break;  
761
0
    case CASE(NCJ_INT,NCJ_DOUBLE):
762
0
  sscanf(jvalue->string,"%lld",&output->ival);
763
0
  output->dval = (double)output->ival;
764
0
  break;  
765
0
    case CASE(NCJ_INT,NCJ_STRING):
766
0
        output->sval = nulldup(jvalue->string);
767
0
  break;  
768
769
0
    case CASE(NCJ_DOUBLE,NCJ_BOOLEAN):
770
0
  sscanf(jvalue->string,"%lf",&output->dval);
771
0
  output->bval = (output->dval == 0?0:1);
772
0
  break;  
773
0
    case CASE(NCJ_DOUBLE,NCJ_INT):
774
0
  sscanf(jvalue->string,"%lf",&output->dval);
775
0
  output->ival = (long long)output->dval;
776
0
  break;  
777
0
    case CASE(NCJ_DOUBLE,NCJ_DOUBLE):
778
0
  sscanf(jvalue->string,"%lf",&output->dval);
779
0
  break;  
780
0
    case CASE(NCJ_DOUBLE,NCJ_STRING):
781
0
        output->sval = nulldup(jvalue->string);
782
0
  break;  
783
784
0
    case CASE(NCJ_STRING,NCJ_BOOLEAN):
785
0
  if(strcasecmp(jvalue->string,NCJ_TAG_FALSE)==0) output->bval = 0; else output->bval = 1;
786
0
  break;
787
0
    case CASE(NCJ_STRING,NCJ_INT):
788
0
  sscanf(jvalue->string,"%lld",&output->ival);
789
0
  break;
790
0
    case CASE(NCJ_STRING,NCJ_DOUBLE):
791
0
  sscanf(jvalue->string,"%lf",&output->dval);
792
0
  break;
793
0
    case CASE(NCJ_STRING,NCJ_STRING):
794
0
        output->sval = nulldup(jvalue->string);
795
0
  break;  
796
797
0
    default:
798
0
        stat = NCJTHROW(NCJ_ERR);
799
0
  break;
800
0
    }
801
802
0
done:
803
0
    return NCJTHROW(stat);
804
0
}
805
806
static int
807
listappend(struct NCjlist* list, NCjson* json)
808
0
{
809
0
    int stat = NCJ_OK;
810
811
0
    assert(list->len == 0 || list->contents != NULL);
812
0
    if(json == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;}
813
    /* Make space for two new elements; better than one, but still probably not optimal */
814
0
    if((stat = listsetalloc(list,list->len + 2))<0) goto done;
815
    /* Append the new item */
816
0
    list->contents[list->len++] = json;
817
818
0
done:
819
0
    return NCJTHROW(stat);
820
0
}
821
822
/* Locate the index of a key in a list of (key,value) pairs.
823
@param list pointer to the list
824
@param key  for which to search
825
@param indexp store index of match here
826
@return NCJ_OK if key found, NCJ_EOF if not found, NCJ_ERR if error
827
*/
828
static int
829
listlookup(const struct NCjlist* list, const char* key, size_t* indexp)
830
0
{
831
0
    int stat = NCJ_OK;
832
0
    int i,len,match = -1;
833
834
0
    if(list == NULL || key == NULL || strlen(key) == 0 || list->len %2 == 1)
835
0
  {stat = NCJTHROW(NCJ_ERR); goto done;}
836
0
    len = (int)list->len; /* => |list| < 2 billion or do */
837
0
    for(i=0;i<len;i+=2) {
838
0
  const NCjson* jkey = list->contents[i];
839
0
  if(jkey != NULL && jkey->string != NULL && strcmp(jkey->string,key)==0) {match = i;break;}      
840
0
    }
841
0
    if(match < 0) {stat = NCJ_EOF;}  else {if(indexp) *indexp = (size_t)match;}
842
0
done:
843
0
    return NCJTHROW(stat);
844
0
}
845
846
/* Increase the space available to dict/array.
847
   Even if alloc is zero, ensure that the object's list alloc is >= 1.
848
@param list pointer to the list
849
@param alloc increase allocation to this size
850
@return NCJ_ERR|NCJ_OK
851
*/
852
static int
853
listsetalloc(struct NCjlist* list, size_t alloc)
854
0
{
855
0
    int stat = NCJ_OK;
856
0
    NCjson** newcontents = NULL;
857
858
0
    if(list == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;}
859
0
    assert(list->alloc == 0 || list->contents != NULL);
860
0
    if(alloc == 0) alloc = 1; /* Guarantee that the list->content is not NULL */
861
0
    if(list->alloc >= alloc) goto done;
862
    /* Since alloc > list->alloc > 0, we need to allocate space */
863
0
    if((newcontents=(NCjson**)calloc(alloc,sizeof(NCjson*))) == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;}
864
0
    list->alloc = alloc;
865
0
    if(list->contents != NULL && list->len > 0) {
866
  /* Preserve any existing contents */
867
0
  memcpy((void*)newcontents,
868
0
    (void*)list->contents,
869
0
    sizeof(NCjson*)*list->len);
870
0
    }
871
0
    free(list->contents);
872
0
    list->contents = newcontents; newcontents = NULL;
873
0
    assert(list->alloc > 0 && list->contents != NULL);
874
0
done:
875
0
    if(newcontents != NULL) free(newcontents);
876
0
    return NCJTHROW(stat);
877
0
}
878
879
/**************************************************/
880
881
OPTSTATIC int
882
NCJclone(const NCjson* json, NCjson** clonep)
883
0
{
884
0
    int stat = NCJ_OK;
885
0
    NCjson* clone = NULL;
886
887
0
    if(clonep) *clonep = NULL;
888
0
    if(json == NULL) goto done;
889
0
    switch(NCJsort(json)) {
890
0
    case NCJ_INT:
891
0
    case NCJ_DOUBLE:
892
0
    case NCJ_BOOLEAN:
893
0
    case NCJ_STRING:
894
0
  if((stat=NCJnew(NCJsort(json),&clone))==NCJ_ERR) goto done;
895
0
  NCJsetstring(clone,strdup(NCJstring(json)));
896
0
  if(NCJstring(clone)==NULL)
897
0
      {stat = NCJTHROW(NCJ_ERR); goto done;}
898
0
  break;
899
0
    case NCJ_NULL:
900
0
  if((stat=NCJnew(NCJsort(json),&clone))==NCJ_ERR) goto done;
901
0
  break;
902
0
    case NCJ_DICT:
903
0
  if((stat=NCJcloneDict(json,&clone))==NCJ_ERR) goto done;
904
0
  break;
905
0
    case NCJ_ARRAY:
906
0
  if((stat=NCJcloneArray(json,&clone))==NCJ_ERR) goto done;
907
0
  break;
908
0
    default: break; /* nothing to clone */
909
0
    }
910
0
done:
911
0
    if(stat == NCJ_OK && clonep) {*clonep = clone; clone = NULL;}
912
0
    NCJreclaim(clone);    
913
0
    return NCJTHROW(stat);
914
0
}
915
916
static int
917
NCJcloneArray(const NCjson* array, NCjson** clonep)
918
0
{
919
0
    int stat=NCJ_OK;
920
0
    size_t i;
921
0
    NCjson* clone = NULL;
922
0
    if((stat=NCJnew(NCJ_ARRAY,&clone))==NCJ_ERR) goto done;
923
0
    if((stat=listsetalloc(&clone->list,array->list.len))<0) goto done;
924
0
    for(i=0;i<NCJarraylength(array);i++) {
925
0
  NCjson* elem = NCJith(array,i);
926
0
  NCjson* elemclone = NULL;
927
0
  if((stat=NCJclone(elem,&elemclone))==NCJ_ERR) goto done;
928
0
  NCJappend(clone,elemclone);
929
0
    }
930
0
done:
931
0
    if(stat == NCJ_OK && clonep) {*clonep = clone; clone = NULL;}
932
0
    NCJreclaim(clone);    
933
0
    return stat;
934
0
}
935
936
static int
937
NCJcloneDict(const NCjson* dict, NCjson** clonep)
938
0
{
939
0
    int stat=NCJ_OK;
940
0
    size_t i;
941
0
    NCjson* clone = NULL;
942
0
    if((stat=NCJnew(NCJ_DICT,&clone))==NCJ_ERR) goto done;
943
0
    if((stat=listsetalloc(&clone->list,dict->list.len))<0) goto done;
944
0
    for(i=0;i<NCJarraylength(dict);i++) {
945
0
  NCjson* elem = NCJith(dict,i);
946
0
  NCjson* elemclone = NULL;
947
0
  if((stat=NCJclone(elem,&elemclone))==NCJ_ERR) goto done;
948
0
  NCJappend(clone,elemclone);
949
0
    }
950
0
done:
951
0
    if(stat == NCJ_OK && clonep) {*clonep = clone; clone = NULL;}
952
0
    NCJreclaim(clone);    
953
0
    return NCJTHROW(stat);
954
0
}
955
956
OPTSTATIC int
957
NCJaddstring(NCjson* json, int sort, const char* s)
958
0
{
959
0
    int stat = NCJ_OK;
960
0
    NCjson* jtmp = NULL;
961
962
0
    if(NCJsort(json) != NCJ_DICT && NCJsort(json) != NCJ_ARRAY)
963
0
        {stat = NCJTHROW(NCJ_ERR); goto done;}
964
0
    if((stat = NCJnewstring(sort, s, &jtmp))==NCJ_ERR) goto done;
965
0
    if((stat = NCJappend(json,jtmp))==NCJ_ERR) goto done;
966
0
    jtmp = NULL;
967
    
968
0
done:
969
0
    NCJreclaim(jtmp);
970
0
    return NCJTHROW(stat);
971
0
}
972
973
/* Insert key-value pair into a dict object. key will be strdup'd, jvalue will be used without copying.
974
   If key already exists, then it's value is overwritten
975
*/
976
OPTSTATIC int
977
NCJinsert(NCjson* jdict, const char* key, NCjson* jvalue)
978
0
{
979
0
    int stat = NCJ_OK;
980
0
    size_t i;
981
0
    NCjson* jkey = NULL;
982
0
    NCjson* jprev = NULL;
983
0
    int found;
984
985
0
    if(jdict == NULL
986
0
  || NCJsort(jdict) != NCJ_DICT
987
0
  || key == NULL
988
0
  || jvalue == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;}
989
0
    for(found=(-1),i=0;i < NCJdictlength(jdict); i++) {
990
0
  jkey = NCJdictkey(jdict,i);
991
0
  if (jkey != NULL && strcmp(NCJstring(jkey), key) == 0) {
992
0
      found = (int)i;
993
0
      break;
994
0
  }
995
0
    }
996
0
    if(found >= 0) {
997
0
  jprev = NCJdictvalue(jdict,found);
998
  // replace existing values for new key
999
0
  NCJreclaim(jprev); // free old value
1000
0
  NCJdictvalue(jdict,found) = jvalue; jvalue = NULL;
1001
0
  jkey = NULL; /* avoid reclamation */
1002
0
    } else { /* not found */
1003
0
        if((stat=listsetalloc(&jdict->list,jdict->list.len+2))<0) goto done;
1004
0
  NCJcheck(NCJnewstring(NCJ_STRING, key, (NCjson**)&jkey));
1005
0
  NCJcheck(NCJappend(jdict,jkey)); jkey = NULL;
1006
0
  NCJcheck(NCJappend(jdict,jvalue)); jvalue = NULL;
1007
0
    }
1008
0
done:
1009
0
    NCJreclaim(jkey);
1010
0
    NCJreclaim(jvalue);
1011
0
    return NCJTHROW(stat);
1012
0
}
1013
1014
/* Insert key-value pair into a dict object. key will be strdup'd */
1015
OPTSTATIC int
1016
NCJinsertstring(NCjson* object, const char* key, const char* value)
1017
0
{
1018
0
    int stat = NCJ_OK;
1019
0
    NCjson* jkey = NULL;
1020
0
    NCjson* jvalue = NULL;
1021
0
    if(key == NULL || value == NULL)
1022
0
  {stat = NCJTHROW(NCJ_ERR); goto done;}
1023
0
    if((stat = NCJnewstring(NCJ_STRING,key,&jkey))==NCJ_ERR) goto done;
1024
0
    if((stat = NCJnewstring(NCJ_STRING,value,&jvalue))==NCJ_ERR) goto done;
1025
0
    if((stat = NCJappend(object,jkey))==NCJ_ERR) goto done;
1026
0
    if((stat = NCJappend(object,jvalue))==NCJ_ERR) goto done;
1027
0
done:
1028
0
    return NCJTHROW(stat);
1029
0
}
1030
1031
/* Insert key-value pair into a dict object. key will be strdup'd */
1032
OPTSTATIC int
1033
NCJinsertint(NCjson* object, const char* key, long long value)
1034
0
{
1035
0
    int stat = NCJ_OK;
1036
0
    NCjson* jkey = NULL;
1037
0
    NCjson* jvalue = NULL;
1038
0
    char digits[64];
1039
 
1040
0
    if(key == NULL)
1041
0
  {stat = NCJTHROW(NCJ_ERR); goto done;}
1042
0
    if((stat = NCJnewstring(NCJ_STRING,key,&jkey))==NCJ_ERR) goto done;
1043
0
    snprintf(digits,sizeof(digits),"%lld",value);
1044
0
    if((stat = NCJnewstring(NCJ_INT,digits,&jvalue))==NCJ_ERR) goto done;
1045
0
    listsetalloc(&object->list,object->list.len + 2);
1046
0
    if((stat = NCJappend(object,jkey))==NCJ_ERR) goto done;
1047
0
    if((stat = NCJappend(object,jvalue))==NCJ_ERR) goto done;
1048
0
done:
1049
0
    return NCJTHROW(stat);
1050
0
}
1051
1052
/* Append value to an array or dict object. */
1053
OPTSTATIC int
1054
NCJappend(NCjson* object, NCjson* value)
1055
0
{
1056
0
    if(object == NULL || value == NULL)
1057
0
  return NCJTHROW(NCJ_ERR);
1058
0
    switch (object->sort) {
1059
0
    case NCJ_ARRAY:
1060
0
    case NCJ_DICT:
1061
0
  listappend(&object->list,value);
1062
0
  break;
1063
0
    default:
1064
0
  return NCJTHROW(NCJ_ERR);
1065
0
    }
1066
0
    return NCJTHROW(NCJ_OK);
1067
0
}
1068
1069
/* Append string value to an array or dict object. */
1070
OPTSTATIC int
1071
NCJappendstring(NCjson* object, int sort, const char* s)
1072
0
{
1073
0
    NCjson* js = NULL;    
1074
0
    if(object == NULL || s == NULL)
1075
0
  return NCJTHROW(NCJ_ERR);
1076
0
    NCJnewstring(sort,s,&js);
1077
0
    switch (object->sort) {
1078
0
    case NCJ_ARRAY:
1079
0
    case NCJ_DICT:
1080
0
  listappend(&object->list,js);
1081
0
  break;
1082
0
    default:
1083
0
  return NCJTHROW(NCJ_ERR);
1084
0
    }
1085
0
    return NCJTHROW(NCJ_OK);
1086
0
}
1087
1088
/* Append int value into an array/dict object. */
1089
OPTSTATIC int
1090
NCJappendint(NCjson* object, long long value)
1091
0
{
1092
0
    int stat = NCJ_OK;
1093
0
    NCjson* jvalue = NULL;
1094
0
    char digits[64];
1095
 
1096
0
    snprintf(digits,sizeof(digits),"%lld",value);
1097
0
    NCJcheck(NCJnewstring(NCJ_INT,digits,&jvalue));
1098
0
    NCJcheck(NCJappend(object,jvalue)); jvalue = NULL;
1099
0
done:
1100
0
    NCJreclaim(jvalue);
1101
0
    return NCJTHROW(stat);
1102
0
}
1103
1104
/* Overwrite key-value pair in a dict object.
1105
   If key does not exist, then act like NCJinsert().
1106
*/
1107
OPTSTATIC int
1108
NCJoverwrite(NCjson* dict, const char* key, NCjson* jvalue)
1109
0
{
1110
0
    int stat = NCJ_OK;
1111
0
    size_t index;
1112
0
    NCjson* jkey = NULL;
1113
0
    NCjson* oldvalue = NULL;
1114
1115
0
    if(dict == NULL
1116
0
  || dict->sort != NCJ_DICT
1117
0
  || key == NULL
1118
0
  || jvalue == NULL) {stat = NCJTHROW(NCJ_ERR); goto done;}
1119
    /* See if key already exists */
1120
0
    switch(stat=listlookup(&dict->list,key,&index)) {
1121
0
    case NCJ_OK:
1122
  /* Overwrite value part */
1123
0
  oldvalue = dict->list.contents[index];
1124
0
  dict->list.contents[index] = jvalue;
1125
0
  NCJreclaim(oldvalue);
1126
0
  break;
1127
0
    case NCJ_EOF: /* Not found */
1128
0
  if((stat=listsetalloc(&dict->list,dict->list.len+2))<0) goto done;
1129
0
  if((stat = NCJappend(dict,jkey))==NCJ_ERR) goto done;
1130
0
  if((stat = NCJappend(dict,jvalue))==NCJ_ERR) goto done;
1131
0
  break;
1132
0
    case NCJ_ERR:
1133
0
    default: goto done;
1134
0
    }
1135
0
done:
1136
0
    return NCJTHROW(stat);
1137
0
}
1138
1139
/**************************************************/
1140
/* Unparser to convert NCjson object to text in buffer */
1141
1142
OPTSTATIC int
1143
NCJunparse(const NCjson* json, unsigned flags, char** textp)
1144
0
{
1145
0
    int stat = NCJ_OK;
1146
0
    NCJbuf buf = {0,NULL};
1147
0
    if((stat = NCJunparseR(json,&buf,flags))==NCJ_ERR)
1148
0
  goto done;
1149
0
    if(textp) {*textp = buf.text; buf.text = NULL; buf.len = 0;}
1150
0
done:
1151
0
    nullfree(buf.text);
1152
0
    return NCJTHROW(stat);
1153
0
}
1154
1155
static int
1156
NCJunparseR(const NCjson* json, NCJbuf* buf, unsigned flags)
1157
0
{
1158
0
    int stat = NCJ_OK;
1159
0
    size_t i;
1160
1161
0
    switch (NCJsort(json)) {
1162
0
    case NCJ_STRING:
1163
0
  bytesappendquoted(buf,json->string);
1164
0
  break;
1165
0
    case NCJ_INT:
1166
0
    case NCJ_DOUBLE:
1167
0
    case NCJ_BOOLEAN:
1168
0
  bytesappend(buf,json->string);
1169
0
  break;
1170
0
    case NCJ_DICT:
1171
0
  bytesappendc(buf,NCJ_LBRACE);
1172
0
  if(json->list.len > 0 && json->list.contents != NULL) {
1173
0
      int shortlist = 0;
1174
0
      for(i=0;!shortlist && i < json->list.len;i+=2) {
1175
0
    if(i > 0) {bytesappendc(buf,NCJ_COMMA);bytesappendc(buf,' ');};
1176
0
    NCJunparseR(json->list.contents[i],buf,flags); /* key */
1177
0
    bytesappendc(buf,NCJ_COLON);
1178
0
    bytesappendc(buf,' ');
1179
    /* Allow for the possibility of a short dict entry */
1180
0
    if(json->list.contents[i+1] == NULL) { /* short */
1181
0
          bytesappendc(buf,'?');    
1182
0
        shortlist = 1;
1183
0
    } else {
1184
0
        NCJunparseR(json->list.contents[i+1],buf,flags);
1185
0
    }
1186
0
      }
1187
0
  }
1188
0
  bytesappendc(buf,NCJ_RBRACE);
1189
0
  break;
1190
0
    case NCJ_ARRAY:
1191
0
  bytesappendc(buf,NCJ_LBRACKET);
1192
0
  if(json->list.len > 0 && json->list.contents != NULL) {
1193
0
      for(i=0;i < json->list.len;i++) {
1194
0
          if(i > 0) bytesappendc(buf,NCJ_COMMA);
1195
0
          NCJunparseR(json->list.contents[i],buf,flags);
1196
0
      }
1197
0
  }
1198
0
  bytesappendc(buf,NCJ_RBRACKET);
1199
0
  break;
1200
0
    case NCJ_NULL:
1201
0
  bytesappend(buf,"null");
1202
0
  break;
1203
0
    default:
1204
0
  stat = NCJTHROW(NCJ_ERR); goto done;
1205
0
    }
1206
0
done:
1207
0
    return NCJTHROW(stat);
1208
0
}
1209
1210
/* Escape a string and append to buf */
1211
static int
1212
escape(const char* text, NCJbuf* buf)
1213
0
{
1214
0
    const char* p = text;
1215
0
    char c;
1216
0
    for(;(c=*p++);) {
1217
0
        char replace = 0;
1218
0
        switch (c) {
1219
0
  case '\b': replace = 'b'; break;
1220
0
  case '\f': replace = 'f'; break;
1221
0
  case '\n': replace = 'n'; break;
1222
0
  case '\r': replace = 'r'; break;
1223
0
  case '\t': replace = 't'; break;
1224
0
  case NCJ_QUOTE: replace = '\"'; break;
1225
0
  case NCJ_ESCAPE: replace = '\\'; break;
1226
0
  default: break;
1227
0
  }
1228
0
  if(replace) {
1229
0
      bytesappendc(buf,NCJ_ESCAPE);
1230
0
      bytesappendc(buf,replace);
1231
0
  } else
1232
0
      bytesappendc(buf,(char)c);
1233
0
    }
1234
0
    return NCJTHROW(NCJ_OK);    
1235
0
}
1236
1237
static int
1238
bytesappendquoted(NCJbuf* buf, const char* s)
1239
0
{
1240
0
    bytesappend(buf,"\"");
1241
0
    escape(s,buf);
1242
0
    bytesappend(buf,"\"");
1243
0
    return NCJTHROW(NCJ_OK);
1244
0
}
1245
1246
static int
1247
bytesappend(NCJbuf* buf, const char* s)
1248
0
{
1249
0
    int stat = NCJ_OK;
1250
0
    char* newtext = NULL;
1251
0
    if(buf == NULL)
1252
0
        {stat = NCJTHROW(NCJ_ERR); goto done;}
1253
0
    if(s == NULL) s = "";
1254
0
    if(buf->len == 0) {
1255
0
  assert(buf->text == NULL);
1256
0
  buf->text = strdup(s);
1257
0
  if(buf->text == NULL)
1258
0
      {stat = NCJTHROW(NCJ_ERR); goto done;}
1259
0
  buf->len = strlen(s);
1260
0
    } else {
1261
0
  size_t slen = strlen(s);
1262
0
  size_t newlen = buf->len + slen + 1;
1263
0
        if((newtext = (char*)malloc(newlen))==NULL)
1264
0
            {stat = NCJTHROW(NCJ_ERR); goto done;}
1265
0
        strcpy(newtext,buf->text);
1266
0
  strcat(newtext,s);  
1267
0
  free(buf->text); buf->text = NULL;
1268
0
  buf->text = newtext; newtext = NULL;
1269
0
  buf->len = newlen;
1270
0
    }
1271
1272
0
done:
1273
0
    nullfree(newtext);
1274
0
    return NCJTHROW(stat);
1275
0
}
1276
1277
static int
1278
bytesappendc(NCJbuf* bufp, const char c)
1279
0
{
1280
0
    char s[2];
1281
0
    s[0] = c;
1282
0
    s[1] = '\0';
1283
0
    return bytesappend(bufp,s);
1284
0
}
1285
1286
OPTSTATIC const char*
1287
NCJtotext(const NCjson* json, unsigned flags)
1288
0
{
1289
0
    static char outtext[4096];
1290
0
    char* text = NULL;
1291
0
    NC_UNUSED(flags);
1292
0
    if(json == NULL) {strcpy(outtext,"<null>"); goto done;}
1293
0
    (void)NCJunparse(json,0,&text);
1294
0
    strncpy(outtext,text,sizeof(outtext));
1295
0
    nullfree(text);
1296
0
done:
1297
0
    return outtext;
1298
0
}
1299
1300
OPTSTATIC void
1301
NCJdump(const NCjson* json, unsigned flags, FILE* out)
1302
0
{
1303
0
    const char* text = NCJtotext(json,flags);
1304
0
    if(out == NULL) out = stderr;
1305
0
    fprintf(out,"%s\n",text);
1306
0
    fflush(out);
1307
0
}
1308
1309
static int
1310
pairsort(const void* a, const void* b)
1311
0
{
1312
0
    const NCjson** j1 = (const NCjson**)a;
1313
0
    const NCjson** j2 = (const NCjson**)b;
1314
0
    return strcmp(NCJstring(*j1),NCJstring(*j2));
1315
0
}
1316
1317
OPTSTATIC void
1318
NCJdictsort(NCjson* jdict)
1319
0
{
1320
0
    assert(NCJsort(jdict) == NCJ_DICT);
1321
0
    qsort((void*)NCJcontents(jdict),NCJdictlength(jdict),2*sizeof(NCjson*),pairsort);
1322
0
}
1323
1324
/* Hack to avoid static unused warning */
1325
static void
1326
netcdf_supresswarnings(void)
1327
0
{
1328
0
    void* ignore = NULL;
1329
0
    ignore = (void*)netcdf_supresswarnings;
1330
0
    ignore = (void*)NCJparse;
1331
0
    ignore = (void*)NCJparsen;
1332
0
    ignore = (void*)NCJreclaim;
1333
0
    ignore = (void*)NCJnew;
1334
0
    ignore = (void*)NCJnewstring;
1335
0
    ignore = (void*)NCJnewstringn;
1336
0
    ignore = (void*)NCJdictget;
1337
0
    ignore = (void*)NCJdictlookup;
1338
0
    ignore = (void*)NCJcvt;
1339
0
    ignore = (void*)NCJaddstring;
1340
0
    ignore = (void*)NCJappend;
1341
0
    ignore = (void*)NCJappendstring;
1342
0
    ignore = (void*)NCJappendint;
1343
0
    ignore = (void*)NCJinsert;
1344
0
    ignore = (void*)NCJinsertstring;
1345
0
    ignore = (void*)NCJinsertint;
1346
0
    ignore = (void*)NCJoverwrite;
1347
0
    ignore = (void*)NCJdictsort;
1348
0
    ignore = (void*)NCJdump;
1349
0
    (void)ignore;
1350
0
}