Coverage Report

Created: 2026-07-24 06:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libplist/src/plist.c
Line
Count
Source
1
/*
2
 * plist.c
3
 * Builds plist XML structures
4
 *
5
 * Copyright (c) 2009-2023 Nikias Bassen, All Rights Reserved.
6
 * Copyright (c) 2010-2015 Martin Szulecki, All Rights Reserved.
7
 * Copyright (c) 2008 Zach C., All Rights Reserved.
8
 *
9
 * This library is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU Lesser General Public
11
 * License as published by the Free Software Foundation; either
12
 * version 2.1 of the License, or (at your option) any later version.
13
 *
14
 * This library is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
 * Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public
20
 * License along with this library; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
 */
23
24
#ifdef HAVE_CONFIG_H
25
#include <config.h>
26
#endif
27
28
#define _GNU_SOURCE 1
29
#include <string.h>
30
#include "plist.h"
31
#include <stdlib.h>
32
#include <stdio.h>
33
#include <math.h>
34
#include <assert.h>
35
#include <limits.h>
36
#include <float.h>
37
#include <ctype.h>
38
#include <inttypes.h>
39
40
#ifdef WIN32
41
#include <windows.h>
42
#endif
43
44
#include <node.h>
45
#include <node_list.h>
46
#include <hashtable.h>
47
#include <ptrarray.h>
48
49
#include "common.h"
50
51
#ifdef _MSC_VER
52
typedef SSIZE_T ssize_t;
53
#endif
54
55
#ifdef DEBUG
56
static int plist_debug = 0;
57
107
#define PLIST_ERR(...) if (plist_debug > 0) { fprintf(stderr, "libplist ERROR: " __VA_ARGS__); }
58
#else
59
#define PLIST_ERR(...)
60
#endif
61
62
#ifndef bswap16
63
#define bswap16(x)   ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8))
64
#endif
65
66
#ifndef bswap32
67
#define bswap32(x)   ((((x) & 0xFF000000) >> 24) \
68
                    | (((x) & 0x00FF0000) >>  8) \
69
                    | (((x) & 0x0000FF00) <<  8) \
70
                    | (((x) & 0x000000FF) << 24))
71
#endif
72
73
#ifndef bswap64
74
#define bswap64(x)   ((((x) & 0xFF00000000000000ull) >> 56) \
75
                    | (((x) & 0x00FF000000000000ull) >> 40) \
76
                    | (((x) & 0x0000FF0000000000ull) >> 24) \
77
                    | (((x) & 0x000000FF00000000ull) >>  8) \
78
                    | (((x) & 0x00000000FF000000ull) <<  8) \
79
                    | (((x) & 0x0000000000FF0000ull) << 24) \
80
                    | (((x) & 0x000000000000FF00ull) << 40) \
81
                    | (((x) & 0x00000000000000FFull) << 56))
82
#endif
83
84
#ifndef le16toh
85
#ifdef __BIG_ENDIAN__
86
#define le16toh(x) bswap16(x)
87
#else
88
#define le16toh(x) (x)
89
#endif
90
#endif
91
92
#ifndef le32toh
93
#ifdef __BIG_ENDIAN__
94
#define le32toh(x) bswap32(x)
95
#else
96
#define le32toh(x) (x)
97
#endif
98
#endif
99
100
#ifndef le64toh
101
#ifdef __BIG_ENDIAN__
102
#define le64toh(x) bswap64(x)
103
#else
104
#define le64toh(x) (x)
105
#endif
106
#endif
107
108
// Reference: https://stackoverflow.com/a/2390626/1806760
109
// Initializer/finalizer sample for MSVC and GCC/Clang.
110
// 2010-2016 Joe Lowe. Released into the public domain.
111
112
#ifdef __cplusplus
113
    #define INITIALIZER(f) \
114
        static void f(void); \
115
        struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
116
        static void f(void)
117
#elif defined(_MSC_VER)
118
    #pragma section(".CRT$XCU",read)
119
    #define INITIALIZER2_(f,p) \
120
        static void f(void); \
121
        __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
