Coverage Report

Created: 2026-09-14 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.27.6~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 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
2.27M
    do {                                              \
109
2.27M
        if (unlikely((lvl) <= debuglevel &&           \
110
2.27M
                      NULL != debugjsfp) ) {            \
111
0
            json_trace(fmt, __VA_ARGS__);              \
112
0
        }                                             \
113
2.27M
    } 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
257k
{
120
257k
    char *targetaddr = NULL;
121
257k
    if (NULL == parent ||
122
174k
        parent->element_type != t_structobject) {
123
        // ordinary case - use the address in the cursor structure
124
174k
        switch (cursor->type) {
125
0
        case t_byte:
126
0
            targetaddr = (char *)&cursor->addr.byte[offset];
127
0
            break;
128
735
        case t_ubyte:
129
735
            targetaddr = (char *)&cursor->addr.ubyte[offset];
130
735
            break;
131
42.1k
        case t_ignore:
132
42.1k
            targetaddr = NULL;
133
42.1k
            break;
134
22.1k
        case t_integer:
135
22.1k
            targetaddr = (char *)&cursor->addr.integer[offset];
136
22.1k
            break;
137
9.88k
        case t_uinteger:
138
9.88k
            targetaddr = (char *)&cursor->addr.uinteger[offset];
139
9.88k
            break;
140
1.82k
        case t_longint:
141
1.82k
            targetaddr = (char *)&cursor->addr.longint[offset];
142
1.82k
            break;
143
419
        case t_ulongint:
144
419
            targetaddr = (char *)&cursor->addr.ulongint[offset];
145
419
            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
8.42k
        case t_time:
153
8.42k
            targetaddr = (char *)&cursor->addr.ts[offset];
154
8.42k
            break;
155
0
        case t_timespec:
156
0
            targetaddr = (char *)&cursor->addr.ts[offset];
157
0
            break;
158
44.8k
        case t_real:
159
44.8k
            targetaddr = (char *)&cursor->addr.real[offset];
160
44.8k
            break;
161
21.1k
        case t_string:
162
21.1k
            targetaddr = cursor->addr.string;
163
21.1k
            break;
164
10.6k
        case t_boolean:
165
10.6k
            targetaddr = (char *)&cursor->addr.boolean[offset];
166
10.6k
            break;
167
3.10k
        case t_character:
168
3.10k
            targetaddr = (char *)&cursor->addr.character[offset];
169
3.10k
            break;
170
659
        case t_array:
171
659
            FALLTHROUGH
172
8.93k
        case t_check:
173
8.93k
            FALLTHROUGH
174
8.93k
        case t_object:
175
8.93k
            FALLTHROUGH
176
8.93k
        case t_structobject:
177
8.93k
            FALLTHROUGH
178
8.93k
        default:
179
8.93k
            targetaddr = NULL;
180
8.93k
            break;
181
174k
        }
182
174k
    } else {
183
        // tricky case - hacking a member in an array of structures
184
83.2k
        targetaddr =
185
83.2k
            parent->arr.objects.base + (offset * parent->arr.objects.stride) +
186
83.2k
            cursor->addr.offset;
187
83.2k
    }
188
257k
    json_debug_trace(1, "json: Target address for %s (offset %d) is %p\n",
189
257k
                      cursor->attribute, offset, targetaddr);
190
257k
    return targetaddr;
191
257k
}
192
193
194
static int json_internal_read_object(const char *cp,
195
                                     const struct json_attr_t *attrs,
196
                                     const struct json_array_t *parent,
197
                                     int offset,
198
                                     const char **end)
