Coverage Report

Created: 2025-11-11 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.26.2~dev/libgps/json.c
Line
Count
Source
1
/****************************************************************************
2
3
NAME
4
   json.c - parse JSON into fixed-extent data structures
5
6
DESCRIPTION
7
   This module parses a large subset of JSON (JavaScript Object
8
Notation).  Unlike more general JSON parsers, it doesn't use malloc(3)
9
and doesn't support polymorphism; you need to give it a set of
10
template structures describing the expected shape of the incoming
11
JSON, and it will error out if that shape is not matched.  When the
12
parse succeeds, attribute values will be extracted into static
13
locations specified in the template structures.
14
15
   The "shape" of a JSON object in the type signature of its
16
attributes (and attribute values, and so on recursively down through
17
all nestings of objects and arrays).  This parser is indifferent to
18
the order of attributes at any level, but you have to tell it in
19
advance what the type of each attribute value will be and where the
20
parsed value will be stored. The template structures may supply
21
default values to be used when an expected attribute is omitted.
22
23
   The preceding paragraph told one fib.  A single attribute may
24
actually have a span of multiple specifications with different
25
syntactically distinguishable types (e.g. string vs. real vs. integer
26
vs. boolean, but not signed integer vs. unsigned integer).  The parser
27
will match the right spec against the actual data.
28
29
   The dialect this parses has some limitations.  First, it cannot
30
recognize the JSON "null" value.  Secondly, arrays may not have
31
character values as elements (this limitation could be easily removed
32
if required). Third, all elements of an array must be of the same
33
type.  Fourth, it can not handle NaN's in doubles (Issue 53150).
34
35
   There are separate entry points for beginning a parse of either
36
JSON object or a JSON array. JSON "float" quantities are actually
37
stored as doubles.
38
39
   This parser processes object arrays in one of two different ways,
40
defending on whether the array subtype is declared as object or
41
structobject.
42
43
   Object arrays take one base address per object subfield, and are
44
mapped into parallel C arrays (one per subfield).  Strings are not
45
supported in this kind of array, as they don't have a "natural" size
46
to use as an offset multiplier.
47
48
   Structobjects arrays are a way to parse a list of objects to a set
49
of modifications to a corresponding array of C structs.  The trick is
50
that the array object initialization has to specify both the C struct
51
array's base address and the stride length (the size of the C struct).
52
If you initialize the offset fields with the correct offsetof calls,
53
everything will work. Strings are supported but all string storage
54
has to be inline in the struct.
55
56
NOTE
57
   This code has been spun out, packaged, and documented as a
58
reusable module; search for "microjson".
59
60
PERMISSIONS
61
   This file is Copyright 2010 by the GPSD project
62
   SPDX-License-Identifier: BSD-2-clause
63
64
***************************************************************************/
65
#include "../include/gpsd_config.h"  // must be before all includes
66
67
#include <ctype.h>
68
#include <math.h>       // for HUGE_VAL
69
#include <stdarg.h>     // for va_arg(), etc.
70
#include <stdbool.h>
71
#include <stdio.h>
72
#include <stdlib.h>
73
#include <string.h>
74
75
#include "../include/compiler.h"   // for FALLTHROUGH
76
#include "../include/os_compat.h"
77
#include "../include/json.h"
78
79
#include "../include/gps.h"                // for safe_atof() prototype
80
#include "../include/strfuncs.h"
81
#include "../include/timespec.h"
82
83
static int debuglevel = 0;
84
static FILE *debugjsfp = NULL;
85
86
// control the level and destination of debug trace messages
87
void json_enable_debug(int level, FILE * fp)
88
0
{
89
0
    debuglevel = level;
90
0
    debugjsfp = fp;
91
0
}
92
93
static void json_trace(const char *, ...);
94
95
// assemble command in printf(3) style
96
static void json_trace(const char *fmt, ...)
97
0
{
98
0
    va_list ap;
99
100
0
    va_start(ap, fmt);
101
0
    (void)vfprintf(debugjsfp, fmt, ap);
102
0
    va_end(ap);
103
0
}
104
105
// HOTCODE!  Do not change without profiling.
106
// major speedup by checking debuglvl before callin json_trace()
107
#define json_debug_trace(lvl, fmt, ...)                \
108
1.11M
    do {                                              \
109
1.11M
        if (unlikely((lvl) <= debuglevel &&           \
110
1.11M
                      NULL != debugjsfp) ) {            \
111
0
            json_trace(fmt, __VA_ARGS__);              \
112
0
        }                                             \
113
1.11M
    } while (0)
114
115
116
static char *json_target_address(const struct json_attr_t *cursor,
117
                                 const struct json_array_t
118
                                 *parent, int offset)
