Coverage Report

Created: 2026-07-16 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libplist/src/jplist.c
Line
Count
Source
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
38
#include "plist.h"
39
#include "strbuf.h"
40
#include "jsmn.h"
41
#include "hashtable.h"
42
#include "base64.h"
43
#include "time64.h"
44
#include "common.h"
45
46
#ifdef DEBUG
47
static int plist_json_debug = 0;
48
0
#define PLIST_JSON_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonparser] ERROR: " __VA_ARGS__); }
49
0
#define PLIST_JSON_WRITE_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonwriter] ERROR: " __VA_ARGS__); }
50
#else
51
#define PLIST_JSON_ERR(...)
52
#define PLIST_JSON_WRITE_ERR(...)
53
#endif
54
55
void plist_json_init(void)
56
2
{
57
    /* init JSON stuff */
58
2
#ifdef DEBUG
59
2
    char *env_debug = getenv("PLIST_JSON_DEBUG");
60
2
    if (env_debug && !strcmp(env_debug, "1")) {
61
0
        plist_json_debug = 1;
62
0
    }
63
2
#endif
64
2
}
65
66
void plist_json_deinit(void)
67
0
{
68
    /* deinit JSON stuff */
69
0
}
70
71
void plist_json_set_debug(int debug)
72
0
{
73
0
#ifdef DEBUG
74
0
    plist_json_debug = debug;
75
0
#endif
76
0
}
77
78
#ifndef HAVE_STRNDUP
79
#ifndef _MSC_VER
80
#pragma GCC diagnostic push
81
#pragma GCC diagnostic ignored "-Wshadow"
82
#endif
83
static char* strndup(const char* str, size_t len)
84
{
85
    char *newstr = (char *)malloc(len+1);
86
    if (newstr) {
87
        strncpy(newstr, str, len);
88
        newstr[len]= '\0';
89
    }
90
    return newstr;
91
}
92
#ifndef _MSC_VER
93
#pragma GCC diagnostic pop
94
#endif
95
#endif
96
97
static plist_err_t node_to_json(node_t node, bytearray_t **outbuf, uint32_t depth, int prettify, int coerce)
98
0
{
99
0
    plist_data_t node_data = NULL;
100
101
0
    char *val = NULL;
102
0
    size_t val_len = 0;
103
104
0
    uint32_t i = 0;
105
106
0
    if (!node)
107
0
        return PLIST_ERR_INVALID_ARG;
108
109
0
    node_data = plist_get_data(node);
110
111
0
    switch (node_data->type)
112
0
    {
113
0
    case PLIST_BOOLEAN:
114
0
    {
115
0
        if (node_data->boolval) {
116
0
            str_buf_append(*outbuf, "true", 4);
117
0
        } else {
118
0
            str_buf_append(*outbuf, "false", 5);
119
0
        }
120
0
    }
121
0
    break;
122
123
0
    case PLIST_NULL:
124
0
        str_buf_append(*outbuf, "null", 4);
125
0
  break;
126
127
0
    case PLIST_INT:
128
0
        val = (char*)malloc(64);
129
0
        if (node_data->length == 16) {
130
0
            val_len = snprintf(val, 64, "%" PRIu64, node_data->intval);
131
0
        } else {
132
0
            val_len = snprintf(val, 64, "%" PRIi64, node_data->intval);
133
0
        }
134
0
        str_buf_append(*outbuf, val, val_len);
135
0
        free(val);
136
0
        break;
137
138
0
    case PLIST_REAL:
139
0
        val = (char*)malloc(64);
140
0
        val_len = dtostr(val, 64, node_data->realval);
141
0
        str_buf_append(*outbuf, val, val_len);
142
0
        free(val);
143
0
        break;
144
145
0
    case PLIST_STRING:
146
0
    case PLIST_KEY: {
147
0
        const char *charmap[32] = {
148
0
            "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007",
149
0
            "\\b",     "\\t",     "\\n",     "\\u000b", "\\f",     "\\r",     "\\u000e", "\\u000f",
150
0
            "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017",
151
0
            "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f",
152
0
        };
153
0
        size_t j = 0;
154
0
        size_t len = 0;
155
0
        off_t start = 0;
156
0
        off_t cur = 0;
157
158
0
        str_buf_append(*outbuf, "\"", 1);
159
160
0
        len = node_data->length;
161
0
        for (j = 0; j < len; j++) {
162
0
            unsigned char ch = (unsigned char)node_data->strval[j];
163
0
            if (ch < 0x20) {
164
0
                str_buf_append(*outbuf, node_data->strval + start, cur - start);
165
0
                str_buf_append(*outbuf, charmap[ch], (charmap[ch][1] == 'u') ? 6 : 2);
166
0
                start = cur+1;
167
0
            } else if (ch == '"') {
168
0
                str_buf_append(*outbuf, node_data->strval + start, cur - start);
169
0
                str_buf_append(*outbuf, "\\\"", 2);
170
0
                start = cur+1;
171
0
            }
172
0
            cur++;
173
0
        }
174
0
        str_buf_append(*outbuf, node_data->strval + start, cur - start);
175
176
0
        str_buf_append(*outbuf, "\"", 1);
177
0
        } break;
178
179
0
    case PLIST_ARRAY: {
180
0
        str_buf_append(*outbuf, "[", 1);
181
0
        node_t ch;
182
0
        uint32_t cnt = 0;
183
0
        for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
184
0
            if (cnt > 0) {
185
0
                str_buf_append(*outbuf, ",", 1);
186
0
            }
187
0
            if (prettify) {
188
0
                str_buf_append(*outbuf, "\n", 1);
189
0
                for (i = 0; i <= depth; i++) {
190
0
                    str_buf_append(*outbuf, "  ", 2);
191
0
                }
192
0
            }
193
0
            plist_err_t res = node_to_json(ch, outbuf, depth+1, prettify, coerce);
194
0
            if (res < 0) {
195
0
                return res;
196
0
            }
197
0
            cnt++;
198
0
        }
199
0
        if (cnt > 0 && prettify) {
200
0
            str_buf_append(*outbuf, "\n", 1);
201
0
            for (i = 0; i < depth; i++) {
202
0
                str_buf_append(*outbuf, "  ", 2);
203
0
            }
204
0
        }
205
0
        str_buf_append(*outbuf, "]", 1);
206
0
        } break;
207
0
    case PLIST_DICT: {
208
0
        str_buf_append(*outbuf, "{", 1);
209
0
        node_t ch;
210
0
        uint32_t cnt = 0;
211
0
        for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
212
0
            if (cnt > 0 && cnt % 2 == 0) {
213
0
                str_buf_append(*outbuf, ",", 1);
214
0
            }
215
0
            if (cnt % 2 == 0 && prettify) {
216
0
                str_buf_append(*outbuf, "\n", 1);
217
0
                for (i = 0; i <= depth; i++) {
218
0
                    str_buf_append(*outbuf, "  ", 2);
219
0
                }
220
0
            }
221
0
            plist_err_t res = node_to_json(ch, outbuf, depth+1, prettify, coerce);
222
0
            if (res < 0) {
223
0
                return res;
224
0
            }
225
0
            if (cnt % 2 == 0) {
226
0
                str_buf_append(*outbuf, ":", 1);
227
0
                if (prettify) {
228
0
                  str_buf_append(*outbuf, " ", 1);
229
0
                }
230
0
            }
231
0
            cnt++;
232
0
        }
233
0
        if (cnt > 0 && prettify) {
234
0
            str_buf_append(*outbuf, "\n", 1);
235
0
            for (i = 0; i < depth; i++) {
236
0
                str_buf_append(*outbuf, "  ", 2);
237
0
            }
238
0
        }
239
0
        str_buf_append(*outbuf, "}", 1);
240
0
        } break;