199
13.6k
{
200
13.6k
    enum
201
13.6k
    { init, await_attr, in_attr, await_value, in_val_string,
202
13.6k
        in_escape, in_val_token, post_val, post_element, in_ignore_array
203
13.6k
    } state = 0;
204
13.6k
    char *statenames[] = {
205
13.6k
        "init", "await_attr", "in_attr", "await_value", "in_val_string",
206
13.6k
        "in_escape", "in_val_token", "post_val", "post_element",
207
13.6k
        "in_ignore_array"
208
13.6k
    };
209
13.6k
    char attrbuf[JSON_ATTR_MAX + 1], *pattr = NULL;
210
13.6k
    char valbuf[JSON_VAL_MAX + 1], *pval = NULL;
211
13.6k
    bool value_quoted = false;
212
13.6k
    char uescape[5];            // enough space for 4 hex digits and a NUL
213
13.6k
    const struct json_attr_t *cursor;
214
13.6k
    int substatus, maxlen = 0;
215
13.6k
    unsigned int u;
216
13.6k
    const struct json_enum_t *mp;
217
13.6k
    char *lptr;
218
219
13.6k
    if (NULL != end) {
220
5.39k
        *end = NULL;    // give it a well-defined value on parse failure
221
5.39k
    }
222
13.6k
    memset(valbuf, 0, sizeof(valbuf));
223
224
    // stuff fields with defaults in case they're omitted in the JSON input
225
208k
    for (cursor = attrs; cursor->attribute != NULL; cursor++)
226
194k
        if (!cursor->nodefault) {
227
190k
            lptr = json_target_address(cursor, parent, offset);
228
190k
            if (NULL != lptr)
229
173k
                switch (cursor->type) {
230
3.66k
                case t_byte:
231
3.66k
                    lptr[0] = cursor->dflt.byte;
232
3.66k
                    break;
233
21.5k
                case t_ubyte:
234
21.5k
                    lptr[0] = cursor->dflt.ubyte;
235
21.5k
                    break;
236
20.9k
                case t_integer:
237
20.9k
                    memcpy(lptr, &cursor->dflt.integer, sizeof(int));
238
20.9k
                    break;
239
10.4k
                case t_uinteger:
240
10.4k
                    memcpy(lptr, &cursor->dflt.uinteger, sizeof(unsigned int));
241
10.4k
                    break;
242
940
                case t_longint:
243
940
                    memcpy(lptr, &cursor->dflt.longint, sizeof(long));
244
940
                    break;
245
138
                case t_ulongint:
246
138
                    memcpy(lptr, &cursor->dflt.ulongint,
247
138
                           sizeof(unsigned long));
248
138
                    break;
249
3.66k
                case t_short:
250
3.66k
                    memcpy(lptr, &cursor->dflt.shortint, sizeof(short));
251
3.66k
                    break;
252
0
                case t_ushort:
253
0
                    memcpy(lptr, &cursor->dflt.ushortint,
254
0
                           sizeof(unsigned short));
255
0
                    break;
256
3.21k
                case t_time:
257
3.21k
                    memcpy(lptr, &cursor->dflt.ts, sizeof(timespec_t));
258
3.21k
                    break;
259
1.12k
                case t_timespec:
260
1.12k
                    memcpy(lptr, &cursor->dflt.ts, sizeof(timespec_t));
261
1.12k
                    break;
262
63.4k
                case t_real:
263
63.4k
                    memcpy(lptr, &cursor->dflt.real, sizeof(double));
264
63.4k
                    break;
265
23.2k
                case t_string:
266
23.2k
                    if (parent != NULL
267
4.52k
                        && parent->element_type != t_structobject
268
0
                        && offset > 0)
269
0
                        return JSON_ERR_NOPARSTR;
270
23.2k
                    lptr[0] = '\0';
271
23.2k
                    break;
272
12.4k
                case t_boolean:
273
12.4k
                    memcpy(lptr, &cursor->dflt.boolean, sizeof(bool));
274
12.4k
                    break;
275
2.66k
                case t_character:
276
2.66k
                    lptr[0] = cursor->dflt.character;
277
2.66k
                    break;
278
0
                case t_object:  // silences a compiler warning
279
0
                    FALLTHROUGH
280
0
                case t_structobject:
281
0
                    FALLTHROUGH
282
0
                case t_array:
283
0
                    FALLTHROUGH
284
564
                case t_check:
285
564
                    FALLTHROUGH
286
5.94k
                case t_ignore:
287
5.94k
                    break;
288
173k
                }
289
190k
        }
290
291
13.6k
    json_debug_trace(1, "json: JSON parse of '%s' begins.\n", cp);
292
293
    // parse input JSON
294
1.00M
    for (; *cp != '\0'; cp++) {
295
999k
        json_debug_trace(2, "json: State %-14s, looking at '%c' (%p)\n",
296
999k
                          statenames[state], *cp, cp);
297
999k
        switch (state) {
298
14.6k
        case init:
299
14.6k
            if (isspace((unsigned char) *cp)) {
300
1.01k
                continue;
301
1.01k
            }
302
13.6k
            if (*cp == '{') {
303
13.5k
                state = await_attr;
304
13.5k
            } else {
305
97
                json_debug_trace(1, "json: %s",
306
97
                                  "Non-WS when expecting object start.\n");
307
97
                if (NULL != end) {
308
43
                    *end = cp;
309
43
                }
310
97
                return JSON_ERR_OBSTART;
311
97
            }
312
13.5k
            break;
313
74.1k
        case await_attr:
314
74.1k
            if (isspace((unsigned char) *cp)) {
315
424
                continue;
316
424
            }
317
73.7k
            if (*cp == '"') {
318
73.1k
                state = in_attr;
319
73.1k
                pattr = attrbuf;
320
73.1k
                if (NULL != end) {
321
6.81k
                    *end = cp;
322
6.81k
                }
323
73.1k
            } else if (*cp == '}') {
324
394
                break;
325
394
            } else {
326
153
                json_debug_trace(1, "json: %s",
327
153
                          "Non-WS when expecting attribute.\n");
328
153
                if (NULL != end) {
329
2
                    *end = cp;
330
2
                }
331
153
                return JSON_ERR_ATTRSTART;
332
153
            }
333
73.1k
            break;
334
353k
        case in_attr:
335
353k
            if (NULL == pattr) {
336
                // don't update end here, leave at attribute start
337
0
                return JSON_ERR_NULLPTR;
338
0
            }
339
353k
            if (*cp == '"') {
340
73.0k
                *pattr++ = '\0';
341
73.0k
                json_debug_trace(1, "json: Collected attribute name %s\n",
342
73.0k
                                  attrbuf);
343
828k
                for (cursor = attrs; cursor->attribute != NULL; cursor++) {
344
828k
                    json_debug_trace(2, "json: Checking against %s\n",
345
828k
                                      cursor->attribute);
346
828k
                    if (strcmp(cursor->attribute, attrbuf) == 0) {
347
46.1k
                        break;
348
46.1k
                    }
349
782k
                    if (cursor->type == t_ignore &&
350
27.0k
                        strncmp(cursor->attribute, "", 1) == 0) {
351
26.8k
                        break;
352
26.8k
                    }
353
782k
                }
354
73.0k
                if (NULL == cursor->attribute) {
355
96
                    json_debug_trace(1,
356
96
                                      "json: Unknown attribute name '%s'"
357
96
                                      " (attributes begin with '%s').\n",
358
96
                                      attrbuf, attrs->attribute);
359
                    // don't update end here, leave at attribute start
360
96
                    return JSON_ERR_BADATTR;
361
96
                }
362
72.9k
                state = await_value;
363
72.9k
                if (cursor->type == t_string) {
364
2.91k
                    maxlen = (int)cursor->len - 1;
365
70.0k
                } else if (cursor->type == t_check) {
366
2.72k
                    maxlen = (int)strnlen(cursor->dflt.check, JSON_VAL_MAX);
367
67.3k
                } else if (cursor->type == t_time ||
368
61.5k
                           cursor->type == t_ignore) {
369
43.8k
                    maxlen = JSON_VAL_MAX;
370
43.8k
                } else if (NULL != cursor->map) {
371
447
                    maxlen = (int)sizeof(valbuf) - 1;
372
447
                }
373
72.9k
                pval = valbuf;
374
280k
            } else if (pattr >= attrbuf + JSON_ATTR_MAX - 1) {
375
25
                json_debug_trace(1, "json: %s","Attribute name too long.\n");
376
                // don't update end here, leave at attribute start
377
25
                return JSON_ERR_ATTRLEN;
378
280k
            } else {
379
280k
                *pattr++ = *cp;
380
280k
            }
381
353k
            break;
382
353k
        case await_value:
383
75.7k
            if (isspace((unsigned char) *cp) ||
384
75.3k
                *cp == ':') {
385
3.18k
                continue;
386
3.18k
            }
387
72.5k
            if (*cp == '[') {
388
3.47k
                if (cursor->type == t_ignore) {
389
                    /* skip to terminating ], not being fooled by
390
                     * sub arrays, etc.
391
                     * FIXME:  someday  */
392
2.50k
                    state = in_ignore_array;;
393
2.50k
                    value_quoted = false;
394
2.50k
                    break;
395
2.50k
                }
396
966
                if (cursor->type != t_array) {
397
60
                    json_debug_trace(1,"json: %s",
398
60
                                      "Saw [ when not expecting array.\n");
399
60
                    if (NULL != end) {
400
2
                        *end = cp;
401
2
                    }
402
60
                    return JSON_ERR_NOARRAY;
403
60
                }
404
906
                substatus = json_read_array(cp, &cursor->addr.array, &cp);
405
906
                if (substatus != 0) {
406
318
                    return substatus;
407
318
                }
408
588
                state = post_element;
409
69.1k
            } else if (cursor->type == t_array) {
410
1
                json_debug_trace(1, "json: %s",
411
1
                                  "Array element was specified, but no [.\n");
412
1
                if (NULL != end) {
413
0
                    *end = cp;
414
0
                }
415
1
                return JSON_ERR_NOBRAK;
416
69.1k
            } else if (*cp == '"') {
417
18.9k
                value_quoted = true;
418
18.9k
                state = in_val_string;
419
18.9k
                pval = valbuf;
420
50.1k
            } else {
421
50.1k
                value_quoted = false;
422
50.1k
                state = in_val_token;
423
50.1k
                pval = valbuf;
424
50.1k
                *pval++ = *cp;
425
50.1k
            }
426
69.7k
            break;
427
92.8k
        case in_ignore_array:
428
92.8k
            if (*cp == '"') {
429
22.9k
                if (value_quoted == false) {
430
                    // now in quotes
431
11.5k
                    value_quoted = true;
432
11.5k
                } else {
433
11.4k
                    value_quoted = false;
434
11.4k
                }
435
69.8k
            } else if (*cp == ']') {
436
                // FIXME: does not handle sub arrays, etc.
437
2.43k
                if (value_quoted == false) {
438
2.03k
                    state = post_val;
439
2.03k
                }
440
                // else in quotes, ignore it.
441
2.43k
            }
442
92.8k
            break;
443
138k
        case in_val_string:
444
138k
            if (NULL == pval) {
445
                // don't update end here, leave at value start
446
0
                return JSON_ERR_NULLPTR;
447
0
            }
448
138k
            if (*cp == '\\') {
449
3.97k
                state = in_escape;
450
134k
            } else if (*cp == '"') {
451
18.2k
                *pval++ = '\0';
452
18.2k
                json_debug_trace(1, "json: Collected string value %s\n", valbuf);
453
18.2k
                state = post_val;
454
116k
            } else if (pval > valbuf + JSON_VAL_MAX - 1 ||
455
116k
                       pval > valbuf + maxlen - 1) {
456
162
                json_debug_trace(1, "json: %s",  "String value too long.\n");
457
                // don't update end here, leave at value start
458
162
                return JSON_ERR_STRLONG;
459
116k
            } else {
460
116k
                *pval++ = *cp;
461
116k
            }
462
138k
            break;
463
138k
        case in_escape:
464
3.95k
            if (NULL == pval) {
465
                /* don't update end here, leave at value start */
466
0
                return JSON_ERR_NULLPTR;
467
0
            }
468
3.95k
            if (pval > valbuf + JSON_VAL_MAX - 1 ||
469
3.95k
                pval > valbuf + maxlen) {
470
22
                json_debug_trace(1, "json: %s",  "String value too long.\n");
471
                // don't update end here, leave at value start
472
22
                return JSON_ERR_STRLONG;
473
22
            }
474
3.93k
            switch (*cp) {
475
455
            case 'b':
476
455
                *pval++ = '\b';
477
455
                break;
478
394
            case 'f':
479
394
                *pval++ = '\f';
480
394
                break;
481
403
            case 'n':
482
403
                *pval++ = '\n';
483
403
                break;
484
411
            case 'r':
485
411
                *pval++ = '\r';
486
411
                break;
487
628
            case 't':
488
628
                *pval++ = '\t';
489
628
                break;
490
489
            case 'u':
491
489
                {
492
489
                    unsigned n;
493
494
489
                    cp++;                   // skip the 'u'
495
                    // NetBSD 6 wants the cast
496
2.11k
                    for (n = 0; n < 4 && isxdigit((int)*cp); n++) {
497
1.63k
                        uescape[n] = *cp++;
498
1.63k
                    }
499
489
                    uescape[n] = '\0';      // terminate
500
489
                    --cp;
501
                    // ECMA-404 says JSON \u must have 4 hex digits
502
489
                    if ((4 != n) ||
503
388
                        (1 != sscanf(uescape, "%4x", &u))) {
504
101
                        return JSON_ERR_BADSTRING;
505
101
                    }
506
                    // truncate values above 0xff
507
388
                    *pval++ = (unsigned char)u;
508
388
                }
509
0
                break;
510
1.15k
            default:            // handles double quote and solidus
511
1.15k
                *pval++ = *cp;
512
1.15k
                break;
513
3.93k
            }
514
3.83k
            state = in_val_string;
515
3.83k
            break;
516
175k
        case in_val_token:
517
175k
            if (NULL == pval) {
518
                // don't update end here, leave at value start
519
0
                return JSON_ERR_NULLPTR;
520
0
            }
521
175k
            if (isspace((unsigned char) *cp) ||
522
174k
                *cp == ',' ||
523
130k
                *cp == '}') {
524
49.8k
                *pval = '\0';
525
49.8k
                json_debug_trace(1, "json: Collected token valuen %s\n",
526
49.8k
                                 valbuf);
527
49.8k
                state = post_val;
528
49.8k
                if (*cp == '}' ||
529
49.0k
                    *cp == ',') {
530
49.0k
                    --cp;
531
49.0k
                }
532
125k
            } else if (pval > valbuf + JSON_VAL_MAX - 1) {
533
9
                json_debug_trace(1, "json: %s", "Token value too long.\n");
534
                // don't update end here, leave at value start
535
9
                return JSON_ERR_TOKLONG;
536
125k
            } else {
537
125k
                *pval++ = *cp;
538
125k
            }
539
175k
            break;
540
            // coverity[unterminated_case]
541
175k
        case post_val:
542
            // Ignore whitespace after either string or token values.
543
68.9k
    if (isspace((unsigned char) *cp)) {
544
1.85k
                while (*cp != '\0' && isspace((unsigned char) *cp)) {
545
1.30k
                    ++cp;
546
1.30k
                }
547
555
                json_debug_trace(1,
548
555
                    "json: Skipped trailing whitespace: value \"%s\"\n", valbuf);
549
555
            }
550
            /*
551
             * We know that cursor points at the first spec matching
552
             * the current attribute.  We don't know that it's *the*
553
             * correct spec; our dialect allows there to be any number
554
             * of adjacent ones with the same attrname but different
555
             * types.  Here's where we try to seek forward for a
556
             * matching type/attr pair if we're not looking at one.
557
             */
558
68.9k
            for (;;) {
559
68.9k
                int seeking = cursor->type;
560
561
68.9k
                if (value_quoted &&
562
17.1k
                    (cursor->type == t_string ||
563
14.3k
                     cursor->type == t_time)) {
564
6.96k
                    break;
565
6.96k
                }
566
62.0k
                if ((strcmp(valbuf, "true") == 0 ||
567
59.2k
                     strcmp(valbuf, "false") == 0) &&
568
3.17k
                    seeking == t_boolean) {
569
2.09k
                    break;
570
2.09k
                }
571
59.9k
                if (isdigit((unsigned char) valbuf[0])) {
572
28.5k
                    bool decimal = strchr(valbuf, '.') != NULL;
573
574
28.5k
                    if (decimal &&
575
1.85k
                        seeking == t_real) {
576
427
                        break;
577
427
                    }
578
28.0k
                    if (!decimal && (seeking == t_byte ||
579
26.4k
                                     seeking == t_ubyte ||
580
25.8k
                                     seeking == t_integer ||
581
23.9k
                                     seeking == t_uinteger ||
582
22.5k
                                     seeking == t_longint ||
583
21.7k
                                     seeking == t_ulongint ||
584
21.5k
                                     seeking == t_time ||
585
21.0k
                                     seeking == t_short ||
586
19.2k
                                     seeking == t_ushort))
587
7.43k
                        break;
588
28.0k
                }
589
52.0k
                if (NULL == cursor[1].attribute) {  // out of possibilities
590
36.6k
                    break;
591
36.6k
                }
592
15.3k
                if (0 != strcmp(cursor[1].attribute, attrbuf)) {
593
15.3k
                    break;
594
15.3k
                }
595
0
                ++cursor;
596
0
            }
597
68.9k
            if (value_quoted &&
598
17.1k
                (cursor->type != t_string &&
599
14.3k
                 cursor->type != t_character &&
600
13.8k
                 cursor->type != t_check &&
601
12.5k
                 cursor->type != t_time &&
602
8.41k
                 cursor->type != t_ignore &&
603
482
                 cursor->map == 0)) {
604
43
                json_debug_trace(1, "json: %s", "Saw quoted value when expecting"
605
43
                                  " non-string.\n");
606
43
                return JSON_ERR_QNONSTRING;
607
43
            }
608
68.9k
            if (!value_quoted &&
609
51.7k
                (cursor->type == t_string ||
610
51.7k
                 cursor->type == t_check ||
611
51.6k
                 cursor->map != 0)) {
612
97
                json_debug_trace(1, "json: %s",
613
97
                                 "Didn't see quoted value when expecting"
614
97
                                 " string.\n");
615
97
                return JSON_ERR_NONQSTRING;
616
97
            }
617
68.8k
            if (cursor->map != 0) {
618
882
                for (mp = cursor->map; mp->name != NULL; mp++)
619
817
                    if (strcmp(mp->name, valbuf) == 0) {
620
374
                        goto foundit;
621
374
                    }
622
65
                json_debug_trace(1,
623
65
                                 "json: Invalid enumerated value string %s.\n",
624
65
                                  valbuf);
625
65
                return JSON_ERR_BADENUM;
626
374
              foundit:
627
374
                (void)snprintf(valbuf, sizeof(valbuf), "%d", mp->value);
628
374
            }
629
68.7k
            if (cursor->type == t_check) {
630
1.28k
                lptr = cursor->dflt.check;
631
67.4k
            } else {
632
67.4k
                lptr = json_target_address(cursor, parent, offset);
633
67.4k
            }
634
68.7k
            if (NULL != lptr) {
635
34.3k
                switch (cursor->type) {
636
259
                case t_byte:
637
259
                    {
638
259
                        int tmp = atoi(valbuf);
639
259
                        lptr[0] = (char)tmp;
640
259
                    }
641
259
                    break;
642
706
                case t_ubyte:
643
706
                    {
644
706
                        int tmp = atoi(valbuf);
645
706
                        lptr[0] = (unsigned char)tmp;
646
706
                    }
647
706
                    break;
648
2.39k
                case t_integer:
649
2.39k
                    {
650
2.39k
                        int tmp = atoi(valbuf);
651
2.39k
                        memcpy(lptr, &tmp, sizeof(int));
652
2.39k
                    }
653
2.39k
                    break;
654
1.71k
                case t_uinteger:
655
1.71k
                    {
656
1.71k
                        unsigned int tmp = (unsigned int)atol(valbuf);
657
1.71k
                        memcpy(lptr, &tmp, sizeof(unsigned int));
658
1.71k
                    }
659
1.71k
                    break;
660
882
                case t_longint:
661
882
                    {
662
882
                        long tmp = atol(valbuf);
663
882
                        memcpy(lptr, &tmp, sizeof(long));
664
882
                    }
665
882
                    break;
666
281
                case t_ulongint:
667
281
                    {
668
281
                        unsigned long tmp = (unsigned long)atoll(valbuf);
669
281
                        memcpy(lptr, &tmp, sizeof(unsigned long));
670
281
                    }
671
281
                    break;
672
1.87k
                case t_short:
673
1.87k
                    {
674
1.87k
                        short tmp = atoi(valbuf);
675
1.87k
                        memcpy(lptr, &tmp, sizeof(short));
676
1.87k
                    }
677
1.87k
                    break;
678
0
                case t_ushort:
679
0
                    {
680
0
                        unsigned short tmp = (unsigned int)atoi(valbuf);
681
0
                        memcpy(lptr, &tmp, sizeof(unsigned short));
682
0
                    }
683
0
                    break;
684
5.77k
                case t_time:
685
5.77k
                    {
686
5.77k
                        timespec_t ts_tmp = {0, 0};
687
688
5.77k
                        if (value_quoted) {
689
4.12k
                            ts_tmp = iso8601_to_timespec(valbuf);
690
4.12k
                        } else if (NULL == strchr(valbuf, '.')) {
691
                            // integer
692
552
                            ts_tmp.tv_sec = (time_t)atoll(valbuf);
693
1.09k
                        } else {
694
                            // real
695
1.09k
                            double sec_tmp = safe_atof(valbuf);
696
1.09k
                            if (0 != isfinite(sec_tmp)) {
697
703
                                DTOTS(&ts_tmp, sec_tmp);
698
703
                            }  // else, leave at default
699
1.09k
                        }
700
5.77k
                        memcpy(lptr, &ts_tmp, sizeof(timespec_t));
701
5.77k
                    }
702
5.77k
                    break;
703
389
                case t_timespec:
704
389
                    {
705
389
                        double sec_tmp = safe_atof(valbuf);
706
389
                        timespec_t ts_tmp;
707
389
                        if (0 != isfinite(sec_tmp)) {
708
194
                            DTOTS(&ts_tmp, sec_tmp);
709
194
                            memcpy(lptr, &ts_tmp, sizeof(timespec_t));
710
194
                        } // else leave at .dflt
711
389
                    }
712
389
                    break;
713
9.18k
                case t_real:
714
9.18k
                    {
715
9.18k
                        double tmp = safe_atof(valbuf);
716
9.18k
                        if (0 != isfinite(tmp)) {
717
7.19k
                            memcpy(lptr, &tmp, sizeof(double));
718
7.19k
                        } // else leave at .dflt
719
9.18k
                    }
720
9.18k
                    break;
721
2.83k
                case t_string:
722
2.83k
                    if (NULL != parent &&
723
388
                        parent->element_type != t_structobject &&
724
0
                        offset > 0) {
725
0
                        return JSON_ERR_NOPARSTR;
726
0
                    }
727
2.83k
                    (void)strlcpy(lptr, valbuf, cursor->len);
728
2.83k
                    break;
729
3.63k
                case t_boolean:
730
3.63k
                    {
731
3.63k
                        bool tmp = (strcmp(valbuf, "true") == 0);
732
3.63k
                        memcpy(lptr, &tmp, sizeof(bool));
733
3.63k
                    }
734
3.63k
                    break;
735
1.00k
                case t_character:
736
1.00k
                    if (strnlen(valbuf, 2) > 1) {
737
                        // don't update end here, leave at value start
738
11
                        return JSON_ERR_STRLONG;
739
995
                    } else {
740
995
                        lptr[0] = valbuf[0];
741
995
                    }
742
995
                    break;
743
2.10k
                case t_ignore:  // silences a compiler warning
744
2.10k
                    FALLTHROUGH
745
2.10k
                case t_object:  // silences a compiler warning
746
2.10k
                    FALLTHROUGH
747
2.10k
                case t_structobject:
748
2.10k
                    FALLTHROUGH
749
2.10k
                case t_array:
750
2.10k
                    break;
751
1.28k
                case t_check:
752
1.28k
                    if (strcmp(cursor->dflt.check, valbuf) != 0) {
753
295
                        json_debug_trace(1, "json: Required attribute value %s"
754
295
                                          " not present.\n",
755
295
                                          cursor->dflt.check);
756
                        // don't update end here, leave at start of attribute
757
295
                        return JSON_ERR_CHECKFAIL;
758
295
                    }
759
994
                    break;
760
34.3k
                }
761
34.3k
            }
762
68.4k
            FALLTHROUGH
763
69.3k
        case post_element:
764
69.3k
            if (isspace((unsigned char) *cp)) {
765
374
                continue;
766
374
            }
767
68.9k
            if (*cp == ',') {
768
61.9k
                state = await_attr;
769
61.9k
            } else if (*cp == '}') {
770
5.81k
                ++cp;
771
5.81k
                goto good_parse;
772
5.81k
            } else {
773
1.20k
                json_debug_trace(1, "json: %s",
774
1.20k
                                "Garbage while expecting comma or }\n");
775
1.20k
                if (NULL != end) {
776
27
                    *end = cp;
777
27
                }
778
1.20k
                return JSON_ERR_BADTRAIL;
779
1.20k
            }
780
61.9k
            break;
781
999k
        }
782
999k
    }
783
5.09k
    if (state == init) {
784
60
        json_debug_trace(1, "json: %s", "Input was empty or white-space only\n");
785
60
        return JSON_ERR_EMPTY;
786
60
    }
787
788
10.8k
  good_parse:
789
    // in case there's another object following, consume trailing WS
790
10.8k
    while (isspace((unsigned char)*cp)) {
791
395
        ++cp;
792
395
    }
793
10.8k
    if (NULL != end) {
794
5.24k
        *end = cp;
795
5.24k
    }
796
10.8k
    json_debug_trace(1, "%s", "json: JSON parse ends.\n");
797
10.8k
    return 0;
798
5.09k
}
799
800
int json_read_array(const char *cp, const struct json_array_t *arr,
801
                    const char **end)