119
161k
{
120
161k
    char *targetaddr = NULL;
121
161k
    if (NULL == parent ||
122
89.6k
        parent->element_type != t_structobject) {
123
        // ordinary case - use the address in the cursor structure
124
89.6k
        switch (cursor->type) {
125
0
        case t_byte:
126
0
            targetaddr = (char *)&cursor->addr.byte[offset];
127
0
            break;
128
733
        case t_ubyte:
129
733
            targetaddr = (char *)&cursor->addr.ubyte[offset];
130
733
            break;
131
18.6k
        case t_ignore:
132
18.6k
            targetaddr = NULL;
133
18.6k
            break;
134
16.0k
        case t_integer:
135
16.0k
            targetaddr = (char *)&cursor->addr.integer[offset];
136
16.0k
            break;
137
5.63k
        case t_uinteger:
138
5.63k
            targetaddr = (char *)&cursor->addr.uinteger[offset];
139
5.63k
            break;
140
0
        case t_longint:
141
0
            targetaddr = (char *)&cursor->addr.longint[offset];
142
0
            break;
143
393
        case t_ulongint:
144
393
            targetaddr = (char *)&cursor->addr.ulongint[offset];
145
393
            break;
146
0
        case t_short:
147
0
            targetaddr = (char *)&cursor->addr.shortint[offset];
148
0
            break;
149
0
        case t_ushort:
150
0
            targetaddr = (char *)&cursor->addr.ushortint[offset];
151
0
            break;
152
3.34k
        case t_time:
153
3.34k
            targetaddr = (char *)&cursor->addr.ts[offset];
154
3.34k
            break;
155
0
        case t_timespec:
156
0
            targetaddr = (char *)&cursor->addr.ts[offset];
157
0
            break;
158
30.9k
        case t_real:
159
30.9k
            targetaddr = (char *)&cursor->addr.real[offset];
160
30.9k
            break;
161
6.23k
        case t_string:
162
6.23k
            targetaddr = cursor->addr.string;
163
6.23k
            break;
164
1.60k
        case t_boolean:
165
1.60k
            targetaddr = (char *)&cursor->addr.boolean[offset];
166
1.60k
            break;
167
993
        case t_character:
168
993
            targetaddr = (char *)&cursor->addr.character[offset];
169
993
            break;
170
5.03k
        default:
171
5.03k
            targetaddr = NULL;
172
5.03k
            break;
173
89.6k
        }
174
89.6k
    } else {
175
        // tricky case - hacking a member in an array of structures
176
72.2k
        targetaddr =
177
72.2k
            parent->arr.objects.base + (offset * parent->arr.objects.stride) +
178
72.2k
            cursor->addr.offset;
179
72.2k
    }
180
161k
    json_debug_trace(1, "json: Target address for %s (offset %d) is %p\n",
181
161k
                      cursor->attribute, offset, targetaddr);
182
161k
    return targetaddr;
183
161k
}
184
185
186
static int json_internal_read_object(const char *cp,
187
                                     const struct json_attr_t *attrs,
188
                                     const struct json_array_t *parent,
189
                                     int offset,
190
                                     const char **end)