122
        __pragma(comment(linker,"/include:" p #f "_")) \
123
        static void f(void)
124
    #ifdef _WIN64
125
        #define INITIALIZER(f) INITIALIZER2_(f,"")
126
    #else
127
        #define INITIALIZER(f) INITIALIZER2_(f,"_")
128
    #endif
129
#else
130
    #define INITIALIZER(f) \
131
        static void f(void) __attribute__((__constructor__)); \
132
        static void f(void)
133
#endif
134
135
extern void plist_xml_init(void);
136
extern void plist_xml_deinit(void);
137
extern void plist_bin_init(void);
138
extern void plist_bin_deinit(void);
139
extern void plist_json_init(void);
140
extern void plist_json_deinit(void);
141
extern void plist_ostep_init(void);
142
extern void plist_ostep_deinit(void);
143
144
static void internal_plist_deinit(void)
145
0
{
146
0
    plist_bin_deinit();
147
0
    plist_xml_deinit();
148
0
    plist_json_deinit();
149
0
    plist_ostep_deinit();
150
0
}
151
152
INITIALIZER(internal_plist_init)
153
8
{
154
8
    plist_bin_init();
155
8
    plist_xml_init();
156
8
    plist_json_init();
157
8
    plist_ostep_init();
158
8
    atexit(internal_plist_deinit);
159
8
}
160
161
#ifndef HAVE_MEMMEM
162
// see https://sourceware.org/legacy-ml/libc-alpha/2007-12/msg00000.html
163
164
#ifndef _LIBC
165
# define __builtin_expect(expr, val)   (expr)
166
#endif
167
168
#undef memmem
169
170
/* Return the first occurrence of NEEDLE in HAYSTACK. */
171
void* memmem(const void* haystack, size_t haystack_len, const void* needle, size_t needle_len)
172
{
173
    /* not really Rabin-Karp, just using additive hashing */
174
    char* haystack_ = (char*)haystack;
175
    char* needle_ = (char*)needle;
176
    int hash = 0;  /* this is the static hash value of the needle */
177
    int hay_hash = 0;  /* rolling hash over the haystack */
178
    char* last;
179
    size_t i;
180
181
    if (haystack_len < needle_len)
182
        return NULL;
183
184
    if (!needle_len)
185
        return haystack_;
186
187
    /* initialize hashes */
188
    for (i = needle_len; i; --i) {
189
        hash += *needle_++;
190
        hay_hash += *haystack_++;
191
    }
192
193
    /* iterate over the haystack */
194
    haystack_ = (char*)haystack;
195
    needle_ = (char*)needle;
196
    last = haystack_+(haystack_len - needle_len + 1);
197
    for (; haystack_ < last; ++haystack_) {
198
        if (__builtin_expect(hash == hay_hash, 0)
199
              && *haystack_ == *needle_ /* prevent calling memcmp, was a optimization from existing glibc */
200
              && !memcmp (haystack_, needle_, needle_len)) {
201
            return haystack_;
202
        }
203
        /* roll the hash */
204
        hay_hash -= *haystack_;
205
        hay_hash += *(haystack_+needle_len);
206
    }
207
    return NULL;
208
}
209
#endif
210
211
int plist_is_binary(const char *plist_data, uint32_t length)
212
0
{
213
0
    if (plist_data == NULL || length < 8) {
214
0
        return 0;
215
0
    }
216
217
0
    return (memcmp(plist_data, "bplist00", 8) == 0);
218
0
}
219
220
#define SKIP_WS(blob, pos, len) \
221
0
    while (pos < len && ((blob[pos] == ' ') || (blob[pos] == '\t') || (blob[pos] == '\r') || (blob[pos] == '\n'))) pos++;
222
#define FIND_NEXT(blob, pos, len, chr) \
223
0
    while (pos < len && (blob[pos] != chr)) pos++;
224
225
plist_err_t plist_from_memory(const char *plist_data, uint32_t length, plist_t *plist, plist_format_t *format)
226
0
{
227
0
    plist_err_t res = PLIST_ERR_UNKNOWN;
228
0
    if (!plist) {
229
0
        return PLIST_ERR_INVALID_ARG;
230
0
    }
231
0
    *plist = NULL;
232
0
    if (!plist_data || length == 0) {
233
0
        return PLIST_ERR_INVALID_ARG;
234
0
    }
235
0
    plist_format_t fmt = PLIST_FORMAT_NONE;
236
0
    if (format) *format = PLIST_FORMAT_NONE;
237
0
    if (plist_is_binary(plist_data, length)) {
238
0
        res = plist_from_bin(plist_data, length, plist);
239
0
        fmt = PLIST_FORMAT_BINARY;
240
0
    } else {
241
0
        uint32_t pos = 0;
242
0
        int is_json = 0;
243
0
        int is_xml = 0;
244
        /* skip whitespace */
245
0
        SKIP_WS(plist_data, pos, length);
246
0
        if (pos >= length) {
247
0
            return PLIST_ERR_PARSE;
248
0
        }
249
0
        if (plist_data[pos] == '<' && (length-pos > 3) && !isxdigit(plist_data[pos+1]) && !isxdigit(plist_data[pos+2]) && !isxdigit(plist_data[pos+3])) {
250
0
            is_xml = 1;
251
0
        } else if (plist_data[pos] == '[') {
252
            /* only valid for json */
253
0
            is_json = 1;
254
0
        } else if (plist_data[pos] == '(') {
255
            /* only valid for openstep */
256
0
        } else if (plist_data[pos] == '{') {
257
            /* this could be json or openstep */
258
0
            pos++;
259
0
            SKIP_WS(plist_data, pos, length);
260
0
            if (pos >= length) {
261
0
                return PLIST_ERR_PARSE;
262
0
            }
263
0
            if (plist_data[pos] == '"') {
264
                /* still could be both */
265
0
                pos++;
266
0
                while (pos < length) {
267
0
                    FIND_NEXT(plist_data, pos, length, '"');
268
0
                    if (plist_data[pos-1] != '\\') {
269
0
                        break;
270
0
                    }
271
0
                    pos++;
272
0
                }
273
0
                if (pos >= length) {
274
0
                    return PLIST_ERR_PARSE;
275
0
                }
276
0
                if (plist_data[pos] == '"') {
277
0
                    pos++;
278
0
                    SKIP_WS(plist_data, pos, length);
279
0
                    if (pos >= length) {
280
0
                        return PLIST_ERR_PARSE;
281
0
                    }
282
0
                    if (plist_data[pos] == ':') {
283
                        /* this is definitely json */
284
0
                        is_json = 1;
285
0
                    }
286
0
                }
287
0
            }
288
0
        }
289
0
        if (is_xml) {
290
0
            res = plist_from_xml(plist_data, length, plist);
291
0
            fmt = PLIST_FORMAT_XML;
292
0
        } else if (is_json) {
293
0
            res = plist_from_json(plist_data, length, plist);
294
0
            fmt = PLIST_FORMAT_JSON;
295
0
        } else {
296
0
            res = plist_from_openstep(plist_data, length, plist);
297
0
            fmt = PLIST_FORMAT_OSTEP;
298
0
        }
299
0
    }
300
0
    if (format && res == PLIST_ERR_SUCCESS) {
301
0
        *format = fmt;
302
0
    }
303
0
    return res;
304
0
}
305
306
plist_err_t plist_read_from_file(const char *filename, plist_t *plist, plist_format_t *format)
307
0
{
308
0
    if (!filename || !plist) {
309
0
        return PLIST_ERR_INVALID_ARG;
310
0
    }
311
0
    FILE *f = fopen(filename, "rb");
312
0
    if (!f) {
313
0
        return PLIST_ERR_IO;
314
0
    }
315
0
    struct stat fst;
316
0
    fstat(fileno(f), &fst);
317
0
    if ((uint64_t)fst.st_size > UINT32_MAX) {
318
0
        return PLIST_ERR_NO_MEM;
319
0
    }
320
0
    uint32_t total = (uint32_t)fst.st_size;
321
0
    if (total == 0) {
322
0
        return PLIST_ERR_PARSE;
323
0
    }
324
0
    char *buf = (char*)malloc(total);
325
0
    if (!buf) {
326
0
        fclose(f);
327
0
        return PLIST_ERR_NO_MEM;
328
0
    }
329
0
    uint32_t done = 0;
330
0
    while (done < total) {
331
0
        ssize_t r = fread(buf + done, 1, total - done, f);
332
0
        if (r <= 0) {
333
0
            break;
334
0
        }
335
0
        done += r;
336
0
    }
337
0
    fclose(f);
338
0
    if (done < total) {
339
0
        free(buf);
340
0
        return PLIST_ERR_IO;
341
0
    }
342
0
    plist_err_t res = plist_from_memory(buf, total, plist, format);
343
0
    free(buf);
344
0
    return res;
345
0
}
346
347
plist_t plist_new_node(plist_data_t data)
348
583k
{
349
583k
    return (plist_t) node_create(NULL, data);
350
583k
}
351
352
plist_data_t plist_get_data(plist_t node)
353
57.2M
{
354
57.2M
    if (!node)
355
0
        return NULL;
356
57.2M
    return (plist_data_t)((node_t)node)->data;
357
57.2M
}
358
359
plist_data_t plist_new_plist_data(void)
360
907k
{
361
907k
    return (plist_data_t) calloc(1, sizeof(struct plist_data_s));
362
907k
}
363
364
static unsigned int dict_key_hash(const void *data)
365
147k
{
366
147k
    plist_data_t keydata = (plist_data_t)data;
367
147k
    unsigned int hash = 5381;
368
147k
    size_t i;
369
147k
    char *str = keydata->strval;
370
413k
    for (i = 0; i < keydata->length; str++, i++) {
371
265k
        hash = ((hash << 5) + hash) + *str;
372
265k
    }
373
147k
    return hash;
374
147k
}
375
376
static int dict_key_compare(const void* a, const void* b)
377
8.01k
{
378
8.01k
    plist_data_t data_a = (plist_data_t)a;
379
8.01k
    plist_data_t data_b = (plist_data_t)b;
380
8.01k
    if (data_a->strval == NULL || data_b->strval == NULL) {
381
0
        return FALSE;
382
0
    }
383
8.01k
    if (data_a->length != data_b->length) {
384
3.03k
        return FALSE;
385
3.03k
    }
386
4.97k
    return (strcmp(data_a->strval, data_b->strval) == 0) ? TRUE : FALSE;
387
8.01k
}
388
389
static void _plist_free_data(plist_data_t data)
390
907k
{
391
907k
    if (!data) return;
392
907k
    switch (data->type) {
393
170k
        case PLIST_KEY:
394
450k
        case PLIST_STRING:
395
450k
            free(data->strval);
396
450k
            data->strval = NULL;
397
450k
            break;
398
214k
        case PLIST_DATA:
399
214k
            free(data->buff);
400
214k
            data->buff = NULL;
401
214k
            break;
402
121k
        case PLIST_ARRAY:
403
121k
            ptr_array_free((ptrarray_t*)data->hashtable);
404
121k
            data->hashtable = NULL;
405
121k
            break;
406
39.7k
        case PLIST_DICT: {
407
39.7k
            hashtable_t *ht = (hashtable_t*)data->hashtable;
408
            // PLIST_DICT hashtables must not own/free values; values are freed via node tree.
409
39.7k
            assert(!ht || ht->free_func == NULL);
410
39.7k
            if (ht) ht->free_func = NULL;
411
39.7k
            hash_table_destroy(ht);
412
39.7k
            data->hashtable = NULL;
413
39.7k
            break;
414
39.7k
        }
415
81.2k
        default:
416
81.2k
            break;
417
907k
    }
418
907k
}
419
420
void plist_free_data(plist_data_t data)
421
907k
{
422
907k
    if (!data) return;
423
907k
    _plist_free_data(data);
424
907k
    free(data);
425
907k
}
426
427
static int plist_free_children(node_t root)
428
133k
{
429
133k
    if (!root) return NODE_ERR_INVALID_ARG;
430
431
133k
    if (!node_first_child(root)) {
432
128k
        return NODE_ERR_SUCCESS;
433
128k
    }
434
435
4.26k
    size_t cap = 64, sp = 0;
436
4.26k
    node_t *stack = (node_t*)malloc(cap * sizeof(*stack));
437
4.26k
    if (!stack) return NODE_ERR_NO_MEM;
438
439
    // Push *direct* children onto the stack, detached from root.
440
374k
    for (;;) {
441
374k
        node_t ch = node_first_child(root);
442
374k
        if (!ch) break;
443
444
370k
        int di = node_detach(root, ch);
445
370k
        if (di < 0) {
446
0
            free(stack);
447
0
            return di;
448
0
        }
449
450
370k
        if (sp == cap) {
451
4.87k
            cap += 64;
452
4.87k
            node_t *tmp = (node_t*)realloc(stack, cap * sizeof(*stack));
453
4.87k
            if (!tmp) {
454
0
                free(stack);
455
0
                return NODE_ERR_NO_MEM;
456
0
            }
457
4.87k
            stack = tmp;
458
4.87k
        }
459
370k
        stack[sp++] = ch;
460
370k
    }
461
462
    // Now free the detached subtree nodes (and their descendants).
463
1.18M
    while (sp) {
464
1.17M
        node_t node = stack[sp - 1];
465
1.17M
        node_t ch = node_first_child(node);
466
1.17M
        if (ch) {
467
404k
            int di = node_detach(node, ch);
468
404k
            if (di < 0) {
469
0
                free(stack);
470
0
                return di;
471
0
            }
472
473
404k
            if (sp == cap) {
474
800
                cap += 64;
475
800
                node_t *tmp = (node_t*)realloc(stack, cap * sizeof(*stack));
476
800
                if (!tmp) {
477
0
                    free(stack);
478
0
                    return NODE_ERR_NO_MEM;
479
0
                }
480
800
                stack = tmp;
481
800
            }
482
404k
            stack[sp++] = ch;
483
404k
            continue;
484
404k
        }
485
486
774k
        plist_data_t data = plist_get_data(node);
487
774k
        plist_free_data(data);
488
774k
        node->data = NULL;
489
490
774k
        node_destroy(node);
491
492
774k
        sp--;
493
774k
    }
494
495
4.26k
    free(stack);
496
4.26k
    return NODE_ERR_SUCCESS;
497
4.26k
}
498
499
static int plist_free_node(node_t root)
500
132k
{
501
132k
    if (!root) return NODE_ERR_INVALID_ARG;
502
503
132k
    int root_index = -1;
504
505
132k
    if (root->parent) {
506
1
        root_index = node_detach(root->parent, root);
507
1
        if (root_index < 0) {
508
0
            return root_index;
509
0
        }
510
1
    }
511
512
132k
    int r = plist_free_children(root);
513
132k
    if (r < 0) {
514
        // root is already detached; caller should treat as error.
515
0
        return r;
516
0
    }
517
518
132k
    plist_data_t data = plist_get_data(root);
519
132k
    plist_free_data(data);
520
132k
    root->data = NULL;
521
522
132k
    node_destroy(root);
523
524
132k
    return root_index;
525
132k
}
526
527
plist_t plist_new_dict(void)
528
13.6k
{
529
13.6k
    plist_data_t data = plist_new_plist_data();
530
13.6k
    if (!data) {
531
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
532
0
        return NULL;
533
0
    }
534
13.6k
    data->type = PLIST_DICT;
535
13.6k
    return plist_new_node(data);
536
13.6k
}
537
538
plist_t plist_new_array(void)
539
58.3k
{
540
58.3k
    plist_data_t data = plist_new_plist_data();
541
58.3k
    if (!data) {
542
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
543
0
        return NULL;
544
0
    }
545
58.3k
    data->type = PLIST_ARRAY;
546
58.3k
    return plist_new_node(data);
547
58.3k
}
548
549
//These nodes should not be handled by users
550
static plist_t plist_new_key(const char *val)
551
154k
{
552
154k
    plist_data_t data = plist_new_plist_data();
553
154k
    if (!data) {
554
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
555
0
        return NULL;
556
0
    }
557
154k
    data->type = PLIST_KEY;
558
154k
    data->strval = strdup(val);
559
154k
    if (!data->strval) {
560
0
        plist_free_data(data);
561
0
        PLIST_ERR("%s: strdup failed\n", __func__);
562
0
        return NULL;
563
154k
    } else {
564
154k
        data->length = strlen(val);
565
154k
    }
566
154k
    return plist_new_node(data);
567
154k
}
568
569
plist_t plist_new_string(const char *val)
570
0
{
571
0
    plist_data_t data = plist_new_plist_data();
572
0
    if (!data) {
573
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
574
0
        return NULL;
575
0
    }
576
0
    data->type = PLIST_STRING;
577
0
    data->strval = strdup(val);
578
0
    if (!data->strval) {
579
0
        plist_free_data(data);
580
0
        PLIST_ERR("%s: strdup failed\n", __func__);
581
0
        return NULL;
582
0
    } else {
583
0
        data->length = strlen(val);
584
0
    }
585
0
    return plist_new_node(data);
586
0
}
587
588
plist_t plist_new_bool(uint8_t val)
589
8.30k
{
590
8.30k
    plist_data_t data = plist_new_plist_data();
591
8.30k
    if (!data) {
592
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
593
0
        return NULL;
594
0
    }
595
8.30k
    data->type = PLIST_BOOLEAN;
596
8.30k
    data->boolval = val;
597
8.30k
    data->length = sizeof(uint8_t);
598
8.30k
    return plist_new_node(data);
599
8.30k
}
600
601
plist_t plist_new_uint(uint64_t val)
602
0
{
603
0
    plist_data_t data = plist_new_plist_data();
604
0
    if (!data) {
605
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
606
0
        return NULL;
607
0
    }
608
0
    data->type = PLIST_INT;
609
0
    data->intval = val;
610
0
    data->length = (val > INT_MAX) ? sizeof(uint64_t)*2 : sizeof(uint64_t);
611
0
    return plist_new_node(data);
612
0
}
613
614
plist_t plist_new_int(int64_t val)
615
4.06k
{
616
4.06k
    plist_data_t data = plist_new_plist_data();
617
4.06k
    if (!data) {
618
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
619
0
        return NULL;
620
0
    }
621
4.06k
    data->type = PLIST_INT;
622
4.06k
    data->intval = val;
623
4.06k
    data->length = sizeof(uint64_t);
624
4.06k
    return plist_new_node(data);
625
4.06k
}
626
627
plist_t plist_new_uid(uint64_t val)
628
2
{
629
2
    plist_data_t data = plist_new_plist_data();
630
2
    if (!data) {
631
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
632
0
        return NULL;
633
0
    }
634
2
    data->type = PLIST_UID;
635
2
    data->intval = val;
636
2
    data->length = sizeof(uint64_t);
637
2
    return plist_new_node(data);
638
2
}
639
640
plist_t plist_new_real(double val)
641
2.14k
{
642
2.14k
    plist_data_t data = plist_new_plist_data();
643
2.14k
    if (!data) {
644
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
645
0
        return NULL;
646
0
    }
647
2.14k
    data->type = PLIST_REAL;
648
2.14k
    data->realval = val;
649
2.14k
    data->length = sizeof(double);
650
2.14k
    return plist_new_node(data);
651
2.14k
}
652
653
plist_t plist_new_data(const char *val, uint64_t length)
654
0
{
655
0
    plist_data_t data = plist_new_plist_data();
656
0
    if (!data) {
657
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
658
0
        return NULL;
659
0
    }
660
0
    data->type = PLIST_DATA;
661
0
    if (val && length) {
662
0
        data->buff = (uint8_t *) malloc(length);
663
0
        if (!data->buff) {
664
0
            PLIST_ERR("%s: failed to allocate %" PRIu64 " bytes\n", __func__, length);
665
0
            return NULL;
666
0
        }
667
0
        memcpy(data->buff, val, length);
668
0
    }
669
0
    data->length = length;
670
0
    return plist_new_node(data);
671
0
}
672
673
plist_t plist_new_date(int32_t sec, int32_t usec)
674
0
{
675
0
    plist_data_t data = plist_new_plist_data();
676
0
    if (!data) {
677
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
678
0
        return NULL;
679
0
    }
680
0
    data->type = PLIST_DATE;
681
0
    data->realval = (double)sec + (double)usec / 1000000;
682
0
    data->length = sizeof(double);
683
0
    return plist_new_node(data);
684
0
}
685
686
plist_t plist_new_unix_date(int64_t sec)
687
0
{
688
0
    plist_data_t data = plist_new_plist_data();
689
0
    if (!data) {
690
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
691
0
        return NULL;
692
0
    }
693
0
    data->type = PLIST_DATE;
694
0
    data->realval = (double)sec - MAC_EPOCH;
695
0
    data->length = sizeof(double);
696
0
    return plist_new_node(data);
697
0
}
698
699
plist_t plist_new_null(void)
700
0
{
701
0
    plist_data_t data = plist_new_plist_data();
702
0
    if (!data) {
703
0
        PLIST_ERR("%s: failed to allocate plist data\n", __func__);
704
0
        return NULL;
705
0
    }
706
0
    data->type = PLIST_NULL;
707
0
    data->intval = 0;
708
0
    data->length = 0;
709
0
    return plist_new_node(data);
710
0
}
711
712
void plist_free(plist_t plist)
713
255k
{
714
255k
    if (plist)
715
125k
    {
716
125k
        plist_free_node((node_t)plist);
717
125k
    }
718
255k
}
719
720
void plist_mem_free(void* ptr)
721
0
{
722
0
    if (ptr)
723
0
    {
724
0
        free(ptr);
725
0
    }
726
0
}
727
728
static int plist_copy_node_shallow(node_t node, plist_t *out_newnode, plist_data_t *out_newdata, plist_type *out_type)
729
0
{
730
0
    if (!node || !out_newnode || !out_newdata || !out_type) return NODE_ERR_INVALID_ARG;
731
732
0
    plist_data_t data = plist_get_data(node);
733
0
    if (!data) return NODE_ERR_INVALID_ARG;
734
735
0
    plist_data_t newdata = plist_new_plist_data();
736
0
    if (!newdata) return NODE_ERR_NO_MEM;
737
738
0
    memcpy(newdata, data, sizeof(struct plist_data_s));
739
740
0
    plist_type node_type = plist_get_node_type(node);
741
0
    switch (node_type) {
742
0
        case PLIST_DATA:
743
0
            if (data->buff) {
744
0
                newdata->buff = (uint8_t*)malloc(data->length);
745
0
                if (!newdata->buff) {
746
0
                    plist_free_data(newdata);
747
0
                    return NODE_ERR_NO_MEM;
748
0
                }
749
0
                memcpy(newdata->buff, data->buff, data->length);
750
0
            } else {
751
0
                newdata->buff = NULL;
752
0
                newdata->length = 0;
753
0
            }
754
0
            break;
755
756
0
        case PLIST_KEY:
757
0
        case PLIST_STRING:
758
0
            if (data->strval) {
759
0
                size_t n = strlen(data->strval);
760
0
                newdata->strval = (char*)malloc(n+1);
761
0
                if (!newdata->strval) {
762
0
                    plist_free_data(newdata);
763
0
                    return NODE_ERR_NO_MEM;
764
0
                }
765
0
                memcpy(newdata->strval, data->strval, n+1);
766
0
                newdata->length = (uint64_t)n;
767
0
            } else {
768
0
                newdata->strval = NULL;
769
0
                newdata->length = 0;
770
0
            }
771
0
            break;
772
773
0
        case PLIST_ARRAY:
774
0
            if (data->hashtable) {
775
0
                ptrarray_t* pa = ptr_array_new(((ptrarray_t*)data->hashtable)->capacity);
776
0
                if (!pa) {
777
0
                    plist_free_data(newdata);
778
0
                    return NODE_ERR_NO_MEM;
779
0
                }
780
0
                newdata->hashtable = pa;
781
0
            }
782
0
            break;
783
784
0
        case PLIST_DICT:
785
0
            if (data->hashtable) {
786
0
                hashtable_t* ht = hash_table_new(dict_key_hash, dict_key_compare, NULL);
787
0
                if (!ht) {
788
0
                    plist_free_data(newdata);
789
0
                    return NODE_ERR_NO_MEM;
790
0
                }
791
0
                newdata->hashtable = ht;
792
0
            }
793
0
            break;
794
795
0
        default:
796
0
            break;
797
0
    }
798
799
0
    plist_t newnode = plist_new_node(newdata);
800
0
    if (!newnode) {
801
0
        plist_free_data(newdata);
802
0
        return NODE_ERR_NO_MEM;
803
0
    }
804
805
0
    *out_newnode = newnode;
806
0
    *out_newdata = newdata;
807
0
    *out_type = node_type;
808
0
    return NODE_ERR_SUCCESS;
809
0
}
810
811
static plist_t plist_copy_node(node_t root)
812
0
{
813
0
    typedef struct copy_frame {
814
0
        node_t       orig;        // original node
815
0
        plist_t      copy;        // copied node
816
0
        plist_data_t copydata;    // copied node's data (for cache updates)
817
0
        plist_type   type;        // copied node type
818
0
        node_t       next_child;  // next child of orig to process
819
0
        unsigned int node_index;  // child index (for dict key/value odd/even)
820
0
        int          depth;       // optional depth tracking
821
0
    } copy_frame_t;
822
823
0
    if (!root) return NULL;
824
825
    // shallow-copy root first
826
0
    plist_t newroot = NULL;
827
0
    plist_data_t newroot_data = NULL;
828
0
    plist_type newroot_type = PLIST_NONE;
829
830
0
    int r = plist_copy_node_shallow(root, &newroot, &newroot_data, &newroot_type);
831
0
    if (r != NODE_ERR_SUCCESS) {
832
0
        PLIST_ERR("%s: shallow node copy failed (%d)\n", __func__, r);
833
0
        return NULL;
834
0
    }
835
836
    // stack of frames
837
0
    size_t cap = 64, sp = 0;
838
0
    copy_frame_t *st = (copy_frame_t*)malloc(cap * sizeof(*st));
839
0
    if (!st) {
840
0
        plist_free_node((node_t)newroot);
841
0
        return NULL;
842
0
    }
843
844
0
    copy_frame_t cf;
845
0
    cf.orig = root;
846
0
    cf.copy = newroot;
847
0
    cf.copydata = newroot_data;
848
0
    cf.type = newroot_type;
849
0
    cf.next_child = node_first_child(root);
850
0
    cf.node_index = 0;
851
0
    cf.depth = 0;
852
0
    st[sp++] = cf;
853
854
0
    while (sp) {
855
0
        copy_frame_t *f = &st[sp - 1];
856
857
0
        if (f->depth > NODE_MAX_DEPTH) {
858
0
            plist_free_node((node_t)newroot);
859
0
            free(st);
860
0
            PLIST_ERR("%s: maximum nesting depth exceeded\n", __func__);
861
0
            return NULL;
862
0
        }
863
864
        // done with this node?
865
0
        if (!f->next_child) {
866
0
            sp--;
867
0
            continue;
868
0
        }
869
870
        // take next child and advance iterator
871
0
        node_t ch = f->next_child;
872
0
        f->next_child = node_next_sibling(ch);
873
874
        // shallow copy child
875
0
        plist_t newch = NULL;
876
0
        plist_data_t newch_data = NULL;
877
0
        plist_type newch_type = PLIST_NONE;
878
879
0
        r = plist_copy_node_shallow(ch, &newch, &newch_data, &newch_type);
880
0
        if (r != NODE_ERR_SUCCESS) {
881
0
            plist_free_node((node_t)newroot);
882
0
            free(st);
883
0
            PLIST_ERR("%s: shallow node copy failed (%d)\n", __func__, r);
884
0
            return NULL;
885
0
        }
886
887
        // attach child to copied parent
888
0
        r = node_attach((node_t)f->copy, (node_t)newch);
889
0
        if (r != NODE_ERR_SUCCESS) {
890
0
            plist_free_node((node_t)newch);
891
0
            plist_free_node((node_t)newroot);
892
0
            free(st);
893
0
            PLIST_ERR("%s: failed to attach child to copied parent (%d)\n", __func__, r);
894
0
            return NULL;
895
0
        }
896
897
        // update lookup cache on the *parent* copy
898
0
        switch (f->type) {
899
0
            case PLIST_ARRAY:
900
0
                if (f->copydata->hashtable) {
901
0
                    ptr_array_add((ptrarray_t*)f->copydata->hashtable, newch);
902
0
                }
903
0
                break;
904
905
0
            case PLIST_DICT:
906
0
                if (f->copydata->hashtable && (f->node_index % 2 != 0)) {
907
0
                    node_t new_key = node_prev_sibling((node_t)newch);
908
0
                    if (new_key) {
909
0
                        hash_table_insert((hashtable_t*)f->copydata->hashtable, new_key->data, newch);
910
0
                    }
911
0
                }
912
0
                break;
913
914
0
            default:
915
0
                break;
916
0
        }
917
918
0
        f->node_index++;
919
920
        // push child frame to process its children
921
0
        if (sp == cap) {
922
0
            cap += 64;
923
0
            copy_frame_t *tmp = (copy_frame_t*)realloc(st, cap * sizeof(*st));
924
0
            if (!tmp) {
925
0
                plist_free_node((node_t)newroot);
926
0
                free(st);
927
0
                PLIST_ERR("%s: out of memory when reallocating\n", __func__);
928
0
                return NULL;
929
0
            }
930
0
            st = tmp;
931
0
        }
932
933
0
        copy_frame_t nf;
934
0
        nf.orig = ch;
935
0
        nf.copy = newch;
936
0
        nf.copydata = newch_data;
937
0
        nf.type = newch_type;
938
0
        nf.next_child = node_first_child(ch);
939
0
        nf.node_index = 0;
940
0
        nf.depth = f->depth + 1;
941
0
        st[sp++] = nf;
942
0
    }
943
944
0
    free(st);
945
0
    return newroot;
946
0
}
947
948
plist_t plist_copy(plist_t node)
949
0
{
950
0
    return node ? plist_copy_node((node_t)node) : NULL;
951
0
}
952
953
uint32_t plist_array_get_size(plist_t node)
954
0
{
955
0
    uint32_t ret = 0;
956
0
    if (PLIST_IS_ARRAY(node)) {
957
0
        ret = node_n_children((node_t)node);
958
0
    }
959
0
    return ret;
960
0
}
961
962
plist_t plist_array_get_item(plist_t node, uint32_t n)
963
0
{
964
0
    plist_t ret = NULL;
965
0
    if (PLIST_IS_ARRAY(node) && n < INT_MAX) {
966
0
        ptrarray_t *pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable;
967
0
        if (pa) {
968
0
            ret = (plist_t)ptr_array_index(pa, n);
969
0
        } else {
970
0
            ret = (plist_t)node_nth_child((node_t)node, n);
971
0
        }
972
0
    }
973
0
    return ret;
974
0
}
975
976
uint32_t plist_array_get_item_index(plist_t node)
977
0
{
978
0
    plist_t parent = plist_get_parent(node);
979
0
    if (PLIST_IS_ARRAY(parent)) {
980
0
        return node_child_position((node_t)parent, (node_t)node);
981
0
    }
982
0
    return UINT_MAX;
983
0
}
984
985
static void _plist_array_post_insert(plist_t node, plist_t item, long n)
986
144k
{
987
144k
    ptrarray_t *pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable;
988
144k
    if (pa) {
989
        /* store pointer to item in array */
990
29.9k
        ptr_array_insert(pa, item, n);
991
29.9k
        return;
992
29.9k
    }
993
994
114k
    if (((node_t)node)->count > 100) {
995
       /* make new lookup array */
996
360
       pa = ptr_array_new(128);
997
360
       plist_t current = NULL;
998
360
       for (current = (plist_t)node_first_child((node_t)node);
999
36.7k
            pa && current;
1000
36.3k
            current = (plist_t)node_next_sibling((node_t)current))
1001
36.3k
       {
1002
36.3k
           ptr_array_add(pa, current);
1003
36.3k
       }
1004
360
       ((plist_data_t)((node_t)node)->data)->hashtable = pa;
1005
360
    }
1006
114k
}
1007
1008
static void _plist_array_post_set(plist_t node, plist_t item, long n)
1009
0
{
1010
0
    ptrarray_t *pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable;
1011
1012
0
    if (pa) {
1013
0
        if (n < 0 || n >= pa->len) {
1014
0
           PLIST_ERR("%s: cache index out of range (n=%ld len=%ld)\n", __func__, n, pa->len);
1015
0
           return;
1016
0
        }
1017
0
        ptr_array_set(pa, item, n);
1018
0
        return;
1019
0
    }
1020
1021
0
    if (((node_t)node)->count > 100) {
1022
0
        pa = ptr_array_new(128);
1023
0
        plist_t current = NULL;
1024
0
        for (current = (plist_t)node_first_child((node_t)node);
1025
0
             pa && current;
1026
0
             current = (plist_t)node_next_sibling((node_t)current))
1027
0
        {
1028
0
            ptr_array_add(pa, current);
1029
0
        }
1030
0
        ((plist_data_t)((node_t)node)->data)->hashtable = pa;
1031
1032
        // Now that it exists (and is filled), apply the set (will no-op if out of range)
1033
0
        if (pa) {
1034
0
            ptr_array_set(pa, item, n);
1035
0
        }
1036
0
    }
1037
0
}
1038
1039
plist_err_t plist_array_set_item(plist_t node, plist_t item, uint32_t n)
1040
0
{
1041
0
    if (!PLIST_IS_ARRAY(node) || !item || n >= INT_MAX) {
1042
0
        PLIST_ERR("invalid argument passed to %s (node=%p, item=%p, n=%u)\n", __func__, node, item, n);
1043
0
        return PLIST_ERR_INVALID_ARG;
1044
0
    }
1045
0
    node_t it = (node_t)item;
1046
0
    if (it->parent != NULL) {
1047
0
        assert(it->parent == NULL && "item already has a parent; use plist_copy() or detach first");
1048
0
        PLIST_ERR("%s: item already has a parent; use plist_copy() or detach first\n", __func__);
1049
0
        return PLIST_ERR_INVALID_ARG;
1050
0
    }
1051
0
    plist_t old_item = plist_array_get_item(node, n);
1052
0
    if (!old_item) return PLIST_ERR_INVALID_ARG;
1053
1054
0
    int idx = node_detach((node_t)node, (node_t)old_item);
1055
0
    if (idx < 0) {
1056
0
        PLIST_ERR("%s: Failed to detach old item (err=%d)\n", __func__, idx);
1057
0
        return PLIST_ERR_UNKNOWN;
1058
0
    }
1059
1060
0
    int r = node_insert((node_t)node, (unsigned)idx, (node_t)item);
1061
0
    if (r != NODE_ERR_SUCCESS) {
1062
0
        int rb = node_insert((node_t)node, (unsigned)idx, (node_t)old_item);
1063
0
        if (rb == NODE_ERR_SUCCESS) {
1064
0
            _plist_array_post_set(node, old_item, idx); // restore cache correctly
1065
0
            PLIST_ERR("%s: failed to insert replacement (idx=%d err=%d); rollback succeeded\n", __func__, idx, r);
1066
0
            return (r == NODE_ERR_NO_MEM) ? PLIST_ERR_NO_MEM : PLIST_ERR_UNKNOWN;
1067
0
        } else {
1068
0
            PLIST_ERR("%s: insert failed (err=%d) and rollback failed (err=%d); array now missing element at idx=%d\n", __func__, r, rb, idx);
1069
0
            return PLIST_ERR_UNKNOWN;
1070
0
        }
1071
0
    }
1072
1073
0
    _plist_array_post_set(node, item, idx); // update cache
1074
0
    plist_free_node((node_t)old_item);
1075
1076
0
    return PLIST_ERR_SUCCESS;
1077
0
}
1078
1079
plist_err_t plist_array_append_item(plist_t node, plist_t item)
1080
144k
{
1081
144k
    if (!PLIST_IS_ARRAY(node) || !item) {
1082
0
        PLIST_ERR("invalid argument passed to %s (node=%p, item=%p)\n", __func__, node, item);
1083
0
        return PLIST_ERR_INVALID_ARG;
1084
0
    }
1085
144k
    node_t it = (node_t)item;
1086
144k
    if (it->parent != NULL) {
1087
0
        assert(it->parent == NULL && "item already has a parent; use plist_copy() or detach first");
1088
0
        PLIST_ERR("%s: item already has a parent; use plist_copy() or detach first\n", __func__);
1089
0
        return PLIST_ERR_INVALID_ARG;
1090
0
    }
1091
1092
144k
    int r = node_attach((node_t)node, (node_t)item);
1093
144k
    if (r != NODE_ERR_SUCCESS) {
1094
68
        PLIST_ERR("%s: failed to append item (err=%d)\n", __func__, r);
1095
68
        return PLIST_ERR_UNKNOWN;
1096
68
    }
1097
144k
    _plist_array_post_insert(node, item, -1);
1098
1099
144k
    return PLIST_ERR_SUCCESS;
1100
144k
}
1101
1102
plist_err_t plist_array_insert_item(plist_t node, plist_t item, uint32_t n)
1103
0
{
1104
0
    if (!PLIST_IS_ARRAY(node) || !item || n >= INT_MAX) {
1105
0
        PLIST_ERR("invalid argument passed to %s (node=%p, item=%p, n=%u)\n", __func__, node, item, n);
1106
0
        return PLIST_ERR_INVALID_ARG;
1107
0
    }
1108
0
    node_t it = (node_t)item;
1109
0
    if (it->parent != NULL) {
1110
0
        assert(it->parent == NULL && "item already has a parent; use plist_copy() or detach first");
1111
0
        PLIST_ERR("%s: item already has a parent; use plist_copy() or detach first\n", __func__);
1112
0
        return PLIST_ERR_INVALID_ARG;
1113
0
    }
1114
1115
0
    int r = node_insert((node_t)node, n, (node_t)item);
1116
0
    if (r != NODE_ERR_SUCCESS) {
1117
0
        PLIST_ERR("%s: Failed to insert item at index %u (err=%d)\n", __func__, n, r);
1118
0
        return PLIST_ERR_UNKNOWN;
1119
0
    }
1120
0
    _plist_array_post_insert(node, item, (long)n);
1121
1122
0
    return PLIST_ERR_SUCCESS;
1123
0
}
1124
1125
plist_err_t plist_array_remove_item(plist_t node, uint32_t n)
1126
0
{
1127
0
    if (!PLIST_IS_ARRAY(node) || n >= INT_MAX || n > plist_array_get_size(node)) {
1128
0
        PLIST_ERR("invalid argument passed to %s (node=%p, n=%u)\n", __func__, node, n);
1129
0
        return PLIST_ERR_INVALID_ARG;
1130
0
    }
1131
1132
0
    plist_t old_item = plist_array_get_item(node, n);
1133
0
    if (!old_item) {
1134
0
        PLIST_ERR("item not found at index %u\n", n);
1135
0
        return PLIST_ERR_INVALID_ARG;
1136
0
    }
1137
0
    ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable;
1138
0
    if (pa) {
1139
0
        ptr_array_remove(pa, n);
1140
0
    }
1141
0
    plist_free(old_item);
1142
1143
0
    return PLIST_ERR_SUCCESS;
1144
0
}
1145
1146
plist_err_t plist_array_item_remove(plist_t item)
1147
0
{
1148
0
    plist_t parent = plist_get_parent(item);
1149
0
    if (PLIST_IS_ARRAY(parent)) {
1150
0
        int n = node_child_position((node_t)parent, (node_t)item);
1151
0
        if (n < 0) return PLIST_ERR_INVALID_ARG;
1152
0
        ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)parent)->data)->hashtable;
1153
0
        if (pa) {
1154
0
            ptr_array_remove(pa, n);
1155
0
        }
1156
0
        plist_free(item);
1157
0
    }