802
906
{
803
906
    int substatus;
804
906
    unsigned offset, arrcount;
805
906
    char *tp;
806
807
906
    if (NULL != end) {
808
906
        *end = NULL;    // give it a well-defined value on parse failure
809
906
    }
810
811
906
    json_debug_trace(1, "json: %s", "Entered json_read_array()\n");
812
813
906
    while (isspace((unsigned char)*cp)) {
814
0
        cp++;
815
0
    }
816
906
    if (*cp != '[') {
817
0
        json_debug_trace(1, "json: %s", "Didn't find expected array start\n");
818
0
        return JSON_ERR_ARRAYSTART;
819
0
    }
820
906
    cp++;
821
822
906
    tp = arr->arr.strings.store;
823
906
    arrcount = 0;
824
825
    // Check for empty array
826
906
    while (isspace((unsigned char)*cp)) {
827
197
        cp++;
828
197
    }
829
906
    if (*cp == ']') {
830
212
        goto breakout;
831
212
    }
832
833
8.50k
    for (offset = 0; offset < arr->maxlen; offset++) {
834
8.49k
        char *ep = NULL;
835
836
8.49k
        json_debug_trace(1, "json: Looking at %s\n", cp);
837
8.49k
        switch (arr->element_type) {
838
3.10k
        case t_string:
839
3.10k
            if (isspace((unsigned char) *cp)) {
840
194
                cp++;
841
194
            }
842
3.10k
            if (*cp != '"') {
843
16
                return JSON_ERR_BADSTRING;
844
3.08k
            } else {
845
3.08k
                ++cp;
846
3.08k
            }
847
3.08k
            arr->arr.strings.ptrs[offset] = tp;
848
14.0k
            for (; tp - arr->arr.strings.store < arr->arr.strings.storelen;
849
10.9k
                 tp++)
850
14.0k
                if (*cp == '"') {
851
3.08k
                    ++cp;
852
3.08k
                    *tp++ = '\0';
853
3.08k
                    goto stringend;
854
10.9k
                } else if (*cp == '\0') {
855
1
                    json_debug_trace(1, "json: %s",
856
1
                                      "Bad string syntax in string list.\n");
857
1
                    return JSON_ERR_BADSTRING;
858
10.9k
                } else {
859
10.9k
                    *tp = *cp++;
860
10.9k
                }
861
3
            json_debug_trace(1, "json: %s",
862
3
                             "Bad string syntax in string list.\n");
863
3
            return JSON_ERR_BADSTRING;
864
3.08k
          stringend:
865
3.08k
            break;
866
0
        case t_object:
867
0
            FALLTHROUGH
868
5.39k
        case t_structobject:
869
5.39k
            substatus =
870
5.39k
                json_internal_read_object(cp, arr->arr.objects.subtype, arr,
871
5.39k
                                          offset, &cp);
872
5.39k
            if (substatus != 0) {
873
149
                if (NULL != end) {
874
149
                    *end = cp;
875
149
                }
876
149
                return substatus;
877
149
            }
878
5.24k
            break;
879
5.24k
        case t_integer:
880
0
            arr->arr.integers.store[offset] = (int)strtol(cp, &ep, 0);
881
0
            if (ep == cp) {
882
0
                return JSON_ERR_BADNUM;
883
0
            }
884
0
            cp = ep;
885
0
            break;
886
0
        case t_uinteger:
887
0
            arr->arr.uintegers.store[offset] = (unsigned int)strtoul(cp,
888
0
                                                                     &ep, 0);
889
0
            if (ep == cp) {
890
0
                return JSON_ERR_BADNUM;
891
0
            }
892
0
            cp = ep;
893
0
            break;
894
0
        case t_longint:
895
0
            arr->arr.longint.store[offset] = strtol(cp, &ep, 0);
896
0
            if (ep == cp) {
897
0
                return JSON_ERR_BADNUM;
898
0
            }
899
0
            cp = ep;
900
0
            break;
901
0
        case t_ulongint:
902
0
            arr->arr.ulongint.store[offset] = strtoul(cp, &ep, 0);
903
0
            if (ep == cp) {
904
0
                return JSON_ERR_BADNUM;
905
0
            }
906
0
            cp = ep;
907
0
            break;
908
0
        case t_byte:
909
0
            arr->arr.bytes.store[offset] = (char)strtol(cp, &ep, 0);
910
0
            if (ep == cp) {
911
0
                return JSON_ERR_BADNUM;
912
0
            }
913
0
            cp = ep;
914
0
            break;
915
0
        case t_ubyte:
916
0
            arr->arr.ubytes.store[offset] = (unsigned char)strtoul(cp,
917
0
                                                                   &ep, 0);
918
0
            if (ep == cp) {
919
0
                return JSON_ERR_BADNUM;
920
0
            }
921
0
            cp = ep;
922
0
            break;
923
0
        case t_short:
924
0
            arr->arr.shorts.store[offset] = (short)strtol(cp, &ep, 0);
925
0
            if (ep == cp) {
926
0
                return JSON_ERR_BADNUM;
927
0
            }
928
0
            cp = ep;
929
0
            break;
930
0
        case t_ushort:
931
0
            arr->arr.ushorts.store[offset] = (unsigned short)strtoul(cp,
932
0
                                                                     &ep, 0);
933
0
            if (ep == cp) {
934
0
                return JSON_ERR_BADNUM;
935
0
            }
936
0
            cp = ep;
937
0
            break;
938
0
        case t_time:
939
0
            {
940
0
                timespec_t ts_tmp;
941
0
                if (*cp != '"') {
942
0
                    return JSON_ERR_BADSTRING;
943
0
                } else {
944
0
                    ++cp;
945
0
                }
946
0
                ts_tmp = iso8601_to_timespec(cp);
947
0
                arr->arr.timespecs.store[offset] = ts_tmp;
948
0
                while (*cp &&
949
0
                       *cp != '"') {
950
0
                    cp++;
951
0
                }
952
0
                if (*cp != '"') {
953
0
                    return JSON_ERR_BADSTRING;
954
0
                }
955
0
                ++cp;
956
0
            }
957
0
            break;
958
0
        case t_timespec:
959
            // TODO not sure how to implement this
960
0
            return JSON_ERR_BADNUM;
961
0
            break;
962
0
        case t_real:
963
0
            arr->arr.reals.store[offset] = strtod(cp, &ep);
964
0
            if (ep == cp) {
965
0
                return JSON_ERR_BADNUM;
966
0
            }
967
0
            cp = ep;
968
0
            break;
969
0
        case t_boolean:
970
0
            if (str_starts_with(cp, "true")) {
971
0
                arr->arr.booleans.store[offset] = true;
972
0
                cp += 4;
973
0
            } else if (str_starts_with(cp, "false")) {
974
0
                arr->arr.booleans.store[offset] = false;
975
0
                cp += 5;
976
0
            }
977
0
            break;
978
0
        case t_character:
979
0
            FALLTHROUGH
980
0
        case t_array:
981
0
            FALLTHROUGH
982
0
        case t_check:
983
0
            FALLTHROUGH
984
0
        case t_ignore:
985
0
            json_debug_trace(1, "json: %s", "Invalid array subtype.\n");
986
0
            return JSON_ERR_SUBTYPE;
987
8.49k
        }
988
8.33k
        arrcount++;
989
8.33k
        if (isspace((unsigned char)*cp)) {
990
196
            cp++;
991
196
        }
992
8.33k
        if (*cp == ']') {
993
376
            json_debug_trace(1, "json: %s", "End of array found.\n");
994
376
            goto breakout;
995
376
        }
996
7.95k
        if (*cp == ',') {
997
7.80k
            cp++;
998
7.80k
        } else {
999
148
            json_debug_trace(1, "json: %s", "Bad trailing syntax on array.\n");
1000
148
            return JSON_ERR_BADSUBTRAIL;
1001
148
        }
1002
7.95k
    }
1003
1
    json_debug_trace(1, "json: %s", "Too many elements in array.\n");
1004
1
    if (NULL != end) {
1005
1
        *end = cp;
1006
1
    }
1007
1
    return JSON_ERR_SUBTOOLONG;
1008
588
  breakout:
1009
588
    if (NULL != arr->count) {
1010
588
        *(arr->count) = arrcount;
1011
588
    }
1012
588
    if (NULL != end) {
1013
588
        *end = cp;
1014
588
    }
1015
588
    json_debug_trace(1, "json: leaving json_read_array() with %d elements\n",
1016
588
                      arrcount);
1017
588
    return 0;
1018
694
}
1019
1020
int json_read_object(const char *cp, const struct json_attr_t *attrs,
1021
                     const char **end)