191
9.12k
{
192
9.12k
    enum
193
9.12k
    { init, await_attr, in_attr, await_value, in_val_string,
194
9.12k
        in_escape, in_val_token, post_val, post_element
195
9.12k
    } state = 0;
196
9.12k
    char *statenames[] = {
197
9.12k
        "init", "await_attr", "in_attr", "await_value", "in_val_string",
198
9.12k
        "in_escape", "in_val_token", "post_val", "post_element",
199
9.12k
    };
200
9.12k
    char attrbuf[JSON_ATTR_MAX + 1], *pattr = NULL;
201
9.12k
    char valbuf[JSON_VAL_MAX + 1], *pval = NULL;
202
9.12k
    bool value_quoted = false;
203
9.12k
    char uescape[5];            // enough space for 4 hex digits and a NUL
204
9.12k
    const struct json_attr_t *cursor;
205
9.12k
    int substatus, maxlen = 0;
206
9.12k
    unsigned int u;
207
9.12k
    const struct json_enum_t *mp;
208
9.12k
    char *lptr;
209
210
9.12k
    if (NULL != end) {
211
4.59k
        *end = NULL;    // give it a well-defined value on parse failure
212
4.59k
    }
213
214
    // stuff fields with defaults in case they're omitted in the JSON input
215
138k
    for (cursor = attrs; cursor->attribute != NULL; cursor++)
216
129k
        if (!cursor->nodefault) {
217
128k
            lptr = json_target_address(cursor, parent, offset);
218
128k
            if (NULL != lptr)
219
119k
                switch (cursor->type) {
220
3.05k
                case t_byte:
221
3.05k
                    lptr[0] = cursor->dflt.byte;
222
3.05k
                    break;
223
17.5k
                case t_ubyte:
224
17.5k
                    lptr[0] = cursor->dflt.ubyte;
225
17.5k
                    break;
226
15.6k
                case t_integer:
227
15.6k
                    memcpy(lptr, &cursor->dflt.integer, sizeof(int));
228
15.6k
                    break;
229
6.68k
                case t_uinteger:
230
6.68k
                    memcpy(lptr, &cursor->dflt.uinteger, sizeof(unsigned int));
231
6.68k
                    break;
232
0
                case t_longint:
233
0
                    memcpy(lptr, &cursor->dflt.longint, sizeof(long));
234
0
                    break;
235
111
                case t_ulongint:
236
111
                    memcpy(lptr, &cursor->dflt.ulongint,
237
111
                           sizeof(unsigned long));
238
111
                    break;
239
3.05k
                case t_short:
240
3.05k
                    memcpy(lptr, &cursor->dflt.shortint, sizeof(short));
241
3.05k
                    break;
242
0
                case t_ushort:
243
0
                    memcpy(lptr, &cursor->dflt.ushortint,
244
0
                           sizeof(unsigned short));
245
0
                    break;
246
1.64k
                case t_time:
247
1.64k
                    memcpy(lptr, &cursor->dflt.ts, sizeof(timespec_t));
248
1.64k
                    break;
249
1.31k
                case t_timespec:
250
1.31k
                    memcpy(lptr, &cursor->dflt.ts, sizeof(timespec_t));
251
1.31k
                    break;
252
49.9k
                case t_real:
253
49.9k
                    memcpy(lptr, &cursor->dflt.real, sizeof(double));
254
49.9k
                    break;
255
10.3k
                case t_string:
256
10.3k
                    if (parent != NULL
257
4.80k
                        && parent->element_type != t_structobject
258
0
                        && offset > 0)
259
0
                        return JSON_ERR_NOPARSTR;
260
10.3k
                    lptr[0] = '\0';
261
10.3k
                    break;
262
4.08k
                case t_boolean:
263
4.08k
                    memcpy(lptr, &cursor->dflt.boolean, sizeof(bool));
264
4.08k
                    break;
265
1.11k
                case t_character:
266
1.11k
                    lptr[0] = cursor->dflt.character;
267
1.11k
                    break;
268
0
                case t_object:  // silences a compiler warning
269
0
                    FALLTHROUGH
270
0
                case t_structobject:
271
0
                    FALLTHROUGH
272
0
                case t_array:
273
0
                    FALLTHROUGH
274
655
                case t_check:
275
655
                    FALLTHROUGH
276
5.24k
                case t_ignore:
277
5.24k
                    break;
278
119k
                }
279
128k
        }
280
281
9.12k
    json_debug_trace(1, "json: JSON parse of '%s' begins.\n", cp);
282
283
    // parse input JSON
284
470k
    for (; *cp != '\0'; cp++) {
285
468k
        json_debug_trace(2, "json: State %-14s, looking at '%c' (%p)\n",
286
468k
                          statenames[state], *cp, cp);
287
468k
        switch (state) {
288
9.75k
        case init:
289
9.75k
            if (isspace((unsigned char) *cp)) {
290
645
                continue;
291
645
            }
292
9.10k
            if (*cp == '{') {
293
9.08k
                state = await_attr;
294
9.08k
            } else {
295
18
                json_debug_trace(1, "json: %s",
296
18
                                  "Non-WS when expecting object start.\n");
297
18
                if (NULL != end) {
298
18
                    *end = cp;
299
18
                }
300
18
                return JSON_ERR_OBSTART;
301
18
            }
302
9.08k
            break;
303
37.5k
        case await_attr:
304
37.5k
            if (isspace((unsigned char) *cp)) {
305
388
                continue;
306
388
            }
307
37.1k
            if (*cp == '"') {
308
36.8k
                state = in_attr;
309
36.8k
                pattr = attrbuf;
310
36.8k
                if (NULL != end) {
311
6.09k
                    *end = cp;
312
6.09k
                }
313
36.8k
            } else if (*cp == '}') {
314
200
                break;
315
200
            } else {
316
91
                json_debug_trace(1, "json: %s",
317
91
                          "Non-WS when expecting attribute.\n");
318
91
                if (NULL != end) {
319
9
                    *end = cp;
320
9
                }
321
91
                return JSON_ERR_ATTRSTART;
322
91
            }
323
36.8k
            break;
324
157k
        case in_attr:
325
157k
            if (NULL == pattr) {
326
                // don't update end here, leave at attribute start
327
0
                return JSON_ERR_NULLPTR;
328
0
            }
329
157k
            if (*cp == '"') {
330
36.8k
                *pattr++ = '\0';
331
36.8k
                json_debug_trace(1, "json: Collected attribute name %s\n",
332
36.8k
                                  attrbuf);
333
380k
                for (cursor = attrs; cursor->attribute != NULL; cursor++) {
334
380k
                    json_debug_trace(2, "json: Checking against %s\n",
335
380k
                                      cursor->attribute);
336
380k
                    if (strcmp(cursor->attribute, attrbuf) == 0) {
337
23.8k
                        break;
338
23.8k
                    }
339
356k
                    if (cursor->type == t_ignore &&
340
13.1k
                        strncmp(cursor->attribute, "", 1) == 0) {
341
12.8k
                        break;
342
12.8k
                    }
343
356k
                }
344
36.8k
                if (NULL == cursor->attribute) {
345
90
                    json_debug_trace(1,
346
90
                                      "json: Unknown attribute name '%s'"
347
90
                                      " (attributes begin with '%s').\n",
348
90
                                      attrbuf, attrs->attribute);
349
                    // don't update end here, leave at attribute start
350
90
                    return JSON_ERR_BADATTR;
351
90
                }
352
36.7k
                state = await_value;
353
36.7k
                if (cursor->type == t_string) {
354
954
                    maxlen = (int)cursor->len - 1;
355
35.7k
                } else if (cursor->type == t_check) {
356
1.95k
                    maxlen = (int)strnlen(cursor->dflt.check, JSON_VAL_MAX);
357
33.8k
                } else if (cursor->type == t_time ||
358
31.2k
                           cursor->type == t_ignore) {
359
19.7k
                    maxlen = JSON_VAL_MAX;
360
19.7k
                } else if (NULL != cursor->map) {
361
446
                    maxlen = (int)sizeof(valbuf) - 1;
362
446
                }
363
36.7k
                pval = valbuf;
364
120k
            } else if (pattr >= attrbuf + JSON_ATTR_MAX - 1) {
365
7
                json_debug_trace(1, "json: %s","Attribute name too long.\n");
366
                // don't update end here, leave at attribute start
367
7
                return JSON_ERR_ATTRLEN;
368
120k
            } else {
369
120k
                *pattr++ = *cp;
370
120k
            }
371
157k
            break;
372
157k
        case await_value:
373
39.2k
            if (isspace((unsigned char) *cp) ||
374
38.9k
                *cp == ':') {
375
2.58k
                continue;
376
2.58k
            }
377
36.6k
            if (*cp == '[') {
378
1.18k
                if (cursor->type != t_array) {
379
314
                    json_debug_trace(1,"json: %s",
380
314
                                      "Saw [ when not expecting array.\n");
381
314
                    if (NULL != end) {
382
1
                        *end = cp;
383
1
                    }
384
314
                    return JSON_ERR_NOARRAY;
385
314
                }
386
875
                substatus = json_read_array(cp, &cursor->addr.array, &cp);
387
875
                if (substatus != 0) {
388
198
                    return substatus;
389
198
                }
390
677
                state = post_element;
391
35.4k
            } else if (cursor->type == t_array) {
392
1
                json_debug_trace(1, "json: %s",
393
1
                                  "Array element was specified, but no [.\n");
394
1
                if (NULL != end) {
395
0
                    *end = cp;
396
0
                }
397
1
                return JSON_ERR_NOBRAK;
398
35.4k
            } else if (*cp == '"') {
399
9.64k
                value_quoted = true;
400
9.64k
                state = in_val_string;
401
9.64k
                pval = valbuf;
402
25.7k
            } else {
403
25.7k
                value_quoted = false;
404
25.7k
                state = in_val_token;
405
25.7k
                pval = valbuf;
406
25.7k
                *pval++ = *cp;
407
25.7k
            }
408
36.1k
            break;
409
69.3k
        case in_val_string:
410
69.3k
            if (NULL == pval) {
411
                // don't update end here, leave at value start
412
0
                return JSON_ERR_NULLPTR;
413
0
            }
414
69.3k
            if (*cp == '\\') {
415
1.68k
                state = in_escape;
416
67.6k
            } else if (*cp == '"') {
417
9.24k
                *pval++ = '\0';
418
9.24k
                json_debug_trace(1, "json: Collected string value %s\n", valbuf);
419
9.24k
                state = post_val;
420
58.3k
            } else if (pval > valbuf + JSON_VAL_MAX - 1 ||
421
58.3k
                       pval > valbuf + maxlen - 1) {
422
116
                json_debug_trace(1, "json: %s",  "String value too long.\n");
423
                // don't update end here, leave at value start
424
116
                return JSON_ERR_STRLONG;
425
58.2k
            } else {
426
58.2k
                *pval++ = *cp;
427
58.2k
            }
428
69.1k
            break;
429
69.1k
        case in_escape:
430
1.67k
            if (NULL == pval) {
431
                /* don't update end here, leave at value start */
432
0
                return JSON_ERR_NULLPTR;
433
0
            }
434
1.67k
            if (pval > valbuf + JSON_VAL_MAX - 1 ||
435
1.67k
                pval > valbuf + maxlen) {
436
9
                json_debug_trace(1, "json: %s",  "String value too long.\n");
437
                // don't update end here, leave at value start
438
9
                return JSON_ERR_STRLONG;
439
9
            }
440
1.66k
            switch (*cp) {
441
203
            case 'b':
442
203
                *pval++ = '\b';
443
203
                break;
444
194
            case 'f':
445
194
                *pval++ = '\f';
446
194
                break;
447
326
            case 'n':
448
326
                *pval++ = '\n';
449
326
                break;
450
196
            case 'r':
451
196
                *pval++ = '\r';
452
196
                break;
453
259
            case 't':
454
259
                *pval++ = '\t';
455
259
                break;
456
241
            case 'u':
457
241
                {
458
241
                    unsigned n;
459
460
241
                    cp++;                   // skip the 'u'
461
                    // NetBSD 6 wants the cast
462
1.05k
                    for (n = 0; n < 4 && isxdigit((int)*cp); n++) {
463
815
                        uescape[n] = *cp++;
464
815
                    }
465
241
                    uescape[n] = '\0';      // terminate
466
241
                    --cp;
467
                    // ECMA-404 says JSON \u must have 4 hex digits
468
241
                    if ((4 != n) ||
469
194
                        (1 != sscanf(uescape, "%4x", &u))) {
470
47
                        return JSON_ERR_BADSTRING;
471
47
                    }
472
                    // truncate values above 0xff
473
194
                    *pval++ = (unsigned char)u;
474
194
                }
475
0
                break;
476
247
            default:            // handles double quote and solidus
477
247
                *pval++ = *cp;
478
247
                break;
479
1.66k
            }
480
1.61k
            state = in_val_string;
481
1.61k
            break;
482
118k
        case in_val_token:
483
118k
            if (NULL == pval) {
484
                // don't update end here, leave at value start
485
0
                return JSON_ERR_NULLPTR;
486
0
            }
487
118k
            if (isspace((unsigned char) *cp) ||
488
118k
                *cp == ',' ||
489
97.4k
                *cp == '}') {
490
25.6k
                *pval = '\0';
491
25.6k
                json_debug_trace(1, "json: Collected token valuen %s\n",
492
25.6k
                                 valbuf);
493
25.6k
                state = post_val;
494
25.6k
                if (*cp == '}' ||
495
25.2k
                    *cp == ',') {
496
25.2k
                    --cp;
497
25.2k
                }
498
92.7k
            } else if (pval > valbuf + JSON_VAL_MAX - 1) {
499
6
                json_debug_trace(1, "json: %s", "Token value too long.\n");
500
                // don't update end here, leave at value start
501
6
                return JSON_ERR_TOKLONG;
502
92.7k
            } else {
503
92.7k
                *pval++ = *cp;
504
92.7k
            }
505
118k
            break;
506
            // coverity[unterminated_case]
507
118k
        case post_val:
508
            // Ignore whitespace after either string or token values.
509
33.9k
    if (isspace((unsigned char) *cp)) {
510
678
                while (*cp != '\0' && isspace((unsigned char) *cp)) {
511
448
                    ++cp;
512
448
                }
513
230
                json_debug_trace(1,
514
230
                    "json: Skipped trailing whitespace: value \"%s\"\n", valbuf);
515
230
            }
516
            /*
517
             * We know that cursor points at the first spec matching
518
             * the current attribute.  We don't know that it's *the*
519
             * correct spec; our dialect allows there to be any number
520
             * of adjacent ones with the same attrname but different
521
             * types.  Here's where we try to seek forward for a
522
             * matching type/attr pair if we're not looking at one.
523
             */
524
34.0k
            for (;;) {
525
34.0k
                int seeking = cursor->type;
526
527
34.0k
                if (value_quoted &&
528
8.30k
                    (cursor->type == t_string ||
529
7.38k
                     cursor->type == t_time)) {
530
3.28k
                    break;
531
3.28k
                }
532
30.8k
                if ((strcmp(valbuf, "true") == 0 ||
533
29.0k
                     strcmp(valbuf, "false") == 0) &&
534
1.98k
                    seeking == t_boolean) {
535
1.49k
                    break;
536
1.49k
                }
537
29.3k
                if (isdigit((unsigned char) valbuf[0])) {
538
15.7k
                    bool decimal = strchr(valbuf, '.') != NULL;
539
540
15.7k
                    if (decimal &&
541
1.18k
                        seeking == t_real) {
542
291
                        break;
543
291
                    }
544
15.4k
                    if (!decimal && (seeking == t_byte ||
545
14.2k
                                     seeking == t_ubyte ||
546
13.8k
                                     seeking == t_integer ||
547
12.7k
                                     seeking == t_uinteger ||
548
11.9k
                                     seeking == t_longint ||
549
11.9k
                                     seeking == t_ulongint ||
550
11.6k
                                     seeking == t_short ||
551
10.0k
                                     seeking == t_ushort))
552
4.51k
                        break;
553
15.4k
                }
554
24.5k
                if (NULL == cursor[1].attribute) {  // out of possibilities
555
16.6k
                    break;
556
16.6k
                }
557
7.85k
                if (0 != strcmp(cursor[1].attribute, attrbuf)) {
558
7.65k
                    break;
559
7.65k
                }
560
197
                ++cursor;
561
197
            }
562
33.9k
            if (value_quoted &&
563
8.30k
                (cursor->type != t_string &&
564
7.38k
                 cursor->type != t_character &&
565
7.18k
                 cursor->type != t_check &&
566
6.48k
                 cursor->type != t_time &&
567
4.12k
                 cursor->type != t_ignore &&
568
460
                 cursor->map == 0)) {
569
21
                json_debug_trace(1, "json: %s", "Saw quoted value when expecting"
570
21
                                  " non-string.\n");
571
21
                return JSON_ERR_QNONSTRING;
572
21
            }
573
33.8k
            if (!value_quoted &&
574
25.5k
                (cursor->type == t_string ||
575
25.5k
                 cursor->type == t_check ||
576
25.5k
                 cursor->type == t_time ||
577
25.5k
                 cursor->map != 0)) {
578
57
                json_debug_trace(1, "json: %s",
579
57
                                 "Didn't see quoted value when expecting"
580
57
                                 " string.\n");
581
57
                return JSON_ERR_NONQSTRING;
582
57
            }
583
33.8k
            if (cursor->map != 0) {
584
882
                for (mp = cursor->map; mp->name != NULL; mp++)
585
817
                    if (strcmp(mp->name, valbuf) == 0) {
586
374
                        goto foundit;
587
374
                    }
588
65
                json_debug_trace(1,
589
65
                                 "json: Invalid enumerated value string %s.\n",
590
65
                                  valbuf);
591
65
                return JSON_ERR_BADENUM;
592
374
              foundit:
593
374
                (void)snprintf(valbuf, sizeof(valbuf), "%d", mp->value);
594
374
            }
595
33.7k
            if (cursor->type == t_check) {
596
702
                lptr = cursor->dflt.check;
597
33.0k
            } else {
598
33.0k
                lptr = json_target_address(cursor, parent, offset);
599
33.0k
            }
600
33.7k
            if (NULL != lptr) {
601
19.0k
                switch (cursor->type) {
602
411
                case t_byte:
603
411
                    {
604
411
                        int tmp = atoi(valbuf);
605
411
                        lptr[0] = (char)tmp;
606
411
                    }
607
411
                    break;
608
693
                case t_ubyte:
609
693
                    {
610
693
                        int tmp = atoi(valbuf);
611
693
                        lptr[0] = (unsigned char)tmp;
612
693
                    }
613
693
                    break;
614
1.70k
                case t_integer:
615
1.70k
                    {
616
1.70k
                        int tmp = atoi(valbuf);
617
1.70k
                        memcpy(lptr, &tmp, sizeof(int));
618
1.70k
                    }
619
1.70k
                    break;
620
1.16k
                case t_uinteger:
621
1.16k
                    {
622
1.16k
                        unsigned int tmp = (unsigned int)atol(valbuf);
623
1.16k
                        memcpy(lptr, &tmp, sizeof(unsigned int));
624
1.16k
                    }
625
1.16k
                    break;
626
0
                case t_longint:
627
0
                    {
628
0
                        long tmp = atol(valbuf);
629
0
                        memcpy(lptr, &tmp, sizeof(long));
630
0
                    }
631
0
                    break;
632
282
                case t_ulongint:
633
282
                    {
634
282
                        unsigned long tmp = (unsigned long)atoll(valbuf);
635
282
                        memcpy(lptr, &tmp, sizeof(unsigned long));
636
282
                    }
637
282
                    break;
638
1.63k
                case t_short:
639
1.63k
                    {
640
1.63k
                        short tmp = atoi(valbuf);
641
1.63k
                        memcpy(lptr, &tmp, sizeof(short));
642
1.63k
                    }
643
1.63k
                    break;
644
0
                case t_ushort:
645
0
                    {
646
0
                        unsigned short tmp = (unsigned int)atoi(valbuf);
647
0
                        memcpy(lptr, &tmp, sizeof(unsigned short));
648
0
                    }
649
0
                    break;
650
2.36k
                case t_time:
651
2.36k
                    {
652
2.36k
                        timespec_t ts_tmp = iso8601_to_timespec(valbuf);
653
2.36k
                        memcpy(lptr, &ts_tmp, sizeof(timespec_t));
654
2.36k
                    }
655
2.36k
                    break;
656
427
                case t_timespec:
657
427
                    {
658
427
                        double sec_tmp = safe_atof(valbuf);
659
427
                        timespec_t ts_tmp;
660
427
                        if (0 != isfinite(sec_tmp)) {
661
206
                            DTOTS(&ts_tmp, sec_tmp);
662
206
                            memcpy(lptr, &ts_tmp, sizeof(timespec_t));
663
206
                        } // else leave at .dflt
664
427
                    }
665
427
                    break;
666
4.48k
                case t_real:
667
4.48k
                    {
668
4.48k
                        double tmp = safe_atof(valbuf);
669
4.48k
                        if (0 != isfinite(tmp)) {
670
3.47k
                            memcpy(lptr, &tmp, sizeof(double));
671
3.47k
                        } // else leave at .dflt
672
4.48k
                    }
673
4.48k
                    break;
674
921
                case t_string:
675
921
                    if (NULL != parent &&
676
258
                        parent->element_type != t_structobject &&
677
0
                        offset > 0) {
678
0
                        return JSON_ERR_NOPARSTR;
679
0
                    }
680
921
                    (void)strlcpy(lptr, valbuf, cursor->len);
681
921
                    break;
682
1.88k
                case t_boolean:
683
1.88k
                    {
684
1.88k
                        bool tmp = (strcmp(valbuf, "true") == 0);
685
1.88k
                        memcpy(lptr, &tmp, sizeof(bool));
686
1.88k
                    }
687
1.88k
                    break;
688
534
                case t_character:
689
534
                    if (strnlen(valbuf, 2) > 1) {
690
                        // don't update end here, leave at value start
691
5
                        return JSON_ERR_STRLONG;
692
529
                    } else {
693
529
                        lptr[0] = valbuf[0];
694
529
                    }
695
529
                    break;
696
1.83k
                case t_ignore:  // silences a compiler warning
697
1.83k
                    FALLTHROUGH
698
1.83k
                case t_object:  // silences a compiler warning
699
1.83k
                    FALLTHROUGH
700
1.83k
                case t_structobject:
701
1.83k
                    FALLTHROUGH
702
1.83k
                case t_array:
703
1.83k
                    break;
704
702
                case t_check:
705
702
                    if (strcmp(cursor->dflt.check, valbuf) != 0) {
706
148
                        json_debug_trace(1, "json: Required attribute value %s"
707
148
                                          " not present.\n",
708
148
                                          cursor->dflt.check);
709
                        // don't update end here, leave at start of attribute
710
148
                        return JSON_ERR_CHECKFAIL;
711
148
                    }
712
554
                    break;
713
19.0k
                }
714
19.0k
            }
715
33.6k
            FALLTHROUGH
716
34.8k
        case post_element:
717
34.8k
            if (isspace((unsigned char) *cp)) {
718
593
                continue;
719
593
            }
720
34.2k
            if (*cp == ',') {
721
28.6k
                state = await_attr;
722
28.6k
            } else if (*cp == '}') {
723
4.96k
                ++cp;
724
4.96k
                goto good_parse;
725
4.96k
            } else {
726
580
                json_debug_trace(1, "json: %s",
727
580
                                "Garbage while expecting comma or }\n");
728
580
                if (NULL != end) {
729
20
                    *end = cp;
730
20
                }
731
580
                return JSON_ERR_BADTRAIL;
732
580
            }
733
28.6k
            break;
734
468k
        }
735
468k
    }
736
2.38k
    if (state == init) {
737
22
        json_debug_trace(1, "json: %s", "Input was empty or white-space only\n");
738
22
        return JSON_ERR_EMPTY;
739
22
    }
740
741
7.33k
  good_parse:
742
    // in case there's another object following, consume trailing WS
743
7.33k
    while (isspace((unsigned char)*cp)) {
744
215
        ++cp;
745
215
    }
746
7.33k
    if (NULL != end) {
747
4.50k
        *end = cp;
748
4.50k
    }
749
7.33k
    json_debug_trace(1, "%s", "json: JSON parse ends.\n");
750
7.33k
    return 0;
751
2.38k
}
752
753
int json_read_array(const char *cp, const struct json_array_t *arr,
754
                    const char **end)
