Coverage Report

Created: 2023-05-28 06:42

/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
}