1022
8.27k
{
1023
8.27k
    int st;
1024
1025
8.27k
    json_debug_trace(1, "json: json_read_object() sees '%s'\n", cp);
1026
8.27k
    st = json_internal_read_object(cp, attrs, NULL, 0, end);
1027
8.27k
    return st;
1028
8.27k
}
1029
1030
const char *json_error_string(int err)
1031
0
{
1032
0
    const char *errors[] = {
1033
0
        "unknown error while parsing JSON",
1034
0
        "non-whitespace when expecting object start",
1035
0
        "non-whitespace when expecting attribute start",
1036
0
        "unknown attribute name",
1037
0
        "attribute name too long",
1038
0
        "saw [ when not expecting array",
1039
0
        "array element specified, but no [",
1040
0
        "string value too long",
1041
0
        "token value too long",
1042
0
        "garbage while expecting comma or } or ]",
1043
0
        "didn't find expected array start",
1044
0
        "error while parsing object array",
1045
0
        "too many array elements",
1046
0
        "garbage while expecting array comma",
1047
0
        "unsupported array element type",
1048
0
        "error while string parsing",
1049
0
        "check attribute not matched",
1050
0
        "can't support strings in parallel arrays",
1051
0
        "invalid enumerated value",
1052
0
        "saw quoted value when expecting nonstring",
1053
0
        "didn't see quoted value when expecting string",
1054
0
        "other data conversion error",
1055
0
        "unexpected null value or attribute pointer",
1056
0
        "object element specified, but no {",
1057
0
        "input was empty or white-space only",
1058
0
    };
1059
1060
0
    if (err <= 0 ||
1061
0
        err >= (int)(sizeof(errors) / sizeof(errors[0]))) {
1062
0
        return errors[0];
1063
0
    }
1064
0
    return errors[err];
1065
0
}
1066
1067
/* quote a JSON string so it can be used as a simple JSON string.
1068
 * Used to output the JSON as a literal JSON string
1069
 * escape control chars, escape double quote.
1070
 * stop at NUL, in_len or bad unicode char
1071
 */