755
875
{
756
875
    int substatus, offset, arrcount;
757
875
    char *tp;
758
759
875
    if (NULL != end)
760
875
        *end = NULL;    // give it a well-defined value on parse failure
761
762
875
    json_debug_trace(1, "json: %s", "Entered json_read_array()\n");
763
764
875
    while (isspace((unsigned char)*cp)) {
765
0
        cp++;
766
0
    }
767
875
    if (*cp != '[') {
768
0
        json_debug_trace(1, "json: %s", "Didn't find expected array start\n");
769
0
        return JSON_ERR_ARRAYSTART;
770
0
    }
771
875
    cp++;
772
773
875
    tp = arr->arr.strings.store;
774
875
    arrcount = 0;
775
776
    // Check for empty array
777
875
    while (isspace((unsigned char)*cp)) {
778
218
        cp++;
779
218
    }
780
875
    if (*cp == ']') {
781
206
        goto breakout;
782
206
    }
783
784
8.05k
    for (offset = 0; offset < arr->maxlen; offset++) {
785
8.05k
        char *ep = NULL;
786
787
8.05k
        json_debug_trace(1, "json: Looking at %s\n", cp);
788
8.05k
        switch (arr->element_type) {
789
3.45k
        case t_string:
790
3.45k
            if (isspace((unsigned char) *cp)) {
791
226
                cp++;
792
226
            }
793
3.45k
            if (*cp != '"') {
794
9
                return JSON_ERR_BADSTRING;
795
3.44k
            } else {
796
3.44k
                ++cp;
797
3.44k
            }
798
3.44k
            arr->arr.strings.ptrs[offset] = tp;
799
12.0k
            for (; tp - arr->arr.strings.store < arr->arr.strings.storelen;
800
8.58k
                 tp++)
801
12.0k
                if (*cp == '"') {
802
3.44k
                    ++cp;
803
3.44k
                    *tp++ = '\0';
804
3.44k
                    goto stringend;
805
8.58k
                } else if (*cp == '\0') {
806
1
                    json_debug_trace(1, "json: %s",
807
1
                                      "Bad string syntax in string list.\n");
808
1
                    return JSON_ERR_BADSTRING;
809
8.58k
                } else {
810
8.58k
                    *tp = *cp++;
811
8.58k
                }
812
3
            json_debug_trace(1, "json: %s",
813
3
                             "Bad string syntax in string list.\n");
814
3
            return JSON_ERR_BADSTRING;
815
3.44k
          stringend:
816
3.44k
            break;
817
0
        case t_object:
818
0
            FALLTHROUGH
819
4.59k
        case t_structobject:
820
4.59k
            substatus =
821
4.59k
                json_internal_read_object(cp, arr->arr.objects.subtype, arr,
822
4.59k
                                          offset, &cp);
823
4.59k
            if (substatus != 0) {
824
85
                if (NULL != end) {
825
85
                    *end = cp;
826
85
                }
827
85
                return substatus;
828
85
            }
829
4.50k
            break;
830
4.50k
        case t_integer:
831
0
            arr->arr.integers.store[offset] = (int)strtol(cp, &ep, 0);
832
0
            if (ep == cp) {
833
0
                return JSON_ERR_BADNUM;
834
0
            }
835
0
            cp = ep;
836
0
            break;
837
0
        case t_uinteger:
838
0
            arr->arr.uintegers.store[offset] = (unsigned int)strtoul(cp,
839
0
                                                                     &ep, 0);
840
0
            if (ep == cp) {
841
0
                return JSON_ERR_BADNUM;
842
0
            }
843
0
            cp = ep;
844
0
            break;
845
0
        case t_longint:
846
0
            arr->arr.longint.store[offset] = strtol(cp, &ep, 0);
847
0
            if (ep == cp) {
848
0
                return JSON_ERR_BADNUM;
849
0
            }
850
0
            cp = ep;
851
0
            break;
852
0
        case t_ulongint:
853
0
            arr->arr.ulongint.store[offset] = strtoul(cp, &ep, 0);
854
0
            if (ep == cp) {
855
0
                return JSON_ERR_BADNUM;
856
0
            }
857
0
            cp = ep;
858
0
            break;
859
0
        case t_byte:
860
0
            arr->arr.bytes.store[offset] = (char)strtol(cp, &ep, 0);
861
0
            if (ep == cp) {
862
0
                return JSON_ERR_BADNUM;
863
0
            }
864
0
            cp = ep;
865
0
            break;
866
0
        case t_ubyte:
867
0
            arr->arr.ubytes.store[offset] = (unsigned char)strtoul(cp,
868
0
                                                                   &ep, 0);
869
0
            if (ep == cp) {
870
0
                return JSON_ERR_BADNUM;
871
0
            }
872
0
            cp = ep;
873
0
            break;
874
0
        case t_short:
875
0
            arr->arr.shorts.store[offset] = (short)strtol(cp, &ep, 0);
876
0
            if (ep == cp) {
877
0
                return JSON_ERR_BADNUM;
878
0
            }
879
0
            cp = ep;
880
0
            break;
881
0
        case t_ushort:
882
0
            arr->arr.ushorts.store[offset] = (unsigned short)strtoul(cp,
883
0
                                                                     &ep, 0);
884
0
            if (ep == cp) {
885
0
                return JSON_ERR_BADNUM;
886
0
            }
887
0
            cp = ep;
888
0
            break;
889
0
        case t_time:
890
0
            {
891
0
                timespec_t ts_tmp;
892
0
                if (*cp != '"') {
893
0
                    return JSON_ERR_BADSTRING;
894
0
                } else {
895
0
                    ++cp;
896
0
                }
897
0
                ts_tmp = iso8601_to_timespec(cp);
898
0
                arr->arr.timespecs.store[offset] = ts_tmp;
899
0
                while (*cp &&
900
0
                       *cp != '"') {
901
0
                    cp++;
902
0
                }
903
0
                if (*cp != '"') {
904
0
                    return JSON_ERR_BADSTRING;
905
0
                }
906
0
                ++cp;
907
0
            }
908
0
            break;
909
0
        case t_timespec:
910
            // TODO not sure how to implement this
911
0
            return JSON_ERR_BADNUM;
912
0
            break;
913
0
        case t_real:
914
0
            arr->arr.reals.store[offset] = strtod(cp, &ep);
915
0
            if (ep == cp) {
916
0
                return JSON_ERR_BADNUM;
917
0
            }
918
0
            cp = ep;
919
0
            break;
920
0
        case t_boolean:
921
0
            if (str_starts_with(cp, "true")) {
922
0
                arr->arr.booleans.store[offset] = true;
923
0
                cp += 4;
924
0
            } else if (str_starts_with(cp, "false")) {
925
0
                arr->arr.booleans.store[offset] = false;
926
0
                cp += 5;
927
0
            }
928
0
            break;
929
0
        case t_character:
930
0
            FALLTHROUGH
931
0
        case t_array:
932
0
            FALLTHROUGH
933
0
        case t_check:
934
0
            FALLTHROUGH
935
0
        case t_ignore:
936
0
            json_debug_trace(1, "json: %s", "Invalid array subtype.\n");
937
0
            return JSON_ERR_SUBTYPE;
938
8.05k
        }
939
7.95k
        arrcount++;
940
7.95k
        if (isspace((unsigned char)*cp)) {
941
221
            cp++;
942
221
        }
943
7.95k
        if (*cp == ']') {
944
471
            json_debug_trace(1, "json: %s", "End of array found.\n");
945
471
            goto breakout;
946
471
        }
947
7.48k
        if (*cp == ',') {
948
7.38k
            cp++;
949
7.38k
        } else {
950
97
            json_debug_trace(1, "json: %s", "Bad trailing syntax on array.\n");
951
97
            return JSON_ERR_BADSUBTRAIL;
952
97
        }
953
7.48k
    }
954
3
    json_debug_trace(1, "json: %s", "Too many elements in array.\n");
955
3
    if (NULL != end) {
956
3
        *end = cp;
957
3
    }
958
3
    return JSON_ERR_SUBTOOLONG;
959
677
  breakout:
960
677
    if (NULL != arr->count) {
961
677
        *(arr->count) = arrcount;
962
677
    }
963
677
    if (NULL != end) {
964
677
        *end = cp;
965
677
    }
966
677
    json_debug_trace(1, "json: leaving json_read_array() with %d elements\n",
967
677
                      arrcount);
968
677
    return 0;
969
669
}
970
971
int json_read_object(const char *cp, const struct json_attr_t *attrs,
972
                     const char **end)
