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