summaryrefslogtreecommitdiffstats
path: root/src/jplist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/jplist.c')
-rw-r--r--src/jplist.c695
1 files changed, 695 insertions, 0 deletions
diff --git a/src/jplist.c b/src/jplist.c
new file mode 100644
index 0000000..08441c0
--- /dev/null
+++ b/src/jplist.c
@@ -0,0 +1,695 @@
1/*
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
68static size_t dtostr(char *buf, size_t bufsize, double realval)
69{
70 size_t len = 0;
71 if (isnan(realval)) {
72 len = snprintf(buf, bufsize, "nan");
73 } else if (isinf(realval)) {
74 len = snprintf(buf, bufsize, "%cinfinity", (realval > 0.0) ? '+' : '-');
75 } else if (realval == 0.0f) {
76 len = snprintf(buf, bufsize, "0.0");
77 } else {
78 size_t i = 0;
79 len = snprintf(buf, bufsize, "%.*g", 17, realval);
80 for (i = 0; buf && i < len; i++) {
81 if (buf[i] == ',') {
82 buf[i] = '.';
83 break;
84 } else if (buf[i] == '.') {
85 break;
86 }
87 }
88 }
89 return len;
90}
91
92static int node_to_json(node_t* node, bytearray_t **outbuf, uint32_t depth, int prettify)
93{
94 plist_data_t node_data = NULL;
95
96 char *val = NULL;
97 size_t val_len = 0;
98
99 uint32_t i = 0;
100
101 if (!node)
102 return PLIST_ERR_INVALID_ARG;
103
104 node_data = plist_get_data(node);
105
106 switch (node_data->type)
107 {
108 case PLIST_BOOLEAN:
109 {
110 if (node_data->boolval) {
111 str_buf_append(*outbuf, "true", 4);
112 } else {
113 str_buf_append(*outbuf, "false", 5);
114 }
115 }
116 break;
117
118 case PLIST_NULL:
119 str_buf_append(*outbuf, "null", 4);
120 break;
121
122 case PLIST_UINT:
123 val = (char*)malloc(64);
124 if (node_data->length == 16) {
125 val_len = snprintf(val, 64, "%"PRIu64, node_data->intval);
126 } else {
127 val_len = snprintf(val, 64, "%"PRIi64, node_data->intval);
128 }
129 str_buf_append(*outbuf, val, val_len);
130 free(val);
131 break;
132
133 case PLIST_REAL:
134 val = (char*)malloc(64);
135 val_len = dtostr(val, 64, node_data->realval);
136 str_buf_append(*outbuf, val, val_len);
137 free(val);
138 break;
139
140 case PLIST_STRING:
141 case PLIST_KEY: {
142 size_t j = 0;
143 size_t len = 0;
144 off_t start = 0;
145 off_t cur = 0;
146
147 str_buf_append(*outbuf, "\"", 1);
148
149 len = node_data->length;
150 for (j = 0; j < len; j++) {
151 switch (node_data->strval[j]) {
152 case '"':
153 str_buf_append(*outbuf, node_data->strval + start, cur - start);
154 str_buf_append(*outbuf, "\\\"", 2);
155 start = cur+1;
156 break;
157 default:
158 break;
159 }
160 cur++;
161 }
162 str_buf_append(*outbuf, node_data->strval + start, cur - start);
163
164 str_buf_append(*outbuf, "\"", 1);
165 } break;
166
167 case PLIST_ARRAY: {
168 str_buf_append(*outbuf, "[", 1);
169 node_t *ch;
170 uint32_t cnt = 0;
171 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
172 if (cnt > 0) {
173 str_buf_append(*outbuf, ",", 1);
174 }
175 if (prettify) {
176 str_buf_append(*outbuf, "\n", 1);
177 for (i = 0; i <= depth; i++) {
178 str_buf_append(*outbuf, " ", 2);
179 }
180 }
181 int res = node_to_json(ch, outbuf, depth+1, prettify);
182 if (res < 0) {
183 return res;
184 }
185 cnt++;
186 }
187 if (cnt > 0 && prettify) {
188 str_buf_append(*outbuf, "\n", 1);
189 for (i = 0; i < depth; i++) {
190 str_buf_append(*outbuf, " ", 2);
191 }
192 }
193 str_buf_append(*outbuf, "]", 1);
194 } break;
195 case PLIST_DICT: {
196 str_buf_append(*outbuf, "{", 1);
197 node_t *ch;
198 uint32_t cnt = 0;
199 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
200 if (cnt > 0 && cnt % 2 == 0) {
201 str_buf_append(*outbuf, ",", 1);
202 }
203 if (cnt % 2 == 0 && prettify) {
204 str_buf_append(*outbuf, "\n", 1);
205 for (i = 0; i <= depth; i++) {
206 str_buf_append(*outbuf, " ", 2);
207 }
208 }
209 int res = node_to_json(ch, outbuf, depth+1, prettify);
210 if (res < 0) {
211 return res;
212 }
213 if (cnt % 2 == 0) {
214 str_buf_append(*outbuf, ":", 1);
215 if (prettify) {
216 str_buf_append(*outbuf, " ", 1);
217 }
218 }
219 cnt++;
220 }
221 if (cnt > 0 && prettify) {
222 str_buf_append(*outbuf, "\n", 1);
223 for (i = 0; i < depth; i++) {
224 str_buf_append(*outbuf, " ", 2);
225 }
226 }
227 str_buf_append(*outbuf, "}", 1);
228 } break;
229 case PLIST_DATA:
230 // NOT VALID FOR JSON
231 PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n");
232 return PLIST_ERR_FORMAT;
233 case PLIST_DATE:
234 // NOT VALID FOR JSON
235 PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n");
236 return PLIST_ERR_FORMAT;
237 case PLIST_UID:
238 // NOT VALID FOR JSON
239 PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n");
240 return PLIST_ERR_FORMAT;
241 default:
242 return PLIST_ERR_UNKNOWN;
243 }
244
245 return PLIST_ERR_SUCCESS;
246}
247
248#define PO10i_LIMIT (INT64_MAX/10)
249
250/* based on https://stackoverflow.com/a/4143288 */
251static int num_digits_i(int64_t i)
252{
253 int n;
254 int64_t po10;
255 n=1;
256 if (i < 0) {
257 i = -i;
258 n++;
259 }
260 po10=10;
261 while (i>=po10) {
262 n++;
263 if (po10 > PO10i_LIMIT) break;
264 po10*=10;
265 }
266 return n;
267}
268
269#define PO10u_LIMIT (UINT64_MAX/10)
270
271/* based on https://stackoverflow.com/a/4143288 */
272static int num_digits_u(uint64_t i)
273{
274 int n;
275 uint64_t po10;
276 n=1;
277 po10=10;
278 while (i>=po10) {
279 n++;
280 if (po10 > PO10u_LIMIT) break;
281 po10*=10;
282 }
283 return n;
284}
285
286static int node_estimate_size(node_t *node, uint64_t *size, uint32_t depth, int prettify)
287{
288 plist_data_t data;
289 if (!node) {
290 return PLIST_ERR_INVALID_ARG;
291 }
292 data = plist_get_data(node);
293 if (node->children) {
294 node_t *ch;
295 unsigned int n_children = node_n_children(node);
296 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
297 int res = node_estimate_size(ch, size, depth + 1, prettify);
298 if (res < 0) {
299 return res;
300 }
301 }
302 switch (data->type) {
303 case PLIST_DICT:
304 *size += 2; // '{' and '}'
305 *size += n_children-1; // number of ':' and ','
306 if (prettify) {
307 *size += n_children; // number of '\n' and extra space
308 *size += n_children * (depth+1); // indent for every 2nd child
309 *size += 1; // additional '\n'
310 }
311 break;
312 case PLIST_ARRAY:
313 *size += 2; // '[' and ']'
314 *size += n_children-1; // number of ','
315 if (prettify) {
316 *size += n_children; // number of '\n'
317 *size += n_children * ((depth+1)<<1); // indent for every child
318 *size += 1; // additional '\n'
319 }
320 break;
321 default:
322 break;
323 }
324 if (prettify)
325 *size += (depth << 1); // indent for {} and []
326 } else {
327 switch (data->type) {
328 case PLIST_STRING:
329 case PLIST_KEY:
330 *size += data->length;
331 *size += 2;
332 break;
333 case PLIST_UINT:
334 if (data->length == 16) {
335 *size += num_digits_u(data->intval);
336 } else {
337 *size += num_digits_i((int64_t)data->intval);
338 }
339 break;
340 case PLIST_REAL:
341 *size += dtostr(NULL, 0, data->realval);
342 break;
343 case PLIST_BOOLEAN:
344 *size += ((data->boolval) ? 4 : 5);
345 break;
346 case PLIST_DICT:
347 case PLIST_ARRAY:
348 *size += 2;
349 break;
350 case PLIST_DATA:
351 // NOT VALID FOR JSON
352 PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n");
353 return PLIST_ERR_FORMAT;
354 case PLIST_DATE:
355 // NOT VALID FOR JSON
356 PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n");
357 return PLIST_ERR_FORMAT;
358 case PLIST_UID:
359 // NOT VALID FOR JSON
360 PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n");
361 return PLIST_ERR_FORMAT;
362 default:
363 PLIST_JSON_WRITE_ERR("invalid node type encountered\n");
364 return PLIST_ERR_UNKNOWN;
365 }
366 }
367 return PLIST_ERR_SUCCESS;
368}
369
370PLIST_API int plist_to_json(plist_t plist, char **json, uint32_t* length, int prettify)
371{
372 uint64_t size = 0;
373 int res;
374
375 if (!plist || !json || !length) {
376 return PLIST_ERR_INVALID_ARG;
377 }
378
379 res = node_estimate_size(plist, &size, 0, prettify);
380 if (res < 0) {
381 return res;
382 }
383
384 strbuf_t *outbuf = str_buf_new(size);
385 if (!outbuf) {
386 PLIST_JSON_WRITE_ERR("Could not allocate output buffer");
387 return PLIST_ERR_NO_MEM;
388 }
389
390 res = node_to_json(plist, &outbuf, 0, prettify);
391 if (res < 0) {
392 str_buf_free(outbuf);
393 *json = NULL;
394 *length = 0;
395 return res;
396 }
397
398 str_buf_append(outbuf, "\0", 1);
399
400 *json = outbuf->data;
401 *length = outbuf->len - 1;
402
403 outbuf->data = NULL;
404 str_buf_free(outbuf);
405
406 return PLIST_ERR_SUCCESS;
407}
408
409static plist_t parse_primitive(const char* js, jsmntok_t* tokens, int* index)
410{
411 if (tokens[*index].type != JSMN_PRIMITIVE) {
412 PLIST_JSON_ERR("%s: token type != JSMN_PRIMITIVE\n", __func__);
413 return NULL;
414 }
415 plist_t val = NULL;
416 const char* str_val = js + tokens[*index].start;
417 const char* str_end = js + tokens[*index].end;
418 size_t str_len = tokens[*index].end - tokens[*index].start;
419 if (!strncmp("false", str_val, str_len)) {
420 val = plist_new_bool(0);
421 } else if (!strncmp("true", str_val, str_len)) {
422 val = plist_new_bool(1);
423 } else if (!strncmp("null", str_val, str_len)) {
424 plist_data_t data = plist_new_plist_data();
425 data->type = PLIST_NULL;
426 val = plist_new_node(data);
427 } else if (str_val[0] == '-' || isdigit(str_val[0])) {
428 char* endp = NULL;
429 long long intpart = strtol(str_val, &endp, 10);
430 if (endp >= str_end) {
431 /* integer */
432 val = plist_new_uint((uint64_t)intpart);
433 } else if (*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) {
434 /* float */
435 char* fendp = endp+1;
436 while (isdigit(*fendp) && fendp < str_end) fendp++;
437 if ((fendp > endp+1 && fendp >= str_end) || (fendp+2 < str_end && (*fendp == 'e' || *fendp == 'E') && (*(fendp+1) == '+' || *(fendp+1) == '-') && isdigit(*(fendp+2)))) {
438 double dval = atof(str_val);
439 val = plist_new_real(dval);
440 } else {
441 PLIST_JSON_ERR("%s: invalid character at offset %d when parsing floating point value\n", __func__, (int)(fendp - js));
442 }
443 } else {
444 PLIST_JSON_ERR("%s: invalid character at offset %d when parsing numerical value\n", __func__, (int)(endp - js));
445 }
446 } else {
447 PLIST_JSON_ERR("%s: invalid primitive value '%.*s' encountered\n", __func__, (int)str_len, str_val);
448 }
449 (*index)++;
450 return val;
451}
452
453static plist_t parse_string(const char* js, jsmntok_t* tokens, int* index)
454{
455 if (tokens[*index].type != JSMN_STRING) {
456 PLIST_JSON_ERR("%s: token type != JSMN_STRING\n", __func__);
457 return NULL;
458 }
459
460 const char* str_val = js + tokens[*index].start;
461 size_t str_len = tokens[*index].end - tokens[*index].start;
462 char* strval = strndup(str_val, str_len);
463 plist_t node;
464
465 /* unescape */
466 size_t i = 0;
467 while (i < str_len) {
468 if (strval[i] == '\\' && i < str_len-1) {
469 switch (strval[i+1]) {
470 case '\"': case '/' : case '\\' : case 'b' :
471 case 'f' : case 'r' : case 'n' : case 't' :
472 memmove(strval+i, strval+i+1, str_len - (i+1));
473 str_len--;
474 switch (strval[i]) {
475 case 'b':
476 strval[i] = '\b';
477 break;
478 case 'f':
479 strval[i] = '\f';
480 break;
481 case 'r':
482 strval[i] = '\r';
483 break;
484 case 'n':
485 strval[i] = '\n';
486 break;
487 case 't':
488 strval[i] = '\t';
489 break;
490 default:
491 break;
492 }
493 break;
494 case 'u': {
495 unsigned int val = 0;
496 if (str_len-(i+2) < 4) {
497 free(strval);
498 PLIST_JSON_ERR("%s: invalid escape sequence '%s' (too short)\n", __func__, strval+i);
499 return NULL;
500 }
501 if (!(isxdigit(strval[i+2]) && isxdigit(strval[i+3]) && isxdigit(strval[i+4]) && isxdigit(strval[i+5])) || sscanf(strval+i+2, "%04x", &val) != 1) {
502 free(strval);
503 PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 6, strval+i);
504 return NULL;
505 }
506 int bytelen = 0;
507 if (val >= 0x800) {
508 /* three bytes */
509 strval[i] = (char)(0xE0 + ((val >> 12) & 0xF));
510 strval[i+1] = (char)(0x80 + ((val >> 6) & 0x3F));
511 strval[i+2] = (char)(0x80 + (val & 0x3F));
512 bytelen = 3;
513 } else if (val >= 0x80) {
514 /* two bytes */
515 strval[i] = (char)(0xC0 + ((val >> 6) & 0x1F));
516 strval[i+1] = (char)(0x80 + (val & 0x3F));
517 bytelen = 2;
518 } else {
519 /* one byte */
520 strval[i] = (char)(val & 0x7F);
521 bytelen = 1;
522 }
523 memmove(strval+i+bytelen, strval+i+6, str_len - (i+5));
524 str_len -= (6-bytelen);
525 } break;
526 default:
527 PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 2, strval+i);
528 free(strval);
529 return NULL;
530 }
531 }
532 i++;
533 }
534
535 plist_data_t data = plist_new_plist_data();
536 data->type = PLIST_STRING;
537 data->strval = strval;
538 data->length = str_len;
539 node = plist_new_node(data);
540
541 (*index)++;
542 return node;
543}
544
545static plist_t parse_object(const char* js, jsmntok_t* tokens, int* index);
546
547static plist_t parse_array(const char* js, jsmntok_t* tokens, int* index)
548{
549 if (tokens[*index].type != JSMN_ARRAY) {
550 PLIST_JSON_ERR("%s: token type != JSMN_ARRAY\n", __func__);
551 return NULL;
552 }
553 plist_t arr = plist_new_array();
554 int num_tokens = tokens[*index].size;
555 int num;
556 int j = (*index)+1;
557 for (num = 0; num < num_tokens; num++) {
558 plist_t val = NULL;
559 switch (tokens[j].type) {
560 case JSMN_OBJECT:
561 val = parse_object(js, tokens, &j);
562 break;
563 case JSMN_ARRAY:
564 val = parse_array(js, tokens, &j);
565 break;
566 case JSMN_STRING:
567 val = parse_string(js, tokens, &j);
568 break;
569 case JSMN_PRIMITIVE:
570 val = parse_primitive(js, tokens, &j);
571 break;
572 default:
573 break;
574 }
575 if (val) {
576 plist_array_append_item(arr, val);
577 }
578 }
579 *(index) = j;
580 return arr;
581}
582
583static plist_t parse_object(const char* js, jsmntok_t* tokens, int* index)
584{
585 if (tokens[*index].type != JSMN_OBJECT) {
586 PLIST_JSON_ERR("%s: token type != JSMN_OBJECT\n", __func__);
587 return NULL;
588 }
589 plist_t obj = plist_new_dict();
590 int num_tokens = tokens[*index].size;
591 int num;
592 int j = (*index)+1;
593 for (num = 0; num < num_tokens; num++) {
594 if (tokens[j].type == JSMN_STRING) {
595 char* key = strndup(js + tokens[j].start, tokens[j].end - tokens[j].start);
596 plist_t val = NULL;
597 j++;
598 num++;
599 switch (tokens[j].type) {
600 case JSMN_OBJECT:
601 val = parse_object(js, tokens, &j);
602 break;
603 case JSMN_ARRAY:
604 val = parse_array(js, tokens, &j);
605 break;
606 case JSMN_STRING:
607 val = parse_string(js, tokens, &j);
608 break;
609 case JSMN_PRIMITIVE:
610 val = parse_primitive(js, tokens, &j);
611 break;
612 default:
613 break;
614 }
615 if (val) {
616 plist_dict_set_item(obj, key, val);
617 }
618 free(key);
619 } else {
620 PLIST_JSON_ERR("%s: keys must be of type STRING\n", __func__);
621 return NULL;
622 }
623 }
624 (*index) = j;
625 return obj;
626}
627
628PLIST_API int plist_from_json(const char *json, uint32_t length, plist_t * plist)
629{
630 if (!plist) {
631 return PLIST_ERR_INVALID_ARG;
632 }
633 *plist = NULL;
634 if (!json || (length == 0)) {
635 return PLIST_ERR_INVALID_ARG;
636 }
637
638 jsmn_parser parser;
639 jsmn_init(&parser);
640 int maxtoks = 256;
641 int r = 0;
642 jsmntok_t *tokens = NULL;
643
644 do {
645 jsmntok_t* newtokens = realloc(tokens, sizeof(jsmntok_t)*maxtoks);
646 if (!newtokens) {
647 PLIST_JSON_ERR("%s: Out of memory\n", __func__);
648 return PLIST_ERR_NO_MEM;
649 }
650 tokens = newtokens;
651
652 r = jsmn_parse(&parser, json, tokens, maxtoks);
653 if (r == JSMN_ERROR_NOMEM) {
654 maxtoks+=16;
655 continue;
656 }
657 } while (0);
658
659 switch(r) {
660 case JSMN_ERROR_NOMEM:
661 PLIST_JSON_ERR("%s: Out of memory...\n", __func__);
662 free(tokens);
663 return PLIST_ERR_NO_MEM;
664 case JSMN_ERROR_INVAL:
665 PLIST_JSON_ERR("%s: Invalid character inside JSON string\n", __func__);
666 free(tokens);
667 return PLIST_ERR_PARSE;
668 case JSMN_ERROR_PART:
669 PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__);
670 free(tokens);
671 return PLIST_ERR_PARSE;
672 default:
673 break;
674 }
675
676 int startindex = 0;
677 switch (tokens[startindex].type) {
678 case JSMN_PRIMITIVE:
679 *plist = parse_primitive(json, tokens, &startindex);
680 break;
681 case JSMN_STRING:
682 *plist = parse_string(json, tokens, &startindex);
683 break;
684 case JSMN_ARRAY:
685 *plist = parse_array(json, tokens, &startindex);
686 break;
687 case JSMN_OBJECT:
688 *plist = parse_object(json, tokens, &startindex);
689 break;
690 default:
691 break;
692 }
693 free(tokens);
694 return PLIST_ERR_SUCCESS;
695}