241
0
    case PLIST_DATA:
242
0
        if (coerce) {
243
0
            size_t b64_len = ((node_data->length + 2) / 3) * 4;
244
0
            char *b64_buf = (char*)malloc(b64_len + 1);
245
0
            if (!b64_buf) {
246
0
                return PLIST_ERR_NO_MEM;
247
0
            }
248
0
            size_t actual_len = base64encode(b64_buf, node_data->buff, node_data->length);
249
0
            str_buf_append(*outbuf, "\"", 1);
250
0
            str_buf_append(*outbuf, b64_buf, actual_len);
251
0
            str_buf_append(*outbuf, "\"", 1);
252
0
            free(b64_buf);
253
0
        } else {
254
0
            PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n");
255
0
            return PLIST_ERR_FORMAT;
256
0
        }
257
0
        break;
258
0
    case PLIST_DATE:
259
0
        if (coerce) {
260
0
            Time64_T timev;
261
0
            if (plist_real_to_time64(node_data->realval, &timev) < 0) {
262
0
                PLIST_JSON_WRITE_ERR("Encountered invalid date value %f\n", node_data->realval);
263
0
                return PLIST_ERR_INVALID_ARG;
264
0
            }
265
0
            struct TM _btime;
266
0
            struct TM *btime = gmtime64_r(&timev, &_btime);
267
0
            char datebuf[32];
268
0
            size_t datelen = 0;
269
0
            if (btime) {
270
0
                struct tm _tmcopy;
271
0
                copy_TM64_to_tm(btime, &_tmcopy);
272
0
                datelen = strftime(datebuf, sizeof(datebuf), "%Y-%m-%dT%H:%M:%SZ", &_tmcopy);
273
0
            }
274
0
            if (datelen <= 0) {
275
0
                datelen = snprintf(datebuf, sizeof(datebuf), "1970-01-01T00:00:00Z");
276
0
            }
277
0
            str_buf_append(*outbuf, "\"", 1);
278
0
            str_buf_append(*outbuf, datebuf, datelen);
279
0
            str_buf_append(*outbuf, "\"", 1);
280
0
        } else {
281
0
            PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n");
282
0
            return PLIST_ERR_FORMAT;
283
0
        }
