blob: 6a44f8cfd25b1729c59ad92911e477ae311ac5d2 [file] [log] [blame]
Nikias Bassen429cbc62021-12-23 03:09:07 +01001/*
2 * jplist.c
3 * JSON plist implementation
4 *
5 * Copyright (c) 2019-2021 Nikias Bassen All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <string.h>
27#include <stdlib.h>
28#include <stdio.h>
29#include <time.h>
30
31#include <inttypes.h>
32#include <ctype.h>
33#include <math.h>
34#include <limits.h>
35
36#include <node.h>
37#include <node_list.h>
38
39#include "plist.h"
40#include "strbuf.h"
41#include "jsmn.h"
42
43#ifdef DEBUG
44static int plist_json_debug = 0;
45#define PLIST_JSON_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonparser] ERROR: " __VA_ARGS__); }
46#define PLIST_JSON_WRITE_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonwriter] ERROR: " __VA_ARGS__); }
47#else
48#define PLIST_JSON_ERR(...)
49#define PLIST_JSON_WRITE_ERR(...)
50#endif
51
52void plist_json_init(void)
53{
54 /* init JSON stuff */
55#ifdef DEBUG
56 char *env_debug = getenv("PLIST_JSON_DEBUG");
57 if (env_debug && !strcmp(env_debug, "1")) {
58 plist_json_debug = 1;
59 }
60#endif
61}
62
63void plist_json_deinit(void)
64{
65 /* deinit JSON stuff */
66}
67
Nikias Bassen6d7fc8a2021-12-23 03:23:37 +010068#ifndef HAVE_STRNDUP
Nikias Bassen91c533a2021-12-23 14:13:49 +010069static char* strndup(const char* str, size_t len)
Nikias Bassen6d7fc8a2021-12-23 03:23:37 +010070{
71 char *newstr = (char *)malloc(len+1);
72 if (newstr) {
73 strncpy(newstr, str, len);
74 newstr[len]= '\0';
75 }
76 return newstr;
77}
78#endif
79
Nikias Bassen429cbc62021-12-23 03:09:07 +010080static size_t dtostr(char *buf, size_t bufsize, double realval)
81{
82 size_t len = 0;
83 if (isnan(realval)) {
84 len = snprintf(buf, bufsize, "nan");
85 } else if (isinf(realval)) {
86 len = snprintf(buf, bufsize, "%cinfinity", (realval > 0.0) ? '+' : '-');
87 } else if (realval == 0.0f) {
88 len = snprintf(buf, bufsize, "0.0");
89 } else {
90 size_t i = 0;
91 len = snprintf(buf, bufsize, "%.*g", 17, realval);
92 for (i = 0; buf && i < len; i++) {
93 if (buf[i] == ',') {
94 buf[i] = '.';
95 break;
96 } else if (buf[i] == '.') {
97 break;
98 }
99 }
100 }
101 return len;
102}
103
104static int node_to_json(node_t* node, bytearray_t **outbuf, uint32_t depth, int prettify)
105{
106 plist_data_t node_data = NULL;
107
108 char *val = NULL;
109 size_t val_len = 0;
110
111 uint32_t i = 0;
112
113 if (!node)
114 return PLIST_ERR_INVALID_ARG;
115
116 node_data = plist_get_data(node);
117
118 switch (node_data->type)
119 {
120 case PLIST_BOOLEAN:
121 {
122 if (node_data->boolval) {
123 str_buf_append(*outbuf, "true", 4);
124 } else {
125 str_buf_append(*outbuf, "false", 5);
126 }
127 }
128 break;
129
130 case PLIST_NULL:
131 str_buf_append(*outbuf, "null", 4);
132 break;
133
134 case PLIST_UINT:
135 val = (char*)malloc(64);
136 if (node_data->length == 16) {
137 val_len = snprintf(val, 64, "%"PRIu64, node_data->intval);
138 } else {
139 val_len = snprintf(val, 64, "%"PRIi64, node_data->intval);
140 }
141 str_buf_append(*outbuf, val, val_len);
142 free(val);
143 break;
144
145 case PLIST_REAL:
146 val = (char*)malloc(64);
147 val_len = dtostr(val, 64, node_data->realval);
148 str_buf_append(*outbuf, val, val_len);
149 free(val);
150 break;
151
152 case PLIST_STRING:
153 case PLIST_KEY: {
Nikias Bassendb93bae2022-04-06 01:06:30 +0200154 const char *charmap[32] = {
155 "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007",
156 "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f",
157 "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017",
158 "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f",
159 };
Nikias Bassen429cbc62021-12-23 03:09:07 +0100160 size_t j = 0;
161 size_t len = 0;
162 off_t start = 0;
163 off_t cur = 0;
164
165 str_buf_append(*outbuf, "\"", 1);
166
167 len = node_data->length;
168 for (j = 0; j < len; j++) {
Nikias Bassendb93bae2022-04-06 01:06:30 +0200169 unsigned char ch = (unsigned char)node_data->strval[j];
170 if (ch < 0x20) {
171 str_buf_append(*outbuf, node_data->strval + start, cur - start);
172 str_buf_append(*outbuf, charmap[ch], (charmap[ch][1] == 'u') ? 6 : 2);
173 start = cur+1;
174 } else if (ch == '"') {
Nikias Bassen429cbc62021-12-23 03:09:07 +0100175 str_buf_append(*outbuf, node_data->strval + start, cur - start);
176 str_buf_append(*outbuf, "\\\"", 2);
177 start = cur+1;
Nikias Bassen429cbc62021-12-23 03:09:07 +0100178 }
179 cur++;
180 }
181 str_buf_append(*outbuf, node_data->strval + start, cur - start);
182
183 str_buf_append(*outbuf, "\"", 1);
184 } break;
185
186 case PLIST_ARRAY: {
187 str_buf_append(*outbuf, "[", 1);
188 node_t *ch;
189 uint32_t cnt = 0;
190 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
191 if (cnt > 0) {
192 str_buf_append(*outbuf, ",", 1);
193 }
194 if (prettify) {
195 str_buf_append(*outbuf, "\n", 1);
196 for (i = 0; i <= depth; i++) {
197 str_buf_append(*outbuf, " ", 2);
198 }
199 }
200 int res = node_to_json(ch, outbuf, depth+1, prettify);
201 if (res < 0) {
202 return res;
203 }
204 cnt++;
205 }
206 if (cnt > 0 && prettify) {
207 str_buf_append(*outbuf, "\n", 1);
208 for (i = 0; i < depth; i++) {
209 str_buf_append(*outbuf, " ", 2);
210 }
211 }
212 str_buf_append(*outbuf, "]", 1);
213 } break;
214 case PLIST_DICT: {
215 str_buf_append(*outbuf, "{", 1);
216 node_t *ch;
217 uint32_t cnt = 0;
218 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
219 if (cnt > 0 && cnt % 2 == 0) {
220 str_buf_append(*outbuf, ",", 1);
221 }
222 if (cnt % 2 == 0 && prettify) {
223 str_buf_append(*outbuf, "\n", 1);
224 for (i = 0; i <= depth; i++) {
225 str_buf_append(*outbuf, " ", 2);
226 }
227 }
228 int res = node_to_json(ch, outbuf, depth+1, prettify);
229 if (res < 0) {
230 return res;
231 }
232 if (cnt % 2 == 0) {
233 str_buf_append(*outbuf, ":", 1);
234 if (prettify) {
235 str_buf_append(*outbuf, " ", 1);
236 }
237 }
238 cnt++;
239 }
240 if (cnt > 0 && prettify) {
241 str_buf_append(*outbuf, "\n", 1);
242 for (i = 0; i < depth; i++) {
243 str_buf_append(*outbuf, " ", 2);
244 }
245 }
246 str_buf_append(*outbuf, "}", 1);
247 } break;
248 case PLIST_DATA:
249 // NOT VALID FOR JSON
250 PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n");
251 return PLIST_ERR_FORMAT;
252 case PLIST_DATE:
253 // NOT VALID FOR JSON
254 PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n");
255 return PLIST_ERR_FORMAT;
256 case PLIST_UID:
257 // NOT VALID FOR JSON
258 PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n");
259 return PLIST_ERR_FORMAT;
260 default:
261 return PLIST_ERR_UNKNOWN;
262 }
263
264 return PLIST_ERR_SUCCESS;
265}
266
267#define PO10i_LIMIT (INT64_MAX/10)
268
269/* based on https://stackoverflow.com/a/4143288 */
270static int num_digits_i(int64_t i)
271{
272 int n;
273 int64_t po10;
274 n=1;
275 if (i < 0) {
Nikias Bassena5316622022-02-07 01:12:58 +0100276 i = (i == INT64_MIN) ? INT64_MAX : -i;
Nikias Bassen429cbc62021-12-23 03:09:07 +0100277 n++;
278 }
279 po10=10;
280 while (i>=po10) {
281 n++;
282 if (po10 > PO10i_LIMIT) break;
283 po10*=10;
284 }
285 return n;
286}
287
288#define PO10u_LIMIT (UINT64_MAX/10)
289
290/* based on https://stackoverflow.com/a/4143288 */
291static int num_digits_u(uint64_t i)
292{
293 int n;
294 uint64_t po10;
295 n=1;
296 po10=10;
297 while (i>=po10) {
298 n++;
299 if (po10 > PO10u_LIMIT) break;
300 po10*=10;
301 }
302 return n;
303}
304
305static int node_estimate_size(node_t *node, uint64_t *size, uint32_t depth, int prettify)
306{
307 plist_data_t data;
308 if (!node) {
309 return PLIST_ERR_INVALID_ARG;
310 }
311 data = plist_get_data(node);
312 if (node->children) {
313 node_t *ch;
314 unsigned int n_children = node_n_children(node);
315 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
316 int res = node_estimate_size(ch, size, depth + 1, prettify);
317 if (res < 0) {
318 return res;
319 }
320 }
321 switch (data->type) {
322 case PLIST_DICT:
323 *size += 2; // '{' and '}'
324 *size += n_children-1; // number of ':' and ','
325 if (prettify) {
326 *size += n_children; // number of '\n' and extra space
327 *size += n_children * (depth+1); // indent for every 2nd child
328 *size += 1; // additional '\n'
329 }
330 break;
331 case PLIST_ARRAY:
332 *size += 2; // '[' and ']'
333 *size += n_children-1; // number of ','
334 if (prettify) {
335 *size += n_children; // number of '\n'
336 *size += n_children * ((depth+1)<<1); // indent for every child
337 *size += 1; // additional '\n'
338 }
339 break;
340 default:
341 break;
342 }
343 if (prettify)
344 *size += (depth << 1); // indent for {} and []
345 } else {
346 switch (data->type) {
347 case PLIST_STRING:
348 case PLIST_KEY:
349 *size += data->length;
350 *size += 2;
351 break;
352 case PLIST_UINT:
353 if (data->length == 16) {
354 *size += num_digits_u(data->intval);
355 } else {
356 *size += num_digits_i((int64_t)data->intval);
357 }
358 break;
359 case PLIST_REAL:
360 *size += dtostr(NULL, 0, data->realval);
361 break;
362 case PLIST_BOOLEAN:
363 *size += ((data->boolval) ? 4 : 5);
364 break;
365 case PLIST_DICT:
366 case PLIST_ARRAY:
367 *size += 2;
368 break;
369 case PLIST_DATA:
370 // NOT VALID FOR JSON
371 PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n");
372 return PLIST_ERR_FORMAT;
373 case PLIST_DATE:
374 // NOT VALID FOR JSON
375 PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n");
376 return PLIST_ERR_FORMAT;
377 case PLIST_UID:
378 // NOT VALID FOR JSON
379 PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n");
380 return PLIST_ERR_FORMAT;
381 default:
382 PLIST_JSON_WRITE_ERR("invalid node type encountered\n");
383 return PLIST_ERR_UNKNOWN;
384 }
385 }
386 return PLIST_ERR_SUCCESS;
387}
388
389PLIST_API int plist_to_json(plist_t plist, char **json, uint32_t* length, int prettify)
390{
391 uint64_t size = 0;
392 int res;
393
394 if (!plist || !json || !length) {
395 return PLIST_ERR_INVALID_ARG;
396 }
397
398 res = node_estimate_size(plist, &size, 0, prettify);
399 if (res < 0) {
400 return res;
401 }
402
403 strbuf_t *outbuf = str_buf_new(size);
404 if (!outbuf) {
405 PLIST_JSON_WRITE_ERR("Could not allocate output buffer");
406 return PLIST_ERR_NO_MEM;
407 }
408
409 res = node_to_json(plist, &outbuf, 0, prettify);
410 if (res < 0) {
411 str_buf_free(outbuf);
412 *json = NULL;
413 *length = 0;
414 return res;
415 }
416
417 str_buf_append(outbuf, "\0", 1);
418
419 *json = outbuf->data;
420 *length = outbuf->len - 1;
421
422 outbuf->data = NULL;
423 str_buf_free(outbuf);
424
425 return PLIST_ERR_SUCCESS;
426}
427
Nikias Bassen924ba962022-01-31 02:55:18 +0100428typedef struct {
429 jsmntok_t* tokens;
430 int count;
431} jsmntok_info_t;
432
Nikias Bassena5316622022-02-07 01:12:58 +0100433static int64_t parse_decimal(const char* str, const char* str_end, char** endp)
Nikias Bassen474c8eb2022-02-02 04:38:16 +0100434{
Nikias Bassena5316622022-02-07 01:12:58 +0100435 uint64_t MAX = INT64_MAX;
436 uint64_t x = 0;
Nikias Bassen474c8eb2022-02-02 04:38:16 +0100437 int is_neg = 0;
438 *endp = (char*)str;
439
440 if (str[0] == '-') {
441 is_neg = 1;
442 (*endp)++;
443 }
Nikias Bassena5316622022-02-07 01:12:58 +0100444 if (is_neg) {
445 MAX++;
446 }
Nikias Bassen474c8eb2022-02-02 04:38:16 +0100447 while (*endp < str_end && isdigit(**endp)) {
Nikias Bassena5316622022-02-07 01:12:58 +0100448 if (x > PO10i_LIMIT) {
449 x = MAX;
450 break;
451 }
452 x = x * 10;
453 unsigned int add = (**endp - '0');
454 if (x + add > MAX) {
455 x = MAX;
456 break;
457 }
458 x += add;
Nikias Bassen474c8eb2022-02-02 04:38:16 +0100459 (*endp)++;
460 }
Nikias Bassena5316622022-02-07 01:12:58 +0100461
462 // swallow the rest of the digits in case we dropped out early
463 while (*endp < str_end && isdigit(**endp)) (*endp)++;
464
465 int64_t result = x;
Nikias Bassen474c8eb2022-02-02 04:38:16 +0100466 if (is_neg) {
Nikias Bassena5316622022-02-07 01:12:58 +0100467 if (x == MAX) {
468 result = INT64_MIN;
469 } else {
470 result = -(int64_t)x;
471 }
Nikias Bassen474c8eb2022-02-02 04:38:16 +0100472 }
Nikias Bassena5316622022-02-07 01:12:58 +0100473 return result;
Nikias Bassen474c8eb2022-02-02 04:38:16 +0100474}
475
Nikias Bassen924ba962022-01-31 02:55:18 +0100476static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index)
Nikias Bassen429cbc62021-12-23 03:09:07 +0100477{
Nikias Bassen924ba962022-01-31 02:55:18 +0100478 if (ti->tokens[*index].type != JSMN_PRIMITIVE) {
Nikias Bassen429cbc62021-12-23 03:09:07 +0100479 PLIST_JSON_ERR("%s: token type != JSMN_PRIMITIVE\n", __func__);
480 return NULL;
481 }
482 plist_t val = NULL;
Nikias Bassen924ba962022-01-31 02:55:18 +0100483 const char* str_val = js + ti->tokens[*index].start;
484 const char* str_end = js + ti->tokens[*index].end;
485 size_t str_len = ti->tokens[*index].end - ti->tokens[*index].start;
Nikias Bassen429cbc62021-12-23 03:09:07 +0100486 if (!strncmp("false", str_val, str_len)) {
487 val = plist_new_bool(0);
488 } else if (!strncmp("true", str_val, str_len)) {
489 val = plist_new_bool(1);
490 } else if (!strncmp("null", str_val, str_len)) {
491 plist_data_t data = plist_new_plist_data();
492 data->type = PLIST_NULL;
493 val = plist_new_node(data);
Nikias Bassen7d2cdc62022-02-11 19:14:23 +0100494 } else if (isdigit(str_val[0]) || (str_val[0] == '-' && str_val+1 < str_end && isdigit(str_val[1]))) {
Nikias Bassen474c8eb2022-02-02 04:38:16 +0100495 char* endp = (char*)str_val;
Nikias Bassend6026ca2022-02-08 16:58:21 +0100496 int64_t intpart = parse_decimal(str_val, str_end, &endp);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100497 if (endp >= str_end) {
498 /* integer */
499 val = plist_new_uint((uint64_t)intpart);
Nikias Bassen106c4ee2022-02-15 04:30:07 +0100500 } else if ((*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) || ((*endp == 'e' || *endp == 'E') && endp+1 < str_end && (isdigit(*(endp+1)) || ((*(endp+1) == '-') && endp+2 < str_end && isdigit(*(endp+2)))))) {
Nikias Bassen474c8eb2022-02-02 04:38:16 +0100501 /* floating point */
502 double dval = (double)intpart;
503 char* fendp = endp;
504 int err = 0;
505 do {
506 if (*endp == '.') {
507 fendp++;
508 int is_neg = (str_val[0] == '-');
509 double frac = 0;
510 double p = 0.1;
Nikias Bassend6026ca2022-02-08 16:58:21 +0100511 while (fendp < str_end && isdigit(*fendp)) {
Nikias Bassen474c8eb2022-02-02 04:38:16 +0100512 frac = frac + (*fendp - '0') * p;
513 p *= 0.1;
514 fendp++;
515 }
516 if (is_neg) {
517 dval -= frac;
518 } else {
519 dval += frac;
520 }
521 }
522 if (fendp >= str_end) {
523 break;
524 }
525 if (fendp+1 < str_end && (*fendp == 'e' || *fendp == 'E') && (isdigit(*(fendp+1)) || ((*(fendp+1) == '-') && fendp+2 < str_end && isdigit(*(fendp+2))))) {
526 double exp = (double)parse_decimal(fendp+1, str_end, &fendp);
527 dval = dval * pow(10, exp);
528 } else {
529 PLIST_JSON_ERR("%s: invalid character at offset %d when parsing floating point value\n", __func__, (int)(fendp - js));
530 err++;
531 }
532 } while (0);
533 if (!err) {
534 if (isinf(dval) || isnan(dval)) {
535 PLIST_JSON_ERR("%s: unrepresentable floating point value at offset %d when parsing numerical value\n", __func__, (int)(str_val - js));
536 } else {
537 val = plist_new_real(dval);
538 }
Nikias Bassen429cbc62021-12-23 03:09:07 +0100539 }
540 } else {
541 PLIST_JSON_ERR("%s: invalid character at offset %d when parsing numerical value\n", __func__, (int)(endp - js));
542 }
543 } else {
544 PLIST_JSON_ERR("%s: invalid primitive value '%.*s' encountered\n", __func__, (int)str_len, str_val);
545 }
546 (*index)++;
547 return val;
548}
549
Nikias Bassencef7ab72022-01-25 01:46:53 +0100550static char* unescape_string(const char* str_val, size_t str_len, size_t *new_len)
Nikias Bassen429cbc62021-12-23 03:09:07 +0100551{
Nikias Bassen429cbc62021-12-23 03:09:07 +0100552 char* strval = strndup(str_val, str_len);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100553 size_t i = 0;
554 while (i < str_len) {
555 if (strval[i] == '\\' && i < str_len-1) {
556 switch (strval[i+1]) {
557 case '\"': case '/' : case '\\' : case 'b' :
558 case 'f' : case 'r' : case 'n' : case 't' :
559 memmove(strval+i, strval+i+1, str_len - (i+1));
560 str_len--;
561 switch (strval[i]) {
562 case 'b':
563 strval[i] = '\b';
564 break;
565 case 'f':
566 strval[i] = '\f';
567 break;
568 case 'r':
569 strval[i] = '\r';
570 break;
571 case 'n':
572 strval[i] = '\n';
573 break;
574 case 't':
575 strval[i] = '\t';
576 break;
577 default:
578 break;
579 }
580 break;
581 case 'u': {
582 unsigned int val = 0;
583 if (str_len-(i+2) < 4) {
Nikias Bassen429cbc62021-12-23 03:09:07 +0100584 PLIST_JSON_ERR("%s: invalid escape sequence '%s' (too short)\n", __func__, strval+i);
Nikias Bassen6ef1c262022-01-28 22:06:02 +0100585 free(strval);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100586 return NULL;
587 }
588 if (!(isxdigit(strval[i+2]) && isxdigit(strval[i+3]) && isxdigit(strval[i+4]) && isxdigit(strval[i+5])) || sscanf(strval+i+2, "%04x", &val) != 1) {
Nikias Bassen429cbc62021-12-23 03:09:07 +0100589 PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 6, strval+i);
Nikias Bassen6ef1c262022-01-28 22:06:02 +0100590 free(strval);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100591 return NULL;
592 }
593 int bytelen = 0;
594 if (val >= 0x800) {
595 /* three bytes */
596 strval[i] = (char)(0xE0 + ((val >> 12) & 0xF));
597 strval[i+1] = (char)(0x80 + ((val >> 6) & 0x3F));
598 strval[i+2] = (char)(0x80 + (val & 0x3F));
599 bytelen = 3;
600 } else if (val >= 0x80) {
601 /* two bytes */
602 strval[i] = (char)(0xC0 + ((val >> 6) & 0x1F));
603 strval[i+1] = (char)(0x80 + (val & 0x3F));
604 bytelen = 2;
605 } else {
606 /* one byte */
607 strval[i] = (char)(val & 0x7F);
608 bytelen = 1;
609 }
610 memmove(strval+i+bytelen, strval+i+6, str_len - (i+5));
611 str_len -= (6-bytelen);
612 } break;
613 default:
614 PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 2, strval+i);
615 free(strval);
616 return NULL;
617 }
618 }
619 i++;
620 }
Nikias Bassencef7ab72022-01-25 01:46:53 +0100621 strval[str_len] = '\0';
622 if (new_len) {
623 *new_len = str_len;
624 }
625 return strval;
626}
627
Nikias Bassen924ba962022-01-31 02:55:18 +0100628static plist_t parse_string(const char* js, jsmntok_info_t* ti, int* index)
Nikias Bassencef7ab72022-01-25 01:46:53 +0100629{
Nikias Bassen924ba962022-01-31 02:55:18 +0100630 if (ti->tokens[*index].type != JSMN_STRING) {
Nikias Bassencef7ab72022-01-25 01:46:53 +0100631 PLIST_JSON_ERR("%s: token type != JSMN_STRING\n", __func__);
632 return NULL;
633 }
634
635 size_t str_len = 0; ;
Nikias Bassen924ba962022-01-31 02:55:18 +0100636 char* strval = unescape_string(js + ti->tokens[*index].start, ti->tokens[*index].end - ti->tokens[*index].start, &str_len);
Nikias Bassen088cdab2022-01-28 22:11:00 +0100637 if (!strval) {
638 return NULL;
639 }
Nikias Bassencef7ab72022-01-25 01:46:53 +0100640 plist_t node;
Nikias Bassen429cbc62021-12-23 03:09:07 +0100641
642 plist_data_t data = plist_new_plist_data();
643 data->type = PLIST_STRING;
644 data->strval = strval;
645 data->length = str_len;
646 node = plist_new_node(data);
647
648 (*index)++;
649 return node;
650}
651
Nikias Bassen924ba962022-01-31 02:55:18 +0100652static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100653
Nikias Bassen924ba962022-01-31 02:55:18 +0100654static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index)
Nikias Bassen429cbc62021-12-23 03:09:07 +0100655{
Nikias Bassen924ba962022-01-31 02:55:18 +0100656 if (ti->tokens[*index].type != JSMN_ARRAY) {
Nikias Bassen429cbc62021-12-23 03:09:07 +0100657 PLIST_JSON_ERR("%s: token type != JSMN_ARRAY\n", __func__);
658 return NULL;
659 }
660 plist_t arr = plist_new_array();
Nikias Bassen924ba962022-01-31 02:55:18 +0100661 int num_tokens = ti->tokens[*index].size;
Nikias Bassen429cbc62021-12-23 03:09:07 +0100662 int num;
663 int j = (*index)+1;
664 for (num = 0; num < num_tokens; num++) {
Nikias Bassen924ba962022-01-31 02:55:18 +0100665 if (j >= ti->count) {
666 PLIST_JSON_ERR("%s: token index out of valid range\n", __func__);
Nikias Bassend7758c02022-02-02 04:45:40 +0100667 plist_free(arr);
Nikias Bassen924ba962022-01-31 02:55:18 +0100668 return NULL;
669 }
Nikias Bassen429cbc62021-12-23 03:09:07 +0100670 plist_t val = NULL;
Nikias Bassen924ba962022-01-31 02:55:18 +0100671 switch (ti->tokens[j].type) {
Nikias Bassen429cbc62021-12-23 03:09:07 +0100672 case JSMN_OBJECT:
Nikias Bassen924ba962022-01-31 02:55:18 +0100673 val = parse_object(js, ti, &j);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100674 break;
675 case JSMN_ARRAY:
Nikias Bassen924ba962022-01-31 02:55:18 +0100676 val = parse_array(js, ti, &j);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100677 break;
678 case JSMN_STRING:
Nikias Bassen924ba962022-01-31 02:55:18 +0100679 val = parse_string(js, ti, &j);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100680 break;
681 case JSMN_PRIMITIVE:
Nikias Bassen924ba962022-01-31 02:55:18 +0100682 val = parse_primitive(js, ti, &j);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100683 break;
684 default:
685 break;
686 }
687 if (val) {
688 plist_array_append_item(arr, val);
Nikias Bassenea893312022-01-28 23:45:56 +0100689 } else {
690 plist_free(arr);
691 return NULL;
Nikias Bassen429cbc62021-12-23 03:09:07 +0100692 }
693 }
694 *(index) = j;
695 return arr;
696}
697
Nikias Bassen924ba962022-01-31 02:55:18 +0100698static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index)
Nikias Bassen429cbc62021-12-23 03:09:07 +0100699{
Nikias Bassen924ba962022-01-31 02:55:18 +0100700 if (ti->tokens[*index].type != JSMN_OBJECT) {
Nikias Bassen429cbc62021-12-23 03:09:07 +0100701 PLIST_JSON_ERR("%s: token type != JSMN_OBJECT\n", __func__);
702 return NULL;
703 }
Nikias Bassen924ba962022-01-31 02:55:18 +0100704 int num_tokens = ti->tokens[*index].size;
Nikias Bassen429cbc62021-12-23 03:09:07 +0100705 int num;
706 int j = (*index)+1;
Nikias Bassenbf44ba82022-02-03 00:37:12 +0100707 if (num_tokens % 2 != 0) {
708 PLIST_JSON_ERR("%s: number of children must be even\n", __func__);
709 return NULL;
710 }
711 plist_t obj = plist_new_dict();
Nikias Bassen429cbc62021-12-23 03:09:07 +0100712 for (num = 0; num < num_tokens; num++) {
Nikias Bassenbf44ba82022-02-03 00:37:12 +0100713 if (j+1 >= ti->count) {
Nikias Bassen924ba962022-01-31 02:55:18 +0100714 PLIST_JSON_ERR("%s: token index out of valid range\n", __func__);
Nikias Bassend7758c02022-02-02 04:45:40 +0100715 plist_free(obj);
Nikias Bassen924ba962022-01-31 02:55:18 +0100716 return NULL;
717 }
718 if (ti->tokens[j].type == JSMN_STRING) {
719 char* key = unescape_string(js + ti->tokens[j].start, ti->tokens[j].end - ti->tokens[j].start, NULL);
Nikias Bassen088cdab2022-01-28 22:11:00 +0100720 if (!key) {
Nikias Bassenea893312022-01-28 23:45:56 +0100721 plist_free(obj);
Nikias Bassen088cdab2022-01-28 22:11:00 +0100722 return NULL;
723 }
Nikias Bassen429cbc62021-12-23 03:09:07 +0100724 plist_t val = NULL;
725 j++;
726 num++;
Nikias Bassen924ba962022-01-31 02:55:18 +0100727 switch (ti->tokens[j].type) {
Nikias Bassen429cbc62021-12-23 03:09:07 +0100728 case JSMN_OBJECT:
Nikias Bassen924ba962022-01-31 02:55:18 +0100729 val = parse_object(js, ti, &j);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100730 break;
731 case JSMN_ARRAY:
Nikias Bassen924ba962022-01-31 02:55:18 +0100732 val = parse_array(js, ti, &j);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100733 break;
734 case JSMN_STRING:
Nikias Bassen924ba962022-01-31 02:55:18 +0100735 val = parse_string(js, ti, &j);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100736 break;
737 case JSMN_PRIMITIVE:
Nikias Bassen924ba962022-01-31 02:55:18 +0100738 val = parse_primitive(js, ti, &j);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100739 break;
740 default:
741 break;
742 }
743 if (val) {
744 plist_dict_set_item(obj, key, val);
Nikias Bassen924ba962022-01-31 02:55:18 +0100745 } else {
Nikias Bassendb4635a2022-01-31 23:53:50 +0100746 free(key);
Nikias Bassen924ba962022-01-31 02:55:18 +0100747 plist_free(obj);
748 return NULL;
Nikias Bassen429cbc62021-12-23 03:09:07 +0100749 }
750 free(key);
751 } else {
752 PLIST_JSON_ERR("%s: keys must be of type STRING\n", __func__);
Nikias Bassenea893312022-01-28 23:45:56 +0100753 plist_free(obj);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100754 return NULL;
755 }
756 }
757 (*index) = j;
758 return obj;
759}
760
761PLIST_API int plist_from_json(const char *json, uint32_t length, plist_t * plist)
762{
763 if (!plist) {
764 return PLIST_ERR_INVALID_ARG;
765 }
766 *plist = NULL;
767 if (!json || (length == 0)) {
768 return PLIST_ERR_INVALID_ARG;
769 }
770
771 jsmn_parser parser;
772 jsmn_init(&parser);
773 int maxtoks = 256;
Nikias Bassen31a353b2022-01-25 18:15:46 +0100774 int curtoks = 0;
Nikias Bassen429cbc62021-12-23 03:09:07 +0100775 int r = 0;
776 jsmntok_t *tokens = NULL;
777
778 do {
779 jsmntok_t* newtokens = realloc(tokens, sizeof(jsmntok_t)*maxtoks);
780 if (!newtokens) {
781 PLIST_JSON_ERR("%s: Out of memory\n", __func__);
782 return PLIST_ERR_NO_MEM;
783 }
Nikias Bassen31a353b2022-01-25 18:15:46 +0100784 memset((unsigned char*)newtokens + sizeof(jsmntok_t)*curtoks, '\0', sizeof(jsmntok_t)*(maxtoks-curtoks));
785 tokens = newtokens;
786 curtoks = maxtoks;
Nikias Bassen429cbc62021-12-23 03:09:07 +0100787
Nikias Bassena22f0f52021-12-24 02:49:56 +0100788 r = jsmn_parse(&parser, json, length, tokens, maxtoks);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100789 if (r == JSMN_ERROR_NOMEM) {
790 maxtoks+=16;
791 continue;
Nikias Bassen31a353b2022-01-25 18:15:46 +0100792 }
793 } while (r == JSMN_ERROR_NOMEM);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100794
795 switch(r) {
796 case JSMN_ERROR_NOMEM:
797 PLIST_JSON_ERR("%s: Out of memory...\n", __func__);
798 free(tokens);
799 return PLIST_ERR_NO_MEM;
800 case JSMN_ERROR_INVAL:
801 PLIST_JSON_ERR("%s: Invalid character inside JSON string\n", __func__);
802 free(tokens);
803 return PLIST_ERR_PARSE;
804 case JSMN_ERROR_PART:
805 PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__);
806 free(tokens);
807 return PLIST_ERR_PARSE;
808 default:
809 break;
810 }
811
812 int startindex = 0;
Nikias Bassen924ba962022-01-31 02:55:18 +0100813 jsmntok_info_t ti = { tokens, parser.toknext };
Nikias Bassen429cbc62021-12-23 03:09:07 +0100814 switch (tokens[startindex].type) {
815 case JSMN_PRIMITIVE:
Nikias Bassen924ba962022-01-31 02:55:18 +0100816 *plist = parse_primitive(json, &ti, &startindex);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100817 break;
818 case JSMN_STRING:
Nikias Bassen924ba962022-01-31 02:55:18 +0100819 *plist = parse_string(json, &ti, &startindex);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100820 break;
821 case JSMN_ARRAY:
Nikias Bassen924ba962022-01-31 02:55:18 +0100822 *plist = parse_array(json, &ti, &startindex);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100823 break;
824 case JSMN_OBJECT:
Nikias Bassen924ba962022-01-31 02:55:18 +0100825 *plist = parse_object(json, &ti, &startindex);
Nikias Bassen429cbc62021-12-23 03:09:07 +0100826 break;
827 default:
828 break;
829 }
830 free(tokens);
831 return PLIST_ERR_SUCCESS;
832}