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