284
0
        break;
285
0
    case PLIST_UID:
286
0
        if (coerce) {
287
0
            val = (char*)malloc(64);
288
0
            if (node_data->length == 16) {
289
0
                val_len = snprintf(val, 64, "%" PRIu64, node_data->intval);
290
0
            } else {
291
0
                val_len = snprintf(val, 64, "%" PRIi64, node_data->intval);
292
0
            }
293
0
            str_buf_append(*outbuf, val, val_len);
294
0
            free(val);
295
0
        } else {
296
0
            PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n");
297
0
            return PLIST_ERR_FORMAT;
298
0
        }
299
0
        break;
300
0
    default:
301
0
        return PLIST_ERR_UNKNOWN;
302
0
    }
303
304
0
    return PLIST_ERR_SUCCESS;
305
0
}
306
307
static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, int coerce, hashtable_t *visited)
308
0
{
309
0
    plist_data_t data;
310
0
    if (!node) {
311
0
        return PLIST_ERR_INVALID_ARG;
312
0
    }
313
314
0
    if (depth > PLIST_MAX_NESTING_DEPTH) {
315
0
        PLIST_JSON_WRITE_ERR("maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH);
316
0
        return PLIST_ERR_MAX_NESTING;
317
0
    }
318
319
0
    if (hash_table_lookup(visited, node)) {
320
0
        PLIST_JSON_WRITE_ERR("circular reference detected\n");
321
0
        return PLIST_ERR_CIRCULAR_REF;
322
0
    }
323
324
    // mark as visited
325
0
    hash_table_insert(visited, node, (void*)1);
326
327
0
    data = plist_get_data(node);
328
0
    if (node->children) {
329
0
        node_t ch;
330
0
        unsigned int n_children = node_n_children(node);
331
0
        for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
332
0
            plist_err_t res = _node_estimate_size(ch, size, depth + 1, prettify, coerce, visited);
333
0
            if (res < 0) {
334
0
                return res;
335
0
            }
336
0
        }
337
0
        switch (data->type) {
338
0
        case PLIST_DICT:
339
0
            *size += 2; // '{' and '}'
340
0
            *size += n_children-1; // number of ':' and ','
341
0
            if (prettify) {
342
0
                *size += n_children; // number of '\n' and extra space
343
0
                *size += (uint64_t)n_children * (depth+1); // indent for every 2nd child
344
0
                *size += 1; // additional '\n'
345
0
            }
346
0
            break;
347
0
        case PLIST_ARRAY:
348
0
            *size += 2; // '[' and ']'
349
0
            *size += n_children-1; // number of ','
350
0
            if (prettify) {
351
0
                *size += n_children; // number of '\n'
352
0
                *size += (uint64_t)n_children * ((depth+1)<<1); // indent for every child
353
0
                *size += 1; // additional '\n'
354
0
            }
355
0
            break;
356
0
        default:
357
0
            break;
358
0
  }
359
0
        if (prettify)
360
0
            *size += (depth << 1); // indent for {} and []
361
0
    } else {
362
0
        switch (data->type) {
363
0
        case PLIST_STRING:
364
0
        case PLIST_KEY:
365
0
            *size += data->length;
366
0
            *size += 2;
367
0
            break;
368
0
        case PLIST_INT:
369
0
            if (data->length == 16) {
370
0
                *size += num_digits_u(data->intval);
371
0
            } else {
372
0
                *size += num_digits_i((int64_t)data->intval);
373
0
            }
374
0
            break;
375
0
        case PLIST_REAL:
376
0
            *size += dtostr(NULL, 0, data->realval);
377
0
            break;
378
0
        case PLIST_BOOLEAN:
379
0
            *size += ((data->boolval) ? 4 : 5);
380
0
            break;
381
0
        case PLIST_NULL:
382
0
            *size += 4;
383
0
            break;
384
0
        case PLIST_DICT:
385
0
        case PLIST_ARRAY:
386
0
            *size += 2;
387
0
            break;
388
0
        case PLIST_DATA:
389
0
            if (coerce) {
390
                // base64 encoded string: 2 quotes + ((len+2)/3)*4 base64 chars
391
0
                *size += 2 + ((data->length + 2) / 3) * 4;
392
0
            } else {
393
0
                PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n");
394
0
                return PLIST_ERR_FORMAT;
395
0
            }
396
0
            break;
397
0
        case PLIST_DATE:
398
0
            if (coerce) {
399
                // ISO 8601 string: "YYYY-MM-DDTHH:MM:SSZ" = 22 chars max
400
0
                *size += 24;
401
0
            } else {
402
0
                PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n");
403
0
                return PLIST_ERR_FORMAT;
404
0
            }
405
0
            break;
406
0
        case PLIST_UID:
407
0
            if (coerce) {
408
                // integer representation
409
0
                if (data->length == 16) {
410
0
                    *size += num_digits_u(data->intval);
411
0
                } else {
412
0
                    *size += num_digits_i((int64_t)data->intval);
413
0
                }
414
0
            } else {
415
0
                PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n");
416
0
                return PLIST_ERR_FORMAT;
417
0
            }
418
0
            break;
419
0
        default:
420
0
            PLIST_JSON_WRITE_ERR("invalid node type encountered\n");
421
0
            return PLIST_ERR_UNKNOWN;
422
0
        }
423
0
    }
424
0
    return PLIST_ERR_SUCCESS;
425
0
}
426
427
static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, int coerce)
428
0
{
429
0
    hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
430
0
    if (!visited) return PLIST_ERR_NO_MEM;
431
0
    plist_err_t err = _node_estimate_size(node, size, depth, prettify, coerce, visited);
432
0
    hash_table_destroy(visited);
433
0
    return err;
434
0
}
435
436
plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify)
437
0
{
438
0
    plist_write_options_t opts = prettify ? PLIST_OPT_NONE : PLIST_OPT_COMPACT;
439
0
    return plist_to_json_with_options(plist, plist_json, length, opts);
440
0
}
441
442
plist_err_t plist_to_json_with_options(plist_t plist, char **plist_json, uint32_t* length, plist_write_options_t options)
443
0
{
444
0
    uint64_t size = 0;
445
0
    plist_err_t res;
446
447
0
    if (!plist || !plist_json || !length) {
448
0
        return PLIST_ERR_INVALID_ARG;
449
0
    }
450
451
0
    if (!PLIST_IS_DICT(plist) && !PLIST_IS_ARRAY(plist)) {
452
0
        PLIST_JSON_WRITE_ERR("plist data is not valid for JSON format\n");
453
0
        return PLIST_ERR_FORMAT;
454
0
    }
455
456
0
    int prettify = !(options & PLIST_OPT_COMPACT);
457
0
    int coerce = options & PLIST_OPT_COERCE;
458
459
0
    res = node_estimate_size((node_t)plist, &size, 0, prettify, coerce);
460
0
    if (res < 0) {
461
0
        return res;
462
0
    }
463
464
0
    strbuf_t *outbuf = str_buf_new(size);
465
0
    if (!outbuf) {
466
0
        PLIST_JSON_WRITE_ERR("Could not allocate output buffer\n");
467
0
        return PLIST_ERR_NO_MEM;
468
0
    }
469
470
0
    res = node_to_json((node_t)plist, &outbuf, 0, prettify, coerce);
471
0
    if (res < 0) {
472
0
        str_buf_free(outbuf);
473
0
        *plist_json = NULL;
474
0
        *length = 0;
475
0
        return res;
476
0
    }
477
0
    if (prettify) {
478
0
        str_buf_append(outbuf, "\n", 1);
479
0
    }
480
481
0
    str_buf_append(outbuf, "\0", 1);
482
483
0
    *plist_json = (char*)outbuf->data;
484
0
    *length = outbuf->len - 1;
485
486
0
    outbuf->data = NULL;
487
0
    str_buf_free(outbuf);
488
489
0
    return PLIST_ERR_SUCCESS;
490
0
}
491
492
typedef struct {
493
    jsmntok_t* tokens;
494
    int count;
495
    plist_err_t err;
496
} jsmntok_info_t;
497
498
static int64_t parse_decimal(const char* str, const char* str_end, char** endp)
499
0
{
500
0
    const uint64_t po10i_limit = INT64_MAX / 10;
501
0
    uint64_t MAX = INT64_MAX;
502
0
    uint64_t x = 0;
503
0
    int is_neg = 0;
504
0
    *endp = (char*)str;
505
506
0
    if (str[0] == '-') {
507
0
        is_neg = 1;
508
0
        (*endp)++;
509
0
    } else if (str[0] == '+') {
510
0
        (*endp)++;
511
0
    }
512
0
    if (is_neg) {
513
0
        MAX++;
514
0
    }
515
0
    while (*endp < str_end && isdigit(**endp)) {
516
0
        if (x > po10i_limit) {
517
0
            x = MAX;
518
0
            break;
519
0
        }
520
0
        x = x * 10;
521
0
        unsigned int add = (**endp - '0');
522
0
        if (x + add > MAX) {
523
0
            x = MAX;
524
0
            break;
525
0
        }
526
0
        x += add;
527
0
        (*endp)++;
528
0
    }
529
530
    // swallow the rest of the digits in case we dropped out early
531
0
    while (*endp < str_end && isdigit(**endp)) (*endp)++;
532
533
0
    int64_t result = x;
534
0
    if (is_neg) {
535
0
        if (x == MAX) {
536
0
            result = INT64_MIN;
537
0
        } else {
538
0
            result = -(int64_t)x;
539
0
        }
540
0
    }
541
0
    return result;
542
0
}
543
544
static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index)
545
0
{
546
0
    if (ti->tokens[*index].type != JSMN_PRIMITIVE) {
547
0
        PLIST_JSON_ERR("%s: token type != JSMN_PRIMITIVE\n", __func__);
548
0
        return NULL;
549
0
    }
550
0
    plist_t val = NULL;
551
0
    const char* str_val = js + ti->tokens[*index].start;
552
0
    const char* str_end = js + ti->tokens[*index].end;
553
0
    size_t str_len = ti->tokens[*index].end - ti->tokens[*index].start;
554
0
    if (!strncmp("false", str_val, str_len)) {
555
0
        val = plist_new_bool(0);
556
0
    } else if (!strncmp("true", str_val, str_len)) {
557
0
        val = plist_new_bool(1);
558
0
    } else if (!strncmp("null", str_val, str_len)) {
559
0
        plist_data_t data = plist_new_plist_data();
560
0
        if (!data) {
561
0
            PLIST_JSON_ERR("%s: failed to allocate plist data\n", __func__);
562
0
            return NULL;
563
0
        }
564
0
        data->type = PLIST_NULL;
565
0
        val = plist_new_node(data);
566
0
    } else if (isdigit(str_val[0]) || (str_val[0] == '-' && str_val+1 < str_end && isdigit(str_val[1]))) {
567
0
        char* endp = (char*)str_val;
568
0
        int is_neg = (str_val[0] == '-');
569
0
        int64_t intpart = parse_decimal(str_val, str_end, &endp);
570
0
        if (endp >= str_end) {
571
            /* integer */
572
0
            if (is_neg || intpart <= INT64_MAX) {
573
0
                val = plist_new_int(intpart);
574
0
            } else {
575
0
                val = plist_new_uint((uint64_t)intpart);
576
0
            }
577
0
        } else if ((*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) || ((*endp == 'e' || *endp == 'E') && endp+1 < str_end && (isdigit(*(endp+1)) || (((*(endp+1) == '-') || (*(endp+1) == '+')) && endp+2 < str_end && isdigit(*(endp+2)))))) {
578
            /* floating point */
579
0
            double dval = (double)intpart;
580
0
            char* fendp = endp;
581
0
            int err = 0;
582
0
            do {
583
0
                if (*endp == '.') {
584
0
                    fendp++;
585
0
                    double frac = 0;
586
0
                    double p = 0.1;
587
0
                    while (fendp < str_end && isdigit(*fendp)) {
588
0
                        frac = frac + (*fendp - '0') * p;
589
0
                        p *= 0.1;
590
0
                        fendp++;
591
0
                    }
592
0
                    if (is_neg) {
593
0
                        dval -= frac;
594
0
                    } else {
595
0
                        dval += frac;
596
0
                    }
597
0
                }
598
0
                if (fendp >= str_end) {
599
0
                    break;
600
0
                }
601
0
                if (fendp+1 < str_end && (*fendp == 'e' || *fendp == 'E') && (isdigit(*(fendp+1)) || (((*(fendp+1) == '-') || (*(fendp+1) == '+')) && fendp+2 < str_end && isdigit(*(fendp+2))))) {
602
0
                    int64_t exp = parse_decimal(fendp+1, str_end, &fendp);
603
0
                    dval = dval * pow(10, (double)exp);
604
0
                } else {
605
0
                    PLIST_JSON_ERR("%s: invalid character at offset %d when parsing floating point value\n", __func__, (int)(fendp - js));
606
0
                    err++;
607
0
                }
608
0
            } while (0);
609
0
            if (!err) {
610
0
                if (isinf(dval) || isnan(dval)) {
611
0
                   PLIST_JSON_ERR("%s: unrepresentable floating point value at offset %d when parsing numerical value\n", __func__, (int)(str_val - js));
612
0
                } else {
613
0
                    val = plist_new_real(dval);
614
0
                }
615
0
            }
616
0
        } else {
617
0
            PLIST_JSON_ERR("%s: invalid character at offset %d when parsing numerical value\n", __func__, (int)(endp - js));
618
0
        }
619
0
    } else {
620
0
        PLIST_JSON_ERR("%s: invalid primitive value '%.*s' encountered\n", __func__, (int)str_len, str_val);
621
0
    }
622
0
    if (!val) {
623
0
        PLIST_JSON_ERR("%s: failed to create node\n", __func__);
624
0
        return NULL;
625
0
    }
626
0
    (*index)++;
627
0
    return val;
628
0
}
629
630
static char* unescape_string(const char* str_val, size_t str_len, size_t *new_len)
631
0
{
632
0
    char* strval = strndup(str_val, str_len);
633
0
    if (!strval) return NULL;
634
0
    size_t i = 0;
635
0
    while (i < str_len) {
636
0
        if (strval[i] == '\\' && i < str_len-1) {
637
0
            switch (strval[i+1]) {
638
0
                case '\"': case '/' : case '\\' : case 'b' :
639
0
                case 'f' : case 'r' : case 'n'  : case 't' :
640
0
                    memmove(strval+i, strval+i+1, str_len - (i+1));
641
0
                    str_len--;
642
0
                    switch (strval[i]) {
643
0
                        case 'b':
644
0
                            strval[i] = '\b';
645
0
                            break;
646
0
                        case 'f':
647
0
                            strval[i] = '\f';
648
0
                            break;
649
0
                        case 'r':
650
0
                            strval[i] = '\r';
651
0
                            break;
652
0
                        case 'n':
653
0
                            strval[i] = '\n';
654
0
                            break;
655
0
                        case 't':
656
0
                            strval[i] = '\t';
657
0
                            break;
658
0
                        default:
659
0
                            break;
660
0
                    }
661
0
                    break;
662
0
                case 'u': {
663
0
                    unsigned int val = 0;
664
0
                    if (str_len-(i+2) < 4) {
665
0
                        PLIST_JSON_ERR("%s: invalid escape sequence '%s' (too short)\n", __func__, strval+i);
666
0
                        free(strval);
667
0
                        return NULL;
668
0
                    }
669
0
                    if (!(isxdigit(strval[i+2]) && isxdigit(strval[i+3]) && isxdigit(strval[i+4]) && isxdigit(strval[i+5])) || sscanf(strval+i+2, "%04x", &val) != 1) {
670
0
                        PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 6, strval+i);
671
0
                        free(strval);
672
0
                        return NULL;
673
0
                    }
674
0
                    int bytelen = 0;
675
0
                    if (val >= 0x800) {
676
                        /* three bytes */
677
0
                        strval[i]   = (char)(0xE0 + ((val >> 12) & 0xF));
678
0
                        strval[i+1] = (char)(0x80 + ((val >> 6) & 0x3F));
679
0
                        strval[i+2] = (char)(0x80 + (val & 0x3F));
680
0
                        bytelen = 3;
681
0
                    } else if (val >= 0x80) {
682
                        /* two bytes */
683
0
                        strval[i]   = (char)(0xC0 + ((val >> 6) & 0x1F));
684
0
                        strval[i+1] = (char)(0x80 + (val & 0x3F));
685
0
                        bytelen = 2;
686
0
                    } else {
687
                        /* one byte */
688
0
                        strval[i] = (char)(val & 0x7F);
689
0
                        bytelen = 1;
690
0
                    }
691
0
                    memmove(strval+i+bytelen, strval+i+6, str_len - (i+5));
692
0
                    str_len -= (6-bytelen);
693
0
                }   break;
694
0
                default:
695
0
                    PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 2, strval+i);
696
0
                    free(strval);
697
0
                    return NULL;
698
0
            }
699
0
        }