973
4.53k
{
974
4.53k
    int st;
975
976
4.53k
    json_debug_trace(1, "json: json_read_object() sees '%s'\n", cp);
977
4.53k
    st = json_internal_read_object(cp, attrs, NULL, 0, end);
978
4.53k
    return st;
979
4.53k
}
980
981
const char *json_error_string(int err)
982
0
{
983
0
    const char *errors[] = {
984
0
        "unknown error while parsing JSON",
985
0
        "non-whitespace when expecting object start",
986
0
        "non-whitespace when expecting attribute start",
987
0
        "unknown attribute name",
988
0
        "attribute name too long",
989
0
        "saw [ when not expecting array",
990
0
        "array element specified, but no [",
991
0
        "string value too long",
992
0
        "token value too long",
993
0
        "garbage while expecting comma or } or ]",
994
0
        "didn't find expected array start",
995
0
        "error while parsing object array",
996
0
        "too many array elements",
997
0
        "garbage while expecting array comma",
998
0
        "unsupported array element type",
999
0
        "error while string parsing",
1000
0
        "check attribute not matched",
1001
0
        "can't support strings in parallel arrays",
1002
0
        "invalid enumerated value",
1003
0
        "saw quoted value when expecting nonstring",
1004
0
        "didn't see quoted value when expecting string",
1005
0
        "other data conversion error",
1006
0
        "unexpected null value or attribute pointer",
1007
0
        "object element specified, but no {",
1008
0
        "input was empty or white-space only",
1009
0
    };
1010
1011
0
    if (err <= 0 ||
1012
0
        err >= (int)(sizeof(errors) / sizeof(errors[0]))) {
1013
0
        return errors[0];
1014
0
    }
1015
0
    return errors[err];
1016
0
}
1017
1018
/* quote a JSON string so it can be used as a simple JSON string.
1019
 * Used to output the JSON as a literal JSON string
1020
 * escape control chars, escape double quote.
1021
 * stop at NUL, in_len or bad unicode char
1022
 */