1158
0
    return PLIST_ERR_SUCCESS;
1159
0
}
1160
1161
typedef struct {
1162
    node_t cur;
1163
} plist_array_iter_private;
1164
1165
void plist_array_new_iter(plist_t node, plist_array_iter *iter)
1166
0
{
1167
0
    if (!iter) return;
1168
0
    *iter = NULL;
1169
0
    if (!PLIST_IS_ARRAY(node)) return;
1170
1171
0
    plist_array_iter_private* it = (plist_array_iter_private*)malloc(sizeof(*it));
1172
0
    if (!it) return;
1173
0
    it->cur = node_first_child((node_t)node);
1174
0
    *iter = (plist_array_iter)it;
1175
0
}
1176
1177
void plist_array_next_item(plist_t node, plist_array_iter iter, plist_t *item)
1178
0
{
1179
0
    if (item) *item = NULL;
1180
0
    if (!iter) return;
1181
0
    if (!PLIST_IS_ARRAY(node)) return;
1182
1183
0
    plist_array_iter_private* it = (plist_array_iter_private*)iter;
1184
0
    node_t cur = it->cur;
1185
0
    if (!cur) return;
1186
1187
0
    if (item) {
1188
0
        *item = (plist_t)cur;
1189
0
    }
1190
0
    it->cur = node_next_sibling(cur);
1191
0
}
1192
1193
void plist_array_free_iter(plist_array_iter iter)
1194
0
{
1195
0
    free(iter);
1196
0
}
1197
1198
uint32_t plist_dict_get_size(plist_t node)
1199
1.25k
{
1200
1.25k
    uint32_t ret = 0;
1201
1.25k
    if (PLIST_IS_DICT(node)) {
1202
1.25k
        ret = node_n_children((node_t)node) / 2;
1203
1.25k
    }
1204
1.25k
    return ret;
1205
1.25k
}
1206
1207
typedef struct {
1208
    node_t cur;
1209
} plist_dict_iter_private;
1210
1211
void plist_dict_new_iter(plist_t node, plist_dict_iter *iter)
1212
0
{
1213
0
    if (!iter) return;
1214
0
    *iter = NULL;
1215
0
    if (!PLIST_IS_DICT(node)) return;
1216
1217
0
    plist_dict_iter_private* it = (plist_dict_iter_private*)malloc(sizeof(*it));
1218
0
    if (!it) return;
1219
0
    it->cur = node_first_child((node_t)node);
1220
0
    *iter = (plist_dict_iter)it;
1221
0
}
1222
1223
void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val)
1224
0
{
1225
0
    if (key) *key = NULL;
1226
0
    if (val) *val = NULL;
1227
0
    if (!iter) return;
1228
0
    if (!PLIST_IS_DICT(node)) return;
1229
1230
0
    plist_dict_iter_private* it = (plist_dict_iter_private*)iter;
1231
1232
0
    node_t k = it->cur;
1233
0
    if (!k) return;
1234
1235
0
    if (!PLIST_IS_KEY((plist_t)k)) {
1236
        // malformed dict, terminate iteration
1237
0
        it->cur = NULL;
1238
0
        return;
1239
0
    }
1240
1241
0
    node_t v = node_next_sibling(k);
1242
0
    if (!v) {
1243
        // key without value, terminate iteration
1244
0
        it->cur = NULL;
1245
0
        return;
1246
0
    }
1247
1248
0
    if (key) {
1249
0
        plist_get_key_val((plist_t)k, key);
1250
0
    }
1251
0
    if (val) {
1252
0
        *val = (plist_t)v;
1253
0
    }
1254
0
    it->cur = node_next_sibling(v);
1255
0
}
1256
1257
void plist_dict_free_iter(plist_dict_iter iter)
1258
0
{
1259
0
    free(iter);
1260
0
}
1261
1262
void plist_dict_get_item_key(plist_t node, char **key)
1263
0
{
1264
0
    plist_t parent = plist_get_parent(node);
1265
0
    if (PLIST_IS_DICT(parent)) {
1266
0
        plist_get_key_val( (plist_t) node_prev_sibling((node_t)node), key);
1267
0
    }
1268
0
}
1269
1270
plist_t plist_dict_item_get_key(plist_t node)
1271
0
{
1272
0
    plist_t ret = NULL;
1273
0
    plist_t parent = plist_get_parent(node);
1274
0
    if (PLIST_IS_DICT(parent)) {
1275
0
        ret = (plist_t)node_prev_sibling((node_t)node);
1276
0
    }
1277
0
    return ret;
1278
0
}
1279
1280
plist_t plist_dict_get_item(plist_t node, const char* key)
1281
162k
{
1282
162k
    plist_t ret = NULL;
1283
162k
    if (!PLIST_IS_DICT(node) || !key) {
1284
0
        PLIST_ERR("invalid argument passed to %s (node=%p, key=%p)\n", __func__, node, key);
1285
0
        return NULL;
1286
0
    }
1287
162k
    plist_data_t data = plist_get_data(node);
1288
162k
    if (!data) {
1289
0
        PLIST_ERR("%s: invalid node\n", __func__);
1290
0
        return NULL;
1291
0
    }
1292
162k
    size_t keylen = strlen(key);
1293
162k
    hashtable_t *ht = (hashtable_t*)data->hashtable;
1294
162k
    if (ht) {
1295
2.33k
        struct plist_data_s sdata = { 0 };
1296
2.33k
        sdata.strval = (char*)key;
1297
2.33k
        sdata.length = keylen;
1298
2.33k
        return (plist_t)hash_table_lookup(ht, &sdata);
1299
160k
    } else {
1300
160k
        plist_t k = NULL;
1301
18.6M
        for (k = (plist_t)node_first_child((node_t)node); k; ) {
1302
18.4M
            plist_t v = (plist_t)node_next_sibling(k);
1303
18.4M
            if (!v) break;
1304
18.4M
            data = plist_get_data(k);
1305
18.4M
            assert(PLIST_IS_KEY(k));
1306
18.4M
            if (!PLIST_IS_KEY(k) || !data || !data->strval) {
1307
0
                PLIST_ERR("invalid key node at %p\n", k);
1308
0
                break;
1309
0
            }
1310
18.4M
            if (data->length == keylen && !memcmp(key, data->strval, keylen+1)) {
1311
6.50k
                ret = v;
1312
6.50k
                break;
1313
6.50k
            }
1314
18.4M
            k = node_next_sibling(v);
1315
18.4M
        }
1316
160k
    }
1317
160k
    return ret;
1318
162k
}
1319
1320
plist_err_t plist_dict_set_item(plist_t node, const char* key, plist_t item)
1321
161k
{
1322
161k
    if (!PLIST_IS_DICT(node) || !key || !item) {
1323
0
        PLIST_ERR("invalid argument passed to %s (node=%p, key=%p, item=%p)\n", __func__, node, key, item);
1324
0
        return PLIST_ERR_INVALID_ARG;
1325
0
    }
1326
161k
    node_t it = (node_t)item;
1327
161k
    if (it->parent != NULL) {
1328
0
        assert(it->parent == NULL && "item already has a parent");
1329
0
        PLIST_ERR("%s: item already has a parent\n", __func__);
1330
0
        return PLIST_ERR_INVALID_ARG;
1331
0
    }
1332
1333
161k
    hashtable_t *ht = (hashtable_t*)((plist_data_t)((node_t)node)->data)->hashtable;
1334
1335
161k
    plist_t old_item = plist_dict_get_item(node, key);
1336
161k
    plist_t key_node = NULL;
1337
1338
161k
    if (old_item) {
1339
        // --- REPLACE EXISTING VALUE ---
1340
7.61k
        node_t old_val = (node_t)old_item;
1341
7.61k
        node_t old_key = node_prev_sibling(old_val);
1342
7.61k
        if (!old_key) {
1343
0
            PLIST_ERR("%s: corrupt dict (value without key)\n", __func__);
1344
0
            return PLIST_ERR_UNKNOWN;
1345
0
        }
1346
7.61k
        if (!PLIST_IS_KEY((plist_t)old_key)) {
1347
0
            PLIST_ERR("%s: corrupt dict ('key' node is not PLIST_KEY\n", __func__);
1348
0
            return PLIST_ERR_UNKNOWN;
1349
0
        }
1350
1351
        // detach old value (do NOT free yet)
1352
7.61k
        int idx = node_detach((node_t)node, old_val);
1353
7.61k
        if (idx < 0) {
1354
0
            PLIST_ERR("%s: failed to detach old value (err=%d)\n", __func__, idx);
1355
0
            return PLIST_ERR_UNKNOWN;
1356
0
        }
1357
1358
        // insert new value at same position
1359
7.61k
        int r = node_insert((node_t)node, (unsigned)idx, (node_t)item);
1360
7.61k
        if (r != NODE_ERR_SUCCESS) {
1361
            // rollback: reinsert old value
1362
3
            int rb = node_insert((node_t)node, (unsigned)idx, old_val);
1363
3
            if (rb == NODE_ERR_SUCCESS && ht) {
1364
1
                hash_table_insert(ht, ((node_t)old_key)->data, old_item);
1365
1
            }
1366
3
            PLIST_ERR("%s: failed to replace dict value (err=%d)\n", __func__, r);
1367
3
            return PLIST_ERR_UNKNOWN;
1368
3
        }
1369
7.60k
        key_node = old_key;
1370
1371
        // update hash table
1372
7.60k
        if (ht) {
1373
1.24k
            hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item);
1374
1.24k
        }
1375
1376
        // now it’s safe to free old value
1377
7.60k
        plist_free_node(old_val);
1378
154k
    } else {
1379
        // --- INSERT NEW KEY/VALUE PAIR ---
1380
154k
        key_node = plist_new_key(key);
1381
154k
        if (!key_node) return PLIST_ERR_NO_MEM;
1382
1383
154k
        int r = node_attach((node_t)node, (node_t)key_node);
1384
154k
        if (r != NODE_ERR_SUCCESS) {
1385
34
            plist_free_node((node_t)key_node);
1386
34
            PLIST_ERR("%s: failed to attach dict key (err=%d)\n", __func__, r);
1387
34
            return PLIST_ERR_UNKNOWN;
1388
34
        }
1389
154k
        r = node_attach((node_t)node, (node_t)item);
1390
154k
        if (r != NODE_ERR_SUCCESS) {
1391
            // rollback key insertion
1392
2
            node_detach((node_t)node, (node_t)key_node);
1393
2
            plist_free_node((node_t)key_node);
1394
2
            PLIST_ERR("%s: failed to attach dict value (err=%d)\n", __func__, r);
1395
2
            return PLIST_ERR_UNKNOWN;
1396
2
        }
1397
1398
154k
        if (ht) {
1399
            // store pointer to item in hash table
1400
1.08k
            hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item);
1401
153k
        } else if (((node_t)node)->count > 500) {
1402
            // make new hash table
1403
571
            ht = hash_table_new(dict_key_hash, dict_key_compare, NULL);
1404
            // calculate the hashes for all entries we have so far
1405
571
            plist_t current = NULL;
1406
571
            for (current = (plist_t)node_first_child((node_t)node);
1407
143k
                 ht && current;
1408
143k
                 current = (plist_t)node_next_sibling(node_next_sibling((node_t)current)))
1409
143k
            {
1410
143k
                hash_table_insert(ht, ((node_t)current)->data, node_next_sibling((node_t)current));
1411
143k
            }
1412
571
            ((plist_data_t)((node_t)node)->data)->hashtable = ht;
1413
571
        }