1072
char *json_quote(const char *in_buffer, char *out_buffer, size_t in_len,
1073
                 size_t out_len)
1074
0
{
1075
0
    const char *escape_match = "'\"/\\\b\f\n\r\t";
1076
0
    const char *escaped_bit = "'\"/\\bfnrt";
1077
0
    unsigned out_index = 0;
1078
0
    const char *escape_ptr;
1079
0
    unsigned in_index = 0;
1080
0
    unsigned to_copy = 0;
1081
1082
0
    out_buffer[0] = '\0';
1083
1084
    // check in string, stop at NUL, done in_len, or out_buffer full
1085
0
    for (in_index = 0; in_buffer[in_index] != '\0'; in_index++) {
1086
1087
0
        if (in_index >= in_len) {
1088
            // got all from input buffer
1089
0
            break;
1090
0
        }
1091
1092
0
        if (out_index > (out_len - 8) ) {
1093
            /* output out_buffer full.  Not enough space for a 4-byte UTF + NUL,
1094
             * or \uxxxx + NUL.  Safer to check once, at the top,
1095
             * than a lot of specific size checks later in the loop.
1096
             */
1097
0
            break;
1098
0
        }
1099
1100
0
        if (in_buffer[in_index] & 0x80) {
1101
            // highbit set. assume unicode
1102
0
            to_copy = 0;    // always reset before use, to shut up coverity
1103
1104
            // check in_len so we don't overrun in_buffer
1105
0
            if ((in_len > (in_index + 1)) &&
1106
0
                (0xC0 == (0xE0 & (uint8_t)in_buffer[in_index])) &&
1107
0
                (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 1]))) {
1108
                // utf-8 ish 16bit rune - deg, plusm, mplus etc.
1109
0
                to_copy = 2;
1110
0
            } else if ((in_len > (in_index + 2)) &&
1111
0
                       (0xE0 == (0xF0 & (uint8_t)in_buffer[in_index])) &&
1112
0
                       (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 1])) &&