700
0
        i++;
701
0
    }
702
0
    strval[str_len] = '\0';
703
0
    if (new_len) {
704
0
        *new_len = str_len;
705
0
    }
706
0
    return strval;
707
0
}
708
709
static plist_t parse_string(const char* js, jsmntok_info_t* ti, int* index)
710
0
{
711
0
    if (ti->tokens[*index].type != JSMN_STRING) {
712
0
        PLIST_JSON_ERR("%s: token type != JSMN_STRING\n", __func__);
713
0
        return NULL;
714
0
    }
715
716
0
    size_t str_len = 0; ;
717
0
    char* strval = unescape_string(js + ti->tokens[*index].start, ti->tokens[*index].end - ti->tokens[*index].start, &str_len);
718
0
    if (!strval) {
719
0
        return NULL;
720
0
    }
721
0
    plist_t node;
722
723
0
    plist_data_t data = plist_new_plist_data();
724
0
    if (!data) {
725
0
        free(strval);
726
0
        PLIST_JSON_ERR("%s: failed to allocate plist data\n", __func__);
727
0
        return NULL;
728
0
    }
729
0
    data->type = PLIST_STRING;
730
0
    data->strval = strval;
731
0
    data->length = str_len;
732
0
    node = plist_new_node(data);
733
0
    if (!node) {
734
0
        plist_free_data(data);
735
0
        PLIST_JSON_ERR("%s: failed to create node\n", __func__);
736
0
        return NULL;
737
0
    }
738
739
0
    (*index)++;
740
0
    return node;
741
0
}
742
743
static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth);
744
745
static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth)
746
0
{
747
0
    if (ti->tokens[*index].type != JSMN_ARRAY) {
748
0
        PLIST_JSON_ERR("%s: token type != JSMN_ARRAY\n", __func__);
749
0
        ti->err = PLIST_ERR_PARSE;
750
0
        return NULL;
751
0
    }
752
0
    if (depth > PLIST_MAX_NESTING_DEPTH) {
753
0
        PLIST_JSON_ERR("%s: maximum nesting depth (%u) exceeded\n", __func__, (unsigned)PLIST_MAX_NESTING_DEPTH);
754
0
        ti->err = PLIST_ERR_MAX_NESTING;
755
0
        return NULL;
756
0
    }
757
0
    plist_t arr = plist_new_array();
758
0
    if (!arr) {
759
0
        PLIST_JSON_ERR("%s: failed to create array node\n", __func__);
760
0
        ti->err = PLIST_ERR_NO_MEM;
761
0
        return NULL;
762
0
    }
763
0
    size_t num_tokens = ti->tokens[*index].size;
764
0
    size_t num;
765
0
    int j = (*index)+1;
766
0
    for (num = 0; num < num_tokens; num++) {
767
0
        if (j >= ti->count) {
768
0
            PLIST_JSON_ERR("%s: token index out of valid range\n", __func__);
769
0
            plist_free(arr);
770
0
            ti->err = PLIST_ERR_PARSE;
771
0
            return NULL;
772
0
        }
773
0
        plist_t val = NULL;
774
0
        switch (ti->tokens[j].type) {
775
0
            case JSMN_OBJECT:
776
0
                val = parse_object(js, ti, &j, depth+1);
777
0
                break;
778
0
            case JSMN_ARRAY:
779
0
                val = parse_array(js, ti, &j, depth+1);
780
0
                break;
781
0
            case JSMN_STRING:
782
0
                val = parse_string(js, ti, &j);
783
0
                break;
784
0
            case JSMN_PRIMITIVE:
785
0
                val = parse_primitive(js, ti, &j);
786
0
                break;
787
0
            default:
788
0
                break;
789
0
        }
790
0
        if (val) {
791
0
            plist_array_append_item(arr, val);
792
            // if append failed, val still has no parent, free it and abort
793
0
            if (((node_t)val)->parent == NULL) {
794
0
                plist_free(val);
795
0
                plist_free(arr);
796
0
                ti->err = PLIST_ERR_NO_MEM;
797
0
                return NULL;
798
0
            }
799
0
        } else {
800
0
            plist_free(arr);
801
0
            ti->err = PLIST_ERR_PARSE;
802
0
            return NULL;
803
0
        }
804
0
    }
805
0
    *(index) = j;
806
0
    return arr;
807
0
}
808
809
static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth)
810
0
{
811
0
    if (ti->tokens[*index].type != JSMN_OBJECT) {
812
0
        PLIST_JSON_ERR("%s: token type != JSMN_OBJECT\n", __func__);
813
0
        ti->err = PLIST_ERR_PARSE;
814
0
        return NULL;
815
0
    }
816
0
    if (depth > PLIST_MAX_NESTING_DEPTH) {
817
0
        PLIST_JSON_ERR("%s: maximum nesting depth (%u) exceeded\n", __func__, (unsigned)PLIST_MAX_NESTING_DEPTH);
818
0
        ti->err = PLIST_ERR_MAX_NESTING;
819
0
        return NULL;
820
0
    }
821
0
    size_t num_tokens = ti->tokens[*index].size;
822
0
    size_t num;
823
0
    int j = (*index)+1;
824
0
    if (num_tokens % 2 != 0) {
825
0
        PLIST_JSON_ERR("%s: number of children must be even\n", __func__);
826
0
        ti->err = PLIST_ERR_PARSE;
827
0
        return NULL;
828
0
    }
829
0
    plist_t obj = plist_new_dict();
830
0
    if (!obj) {
831
0
        PLIST_JSON_ERR("%s: failed to create dict node\n", __func__);
832
0
        ti->err = PLIST_ERR_NO_MEM;
833
0
        return NULL;
834
0
    }
835
0
    for (num = 0; num < num_tokens; num++) {
836
0
        if (j+1 >= ti->count) {
837
0
            PLIST_JSON_ERR("%s: token index out of valid range\n", __func__);
838
0
            plist_free(obj);
839
0
            ti->err = PLIST_ERR_PARSE;
840
0
            return NULL;
841
0
        }
842
0
        if (ti->tokens[j].type == JSMN_STRING) {
843
0
            char* key = unescape_string(js + ti->tokens[j].start, ti->tokens[j].end - ti->tokens[j].start, NULL);
844
0
            if (!key) {
845
0
                plist_free(obj);
846
0
                ti->err = PLIST_ERR_PARSE;
847
0
                return NULL;
848
0
            }
849
0
            plist_t val = NULL;
850
0
            j++;
851
0
            num++;
852
0
            switch (ti->tokens[j].type) {
853
0
                case JSMN_OBJECT:
854
0
                    val = parse_object(js, ti, &j, depth+1);
855
0
                    break;
856
0
                case JSMN_ARRAY:
857
0
                    val = parse_array(js, ti, &j, depth+1);
858
0
                    break;
859
0
                case JSMN_STRING:
860
0
                    val = parse_string(js, ti, &j);
861
0
                    break;
862
0
                case JSMN_PRIMITIVE:
863
0
                    val = parse_primitive(js, ti, &j);
864
0
                    break;
865
0
                default:
866
0
                    break;
867
0
            }
868
0
            if (val) {
869
0
                plist_dict_set_item(obj, key, val);
870
                // if set failed, val still has no parent, free it and abort
871
0
                if (((node_t)val)->parent == NULL) {
872
0
                    plist_free(val);
873
0
                    free(key);
874
0
                    plist_free(obj);
875
0
                    ti->err = PLIST_ERR_NO_MEM;
876
0
                    return NULL;
877
0
                }
878
0
            } else {
879
0
                free(key);
880
0
                plist_free(obj);
881
0
                ti->err = PLIST_ERR_PARSE;
882
0
                return NULL;
883
0
            }
884
0
            free(key);
885
0
        } else {
886
0
            PLIST_JSON_ERR("%s: keys must be of type STRING\n", __func__);
887
0
            plist_free(obj);
888
0
            ti->err = PLIST_ERR_PARSE;
889
0
            return NULL;
890
0
        }
891
0
    }
892
0
    (*index) = j;
893
0
    return obj;
894
0
}
895
896
plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist)
897
0
{
898
0
    if (!plist) {
899
0
        return PLIST_ERR_INVALID_ARG;
900
0
    }
901
0
    *plist = NULL;
902
0
    if (!json || (length == 0)) {
903
0
        return PLIST_ERR_INVALID_ARG;
904
0
    }
905
906
0
    jsmn_parser parser;
907
0
    jsmn_init(&parser);
908
0
    unsigned int maxtoks = 256;
909
0
    unsigned int curtoks = 0;
910
0
    int r = 0;
911
0
    jsmntok_t *tokens = NULL;
912
913
0
    do {
914
0
        jsmntok_t* newtokens = (jsmntok_t*)realloc(tokens, sizeof(jsmntok_t)*maxtoks);
915
0
        if (!newtokens) {
916
0
            free(tokens);
917
0
            PLIST_JSON_ERR("%s: Out of memory\n", __func__);
918
0
            return PLIST_ERR_NO_MEM;
919
0
        }
920
0
        memset((unsigned char*)newtokens + sizeof(jsmntok_t)*curtoks, '\0', sizeof(jsmntok_t)*(maxtoks-curtoks));
921
0
        tokens = newtokens;
922
0
        curtoks = maxtoks;
923
924
0
        r = jsmn_parse(&parser, json, length, tokens, maxtoks);
925
0
        if (r == JSMN_ERROR_NOMEM) {
926
0
            if (maxtoks > (unsigned int)INT_MAX - 16) {
927
0
                free(tokens);
928
0
                return PLIST_ERR_NO_MEM;
929
0
            }
930
0
            maxtoks+=16;
931
0
            continue;
932
0
        } else if (r < 0) {
933
0
            break;
934
0
        }
935
0
    } while (r == JSMN_ERROR_NOMEM);
936
937
0
    switch(r) {
938
0
        case JSMN_ERROR_NOMEM:
939
0
            PLIST_JSON_ERR("%s: Out of memory...\n", __func__);
940
0
            free(tokens);
941
0
            return PLIST_ERR_NO_MEM;
942
0
        case JSMN_ERROR_INVAL:
943
0
            PLIST_JSON_ERR("%s: Invalid character inside JSON string\n", __func__);
944
0
            free(tokens);
945
0
            return PLIST_ERR_PARSE;
946
0
        case JSMN_ERROR_PART:
947
0
            PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__);
948
0
            free(tokens);
949
0
            return PLIST_ERR_PARSE;
950
0
        case JSMN_ERROR_LIMIT:
951
0
            PLIST_JSON_ERR("%s: Input data too large\n", __func__);
952
0
            free(tokens);
953
0
            return PLIST_ERR_PARSE;
954
0
        default:
955
0
            break;
956
0
    }
957
958
0
    int startindex = 0;
959
0
    jsmntok_info_t ti = { tokens, parser.toknext, PLIST_ERR_SUCCESS };
960
0
    switch (tokens[startindex].type) {
961
0
        case JSMN_PRIMITIVE:
962
0
            *plist = parse_primitive(json, &ti, &startindex);
963
0
            break;
964
0
        case JSMN_STRING:
965
0
            *plist = parse_string(json, &ti, &startindex);
966
0
            break;
967
0
        case JSMN_ARRAY:
968
0
            *plist = parse_array(json, &ti, &startindex, 0);
969
0
            break;
970
0
        case JSMN_OBJECT:
971
0
            *plist = parse_object(json, &ti, &startindex, 0);
972
0
            break;
973
0
        default:
974
0
            break;
975
0
    }
976
0
    free(tokens);
977
0
    if (!*plist) {
978
0
        return (ti.err != PLIST_ERR_SUCCESS) ? ti.err : PLIST_ERR_PARSE;
979
0
    }
980
0
    return PLIST_ERR_SUCCESS;
981
0
}