1414
154k
    }
1415
161k
    return PLIST_ERR_SUCCESS;
1416
161k
}
1417
1418
plist_err_t plist_dict_remove_item(plist_t node, const char* key)
1419
0
{
1420
0
    if (!PLIST_IS_DICT(node) || !key) {
1421
0
        PLIST_ERR("invalid argument passed to %s (node=%p, key=%p)\n", __func__, node, key);
1422
0
        return PLIST_ERR_INVALID_ARG;
1423
0
    }
1424
1425
0
    plist_t old_item = plist_dict_get_item(node, key);
1426
0
    if (!old_item) {
1427
0
        PLIST_ERR("item not found for key '%s'\n", key);
1428
0
        return PLIST_ERR_INVALID_ARG;
1429
0
    }
1430
1431
0
    plist_t key_node = node_prev_sibling((node_t)old_item);
1432
0
    hashtable_t* ht = (hashtable_t*)((plist_data_t)((node_t)node)->data)->hashtable;
1433
0
    if (ht) {
1434
0
        hash_table_remove(ht, ((node_t)key_node)->data);
1435
0
    }
1436
0
    plist_free(key_node);
1437
0
    plist_free(old_item);
1438
1439
0
    return PLIST_ERR_SUCCESS;
1440
0
}
1441
1442
plist_err_t plist_dict_merge(plist_t *target, plist_t source)
1443
0
{
1444
0
  if (!target || !PLIST_IS_DICT(*target) || !PLIST_IS_DICT(source))
1445
0
    return PLIST_ERR_INVALID_ARG;
1446
1447
0
  char* key = NULL;
1448
0
  plist_dict_iter it = NULL;
1449
0
  plist_t subnode = NULL;
1450
0
  plist_dict_new_iter(source, &it);
1451
0
  if (!it)
1452
0
    return PLIST_ERR_NO_MEM;
1453
1454
0
  do {
1455
0
    plist_dict_next_item(source, it, &key, &subnode);
1456
0
    if (!key)
1457
0
      break;
1458
1459
0
    plist_dict_set_item(*target, key, plist_copy(subnode));
1460
0
    free(key);
1461
0
    key = NULL;
1462
0
  } while (1);
1463
0
  free(it);
1464
0
  return PLIST_ERR_SUCCESS;
1465
0
}
1466
1467
uint8_t plist_dict_get_bool(plist_t dict, const char *key)
1468
0
{
1469
0
  uint8_t bval = 0;
1470
0
  uint64_t uintval = 0;
1471
0
  const char *strval = NULL;
1472
0
  uint64_t strsz = 0;
1473
0
  plist_t node = plist_dict_get_item(dict, key);
1474
0
  if (!node) {
1475
0
    return 0;
1476
0
  }
1477
0
  switch (plist_get_node_type(node)) {
1478
0
  case PLIST_BOOLEAN:
1479
0
    plist_get_bool_val(node, &bval);
1480
0
    break;
1481
0
  case PLIST_INT:
1482
0
    plist_get_uint_val(node, &uintval);
1483
0
    bval = (uintval) ? 1 : 0;
1484
0
    break;
1485
0
  case PLIST_STRING:
1486
0
    strval = plist_get_string_ptr(node, NULL);
1487
0
    if (strval) {
1488
0
      if (strcmp(strval, "true")) {
1489
0
        bval = 1;
1490
0
      } else if (strcmp(strval, "false")) {
1491
0
        bval = 0;
1492
0
      } else {
1493
0
        PLIST_ERR("%s: invalid string '%s' for string to boolean conversion\n", __func__, strval);
1494
0
      }
1495
0
    }
1496
0
    break;
1497
0
  case PLIST_DATA:
1498
0
    strval = (const char*)plist_get_data_ptr(node, &strsz);
1499
0
    if (strval) {
1500
0
      if (strsz == 1) {
1501
0
        bval = (strval[0]) ? 1 : 0;
1502
0
      } else {
1503
0
        PLIST_ERR("%s: invalid size %" PRIu64 " for data to boolean conversion\n", __func__, strsz);
1504
0
      }
1505
0
    }
1506
0
    break;
1507
0
  default:
1508
0
    break;
1509
0
  }
1510
0
  return bval;
1511
0
}
1512
1513
int64_t plist_dict_get_int(plist_t dict, const char *key)
1514
0
{
1515
0
  int64_t intval = 0;
1516
0
  const char *strval = NULL;
1517
0
  uint64_t strsz = 0;
1518
0
  plist_t node = plist_dict_get_item(dict, key);
1519
0
  if (!node) {
1520
0
    return intval;
1521
0
  }
1522
0
  switch (plist_get_node_type(node)) {
1523
0
  case PLIST_INT:
1524
0
    plist_get_int_val(node, &intval);
1525
0
    break;
1526
0
  case PLIST_STRING:
1527
0
    strval = plist_get_string_ptr(node, NULL);
1528
0
    if (strval) {
1529
0
      intval = strtoll(strval, NULL, 0);
1530
0
    }
1531
0
    break;
1532
0
  case PLIST_DATA:
1533
0
    strval = (const char*)plist_get_data_ptr(node, &strsz);
1534
0
    if (strval) {
1535
0
      if (strsz == 8) {
1536
0
        intval = le64toh(*(int64_t*)strval);
1537
0
      } else if (strsz == 4) {
1538
0
        intval = le32toh(*(int32_t*)strval);
1539
0
      } else if (strsz == 2) {
1540
0
        intval = le16toh(*(int16_t*)strval);
1541
0
      } else if (strsz == 1) {
1542
0
        intval = strval[0];
1543
0
      } else {
1544
0
        PLIST_ERR("%s: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz);
1545
0
      }
1546
0
    }
1547
0
    break;
1548
0
  default:
1549
0
    break;
1550
0
  }
1551
0
  return intval;
1552
0
}
1553
1554
1555
uint64_t plist_dict_get_uint(plist_t dict, const char *key)
1556
0
{
1557
0
  uint64_t uintval = 0;
1558
0
  const char *strval = NULL;
1559
0
  uint64_t strsz = 0;
1560
0
  plist_t node = plist_dict_get_item(dict, key);
1561
0
  if (!node) {
1562
0
    return uintval;
1563
0
  }
1564
0
  switch (plist_get_node_type(node)) {
1565
0
  case PLIST_INT:
1566
0
    plist_get_uint_val(node, &uintval);
1567
0
    break;
1568
0
  case PLIST_STRING:
1569
0
    strval = plist_get_string_ptr(node, NULL);
1570
0
    if (strval) {
1571
0
      uintval = strtoull(strval, NULL, 0);
1572
0
    }
1573
0
    break;
1574
0
  case PLIST_DATA:
1575
0
    strval = (const char*)plist_get_data_ptr(node, &strsz);
1576
0
    if (strval) {
1577
0
      if (strsz == 8) {
1578
0
        uintval = le64toh(*(uint64_t*)strval);
1579
0
      } else if (strsz == 4) {
1580
0
        uintval = le32toh(*(uint32_t*)strval);
1581
0
      } else if (strsz == 2) {
1582
0
        uintval = le16toh(*(uint16_t*)strval);
1583
0
      } else if (strsz == 1) {
1584
0
        uintval = strval[0];
1585
0
      } else {
1586
0
        PLIST_ERR("%s: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz);
1587
0
      }
1588
0
    }
1589
0
    break;
1590
0
  default:
1591
0
    break;
1592
0
  }
1593
0
  return uintval;
1594
0
}
1595
1596
plist_err_t plist_dict_copy_item(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key)
1597
0
{
1598
0
  plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key);
1599
0
  if (!node) {
1600
0
    return PLIST_ERR_INVALID_ARG;
1601
0
  }
1602
0
  plist_dict_set_item(target_dict, key, plist_copy(node));
1603
0
  return PLIST_ERR_SUCCESS;
1604
0
}
1605
1606
plist_err_t plist_dict_copy_bool(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key)
1607
0
{
1608
0
  if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) {
1609
0
    return PLIST_ERR_INVALID_ARG;
1610
0
  }
1611
0
  uint8_t bval = plist_dict_get_bool(source_dict, (alt_source_key) ? alt_source_key : key);
1612
0
  plist_dict_set_item(target_dict, key, plist_new_bool(bval));
1613
0
  return PLIST_ERR_SUCCESS;
1614
0
}
1615
1616
plist_err_t plist_dict_copy_int(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key)
1617
0
{
1618
0
  if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) {
1619
0
    return PLIST_ERR_INVALID_ARG;
1620
0
  }