1113
0
                       (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 2]))) {
1114
                // utf-8 ish 24 bit rune - (double) prime etc.
1115
0
                to_copy = 3;
1116
0
            } else if ((in_len > (in_index + 3)) &&
1117
0
                       (0xF0 == (0xF8 & (uint8_t)in_buffer[in_index])) &&
1118
0
                       (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 1])) &&
1119
0
                       (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 2])) &&
1120
0
                       (0x80 == (0xC0 & (uint8_t)in_buffer[in_index + 3]))) {
1121
                // utf-8 ish 32 bit rune - musical symbol g clef etc.
1122
0
                to_copy = 4;
1123
0
            } else {
1124
                // WTF??  Short UTF?  Bad UTF?
1125
0
                str_appendf(out_buffer, out_len,
1126
0
                            "\\u%04x", in_buffer[in_index] & 0x0ff);
1127
0
                out_index += 6;
1128
0
                continue;
1129
0
            }
1130
1131
0
            memcpy(&out_buffer[out_index], &in_buffer[in_index], to_copy);
1132
0
            out_index += to_copy;
1133
            // minus one as the for loop does in_index++
1134
0
            in_index += to_copy - 1;
1135
0
            out_buffer[out_index] = '\0';
1136
0
            continue;
1137
0
        }
