/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 | } |