1621
0
  int64_t i64val = plist_dict_get_int(source_dict, (alt_source_key) ? alt_source_key : key);
1622
0
  plist_dict_set_item(target_dict, key, plist_new_int(i64val));
1623
0
  return PLIST_ERR_SUCCESS;
1624
0
}
1625
1626
plist_err_t plist_dict_copy_uint(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key)
1627
0
{
1628
0
  if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) {
1629
0
    return PLIST_ERR_INVALID_ARG;
1630
0
  }
1631
0
  uint64_t u64val = plist_dict_get_uint(source_dict, (alt_source_key) ? alt_source_key : key);
1632
0
  plist_dict_set_item(target_dict, key, plist_new_uint(u64val));
1633
0
  return PLIST_ERR_SUCCESS;
1634
0
}
1635
1636
plist_err_t plist_dict_copy_data(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key)
1637
0
{
1638
0
  plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key);
1639
0
  if (!PLIST_IS_DATA(node)) {
1640
0
    return PLIST_ERR_INVALID_ARG;
1641
0
  }
1642
0
  plist_dict_set_item(target_dict, key, plist_copy(node));
1643
0
  return PLIST_ERR_SUCCESS;
1644
0
}
1645
1646
plist_err_t plist_dict_copy_string(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key)
1647
0
{
1648
0
  plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key);