1138
1139
        /* Try to find current byte from in buffer in string escape
1140
         * match if it is there append '\', the corresponding byte
1141
         * from escaped bit, and a null byte to end of out buffer.
1142
         */
1143
0
        escape_ptr = strchr(escape_match, in_buffer[in_index]);
1144
0
        if (escape_ptr >= escape_match) {
1145
0
            out_buffer[out_index++] = '\\';
1146
0
            out_buffer[out_index++] = escaped_bit[escape_ptr-escape_match];
1147
0
            out_buffer[out_index]   = 0;
1148
0
            continue;
1149
0
        }
1150
1151
        // Escape 0-31 and 127 if not previously handled (0-x01f,x7f)
1152
0
        if ('\x1f' >= in_buffer[in_index] || '\x7f' == in_buffer[in_index]) {
1153
0
            str_appendf(out_buffer, out_len, "\\u%04x",
1154
0
                        in_buffer[in_index] & 0x0ff);
1155
0
            out_index += 6;
1156
0
            continue;
1157
0
        }
1158
        // pass through everything not escaped.
1159
0
        out_buffer[out_index++] = in_buffer[in_index];
1160
0
        out_buffer[out_index] = '\0';
1161
0
    }
1162
0
    return out_buffer;
1163
0
}
1164
1165
// vim: set expandtab shiftwidth=4