1023
char *json_quote(const char *in_buffer, char *out_buffer, size_t in_len,
1024
                 size_t out_len)
1025
0
{
1026
0
    const char *escape_match = "'\"/\\\b\f\n\r\t";
1027
0
    const char *escaped_bit = "'\"/\\bfnrt";
1028
0
    unsigned out_index = 0;
1029
0
    const char *escape_ptr;
1030
0
    unsigned in_index = 0;
1031
0
    unsigned to_copy = 0;
1032
1033
0
    out_buffer[0] = '\0';
1034
1035
    // check in string, stop at NUL, done in_len, or out_buffer full
1036
0
    for (in_index = 0; in_buffer[in_index] != '\0'; in_index++) {
1037
1038
0
        if (in_index >= in_len) {
1039
            // got all from input buffer
1040
0
            break;
1041
0
        }
1042
1043
0
        if (out_index > (out_len - 8) ) {
1044
            /* output out_buffer full.  Not enough space for a 4-byte UTF + NUL,
1045
             * or \uxxxx + NUL.  Safer to check once, at the top,
1046
             * than a lot of specific size checks later in the loop.
1047
             */
1048
0
            break;
1049
0
        }
1050
1051
0
        if (in_buffer[in_index] & 0x80) {
1052
            // highbit set. assume unicode
1053
0
            to_copy = 0;    // always reset before use, to shut up coverity
1054
1055
            // check in_len so we don't overrun in_buffer
1056
0
            if ((in_len > (in_index + 1)) &&
1057
0
                (0xC0 == (0xE0 & (uint8_t)in_buffer[in_index])) &&
1058
0
                (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 1]))) {
1059
                // utf-8 ish 16bit rune - deg, plusm, mplus etc.
1060
0
                to_copy = 2;
1061
0
            } else if ((in_len > (in_index + 2)) &&
1062
0
                       (0xE0 == (0xF0 & (uint8_t)in_buffer[in_index])) &&
1063
0
                       (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 1])) &&