1649
0
  if (!PLIST_IS_STRING(node)) {
1650
0
    return PLIST_ERR_INVALID_ARG;
1651
0
  }
1652
0
  plist_dict_set_item(target_dict, key, plist_copy(node));
1653
0
  return PLIST_ERR_SUCCESS;
1654
0
}
1655
1656
plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v)
1657
0
{
1658
0
    plist_t current = plist;
1659
0
    plist_type type = PLIST_NONE;
1660
0
    uint32_t i = 0;
1661
1662
0
    for (i = 0; i < length && current; i++)
1663
0
    {
1664
0
        type = plist_get_node_type(current);
1665
1666
0
        if (type == PLIST_ARRAY)
1667
0
        {
1668
0
            uint32_t n = va_arg(v, uint32_t);
1669
0
            current = plist_array_get_item(current, n);
1670
0
        }
1671
0
        else if (type == PLIST_DICT)
1672
0
        {
1673
0
            const char* key = va_arg(v, const char*);
1674
0
            current = plist_dict_get_item(current, key);
1675
0
        }
1676
0
    }
1677
0
    return current;
1678
0
}
1679
1680
plist_t plist_access_path(plist_t plist, uint32_t length, ...)
1681
0
{
1682
0
    plist_t ret = NULL;
1683
0
    va_list v;
1684
1685
0
    va_start(v, length);
1686
0
    ret = plist_access_pathv(plist, length, v);
1687
0
    va_end(v);
1688
0
    return ret;
1689
0
}
1690
1691
static void plist_get_type_and_value(plist_t node, plist_type * type, void *value, uint64_t * length)
1692
133
{
1693
133
    plist_data_t data = NULL;
1694
1695
133
    if (!node || !type || !value || !length)
1696
0
        return;
1697
1698
133
    data = plist_get_data(node);
1699
133
    if (!data) return;
1700
1701
133
    *type = data->type;
1702
133
    *length = data->length;
1703
1704
133
    switch (*type)
1705
133
    {
1706
0
    case PLIST_BOOLEAN:
1707
0
        *((char *) value) = data->boolval;
1708
0
        break;
1709
133
    case PLIST_INT:
1710
133
    case PLIST_UID:
1711
133
        *((uint64_t *) value) = data->intval;
1712
133
        break;
1713
0
    case PLIST_REAL:
1714
0
    case PLIST_DATE:
1715
0
        *((double *) value) = data->realval;
1716
0
        break;
1717
0
    case PLIST_KEY:
1718
0
    case PLIST_STRING:
1719
0
        *((char **) value) = strdup(data->strval);
1720
0
        if (!*((char **) value)) {
1721
0
            PLIST_ERR("%s: strdup failed\n", __func__);
1722
0
            return;
1723
0
        }
1724
0
        break;
1725
0
    case PLIST_DATA:
1726
0
        *((uint8_t **) value) = (uint8_t *) malloc(*length * sizeof(uint8_t));
1727
0
        if (!*((uint8_t **) value)) {
1728
0
            PLIST_ERR("%s: malloc failed\n", __func__);
1729
0
            return;
1730
0
        }
1731
0
        memcpy(*((uint8_t **) value), data->buff, *length * sizeof(uint8_t));
1732
0
        break;
1733
0
    case PLIST_ARRAY:
1734
0
    case PLIST_DICT:
1735
0
    default:
1736
0
        break;
1737
133
    }
1738
133
}
1739
1740
plist_t plist_get_parent(plist_t node)
1741
0
{
1742
0
    return node ? (plist_t) ((node_t) node)->parent : NULL;
1743
0
}
1744
1745
plist_type plist_get_node_type(plist_t node)
1746
37.6M
{
1747
37.6M
    if (node)
1748
37.6M
    {
1749
37.6M
        plist_data_t data = plist_get_data(node);
1750
37.6M
        if (data)
1751
37.6M
            return data->type;
1752
37.6M
    }
1753
0
    return PLIST_NONE;
1754
37.6M
}
1755
1756
void plist_get_key_val(plist_t node, char **val)
1757
0
{
1758
0
    if (!node || !val)
1759
0
        return;
1760
0
    plist_type type = plist_get_node_type(node);
1761
0
    uint64_t length = 0;
1762
0
    if (PLIST_KEY != type)
1763
0
        return;
1764
0
    plist_get_type_and_value(node, &type, (void *) val, &length);
1765
0
    if (!*val)
1766
0
        return;
1767
0
    assert(length == strlen(*val));
1768
0
}
1769
1770
void plist_get_string_val(plist_t node, char **val)
1771
0
{
1772
0
    if (!node || !val)
1773
0
        return;
1774
0
    plist_type type = plist_get_node_type(node);
1775
0
    uint64_t length = 0;
1776
0
    if (PLIST_STRING != type)
1777
0
        return;
1778
0
    plist_get_type_and_value(node, &type, (void *) val, &length);
1779
0
    if (!*val)
1780
0
        return;
1781
0
    assert(length == strlen(*val));
1782
0
}
1783
1784
const char* plist_get_string_ptr(plist_t node, uint64_t* length)
1785
89.2k
{
1786
89.2k
    if (!node)
1787
0
        return NULL;
1788
89.2k
    plist_type type = plist_get_node_type(node);
1789
89.2k
    if (PLIST_STRING != type)
1790
0
        return NULL;
1791
89.2k
    plist_data_t data = plist_get_data(node);
1792
89.2k
    if (length)
1793
0
        *length = data->length;
1794
89.2k
    return (const char*)data->strval;
1795
89.2k
}
1796
1797
void plist_get_bool_val(plist_t node, uint8_t * val)
1798
0
{
1799
0
    if (!node || !val)
1800
0
        return;
1801
0
    plist_type type = plist_get_node_type(node);
1802
0
    uint64_t length = 0;
1803
0
    if (PLIST_BOOLEAN != type)
1804
0
        return;
1805
0
    plist_get_type_and_value(node, &type, (void *) val, &length);
1806
0
    assert(length == sizeof(uint8_t));
1807
0
}
1808
1809
void plist_get_uint_val(plist_t node, uint64_t * val)
1810
133
{
1811
133
    if (!node || !val)
1812
0
        return;
1813
133
    plist_type type = plist_get_node_type(node);
1814
133
    uint64_t length = 0;
1815
133
    if (PLIST_INT != type)
1816
0
        return;
1817
133
    plist_get_type_and_value(node, &type, (void *) val, &length);
1818
133
    assert(length == sizeof(uint64_t) || length == 16);
1819
133
}
1820
1821
void plist_get_int_val(plist_t node, int64_t * val)
1822
0
{
1823
0
    plist_get_uint_val(node, (uint64_t*)val);
1824
0
}
1825
1826
void plist_get_uid_val(plist_t node, uint64_t * val)
1827
0
{
1828
0
    if (!node || !val)
1829
0
        return;
1830
0
    plist_type type = plist_get_node_type(node);
1831
0
    uint64_t length = 0;
1832
0
    if (PLIST_UID != type)
1833
0
        return;
1834
0
    plist_get_type_and_value(node, &type, (void *) val, &length);
1835
0
    assert(length == sizeof(uint64_t));
1836
0
}
1837
1838
void plist_get_real_val(plist_t node, double *val)
1839
0
{
1840
0
    if (!node || !val)
1841
0
        return;
1842
0
    plist_type type = plist_get_node_type(node);
1843
0
    uint64_t length = 0;
1844
0
    if (PLIST_REAL != type)
1845
0
        return;
1846
0
    plist_get_type_and_value(node, &type, (void *) val, &length);
1847
0
    assert(length == sizeof(double));
1848
0
}
1849
1850
void plist_get_data_val(plist_t node, char **val, uint64_t * length)
1851
0
{
1852
0
    if (!node || !val || !length)
1853
0
        return;
1854
0
    plist_type type = plist_get_node_type(node);
1855
0
    if (PLIST_DATA != type)
1856
0
        return;
1857
0
    plist_get_type_and_value(node, &type, (void *) val, length);
1858
0
}
1859
1860
const char* plist_get_data_ptr(plist_t node, uint64_t* length)
1861
0
{
1862
0
    if (!node || !length)
1863
0
        return NULL;
1864
0
    plist_type type = plist_get_node_type(node);
1865
0
    if (PLIST_DATA != type)
1866
0
        return NULL;
1867
0
    plist_data_t data = plist_get_data(node);
1868
0
    *length = data->length;
1869
0
    return (const char*)data->buff;
1870
0
}
1871
1872
void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec)
1873
0
{
1874
0
    if (!node)
1875
0
        return;
1876
0
    plist_type type = plist_get_node_type(node);
1877
0
    uint64_t length = 0;
1878
0
    double val = 0;
1879
0
    if (PLIST_DATE != type)
1880
0
        return;
1881
0
    plist_get_type_and_value(node, &type, (void *) &val, &length);
1882
0
    assert(length == sizeof(double));
1883
0
    if (sec)
1884
0
        *sec = (int32_t)val;
1885
0
    if (usec)
1886
0
    {
1887
0
  val = fabs((val - (int64_t)val) * 1000000);
1888
0
        *usec = (int32_t)val;
1889
0
    }
1890
0
}
1891
1892
void plist_get_unix_date_val(plist_t node, int64_t *sec)
1893
0
{
1894
0
    if (!node || !sec)
1895
0
        return;
1896
0
    plist_type type = plist_get_node_type(node);
1897
0
    uint64_t length = 0;
1898
0
    double val = 0;
1899
0
    if (PLIST_DATE != type)
1900
0
        return;
1901
0
    plist_get_type_and_value(node, &type, (void *) &val, &length);
1902
0
    assert(length == sizeof(double));
1903
0
    *sec = (int64_t)val + MAC_EPOCH;
1904
0
}
1905
1906
int plist_data_compare(const void *a, const void *b)
1907
0
{
1908
0
    plist_data_t val_a = NULL;
1909
0
    plist_data_t val_b = NULL;
1910
1911
0
    if (a == b)
1912
0
        return TRUE;
1913
1914
0
    if (!a || !b)
1915
0
        return FALSE;
1916
1917
0
    val_a = plist_get_data((plist_t) a);
1918
0
    val_b = plist_get_data((plist_t) b);
1919
1920
0
    if (val_a == NULL && val_b == NULL)
1921
0
        return TRUE;
1922
1923
0
    if (val_a == NULL || val_b == NULL)
1924
0
        return FALSE;
1925
1926
0
    if (val_a->type != val_b->type)
1927
0
        return FALSE;
1928
1929
0
    switch (val_a->type)
1930
0
    {
1931
0
    case PLIST_BOOLEAN:
1932
0
    case PLIST_NULL:
1933
0
    case PLIST_INT:
1934
0
    case PLIST_REAL:
1935
0
    case PLIST_DATE:
1936
0
    case PLIST_UID:
1937
0
        return val_a->length == val_b->length
1938
0
            && val_a->intval == val_b->intval; // it is a union so this is sufficient
1939
1940
0
    case PLIST_KEY:
1941
0
    case PLIST_STRING:
1942
0
        if (!val_a->strval || !val_b->strval)
1943
0
            return val_a->strval == val_b->strval;
1944
0
        return strcmp(val_a->strval, val_b->strval) == 0;
1945
1946
0
    case PLIST_DATA: {
1947
0
        if (val_a->length != val_b->length)
1948
0
            return FALSE;
1949
0
        if (val_a->length == 0)
1950
0
            return TRUE;
1951
0
        return memcmp(val_a->buff, val_b->buff, val_a->length) == 0;
1952
0
    }
1953
0
    case PLIST_ARRAY:
1954
0
    case PLIST_DICT:
1955
        //compare pointer
1956
0
        return a == b;
1957
1958
0
    default:
1959
0
        return FALSE;
1960
0
    }
1961
0
}
1962
1963
char plist_compare_node_value(plist_t node_l, plist_t node_r)
1964
0
{
1965
0
    return plist_data_compare(node_l, node_r);
1966
0
}
1967
1968
static plist_err_t plist_set_element_val(plist_t node, plist_type type, const void *value, uint64_t length)
1969
131
{
1970
    //free previous allocated data
1971
131
    plist_data_t data = plist_get_data(node);
1972
131
    if (!data) { // a node should always have data attached
1973
0
        PLIST_ERR("%s: Failed to allocate plist data\n", __func__);
1974
0
        return PLIST_ERR_NO_MEM;
1975
0
    }
1976
1977
131
    if (node_first_child((node_t)node)) {
1978
131
        int r = plist_free_children((node_t)node);
1979
131
        if (r < 0) return PLIST_ERR_UNKNOWN;
1980
131
    }
1981
131
    _plist_free_data(data);
1982
1983
    //now handle value
1984
1985
131
    data->type = type;
1986
131
    data->length = length;
1987
1988
131
    switch (type)
1989
131
    {
1990
0
    case PLIST_BOOLEAN:
1991
0
        data->boolval = *((char *) value);
1992
0
        break;
1993
0
    case PLIST_INT:
1994
131
    case PLIST_UID:
1995
131
        data->intval = *((uint64_t *) value);
1996
131
        break;
1997
0
    case PLIST_REAL:
1998
0
    case PLIST_DATE:
1999
0
        data->realval = *((double *) value);
2000
0
        break;
2001
0
    case PLIST_KEY:
2002
0
    case PLIST_STRING:
2003
0
        data->strval = strdup((char *) value);
2004
0
        if (!data->strval) {
2005
0
            PLIST_ERR("%s: strdup failed\n", __func__);
2006
0
            return PLIST_ERR_NO_MEM;
2007
0
        }
2008
0
        break;
2009
0
    case PLIST_DATA:
2010
0
        data->buff = (uint8_t *) malloc(length);
2011
0
        if (!data->buff) {
2012
0
            PLIST_ERR("%s: malloc failed\n", __func__);
2013
0
            return PLIST_ERR_NO_MEM;
2014
0
        }
2015
0
        memcpy(data->buff, value, length);
2016
0
        break;
2017
0
    case PLIST_ARRAY:
2018
0
    case PLIST_DICT:
2019
0
    default:
2020
0
        break;
2021
131
    }
2022
131
    return PLIST_ERR_SUCCESS;
2023
131
}
2024
2025
void plist_set_key_val(plist_t node, const char *val)
2026
0
{
2027
0
    plist_t parent = plist_get_parent(node);
2028
0
    plist_t item = plist_dict_get_item(parent, val);
2029
0
    if (item) {
2030
0
        return;
2031
0
    }
2032
0
    plist_set_element_val(node, PLIST_KEY, val, strlen(val));
2033
0
}
2034
2035
void plist_set_string_val(plist_t node, const char *val)
2036
0
{
2037
0
    plist_set_element_val(node, PLIST_STRING, val, strlen(val));
2038
0
}
2039
2040
void plist_set_bool_val(plist_t node, uint8_t val)
2041
0
{
2042
0
    plist_set_element_val(node, PLIST_BOOLEAN, &val, sizeof(uint8_t));
2043
0
}
2044
2045
void plist_set_uint_val(plist_t node, uint64_t val)
2046
0
{
2047
0
    plist_set_element_val(node, PLIST_INT, &val, (val > INT64_MAX) ? sizeof(uint64_t)*2 : sizeof(uint64_t));
2048
0
}
2049
2050
void plist_set_int_val(plist_t node, int64_t val)
2051
0
{
2052
0
    plist_set_element_val(node, PLIST_INT, &val, sizeof(uint64_t));
2053
0
}
2054
2055
void plist_set_uid_val(plist_t node, uint64_t val)
2056
131
{
2057
131
    plist_set_element_val(node, PLIST_UID, &val, sizeof(uint64_t));
2058
131
}
2059
2060
void plist_set_real_val(plist_t node, double val)
2061
0
{
2062
0
    plist_set_element_val(node, PLIST_REAL, &val, sizeof(double));
2063
0
}
2064
2065
void plist_set_data_val(plist_t node, const char *val, uint64_t length)
2066
0
{
2067
0
    plist_set_element_val(node, PLIST_DATA, val, length);
2068
0
}
2069
2070
void plist_set_date_val(plist_t node, int32_t sec, int32_t usec)
2071
0
{
2072
0
    double val = (double)sec + (double)usec / 1000000;
2073
0
    plist_set_element_val(node, PLIST_DATE, &val, sizeof(double));
2074
0
}
2075
2076
void plist_set_unix_date_val(plist_t node, int64_t sec)
2077
0
{
2078
0
    double val = (double)(sec - MAC_EPOCH);
2079
0
    plist_set_element_val(node, PLIST_DATE, &val, sizeof(double));
2080
0
}
2081
2082
int plist_bool_val_is_true(plist_t boolnode)
2083
0
{
2084
0
    if (!PLIST_IS_BOOLEAN(boolnode)) {
2085
0
        return 0;
2086
0
    }
2087
0
    uint8_t bv = 0;
2088
0
    plist_get_bool_val(boolnode, &bv);
2089
0
    return (bv == 1);
2090
0
}
2091
2092
int plist_int_val_is_negative(plist_t intnode)
2093
0
{
2094
0
    if (!PLIST_IS_INT(intnode)) {
2095
0
        return 0;
2096
0
    }
2097
0
    plist_data_t data = plist_get_data(intnode);
2098
0
    if (data->length == 16) {
2099
0
        return 0;
2100
0
    }
2101
0
    if ((int64_t)data->intval < 0) {
2102
0
        return 1;
2103
0
    }
2104
0
    return 0;
2105
0
}
2106
2107
int plist_int_val_compare(plist_t uintnode, int64_t cmpval)
2108
0
{
2109
0
    if (!PLIST_IS_INT(uintnode)) {
2110
0
        return -1;
2111
0
    }
2112
0
    int64_t uintval = 0;
2113
0
    plist_get_int_val(uintnode, &uintval);
2114
0
    if (uintval == cmpval) {
2115
0
        return 0;
2116
0
    }
2117
2118
0
    if (uintval < cmpval) {
2119
0
        return -1;
2120
0
    }
2121
2122
0
    return 1;
2123
0
}
2124
2125
int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval)
2126
0
{
2127
0
    if (!PLIST_IS_INT(uintnode)) {
2128
0
        return -1;
2129
0
    }
2130
0
    uint64_t uintval = 0;
2131
0
    plist_get_uint_val(uintnode, &uintval);
2132
0
    if (uintval == cmpval) {
2133
0
        return 0;
2134
0
    }
2135
2136
0
    if (uintval < cmpval) {
2137
0
        return -1;
2138
0
    }
2139
2140
0
    return 1;
2141
0
}
2142
2143
int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval)
2144
0
{
2145
0
    if (!PLIST_IS_UID(uidnode)) {
2146
0
        return -1;
2147
0
    }
2148
0
    uint64_t uidval = 0;
2149
0
    plist_get_uid_val(uidnode, &uidval);
2150
0
    if (uidval == cmpval) {
2151
0
        return 0;
2152
0
    }
2153
2154
0
    if (uidval < cmpval) {
2155
0
        return -1;
2156
0
    }
2157
2158
0
    return 1;
2159
0
}
2160
2161
int plist_real_val_compare(plist_t realnode, double cmpval)
2162
0
{
2163
0
    if (!PLIST_IS_REAL(realnode)) {
2164
0
        return -1;
2165
0
    }
2166
0
    double a = 0;
2167
0
    double b = cmpval;
2168
0
    plist_get_real_val(realnode, &a);
2169
0
    double abs_a = fabs(a);
2170
0
    double abs_b = fabs(b);
2171
0
    double diff = fabs(a - b);
2172
0
    if (a == b) {
2173
0
        return 0;
2174
0
    }
2175
2176
0
    if (a == 0 || b == 0 || (abs_a + abs_b < DBL_MIN)) {
2177
0
        if (diff < (DBL_EPSILON * DBL_MIN)) {
2178
0
            return 0;
2179
0
        }
2180
2181
0
        if (a < b) {
2182
0
            return -1;
2183
0
        }
2184
0
    } else {
2185
0
        if ((diff / fmin(abs_a + abs_b, DBL_MAX)) < DBL_EPSILON) {
2186
0
            return 0;
2187
0
        }
2188
2189
0
        if (a < b) {
2190
0
            return -1;
2191
0
        }
2192
0
    }
2193
0
    return 1;
2194
0
}
2195
2196
int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec)
2197
0
{
2198
0
    if (!PLIST_IS_DATE(datenode)) {
2199
0
        return -1;
2200
0
    }
2201
0
    plist_data_t data = plist_get_data(datenode);
2202
0
    assert(data->length == sizeof(double));
2203
0
    double val = data->realval;
2204
0
    int32_t sec = (int32_t)val;
2205
0
    val = fabs((val - (int64_t)val) * 1000000);
2206
0
    int32_t usec = (int32_t)val;
2207
0
    uint64_t dateval = ((int64_t)sec << 32) | usec;
2208
0
    uint64_t cmpval = ((int64_t)cmpsec << 32) | cmpusec;
2209
0
    if (dateval == cmpval) {
2210
0
        return 0;
2211
0
    }
2212
2213
0
    if (dateval < cmpval) {
2214
0
        return -1;
2215
0
    }
2216
2217
0
    return 1;
2218
0
}
2219
2220
int plist_unix_date_val_compare(plist_t datenode, int64_t cmpval)
2221
0
{
2222
0
    if (!PLIST_IS_DATE(datenode)) {
2223
0
        return -1;
2224
0
    }
2225
0
    int64_t dateval = 0;
2226
0
    plist_get_unix_date_val(datenode, &dateval);
2227
0
    if (dateval == cmpval) {
2228
0
        return 0;
2229
0
    }
2230
2231
0
    if (dateval < cmpval) {
2232
0
        return -1;
2233
0
    }
2234
2235
0
    return 1;
2236
0
}
2237
2238
int plist_string_val_compare(plist_t strnode, const char* cmpval)
2239
0
{
2240
0
    if (!PLIST_IS_STRING(strnode)) {
2241
0
        return -1;
2242
0
    }
2243
0
    plist_data_t data = plist_get_data(strnode);
2244
0
    return strcmp(data->strval, cmpval);
2245
0
}
2246
2247
int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n)
2248
0
{
2249
0
    if (!PLIST_IS_STRING(strnode)) {
2250
0
        return -1;
2251
0
    }
2252
0
    plist_data_t data = plist_get_data(strnode);
2253
0
    return strncmp(data->strval, cmpval, n);
2254
0
}
2255
2256
int plist_string_val_contains(plist_t strnode, const char* substr)
2257
0
{
2258
0
    if (!PLIST_IS_STRING(strnode)) {
2259
0
        return 0;
2260
0
    }
2261
0
    plist_data_t data = plist_get_data(strnode);
2262
0
    return (strstr(data->strval, substr) != NULL);
2263
0
}
2264
2265
int plist_key_val_compare(plist_t keynode, const char* cmpval)
2266
0
{
2267
0
    if (!PLIST_IS_KEY(keynode)) {
2268
0
        return -1;
2269
0
    }
2270
0
    plist_data_t data = plist_get_data(keynode);
2271
0
    return strcmp(data->strval, cmpval);
2272
0
}
2273
2274
int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n)
2275
0
{
2276
0
    if (!PLIST_IS_KEY(keynode)) {
2277
0
        return -1;
2278
0
    }
2279
0
    plist_data_t data = plist_get_data(keynode);
2280
0
    return strncmp(data->strval, cmpval, n);
2281
0
}
2282
2283
int plist_key_val_contains(plist_t keynode, const char* substr)
2284
0
{
2285
0
    if (!PLIST_IS_KEY(keynode)) {
2286
0
        return 0;
2287
0
    }
2288
0
    plist_data_t data = plist_get_data(keynode);
2289
0
    return (strstr(data->strval, substr) != NULL);
2290
0
}
2291
2292
int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n)
2293
0
{
2294
0
    if (!PLIST_IS_DATA(datanode)) {
2295
0
        return -1;
2296
0
    }
2297
0
    plist_data_t data = plist_get_data(datanode);
2298
0
    if (data->length < n) {
2299
0
        return -1;
2300
0
    }
2301
2302
0
    if (data->length > n) {
2303
0
        return 1;
2304
0
    }
2305
2306
0
    return memcmp(data->buff, cmpval, n);
2307
0
}
2308
2309
int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* cmpval, size_t n)
2310
0
{
2311
0
    if (!PLIST_IS_DATA(datanode)) {
2312
0
        return -1;
2313
0
    }
2314
0
    plist_data_t data = plist_get_data(datanode);
2315
0
    if (data->length < n) {
2316
0
        return -1;
2317
0
    }
2318
0
    return memcmp(data->buff, cmpval, n);
2319
0
}
2320
2321
int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n)
2322
0
{
2323
0
    if (!PLIST_IS_DATA(datanode)) {
2324
0
        return -1;
2325
0
    }
2326
0
    plist_data_t data = plist_get_data(datanode);
2327
0
    return (memmem(data->buff, data->length, cmpval, n) != NULL);
2328
0
}
2329
2330
extern void plist_xml_set_debug(int debug);
2331
extern void plist_bin_set_debug(int debug);
2332
extern void plist_json_set_debug(int debug);
2333
extern void plist_ostep_set_debug(int debug);
2334
2335
void plist_set_debug(int debug)
2336
0
{
2337
0
#if DEBUG
2338
0
    plist_debug = debug;
2339
0
#endif
2340
0
    plist_xml_set_debug(debug);
2341
0
    plist_bin_set_debug(debug);
2342
0
    plist_json_set_debug(debug);
2343
0
    plist_ostep_set_debug(debug);
2344
0
}
2345
2346
void plist_sort(plist_t plist)
2347
0
{
2348
0
    if (!plist) {
2349
0
        return;
2350
0
    }
2351
0
    if (PLIST_IS_ARRAY(plist)) {
2352
0
        uint32_t n = plist_array_get_size(plist);
2353
0
        uint32_t i = 0;
2354
0
        for (i = 0; i < n; i++) {
2355
0
            plist_sort(plist_array_get_item(plist, i));
2356
0
        }
2357
0
    } else if (PLIST_IS_DICT(plist)) {
2358
0
        node_t node = (node_t)plist;
2359
0
        node_t ch;
2360
0
        if (!node_first_child(node)) {
2361
0
            return;
2362
0
        }
2363
0
        for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
2364
0
            ch = node_next_sibling(ch);
2365
0
            plist_sort((plist_t)ch);
2366
0
        }
2367
0
        #define KEY_DATA(x) (x->data)
2368
0
        #define NEXT_KEY(x) (x->next->next)
2369
0
        #define KEY_STRVAL(x) ((plist_data_t)(KEY_DATA(x)))->strval
2370
0
        int swapped = 0;
2371
0
        do {
2372
0
            swapped = 0;
2373
0
            node_t lptr = NULL;
2374
0
            node_t cur_key = node_first_child((node_t)plist);
2375
2376
0
            while (NEXT_KEY(cur_key) != lptr) {
2377
0
                node_t next_key = NEXT_KEY(cur_key);
2378
0
                if (strcmp(KEY_STRVAL(cur_key), KEY_STRVAL(next_key)) > 0) {
2379
0
                    node_t cur_val = cur_key->next;
2380
0
                    node_t next_val = next_key->next;
2381
                    // we need to swap 2 consecutive nodes with the 2 after them
2382
                    // a -> b -> [c] -> [d] -> [e] -> [f] -> g -> h
2383
                    //              cur           next
2384
                    // swapped:
2385
                    // a -> b -> [e] -> [f] -> [c] -> [d] -> g -> h
2386
                    //              next           cur
2387
0
                    node_t tmp_prev = cur_key->prev;
2388
0
                    node_t tmp_next = next_val->next;
2389
0
                    cur_key->prev = next_val;
2390
0
                    cur_val->next = tmp_next;
2391
0
                    next_val->next = cur_key;
2392
0
                    next_key->prev = tmp_prev;
2393
0
                    if (tmp_prev) {
2394
0
                        tmp_prev->next = next_key;
2395
0
                    } else {
2396
0
                        ((node_t)plist)->children->begin = next_key;
2397
0
                    }
2398
0
                    if (tmp_next) {
2399
0
                        tmp_next->prev = cur_val;
2400
0
                    } else {
2401
0
                        ((node_t)plist)->children->end = cur_val;
2402
0
                    }
2403
0
                    cur_key = next_key;
2404
0
                    swapped = 1;
2405
0
                }
2406
0
                cur_key = NEXT_KEY(cur_key);
2407
0
            }
2408
0
            lptr = cur_key;
2409
0
        } while (swapped);
2410
0
    }
