/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.30M | do { \ |
109 | 2.30M | if (unlikely((lvl) <= debuglevel && \ |
110 | 2.30M | NULL != debugjsfp) ) { \ |
111 | 0 | json_trace(fmt, __VA_ARGS__); \ |
112 | 0 | } \ |
113 | 2.30M | } 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 | 267k | { |
120 | 267k | char *targetaddr = NULL; |
121 | 267k | if (NULL == parent || |
122 | 173k | parent->element_type != t_structobject) { |
123 | | // ordinary case - use the address in the cursor structure |
124 | 173k | 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 | 41.5k | case t_ignore: |
132 | 41.5k | targetaddr = NULL; |
133 | 41.5k | break; |
134 | 22.1k | case t_integer: |
135 | 22.1k | targetaddr = (char *)&cursor->addr.integer[offset]; |
136 | 22.1k | break; |
137 | 9.95k | case t_uinteger: |
138 | 9.95k | targetaddr = (char *)&cursor->addr.uinteger[offset]; |
139 | 9.95k | break; |
140 | 1.57k | case t_longint: |
141 | 1.57k | targetaddr = (char *)&cursor->addr.longint[offset]; |
142 | 1.57k | break; |
143 | 658 | case t_ulongint: |
144 | 658 | targetaddr = (char *)&cursor->addr.ulongint[offset]; |
145 | 658 | 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.53k | case t_time: |
153 | 8.53k | targetaddr = (char *)&cursor->addr.ts[offset]; |
154 | 8.53k | break; |
155 | 0 | case t_timespec: |
156 | 0 | targetaddr = (char *)&cursor->addr.ts[offset]; |
157 | 0 | break; |
158 | 44.6k | case t_real: |
159 | 44.6k | targetaddr = (char *)&cursor->addr.real[offset]; |
160 | 44.6k | 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.17k | case t_character: |
168 | 3.17k | targetaddr = (char *)&cursor->addr.character[offset]; |
169 | 3.17k | 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 | 173k | } |
182 | 173k | } else { |
183 | | // tricky case - hacking a member in an array of structures |
184 | 93.1k | targetaddr = |
185 | 93.1k | parent->arr.objects.base + (offset * parent->arr.objects.stride) + |
186 | 93.1k | cursor->addr.offset; |
187 | 93.1k | } |
188 | 267k | json_debug_trace(1, "json: Target address for %s (offset %d) is %p\n", |
189 | 267k | cursor->attribute, offset, targetaddr); |
190 | 267k | return targetaddr; |
191 | 267k | } |
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 | 14.3k | { |
200 | 14.3k | enum |
201 | 14.3k | { init, await_attr, in_attr, await_value, in_val_string, |
202 | 14.3k | in_escape, in_val_token, post_val, post_element, in_ignore_array |
203 | 14.3k | } state = 0; |
204 | 14.3k | char *statenames[] = { |
205 | 14.3k | "init", "await_attr", "in_attr", "await_value", "in_val_string", |
206 | 14.3k | "in_escape", "in_val_token", "post_val", "post_element", |
207 | 14.3k | "in_ignore_array" |
208 | 14.3k | }; |
209 | 14.3k | char attrbuf[JSON_ATTR_MAX + 1], *pattr = NULL; |
210 | 14.3k | char valbuf[JSON_VAL_MAX + 1], *pval = NULL; |
211 | 14.3k | bool value_quoted = false; |
212 | 14.3k | char uescape[5]; // enough space for 4 hex digits and a NUL |
213 | 14.3k | const struct json_attr_t *cursor; |
214 | 14.3k | int substatus, maxlen = 0; |
215 | 14.3k | unsigned int u; |
216 | 14.3k | const struct json_enum_t *mp; |
217 | 14.3k | char *lptr; |
218 | | |
219 | 14.3k | if (NULL != end) { |
220 | 6.04k | *end = NULL; // give it a well-defined value on parse failure |
221 | 6.04k | } |
222 | 14.3k | memset(valbuf, 0, sizeof(valbuf)); |
223 | | |
224 | | // stuff fields with defaults in case they're omitted in the JSON input |
225 | 218k | for (cursor = attrs; cursor->attribute != NULL; cursor++) |
226 | 203k | if (!cursor->nodefault) { |
227 | 198k | lptr = json_target_address(cursor, parent, offset); |
228 | 198k | if (NULL != lptr) |
229 | 182k | switch (cursor->type) { |
230 | 4.36k | case t_byte: |
231 | 4.36k | lptr[0] = cursor->dflt.byte; |
232 | 4.36k | break; |
233 | 23.6k | case t_ubyte: |
234 | 23.6k | lptr[0] = cursor->dflt.ubyte; |
235 | 23.6k | break; |
236 | 20.9k | case t_integer: |
237 | 20.9k | memcpy(lptr, &cursor->dflt.integer, sizeof(int)); |
238 | 20.9k | break; |
239 | 10.5k | case t_uinteger: |
240 | 10.5k | memcpy(lptr, &cursor->dflt.uinteger, sizeof(unsigned int)); |
241 | 10.5k | break; |
242 | 932 | case t_longint: |
243 | 932 | memcpy(lptr, &cursor->dflt.longint, sizeof(long)); |
244 | 932 | break; |
245 | 122 | case t_ulongint: |
246 | 122 | memcpy(lptr, &cursor->dflt.ulongint, |
247 | 122 | sizeof(unsigned long)); |
248 | 122 | break; |
249 | 4.36k | case t_short: |
250 | 4.36k | memcpy(lptr, &cursor->dflt.shortint, sizeof(short)); |
251 | 4.36k | break; |
252 | 0 | case t_ushort: |
253 | 0 | memcpy(lptr, &cursor->dflt.ushortint, |
254 | 0 | sizeof(unsigned short)); |
255 | 0 | break; |
256 | 3.29k | case t_time: |
257 | 3.29k | memcpy(lptr, &cursor->dflt.ts, sizeof(timespec_t)); |
258 | 3.29k | break; |
259 | 1.27k | case t_timespec: |
260 | 1.27k | memcpy(lptr, &cursor->dflt.ts, sizeof(timespec_t)); |
261 | 1.27k | break; |
262 | 66.6k | case t_real: |
263 | 66.6k | memcpy(lptr, &cursor->dflt.real, sizeof(double)); |
264 | 66.6k | break; |
265 | 23.5k | case t_string: |
266 | 23.5k | if (parent != NULL |
267 | 4.84k | && parent->element_type != t_structobject |
268 | 0 | && offset > 0) |
269 | 0 | return JSON_ERR_NOPARSTR; |
270 | 23.5k | lptr[0] = '\0'; |
271 | 23.5k | break; |
272 | 13.1k | case t_boolean: |
273 | 13.1k | memcpy(lptr, &cursor->dflt.boolean, sizeof(bool)); |
274 | 13.1k | break; |
275 | 2.68k | case t_character: |
276 | 2.68k | lptr[0] = cursor->dflt.character; |
277 | 2.68k | 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 | 638 | case t_check: |
285 | 638 | FALLTHROUGH |
286 | 6.66k | case t_ignore: |
287 | 6.66k | break; |
288 | 182k | } |
289 | 198k | } |
290 | | |
291 | 14.3k | json_debug_trace(1, "json: JSON parse of '%s' begins.\n", cp); |
292 | | |
293 | | // parse input JSON |
294 | 1.02M | for (; *cp != '\0'; cp++) { |
295 | 1.01M | json_debug_trace(2, "json: State %-14s, looking at '%c' (%p)\n", |
296 | 1.01M | statenames[state], *cp, cp); |
297 | 1.01M | switch (state) { |
298 | 15.2k | case init: |
299 | 15.2k | if (isspace((unsigned char) *cp)) { |
300 | 1.01k | continue; |
301 | 1.01k | } |
302 | 14.2k | if (*cp == '{') { |
303 | 14.1k | state = await_attr; |
304 | 14.1k | } else { |
305 | 97 | json_debug_trace(1, "json: %s", |
306 | 97 | "Non-WS when expecting object start.\n"); |
307 | 97 | if (NULL != end) { |
308 | 45 | *end = cp; |
309 | 45 | } |
310 | 97 | return JSON_ERR_OBSTART; |
311 | 97 | } |
312 | 14.1k | break; |
313 | 74.6k | case await_attr: |
314 | 74.6k | if (isspace((unsigned char) *cp)) { |
315 | 408 | continue; |
316 | 408 | } |
317 | 74.2k | if (*cp == '"') { |
318 | 73.7k | state = in_attr; |
319 | 73.7k | pattr = attrbuf; |
320 | 73.7k | if (NULL != end) { |
321 | 7.46k | *end = cp; |
322 | 7.46k | } |
323 | 73.7k | } else if (*cp == '}') { |
324 | 397 | break; |
325 | 397 | } else { |
326 | 146 | json_debug_trace(1, "json: %s", |
327 | 146 | "Non-WS when expecting attribute.\n"); |
328 | 146 | if (NULL != end) { |
329 | 3 | *end = cp; |
330 | 3 | } |
331 | 146 | return JSON_ERR_ATTRSTART; |
332 | 146 | } |
333 | 73.7k | break; |
334 | 357k | case in_attr: |
335 | 357k | if (NULL == pattr) { |
336 | | // don't update end here, leave at attribute start |
337 | 0 | return JSON_ERR_NULLPTR; |
338 | 0 | } |
339 | 357k | if (*cp == '"') { |
340 | 73.6k | *pattr++ = '\0'; |
341 | 73.6k | json_debug_trace(1, "json: Collected attribute name %s\n", |
342 | 73.6k | attrbuf); |
343 | 825k | for (cursor = attrs; cursor->attribute != NULL; cursor++) { |
344 | 825k | json_debug_trace(2, "json: Checking against %s\n", |
345 | 825k | cursor->attribute); |
346 | 825k | if (strcmp(cursor->attribute, attrbuf) == 0) { |
347 | 46.8k | break; |
348 | 46.8k | } |
349 | 778k | if (cursor->type == t_ignore && |
350 | 26.9k | strncmp(cursor->attribute, "", 1) == 0) { |
351 | 26.6k | break; |
352 | 26.6k | } |
353 | 778k | } |
354 | 73.6k | if (NULL == cursor->attribute) { |
355 | 95 | json_debug_trace(1, |
356 | 95 | "json: Unknown attribute name '%s'" |
357 | 95 | " (attributes begin with '%s').\n", |
358 | 95 | attrbuf, attrs->attribute); |
359 | | // don't update end here, leave at attribute start |
360 | 95 | return JSON_ERR_BADATTR; |
361 | 95 | } |
362 | 73.5k | state = await_value; |
363 | 73.5k | if (cursor->type == t_string) { |
364 | 2.93k | maxlen = (int)cursor->len - 1; |
365 | 70.5k | } else if (cursor->type == t_check) { |
366 | 2.66k | maxlen = (int)strnlen(cursor->dflt.check, JSON_VAL_MAX); |
367 | 67.9k | } else if (cursor->type == t_time || |
368 | 62.0k | cursor->type == t_ignore) { |
369 | 43.6k | maxlen = JSON_VAL_MAX; |
370 | 43.6k | } else if (NULL != cursor->map) { |
371 | 447 | maxlen = (int)sizeof(valbuf) - 1; |
372 | 447 | } |
373 | 73.5k | pval = valbuf; |
374 | 283k | } else if (pattr >= attrbuf + JSON_ATTR_MAX - 1) { |
375 | 27 | json_debug_trace(1, "json: %s","Attribute name too long.\n"); |
376 | | // don't update end here, leave at attribute start |
377 | 27 | return JSON_ERR_ATTRLEN; |
378 | 283k | } else { |
379 | 283k | *pattr++ = *cp; |
380 | 283k | } |
381 | 357k | break; |
382 | 357k | case await_value: |
383 | 76.2k | if (isspace((unsigned char) *cp) || |
384 | 75.8k | *cp == ':') { |
385 | 3.09k | continue; |
386 | 3.09k | } |
387 | 73.1k | if (*cp == '[') { |
388 | 3.37k | if (cursor->type == t_ignore) { |
389 | | /* skip to terminating ], not being fooled by |
390 | | * sub arrays, etc. |
391 | | * FIXME: someday */ |
392 | 2.39k | state = in_ignore_array;; |
393 | 2.39k | value_quoted = false; |
394 | 2.39k | break; |
395 | 2.39k | } |
396 | 978 | if (cursor->type != t_array) { |
397 | 56 | json_debug_trace(1,"json: %s", |
398 | 56 | "Saw [ when not expecting array.\n"); |
399 | 56 | if (NULL != end) { |
400 | 2 | *end = cp; |
401 | 2 | } |
402 | 56 | return JSON_ERR_NOARRAY; |
403 | 56 | } |
404 | 922 | substatus = json_read_array(cp, &cursor->addr.array, &cp); |
405 | 922 | if (substatus != 0) { |
406 | 326 | return substatus; |
407 | 326 | } |
408 | 596 | state = post_element; |
409 | 69.7k | } 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.7k | } else if (*cp == '"') { |
417 | 18.2k | value_quoted = true; |
418 | 18.2k | state = in_val_string; |
419 | 18.2k | pval = valbuf; |
420 | 51.5k | } else { |
421 | 51.5k | value_quoted = false; |
422 | 51.5k | state = in_val_token; |
423 | 51.5k | pval = valbuf; |
424 | 51.5k | *pval++ = *cp; |
425 | 51.5k | } |
426 | 70.3k | break; |
427 | 114k | case in_ignore_array: |
428 | 114k | if (*cp == '"') { |
429 | 26.6k | if (value_quoted == false) { |
430 | | // now in quotes |
431 | 13.4k | value_quoted = true; |
432 | 13.4k | } else { |
433 | 13.2k | value_quoted = false; |
434 | 13.2k | } |
435 | 87.4k | } else if (*cp == ']') { |
436 | | // FIXME: does not handle sub arrays, etc. |
437 | 2.31k | if (value_quoted == false) { |
438 | 1.90k | state = post_val; |
439 | 1.90k | } |
440 | | // else in quotes, ignore it. |
441 | 2.31k | } |
442 | 114k | break; |
443 | 133k | case in_val_string: |
444 | 133k | if (NULL == pval) { |
445 | | // don't update end here, leave at value start |
446 | 0 | return JSON_ERR_NULLPTR; |
447 | 0 | } |
448 | 133k | if (*cp == '\\') { |
449 | 3.97k | state = in_escape; |
450 | 129k | } else if (*cp == '"') { |
451 | 17.5k | *pval++ = '\0'; |
452 | 17.5k | json_debug_trace(1, "json: Collected string value %s\n", valbuf); |
453 | 17.5k | state = post_val; |
454 | 111k | } else if (pval > valbuf + JSON_VAL_MAX - 1 || |
455 | 111k | pval > valbuf + maxlen - 1) { |
456 | 163 | json_debug_trace(1, "json: %s", "String value too long.\n"); |
457 | | // don't update end here, leave at value start |
458 | 163 | return JSON_ERR_STRLONG; |
459 | 111k | } else { |
460 | 111k | *pval++ = *cp; |
461 | 111k | } |
462 | 133k | break; |
463 | 133k | 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 | 472 | case 'b': |
476 | 472 | *pval++ = '\b'; |
477 | 472 | break; |
478 | 388 | case 'f': |
479 | 388 | *pval++ = '\f'; |
480 | 388 | break; |
481 | 391 | case 'n': |
482 | 391 | *pval++ = '\n'; |
483 | 391 | break; |
484 | 404 | case 'r': |
485 | 404 | *pval++ = '\r'; |
486 | 404 | break; |
487 | 614 | case 't': |
488 | 614 | *pval++ = '\t'; |
489 | 614 | break; |
490 | 483 | case 'u': |
491 | 483 | { |
492 | 483 | unsigned n; |
493 | | |
494 | 483 | cp++; // skip the 'u' |
495 | | // NetBSD 6 wants the cast |
496 | 2.12k | for (n = 0; n < 4 && isxdigit((int)*cp); n++) { |
497 | 1.64k | uescape[n] = *cp++; |
498 | 1.64k | } |
499 | 483 | uescape[n] = '\0'; // terminate |
500 | 483 | --cp; |
501 | | // ECMA-404 says JSON \u must have 4 hex digits |
502 | 483 | if ((4 != n) || |
503 | 388 | (1 != sscanf(uescape, "%4x", &u))) { |
504 | 95 | return JSON_ERR_BADSTRING; |
505 | 95 | } |
506 | | // truncate values above 0xff |
507 | 388 | *pval++ = (unsigned char)u; |
508 | 388 | } |
509 | 0 | break; |
510 | 1.18k | default: // handles double quote and solidus |
511 | 1.18k | *pval++ = *cp; |
512 | 1.18k | break; |
513 | 3.93k | } |
514 | 3.84k | state = in_val_string; |
515 | 3.84k | break; |
516 | 174k | case in_val_token: |
517 | 174k | if (NULL == pval) { |
518 | | // don't update end here, leave at value start |
519 | 0 | return JSON_ERR_NULLPTR; |
520 | 0 | } |
521 | 174k | if (isspace((unsigned char) *cp) || |
522 | 173k | *cp == ',' || |
523 | 128k | *cp == '}') { |
524 | 51.2k | *pval = '\0'; |
525 | 51.2k | json_debug_trace(1, "json: Collected token valuen %s\n", |
526 | 51.2k | valbuf); |
527 | 51.2k | state = post_val; |
528 | 51.2k | if (*cp == '}' || |
529 | 50.4k | *cp == ',') { |
530 | 50.4k | --cp; |
531 | 50.4k | } |
532 | 122k | } 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 | 122k | } else { |
537 | 122k | *pval++ = *cp; |
538 | 122k | } |
539 | 174k | break; |
540 | | // coverity[unterminated_case] |
541 | 174k | case post_val: |
542 | | // Ignore whitespace after either string or token values. |
543 | 69.5k | if (isspace((unsigned char) *cp)) { |
544 | 1.84k | while (*cp != '\0' && isspace((unsigned char) *cp)) { |
545 | 1.29k | ++cp; |
546 | 1.29k | } |
547 | 544 | json_debug_trace(1, |
548 | 544 | "json: Skipped trailing whitespace: value \"%s\"\n", valbuf); |
549 | 544 | } |
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 | 69.5k | for (;;) { |
559 | 69.5k | int seeking = cursor->type; |
560 | | |
561 | 69.5k | if (value_quoted && |
562 | 16.4k | (cursor->type == t_string || |
563 | 13.6k | cursor->type == t_time)) { |
564 | 6.79k | break; |
565 | 6.79k | } |
566 | 62.7k | if ((strcmp(valbuf, "true") == 0 || |
567 | 59.4k | strcmp(valbuf, "false") == 0) && |
568 | 3.68k | seeking == t_boolean) { |
569 | 2.58k | break; |
570 | 2.58k | } |
571 | 60.1k | if (isdigit((unsigned char) valbuf[0])) { |
572 | 29.5k | bool decimal = strchr(valbuf, '.') != NULL; |
573 | | |
574 | 29.5k | if (decimal && |
575 | 1.72k | seeking == t_real) { |
576 | 426 | break; |
577 | 426 | } |
578 | 29.1k | if (!decimal && (seeking == t_byte || |
579 | 27.6k | seeking == t_ubyte || |
580 | 27.3k | seeking == t_integer || |
581 | 25.4k | seeking == t_uinteger || |
582 | 24.0k | seeking == t_longint || |
583 | 23.4k | seeking == t_ulongint || |
584 | 22.9k | seeking == t_time || |
585 | 22.1k | seeking == t_short || |
586 | 20.3k | seeking == t_ushort)) |
587 | 7.48k | break; |
588 | 29.1k | } |
589 | 52.2k | if (NULL == cursor[1].attribute) { // out of possibilities |
590 | 36.3k | break; |
591 | 36.3k | } |
592 | 15.9k | if (0 != strcmp(cursor[1].attribute, attrbuf)) { |
593 | 15.9k | break; |
594 | 15.9k | } |
595 | 0 | ++cursor; |
596 | 0 | } |
597 | 69.5k | if (value_quoted && |
598 | 16.4k | (cursor->type != t_string && |
599 | 13.6k | cursor->type != t_character && |
600 | 13.2k | cursor->type != t_check && |
601 | 11.9k | cursor->type != t_time && |
602 | 8.02k | cursor->type != t_ignore && |
603 | 483 | cursor->map == 0)) { |
604 | 44 | json_debug_trace(1, "json: %s", "Saw quoted value when expecting" |
605 | 44 | " non-string.\n"); |
606 | 44 | return JSON_ERR_QNONSTRING; |
607 | 44 | } |
608 | 69.4k | if (!value_quoted && |
609 | 53.0k | (cursor->type == t_string || |
610 | 52.9k | cursor->type == t_check || |
611 | 52.9k | cursor->map != 0)) { |
612 | 93 | json_debug_trace(1, "json: %s", |
613 | 93 | "Didn't see quoted value when expecting" |
614 | 93 | " string.\n"); |
615 | 93 | return JSON_ERR_NONQSTRING; |
616 | 93 | } |
617 | 69.3k | 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 | 69.3k | if (cursor->type == t_check) { |
630 | 1.26k | lptr = cursor->dflt.check; |
631 | 68.0k | } else { |
632 | 68.0k | lptr = json_target_address(cursor, parent, offset); |
633 | 68.0k | } |
634 | 69.3k | if (NULL != lptr) { |
635 | 35.4k | switch (cursor->type) { |
636 | 228 | case t_byte: |
637 | 228 | { |
638 | 228 | int tmp = atoi(valbuf); |
639 | 228 | lptr[0] = (char)tmp; |
640 | 228 | } |
641 | 228 | break; |
642 | 692 | case t_ubyte: |
643 | 692 | { |
644 | 692 | int tmp = atoi(valbuf); |
645 | 692 | lptr[0] = (unsigned char)tmp; |
646 | 692 | } |
647 | 692 | break; |
648 | 2.47k | case t_integer: |
649 | 2.47k | { |
650 | 2.47k | int tmp = atoi(valbuf); |
651 | 2.47k | memcpy(lptr, &tmp, sizeof(int)); |
652 | 2.47k | } |
653 | 2.47k | break; |
654 | 1.70k | case t_uinteger: |
655 | 1.70k | { |
656 | 1.70k | unsigned int tmp = (unsigned int)atol(valbuf); |
657 | 1.70k | memcpy(lptr, &tmp, sizeof(unsigned int)); |
658 | 1.70k | } |
659 | 1.70k | break; |
660 | 643 | case t_longint: |
661 | 643 | { |
662 | 643 | long tmp = atol(valbuf); |
663 | 643 | memcpy(lptr, &tmp, sizeof(long)); |
664 | 643 | } |
665 | 643 | break; |
666 | 536 | case t_ulongint: |
667 | 536 | { |
668 | 536 | unsigned long tmp = (unsigned long)atoll(valbuf); |
669 | 536 | memcpy(lptr, &tmp, sizeof(unsigned long)); |
670 | 536 | } |
671 | 536 | break; |
672 | 1.84k | case t_short: |
673 | 1.84k | { |
674 | 1.84k | short tmp = atoi(valbuf); |
675 | 1.84k | memcpy(lptr, &tmp, sizeof(short)); |
676 | 1.84k | } |
677 | 1.84k | 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.88k | case t_time: |
685 | 5.88k | { |
686 | 5.88k | timespec_t ts_tmp = {0, 0}; |
687 | | |
688 | 5.88k | if (value_quoted) { |
689 | 3.94k | ts_tmp = iso8601_to_timespec(valbuf); |
690 | 3.94k | } else if (NULL == strchr(valbuf, '.')) { |
691 | | // integer |
692 | 888 | ts_tmp.tv_sec = (time_t)atoll(valbuf); |
693 | 1.05k | } else { |
694 | | // real |
695 | 1.05k | double sec_tmp = safe_atof(valbuf); |
696 | 1.05k | if (0 != isfinite(sec_tmp)) { |
697 | 665 | DTOTS(&ts_tmp, sec_tmp); |
698 | 665 | } // else, leave at default |
699 | 1.05k | } |
700 | 5.88k | memcpy(lptr, &ts_tmp, sizeof(timespec_t)); |
701 | 5.88k | } |
702 | 5.88k | 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.33k | case t_real: |
714 | 9.33k | { |
715 | 9.33k | double tmp = safe_atof(valbuf); |
716 | 9.33k | if (0 != isfinite(tmp)) { |
717 | 7.47k | memcpy(lptr, &tmp, sizeof(double)); |
718 | 7.47k | } // else leave at .dflt |
719 | 9.33k | } |
720 | 9.33k | break; |
721 | 2.85k | case t_string: |
722 | 2.85k | if (NULL != parent && |
723 | 388 | parent->element_type != t_structobject && |
724 | 0 | offset > 0) { |
725 | 0 | return JSON_ERR_NOPARSTR; |
726 | 0 | } |
727 | 2.85k | (void)strlcpy(lptr, valbuf, cursor->len); |
728 | 2.85k | break; |
729 | 4.13k | case t_boolean: |
730 | 4.13k | { |
731 | 4.13k | bool tmp = (strcmp(valbuf, "true") == 0); |
732 | 4.13k | memcpy(lptr, &tmp, sizeof(bool)); |
733 | 4.13k | } |
734 | 4.13k | break; |
735 | 1.13k | case t_character: |
736 | 1.13k | if (strnlen(valbuf, 2) > 1) { |
737 | | // don't update end here, leave at value start |
738 | 12 | return JSON_ERR_STRLONG; |
739 | 1.12k | } else { |
740 | 1.12k | lptr[0] = valbuf[0]; |
741 | 1.12k | } |
742 | 1.12k | break; |
743 | 2.33k | case t_ignore: // silences a compiler warning |
744 | 2.33k | FALLTHROUGH |
745 | 2.33k | case t_object: // silences a compiler warning |
746 | 2.33k | FALLTHROUGH |
747 | 2.33k | case t_structobject: |
748 | 2.33k | FALLTHROUGH |
749 | 2.33k | case t_array: |
750 | 2.33k | break; |
751 | 1.26k | case t_check: |
752 | 1.26k | 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 | 967 | break; |
760 | 35.4k | } |
761 | 35.4k | } |
762 | 69.0k | FALLTHROUGH |
763 | 69.9k | case post_element: |
764 | 69.9k | if (isspace((unsigned char) *cp)) { |
765 | 374 | continue; |
766 | 374 | } |
767 | 69.5k | if (*cp == ',') { |
768 | 61.8k | state = await_attr; |
769 | 61.8k | } else if (*cp == '}') { |
770 | 6.47k | ++cp; |
771 | 6.47k | goto good_parse; |
772 | 6.47k | } else { |
773 | 1.19k | json_debug_trace(1, "json: %s", |
774 | 1.19k | "Garbage while expecting comma or }\n"); |
775 | 1.19k | if (NULL != end) { |
776 | 28 | *end = cp; |
777 | 28 | } |
778 | 1.19k | return JSON_ERR_BADTRAIL; |
779 | 1.19k | } |
780 | 61.8k | break; |
781 | 1.01M | } |
782 | 1.01M | } |
783 | 5.10k | if (state == init) { |
784 | 61 | json_debug_trace(1, "json: %s", "Input was empty or white-space only\n"); |
785 | 61 | return JSON_ERR_EMPTY; |
786 | 61 | } |
787 | | |
788 | 11.5k | good_parse: |
789 | | // in case there's another object following, consume trailing WS |
790 | 11.5k | while (isspace((unsigned char)*cp)) { |
791 | 391 | ++cp; |
792 | 391 | } |
793 | 11.5k | if (NULL != end) { |
794 | 5.88k | *end = cp; |
795 | 5.88k | } |
796 | 11.5k | json_debug_trace(1, "%s", "json: JSON parse ends.\n"); |
797 | 11.5k | return 0; |
798 | 5.10k | } |
799 | | |
800 | | int json_read_array(const char *cp, const struct json_array_t *arr, |
801 | | const char **end) |
802 | 922 | { |
803 | 922 | int substatus; |
804 | 922 | unsigned offset, arrcount; |
805 | 922 | char *tp; |
806 | | |
807 | 922 | if (NULL != end) { |
808 | 922 | *end = NULL; // give it a well-defined value on parse failure |
809 | 922 | } |
810 | | |
811 | 922 | json_debug_trace(1, "json: %s", "Entered json_read_array()\n"); |
812 | | |
813 | 922 | while (isspace((unsigned char)*cp)) { |
814 | 0 | cp++; |
815 | 0 | } |
816 | 922 | 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 | 922 | cp++; |
821 | | |
822 | 922 | tp = arr->arr.strings.store; |
823 | 922 | arrcount = 0; |
824 | | |
825 | | // Check for empty array |
826 | 922 | while (isspace((unsigned char)*cp)) { |
827 | 194 | cp++; |
828 | 194 | } |
829 | 922 | if (*cp == ']') { |
830 | 219 | goto breakout; |
831 | 219 | } |
832 | | |
833 | 9.00k | for (offset = 0; offset < arr->maxlen; offset++) { |
834 | 9.00k | char *ep = NULL; |
835 | | |
836 | 9.00k | json_debug_trace(1, "json: Looking at %s\n", cp); |
837 | 9.00k | switch (arr->element_type) { |
838 | 2.96k | case t_string: |
839 | 2.96k | if (isspace((unsigned char) *cp)) { |
840 | 194 | cp++; |
841 | 194 | } |
842 | 2.96k | if (*cp != '"') { |
843 | 13 | return JSON_ERR_BADSTRING; |
844 | 2.94k | } else { |
845 | 2.94k | ++cp; |
846 | 2.94k | } |
847 | 2.94k | arr->arr.strings.ptrs[offset] = tp; |
848 | 13.4k | for (; tp - arr->arr.strings.store < arr->arr.strings.storelen; |
849 | 10.5k | tp++) |
850 | 13.4k | if (*cp == '"') { |
851 | 2.94k | ++cp; |
852 | 2.94k | *tp++ = '\0'; |
853 | 2.94k | goto stringend; |
854 | 10.5k | } 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.5k | } else { |
859 | 10.5k | *tp = *cp++; |
860 | 10.5k | } |
861 | 3 | json_debug_trace(1, "json: %s", |
862 | 3 | "Bad string syntax in string list.\n"); |
863 | 3 | return JSON_ERR_BADSTRING; |
864 | 2.94k | stringend: |
865 | 2.94k | break; |
866 | 0 | case t_object: |
867 | 0 | FALLTHROUGH |
868 | 6.04k | case t_structobject: |
869 | 6.04k | substatus = |
870 | 6.04k | json_internal_read_object(cp, arr->arr.objects.subtype, arr, |
871 | 6.04k | offset, &cp); |
872 | 6.04k | if (substatus != 0) { |
873 | 158 | if (NULL != end) { |
874 | 158 | *end = cp; |
875 | 158 | } |
876 | 158 | return substatus; |
877 | 158 | } |
878 | 5.88k | break; |
879 | 5.88k | 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 | 9.00k | } |
988 | 8.82k | arrcount++; |
989 | 8.82k | if (isspace((unsigned char)*cp)) { |
990 | 194 | cp++; |
991 | 194 | } |
992 | 8.82k | if (*cp == ']') { |
993 | 377 | json_debug_trace(1, "json: %s", "End of array found.\n"); |
994 | 377 | goto breakout; |
995 | 377 | } |
996 | 8.45k | if (*cp == ',') { |
997 | 8.30k | cp++; |
998 | 8.30k | } else { |
999 | 148 | json_debug_trace(1, "json: %s", "Bad trailing syntax on array.\n"); |
1000 | 148 | return JSON_ERR_BADSUBTRAIL; |
1001 | 148 | } |
1002 | 8.45k | } |
1003 | 3 | json_debug_trace(1, "json: %s", "Too many elements in array.\n"); |
1004 | 3 | if (NULL != end) { |
1005 | 3 | *end = cp; |
1006 | 3 | } |
1007 | 3 | return JSON_ERR_SUBTOOLONG; |
1008 | 596 | breakout: |
1009 | 596 | if (NULL != arr->count) { |
1010 | 596 | *(arr->count) = arrcount; |
1011 | 596 | } |
1012 | 596 | if (NULL != end) { |
1013 | 596 | *end = cp; |
1014 | 596 | } |
1015 | 596 | json_debug_trace(1, "json: leaving json_read_array() with %d elements\n", |
1016 | 596 | arrcount); |
1017 | 596 | return 0; |
1018 | 703 | } |
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 |