1064
0
                       (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 2]))) {
1065
                // utf-8 ish 24 bit rune - (double) prime etc.
1066
0
                to_copy = 3;
1067
0
            } else if ((in_len > (in_index + 3)) &&
1068
0
                       (0xF0 == (0xF8 & (uint8_t)in_buffer[in_index])) &&
1069
0
                       (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 1])) &&
1070
0
                       (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 2])) &&
1071
0
                       (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 3]))) {
1072
                // utf-8 ish 32 bit rune - musical symbol g clef etc.
1073
0
                to_copy = 4;
1074
0
            } else {
1075
                // WTF??  Short UTF?  Bad UTF?
1076
0
                str_appendf(out_buffer, out_len,
1077
0
                            "\\u%04x", in_buffer[in_index] & 0x0ff);
1078
0
                out_index += 6;
1079
0
                continue;
1080
0
            }
1081
1082
0
            memcpy(&out_buffer[out_index], &in_buffer[in_index], to_copy);
1083
0
            out_index += to_copy;
1084
            // minus one as the for loop does in_index++
1085
0
            in_index += to_copy - 1;
1086
0
            out_buffer[out_index] = '\0';
1087
0
            continue;
1088
0
        }
1089
1090
        /* Try to find current byte from in buffer in string escape
1091
         * match if it is there append '\', the corresponding byte
1092
         * from escaped bit, and a null byte to end of out buffer.
1093
         */
1094
0
        escape_ptr = strchr(escape_match, in_buffer[in_index]);
1095
0
        if (escape_ptr >= escape_match) {
1096
0
            out_buffer[out_index++] = '\\';
1097
0
            out_buffer[out_index++] = escaped_bit[escape_ptr-escape_match];
1098
0
            out_buffer[out_index]   = 0;
1099
0
            continue;
1100
0
        }
1101
1102
        // Escape 0-31 and 127 if not previously handled (0-x01f,x7f)
1103
0
        if ('\x1f' >= in_buffer[in_index] || '\x7f' == in_buffer[in_index]) {
1104
0
            str_appendf(out_buffer, out_len, "\\u%04x",
1105
0
                        in_buffer[in_index] & 0x0ff);
1106
0
            out_index += 6;
1107
0
            continue;
1108
0
        }
1109
        // pass through everything not escaped.
1110
0
        out_buffer[out_index++] = in_buffer[in_index];
1111
0
        out_buffer[out_index] = '\0';
1112
0
    }
1113
0
    return out_buffer;
1114
0
}
1115
1116
// vim: set expandtab shiftwidth=4