2411
0
}
2412
2413
plist_err_t plist_write_to_string(plist_t plist, char **output, uint32_t* length, plist_format_t format, plist_write_options_t options)
2414
0
{
2415
0
    plist_err_t err = PLIST_ERR_UNKNOWN;
2416
0
    switch (format) {
2417
0
        case PLIST_FORMAT_XML:
2418
0
            err = plist_to_xml(plist, output, length);
2419
0
            break;
2420
0
        case PLIST_FORMAT_JSON:
2421
0
            err = plist_to_json_with_options(plist, output, length, options);
2422
0
            break;
2423
0
        case PLIST_FORMAT_OSTEP:
2424
0
            err = plist_to_openstep_with_options(plist, output, length, options);
2425
0
            break;
2426
0
        case PLIST_FORMAT_PRINT:
2427
0
            err = plist_write_to_string_default(plist, output, length, options);
2428
0
            break;
2429
0
        case PLIST_FORMAT_LIMD:
2430
0
            err = plist_write_to_string_limd(plist, output, length, options);
2431
0
            break;
2432
0
        case PLIST_FORMAT_PLUTIL:
2433
0
            err = plist_write_to_string_plutil(plist, output, length, options);
2434
0
            break;
2435
0
        default:
2436
            // unsupported output format
2437
0
            err = PLIST_ERR_FORMAT;
2438
0
            break;
2439
0
    }
2440
0
    return err;
2441
0
}
2442
2443
plist_err_t plist_write_to_stream(plist_t plist, FILE *stream, plist_format_t format, plist_write_options_t options)
2444
0
{
2445
0
    if (!plist || !stream) {
2446
0
        return PLIST_ERR_INVALID_ARG;
2447
0
    }
2448
0
    plist_err_t err = PLIST_ERR_UNKNOWN;
2449
0
    char *output = NULL;
2450
0
    uint32_t length = 0;
2451
0
    switch (format) {
2452
0
        case PLIST_FORMAT_BINARY:
2453
0
            err = plist_to_bin(plist, &output, &length);
2454
0
            break;
2455
0
        case PLIST_FORMAT_XML:
2456
0
            err = plist_to_xml(plist, &output, &length);
2457
0
            break;
2458
0
        case PLIST_FORMAT_JSON:
2459
0
            err = plist_to_json_with_options(plist, &output, &length, options);
2460
0
            break;
2461
0
        case PLIST_FORMAT_OSTEP:
2462
0
            err = plist_to_openstep_with_options(plist, &output, &length, options);
2463
0
            break;
2464
0
        case PLIST_FORMAT_PRINT:
2465
0
            err = plist_write_to_stream_default(plist, stream, options);
2466
0
            break;
2467
0
        case PLIST_FORMAT_LIMD:
2468
0
            err = plist_write_to_stream_limd(plist, stream, options);
2469
0
            break;
2470
0
        case PLIST_FORMAT_PLUTIL:
2471
0
            err = plist_write_to_stream_plutil(plist, stream, options);
2472
0
            break;
2473
0
        default:
2474
            // unsupported output format
2475
0
            err = PLIST_ERR_FORMAT;
2476
0
            break;
2477
0
    }
2478
0
    if (output && err == PLIST_ERR_SUCCESS) {
2479
0
        if (fwrite(output, 1, length, stream) < length) {
2480
0
            err = PLIST_ERR_IO;
2481
0
        }
2482
0
        free(output);
2483
0
    }
2484
0
    return err;
2485
0
}
2486
2487
plist_err_t plist_write_to_file(plist_t plist, const char* filename, plist_format_t format, plist_write_options_t options)
2488
0
{
2489
0
    if (!plist || !filename) {
2490
0
        return PLIST_ERR_INVALID_ARG;
2491
0
    }
2492
0
    FILE* f = fopen(filename, "wb");
2493
0
    if (!f) {
2494
0
        return PLIST_ERR_IO;
2495
0
    }
2496
0
    plist_err_t err = plist_write_to_stream(plist, f, format, options);
2497
0
    fclose(f);
2498
0
    return err;
2499
0
}
2500
2501
void plist_print(plist_t plist)
2502
0
{
2503
0
     plist_write_to_stream(plist, stdout, PLIST_FORMAT_PRINT, PLIST_OPT_PARTIAL_DATA);
2504
0
}
2505
2506
const char* libplist_version()
2507
0
{
2508
#ifndef PACKAGE_VERSION
2509
#error PACKAGE_VERSION is not defined!
2510
#endif
2511
0
  return PACKAGE_VERSION;
2512
0
}