diff options
Diffstat (limited to 'src/plist.c')
| -rw-r--r-- | src/plist.c | 1982 |
1 files changed, 1557 insertions, 425 deletions
diff --git a/src/plist.c b/src/plist.c index 27f90b1..2ad1b0a 100644 --- a/src/plist.c +++ b/src/plist.c @@ -2,7 +2,7 @@ * plist.c * Builds plist XML structures * - * Copyright (c) 2009-2019 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2009-2023 Nikias Bassen, All Rights Reserved. * Copyright (c) 2010-2015 Martin Szulecki, All Rights Reserved. * Copyright (c) 2008 Zach C., All Rights Reserved. * @@ -34,88 +34,130 @@ #include <assert.h> #include <limits.h> #include <float.h> +#include <ctype.h> +#include <inttypes.h> #ifdef WIN32 #include <windows.h> -#else -#include <pthread.h> #endif #include <node.h> +#include <node_list.h> #include <hashtable.h> #include <ptrarray.h> -extern void plist_xml_init(void); -extern void plist_xml_deinit(void); -extern void plist_bin_init(void); -extern void plist_bin_deinit(void); +#define MAC_EPOCH 978307200 -static void internal_plist_init(void) -{ - plist_bin_init(); - plist_xml_init(); -} +#ifdef _MSC_VER +typedef SSIZE_T ssize_t; +#endif -static void internal_plist_deinit(void) -{ - plist_bin_deinit(); - plist_xml_deinit(); -} +#ifdef DEBUG +static int plist_debug = 0; +#define PLIST_ERR(...) if (plist_debug > 0) { fprintf(stderr, "libplist ERROR: " __VA_ARGS__); } +#else +#define PLIST_ERR(...) +#endif -#ifdef WIN32 +#ifndef bswap16 +#define bswap16(x) ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) +#endif -typedef volatile struct { - LONG lock; - int state; -} thread_once_t; +#ifndef bswap32 +#define bswap32(x) ((((x) & 0xFF000000) >> 24) \ + | (((x) & 0x00FF0000) >> 8) \ + | (((x) & 0x0000FF00) << 8) \ + | (((x) & 0x000000FF) << 24)) +#endif -static thread_once_t init_once = {0, 0}; -static thread_once_t deinit_once = {0, 0}; +#ifndef bswap64 +#define bswap64(x) ((((x) & 0xFF00000000000000ull) >> 56) \ + | (((x) & 0x00FF000000000000ull) >> 40) \ + | (((x) & 0x0000FF0000000000ull) >> 24) \ + | (((x) & 0x000000FF00000000ull) >> 8) \ + | (((x) & 0x00000000FF000000ull) << 8) \ + | (((x) & 0x0000000000FF0000ull) << 24) \ + | (((x) & 0x000000000000FF00ull) << 40) \ + | (((x) & 0x00000000000000FFull) << 56)) +#endif -void thread_once(thread_once_t *once_control, void (*init_routine)(void)) -{ - while (InterlockedExchange(&(once_control->lock), 1) != 0) { - Sleep(1); - } - if (!once_control->state) { - once_control->state = 1; - init_routine(); - } - InterlockedExchange(&(once_control->lock), 0); -} +#ifndef le16toh +#ifdef __BIG_ENDIAN__ +#define le16toh(x) bswap16(x) +#else +#define le16toh(x) (x) +#endif +#endif -BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) -{ - switch (dwReason) { - case DLL_PROCESS_ATTACH: - thread_once(&init_once, internal_plist_init); - break; - case DLL_PROCESS_DETACH: - thread_once(&deinit_once, internal_plist_deinit); - break; - default: - break; - } - return 1; -} +#ifndef le32toh +#ifdef __BIG_ENDIAN__ +#define le32toh(x) bswap32(x) +#else +#define le32toh(x) (x) +#endif +#endif +#ifndef le64toh +#ifdef __BIG_ENDIAN__ +#define le64toh(x) bswap64(x) #else +#define le64toh(x) (x) +#endif +#endif -static pthread_once_t init_once = PTHREAD_ONCE_INIT; -static pthread_once_t deinit_once = PTHREAD_ONCE_INIT; +// Reference: https://stackoverflow.com/a/2390626/1806760 +// Initializer/finalizer sample for MSVC and GCC/Clang. +// 2010-2016 Joe Lowe. Released into the public domain. + +#ifdef __cplusplus + #define INITIALIZER(f) \ + static void f(void); \ + struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \ + static void f(void) +#elif defined(_MSC_VER) + #pragma section(".CRT$XCU",read) + #define INITIALIZER2_(f,p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ + __pragma(comment(linker,"/include:" p #f "_")) \ + static void f(void) + #ifdef _WIN64 + #define INITIALIZER(f) INITIALIZER2_(f,"") + #else + #define INITIALIZER(f) INITIALIZER2_(f,"_") + #endif +#else + #define INITIALIZER(f) \ + static void f(void) __attribute__((__constructor__)); \ + static void f(void) +#endif -static void __attribute__((constructor)) libplist_initialize(void) +extern void plist_xml_init(void); +extern void plist_xml_deinit(void); +extern void plist_bin_init(void); +extern void plist_bin_deinit(void); +extern void plist_json_init(void); +extern void plist_json_deinit(void); +extern void plist_ostep_init(void); +extern void plist_ostep_deinit(void); + +static void internal_plist_deinit(void) { - pthread_once(&init_once, internal_plist_init); + plist_bin_deinit(); + plist_xml_deinit(); + plist_json_deinit(); + plist_ostep_deinit(); } -static void __attribute__((destructor)) libplist_deinitialize(void) +INITIALIZER(internal_plist_init) { - pthread_once(&deinit_once, internal_plist_deinit); + plist_bin_init(); + plist_xml_init(); + plist_json_init(); + plist_ostep_init(); + atexit(internal_plist_deinit); } -#endif - #ifndef HAVE_MEMMEM // see https://sourceware.org/legacy-ml/libc-alpha/2007-12/msg00000.html @@ -166,28 +208,140 @@ void* memmem(const void* haystack, size_t haystack_len, const void* needle, size } #endif -PLIST_API int plist_is_binary(const char *plist_data, uint32_t length) +int plist_is_binary(const char *plist_data, uint32_t length) { - if (length < 8) { + if (plist_data == NULL || length < 8) { return 0; } return (memcmp(plist_data, "bplist00", 8) == 0); } +#define SKIP_WS(blob, pos, len) \ + while (pos < len && ((blob[pos] == ' ') || (blob[pos] == '\t') || (blob[pos] == '\r') || (blob[pos] == '\n'))) pos++; +#define FIND_NEXT(blob, pos, len, chr) \ + while (pos < len && (blob[pos] != chr)) pos++; -PLIST_API void plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist) +plist_err_t plist_from_memory(const char *plist_data, uint32_t length, plist_t *plist, plist_format_t *format) { - if (length < 8) { - *plist = NULL; - return; + plist_err_t res = PLIST_ERR_UNKNOWN; + if (!plist) { + return PLIST_ERR_INVALID_ARG; } - + *plist = NULL; + if (!plist_data || length == 0) { + return PLIST_ERR_INVALID_ARG; + } + plist_format_t fmt = PLIST_FORMAT_NONE; + if (format) *format = PLIST_FORMAT_NONE; if (plist_is_binary(plist_data, length)) { - plist_from_bin(plist_data, length, plist); + res = plist_from_bin(plist_data, length, plist); + fmt = PLIST_FORMAT_BINARY; } else { - plist_from_xml(plist_data, length, plist); + uint32_t pos = 0; + int is_json = 0; + int is_xml = 0; + /* skip whitespace */ + SKIP_WS(plist_data, pos, length); + if (pos >= length) { + return PLIST_ERR_PARSE; + } + if (plist_data[pos] == '<' && (length-pos > 3) && !isxdigit(plist_data[pos+1]) && !isxdigit(plist_data[pos+2]) && !isxdigit(plist_data[pos+3])) { + is_xml = 1; + } else if (plist_data[pos] == '[') { + /* only valid for json */ + is_json = 1; + } else if (plist_data[pos] == '(') { + /* only valid for openstep */ + } else if (plist_data[pos] == '{') { + /* this could be json or openstep */ + pos++; + SKIP_WS(plist_data, pos, length); + if (pos >= length) { + return PLIST_ERR_PARSE; + } + if (plist_data[pos] == '"') { + /* still could be both */ + pos++; + while (pos < length) { + FIND_NEXT(plist_data, pos, length, '"'); + if (plist_data[pos-1] != '\\') { + break; + } + pos++; + } + if (pos >= length) { + return PLIST_ERR_PARSE; + } + if (plist_data[pos] == '"') { + pos++; + SKIP_WS(plist_data, pos, length); + if (pos >= length) { + return PLIST_ERR_PARSE; + } + if (plist_data[pos] == ':') { + /* this is definitely json */ + is_json = 1; + } + } + } + } + if (is_xml) { + res = plist_from_xml(plist_data, length, plist); + fmt = PLIST_FORMAT_XML; + } else if (is_json) { + res = plist_from_json(plist_data, length, plist); + fmt = PLIST_FORMAT_JSON; + } else { + res = plist_from_openstep(plist_data, length, plist); + fmt = PLIST_FORMAT_OSTEP; + } + } + if (format && res == PLIST_ERR_SUCCESS) { + *format = fmt; } + return res; +} + +plist_err_t plist_read_from_file(const char *filename, plist_t *plist, plist_format_t *format) +{ + if (!filename || !plist) { + return PLIST_ERR_INVALID_ARG; + } + FILE *f = fopen(filename, "rb"); + if (!f) { + return PLIST_ERR_IO; + } + struct stat fst; + fstat(fileno(f), &fst); + if ((uint64_t)fst.st_size > UINT32_MAX) { + return PLIST_ERR_NO_MEM; + } + uint32_t total = (uint32_t)fst.st_size; + if (total == 0) { + return PLIST_ERR_PARSE; + } + char *buf = (char*)malloc(total); + if (!buf) { + fclose(f); + return PLIST_ERR_NO_MEM; + } + uint32_t done = 0; + while (done < total) { + ssize_t r = fread(buf + done, 1, total - done, f); + if (r <= 0) { + break; + } + done += r; + } + fclose(f); + if (done < total) { + free(buf); + return PLIST_ERR_IO; + } + plist_err_t res = plist_from_memory(buf, total, plist, format); + free(buf); + return res; } plist_t plist_new_node(plist_data_t data) @@ -195,17 +349,16 @@ plist_t plist_new_node(plist_data_t data) return (plist_t) node_create(NULL, data); } -plist_data_t plist_get_data(const plist_t node) +plist_data_t plist_get_data(plist_t node) { if (!node) return NULL; - return ((node_t*)node)->data; + return (plist_data_t)((node_t)node)->data; } plist_data_t plist_new_plist_data(void) { - plist_data_t data = (plist_data_t) calloc(sizeof(struct plist_data_s), 1); - return data; + return (plist_data_t) calloc(1, sizeof(struct plist_data_s)); } static unsigned int dict_key_hash(const void *data) @@ -233,62 +386,162 @@ static int dict_key_compare(const void* a, const void* b) return (strcmp(data_a->strval, data_b->strval) == 0) ? TRUE : FALSE; } -void plist_free_data(plist_data_t data) +static void _plist_free_data(plist_data_t data) { - if (data) - { - switch (data->type) - { + if (!data) return; + switch (data->type) { case PLIST_KEY: case PLIST_STRING: free(data->strval); + data->strval = NULL; break; case PLIST_DATA: free(data->buff); + data->buff = NULL; break; case PLIST_ARRAY: - ptr_array_free(data->hashtable); + ptr_array_free((ptrarray_t*)data->hashtable); + data->hashtable = NULL; break; - case PLIST_DICT: - hash_table_destroy(data->hashtable); + case PLIST_DICT: { + hashtable_t *ht = (hashtable_t*)data->hashtable; + // PLIST_DICT hashtables must not own/free values; values are freed via node tree. + assert(!ht || ht->free_func == NULL); + if (ht) ht->free_func = NULL; + hash_table_destroy(ht); + data->hashtable = NULL; break; + } default: break; + } +} + +void plist_free_data(plist_data_t data) +{ + if (!data) return; + _plist_free_data(data); + free(data); +} + +static int plist_free_children(node_t root) +{ + if (!root) return NODE_ERR_INVALID_ARG; + + if (!node_first_child(root)) { + return NODE_ERR_SUCCESS; + } + + size_t cap = 64, sp = 0; + node_t *stack = (node_t*)malloc(cap * sizeof(*stack)); + if (!stack) return NODE_ERR_NO_MEM; + + // Push *direct* children onto the stack, detached from root. + for (;;) { + node_t ch = node_first_child(root); + if (!ch) break; + + int di = node_detach(root, ch); + if (di < 0) { + free(stack); + return di; } - free(data); + + if (sp == cap) { + cap += 64; + node_t *tmp = (node_t*)realloc(stack, cap * sizeof(*stack)); + if (!tmp) { + free(stack); + return NODE_ERR_NO_MEM; + } + stack = tmp; + } + stack[sp++] = ch; + } + + // Now free the detached subtree nodes (and their descendants). + while (sp) { + node_t node = stack[sp - 1]; + node_t ch = node_first_child(node); + if (ch) { + int di = node_detach(node, ch); + if (di < 0) { + free(stack); + return di; + } + + if (sp == cap) { + cap += 64; + node_t *tmp = (node_t*)realloc(stack, cap * sizeof(*stack)); + if (!tmp) { + free(stack); + return NODE_ERR_NO_MEM; + } + stack = tmp; + } + stack[sp++] = ch; + continue; + } + + plist_data_t data = plist_get_data(node); + plist_free_data(data); + node->data = NULL; + + node_destroy(node); + + sp--; } + + free(stack); + return NODE_ERR_SUCCESS; } -static int plist_free_node(node_t* node) +static int plist_free_node(node_t root) { - plist_data_t data = NULL; - int node_index = node_detach(node->parent, node); - data = plist_get_data(node); - plist_free_data(data); - node->data = NULL; + if (!root) return NODE_ERR_INVALID_ARG; + + int root_index = -1; - node_t *ch; - for (ch = node_first_child(node); ch; ) { - node_t *next = node_next_sibling(ch); - plist_free_node(ch); - ch = next; + if (root->parent) { + root_index = node_detach(root->parent, root); + if (root_index < 0) { + return root_index; + } + } + + int r = plist_free_children(root); + if (r < 0) { + // root is already detached; caller should treat as error. + return r; } - node_destroy(node); + plist_data_t data = plist_get_data(root); + plist_free_data(data); + root->data = NULL; - return node_index; + node_destroy(root); + + return root_index; } -PLIST_API plist_t plist_new_dict(void) +plist_t plist_new_dict(void) { plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } data->type = PLIST_DICT; return plist_new_node(data); } -PLIST_API plist_t plist_new_array(void) +plist_t plist_new_array(void) { plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } data->type = PLIST_ARRAY; return plist_new_node(data); } @@ -297,282 +550,598 @@ PLIST_API plist_t plist_new_array(void) static plist_t plist_new_key(const char *val) { plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } data->type = PLIST_KEY; data->strval = strdup(val); - data->length = strlen(val); + if (!data->strval) { + plist_free_data(data); + PLIST_ERR("%s: strdup failed\n", __func__); + return NULL; + } else { + data->length = strlen(val); + } return plist_new_node(data); } -PLIST_API plist_t plist_new_string(const char *val) +plist_t plist_new_string(const char *val) { plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } data->type = PLIST_STRING; data->strval = strdup(val); - data->length = strlen(val); + if (!data->strval) { + plist_free_data(data); + PLIST_ERR("%s: strdup failed\n", __func__); + return NULL; + } else { + data->length = strlen(val); + } return plist_new_node(data); } -PLIST_API plist_t plist_new_bool(uint8_t val) +plist_t plist_new_bool(uint8_t val) { plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } data->type = PLIST_BOOLEAN; data->boolval = val; data->length = sizeof(uint8_t); return plist_new_node(data); } -PLIST_API plist_t plist_new_uint(uint64_t val) +plist_t plist_new_uint(uint64_t val) { plist_data_t data = plist_new_plist_data(); - data->type = PLIST_UINT; + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } + data->type = PLIST_INT; + data->intval = val; + data->length = (val > INT_MAX) ? sizeof(uint64_t)*2 : sizeof(uint64_t); + return plist_new_node(data); +} + +plist_t plist_new_int(int64_t val) +{ + plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } + data->type = PLIST_INT; data->intval = val; data->length = sizeof(uint64_t); return plist_new_node(data); } -PLIST_API plist_t plist_new_uid(uint64_t val) +plist_t plist_new_uid(uint64_t val) { plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } data->type = PLIST_UID; data->intval = val; data->length = sizeof(uint64_t); return plist_new_node(data); } -PLIST_API plist_t plist_new_real(double val) +plist_t plist_new_real(double val) { plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } data->type = PLIST_REAL; data->realval = val; data->length = sizeof(double); return plist_new_node(data); } -PLIST_API plist_t plist_new_data(const char *val, uint64_t length) +plist_t plist_new_data(const char *val, uint64_t length) { plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } data->type = PLIST_DATA; - data->buff = (uint8_t *) malloc(length); - memcpy(data->buff, val, length); + if (val && length) { + data->buff = (uint8_t *) malloc(length); + if (!data->buff) { + PLIST_ERR("%s: failed to allocate %" PRIu64 " bytes\n", __func__, length); + return NULL; + } + memcpy(data->buff, val, length); + } data->length = length; return plist_new_node(data); } -PLIST_API plist_t plist_new_date(int32_t sec, int32_t usec) +plist_t plist_new_date(int32_t sec, int32_t usec) { plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } data->type = PLIST_DATE; data->realval = (double)sec + (double)usec / 1000000; data->length = sizeof(double); return plist_new_node(data); } -PLIST_API void plist_free(plist_t plist) +plist_t plist_new_unix_date(int64_t sec) +{ + plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } + data->type = PLIST_DATE; + data->realval = (double)sec - MAC_EPOCH; + data->length = sizeof(double); + return plist_new_node(data); +} + +plist_t plist_new_null(void) +{ + plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_ERR("%s: failed to allocate plist data\n", __func__); + return NULL; + } + data->type = PLIST_NULL; + data->intval = 0; + data->length = 0; + return plist_new_node(data); +} + +void plist_free(plist_t plist) { if (plist) { - plist_free_node(plist); + plist_free_node((node_t)plist); + } +} + +void plist_mem_free(void* ptr) +{ + if (ptr) + { + free(ptr); } } -static plist_t plist_copy_node(node_t *node) +static int plist_copy_node_shallow(node_t node, plist_t *out_newnode, plist_data_t *out_newdata, plist_type *out_type) { - plist_type node_type = PLIST_NONE; - plist_t newnode = NULL; + if (!node || !out_newnode || !out_newdata || !out_type) return NODE_ERR_INVALID_ARG; + plist_data_t data = plist_get_data(node); - plist_data_t newdata = plist_new_plist_data(); + if (!data) return NODE_ERR_INVALID_ARG; - assert(data); // plist should always have data - assert(newdata); + plist_data_t newdata = plist_new_plist_data(); + if (!newdata) return NODE_ERR_NO_MEM; memcpy(newdata, data, sizeof(struct plist_data_s)); - node_type = plist_get_node_type(node); + plist_type node_type = plist_get_node_type(node); switch (node_type) { case PLIST_DATA: - newdata->buff = (uint8_t *) malloc(data->length); - memcpy(newdata->buff, data->buff, data->length); + if (data->buff) { + newdata->buff = (uint8_t*)malloc(data->length); + if (!newdata->buff) { + plist_free_data(newdata); + return NODE_ERR_NO_MEM; + } + memcpy(newdata->buff, data->buff, data->length); + } else { + newdata->buff = NULL; + newdata->length = 0; + } break; + case PLIST_KEY: case PLIST_STRING: - newdata->strval = strdup((char *) data->strval); + if (data->strval) { + size_t n = strlen(data->strval); + newdata->strval = (char*)malloc(n+1); + if (!newdata->strval) { + plist_free_data(newdata); + return NODE_ERR_NO_MEM; + } + memcpy(newdata->strval, data->strval, n+1); + newdata->length = (uint64_t)n; + } else { + newdata->strval = NULL; + newdata->length = 0; + } break; + case PLIST_ARRAY: if (data->hashtable) { ptrarray_t* pa = ptr_array_new(((ptrarray_t*)data->hashtable)->capacity); - assert(pa); + if (!pa) { + plist_free_data(newdata); + return NODE_ERR_NO_MEM; + } newdata->hashtable = pa; } break; + case PLIST_DICT: if (data->hashtable) { hashtable_t* ht = hash_table_new(dict_key_hash, dict_key_compare, NULL); - assert(ht); + if (!ht) { + plist_free_data(newdata); + return NODE_ERR_NO_MEM; + } newdata->hashtable = ht; } break; + default: break; } - newnode = plist_new_node(newdata); - - node_t *ch; - unsigned int node_index = 0; - for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { - /* copy child node */ - plist_t newch = plist_copy_node(ch); - /* attach to new parent node */ - node_attach(newnode, newch); - /* if needed, add child node to lookup table of parent node */ - switch (node_type) { + + plist_t newnode = plist_new_node(newdata); + if (!newnode) { + plist_free_data(newdata); + return NODE_ERR_NO_MEM; + } + + *out_newnode = newnode; + *out_newdata = newdata; + *out_type = node_type; + return NODE_ERR_SUCCESS; +} + +static plist_t plist_copy_node(node_t root) +{ + typedef struct copy_frame { + node_t orig; // original node + plist_t copy; // copied node + plist_data_t copydata; // copied node's data (for cache updates) + plist_type type; // copied node type + node_t next_child; // next child of orig to process + unsigned int node_index; // child index (for dict key/value odd/even) + int depth; // optional depth tracking + } copy_frame_t; + + if (!root) return NULL; + + // shallow-copy root first + plist_t newroot = NULL; + plist_data_t newroot_data = NULL; + plist_type newroot_type = PLIST_NONE; + + int r = plist_copy_node_shallow(root, &newroot, &newroot_data, &newroot_type); + if (r != NODE_ERR_SUCCESS) { + PLIST_ERR("%s: shallow node copy failed (%d)\n", __func__, r); + return NULL; + } + + // stack of frames + size_t cap = 64, sp = 0; + copy_frame_t *st = (copy_frame_t*)malloc(cap * sizeof(*st)); + if (!st) { + plist_free_node((node_t)newroot); + return NULL; + } + + copy_frame_t cf; + cf.orig = root; + cf.copy = newroot; + cf.copydata = newroot_data; + cf.type = newroot_type; + cf.next_child = node_first_child(root); + cf.node_index = 0; + cf.depth = 0; + st[sp++] = cf; + + while (sp) { + copy_frame_t *f = &st[sp - 1]; + + if (f->depth > NODE_MAX_DEPTH) { + plist_free_node((node_t)newroot); + free(st); + PLIST_ERR("%s: maximum nesting depth exceeded\n", __func__); + return NULL; + } + + // done with this node? + if (!f->next_child) { + sp--; + continue; + } + + // take next child and advance iterator + node_t ch = f->next_child; + f->next_child = node_next_sibling(ch); + + // shallow copy child + plist_t newch = NULL; + plist_data_t newch_data = NULL; + plist_type newch_type = PLIST_NONE; + + r = plist_copy_node_shallow(ch, &newch, &newch_data, &newch_type); + if (r != NODE_ERR_SUCCESS) { + plist_free_node((node_t)newroot); + free(st); + PLIST_ERR("%s: shallow node copy failed (%d)\n", __func__, r); + return NULL; + } + + // attach child to copied parent + r = node_attach((node_t)f->copy, (node_t)newch); + if (r != NODE_ERR_SUCCESS) { + plist_free_node((node_t)newch); + plist_free_node((node_t)newroot); + free(st); + PLIST_ERR("%s: failed to attach child to copied parent (%d)\n", __func__, r); + return NULL; + } + + // update lookup cache on the *parent* copy + switch (f->type) { case PLIST_ARRAY: - if (newdata->hashtable) { - ptr_array_add((ptrarray_t*)newdata->hashtable, newch); + if (f->copydata->hashtable) { + ptr_array_add((ptrarray_t*)f->copydata->hashtable, newch); } break; + case PLIST_DICT: - if (newdata->hashtable && (node_index % 2 != 0)) { - hash_table_insert((hashtable_t*)newdata->hashtable, (node_prev_sibling((node_t*)newch))->data, newch); + if (f->copydata->hashtable && (f->node_index % 2 != 0)) { + node_t new_key = node_prev_sibling((node_t)newch); + if (new_key) { + hash_table_insert((hashtable_t*)f->copydata->hashtable, new_key->data, newch); + } } break; + default: break; } - node_index++; + + f->node_index++; + + // push child frame to process its children + if (sp == cap) { + cap += 64; + copy_frame_t *tmp = (copy_frame_t*)realloc(st, cap * sizeof(*st)); + if (!tmp) { + plist_free_node((node_t)newroot); + free(st); + PLIST_ERR("%s: out of memory when reallocating\n", __func__); + return NULL; + } + st = tmp; + } + + copy_frame_t nf; + nf.orig = ch; + nf.copy = newch; + nf.copydata = newch_data; + nf.type = newch_type; + nf.next_child = node_first_child(ch); + nf.node_index = 0; + nf.depth = f->depth + 1; + st[sp++] = nf; } - return newnode; + + free(st); + return newroot; } -PLIST_API plist_t plist_copy(plist_t node) +plist_t plist_copy(plist_t node) { - return node ? plist_copy_node(node) : NULL; + return node ? plist_copy_node((node_t)node) : NULL; } -PLIST_API uint32_t plist_array_get_size(plist_t node) +uint32_t plist_array_get_size(plist_t node) { uint32_t ret = 0; if (node && PLIST_ARRAY == plist_get_node_type(node)) { - ret = node_n_children(node); + ret = node_n_children((node_t)node); } return ret; } -PLIST_API plist_t plist_array_get_item(plist_t node, uint32_t n) +plist_t plist_array_get_item(plist_t node, uint32_t n) { plist_t ret = NULL; if (node && PLIST_ARRAY == plist_get_node_type(node) && n < INT_MAX) { - ptrarray_t *pa = ((plist_data_t)((node_t*)node)->data)->hashtable; + ptrarray_t *pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable; if (pa) { ret = (plist_t)ptr_array_index(pa, n); } else { - ret = (plist_t)node_nth_child(node, n); + ret = (plist_t)node_nth_child((node_t)node, n); } } return ret; } -PLIST_API uint32_t plist_array_get_item_index(plist_t node) +uint32_t plist_array_get_item_index(plist_t node) { plist_t father = plist_get_parent(node); if (PLIST_ARRAY == plist_get_node_type(father)) { - return node_child_position(father, node); + return node_child_position((node_t)father, (node_t)node); } return UINT_MAX; } static void _plist_array_post_insert(plist_t node, plist_t item, long n) { - ptrarray_t *pa = ((plist_data_t)((node_t*)node)->data)->hashtable; + ptrarray_t *pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable; if (pa) { /* store pointer to item in array */ ptr_array_insert(pa, item, n); - } else { - if (((node_t*)node)->count > 100) { - /* make new lookup array */ - pa = ptr_array_new(128); - plist_t current = NULL; - for (current = (plist_t)node_first_child(node); - pa && current; - current = (plist_t)node_next_sibling(current)) - { - ptr_array_add(pa, current); - } - ((plist_data_t)((node_t*)node)->data)->hashtable = pa; - } + return; + } + + if (((node_t)node)->count > 100) { + /* make new lookup array */ + pa = ptr_array_new(128); + plist_t current = NULL; + for (current = (plist_t)node_first_child((node_t)node); + pa && current; + current = (plist_t)node_next_sibling((node_t)current)) + { + ptr_array_add(pa, current); + } + ((plist_data_t)((node_t)node)->data)->hashtable = pa; } } -PLIST_API void plist_array_set_item(plist_t node, plist_t item, uint32_t n) +static void _plist_array_post_set(plist_t node, plist_t item, long n) { - if (node && PLIST_ARRAY == plist_get_node_type(node) && n < INT_MAX) - { - plist_t old_item = plist_array_get_item(node, n); - if (old_item) + ptrarray_t *pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable; + + if (pa) { + if (n < 0 || n >= pa->len) { + PLIST_ERR("%s: cache index out of range (n=%ld len=%ld)\n", __func__, n, pa->len); + return; + } + ptr_array_set(pa, item, n); + return; + } + + if (((node_t)node)->count > 100) { + pa = ptr_array_new(128); + plist_t current = NULL; + for (current = (plist_t)node_first_child((node_t)node); + pa && current; + current = (plist_t)node_next_sibling((node_t)current)) { - int idx = plist_free_node(old_item); - assert(idx >= 0); - if (idx < 0) { - return; - } else { - node_insert(node, idx, item); - ptrarray_t* pa = ((plist_data_t)((node_t*)node)->data)->hashtable; - if (pa) { - ptr_array_set(pa, item, idx); - } - } + ptr_array_add(pa, current); + } + ((plist_data_t)((node_t)node)->data)->hashtable = pa; + + // Now that it exists (and is filled), apply the set (will no-op if out of range) + if (pa) { + ptr_array_set(pa, item, n); } } - return; } -PLIST_API void plist_array_append_item(plist_t node, plist_t item) +void plist_array_set_item(plist_t node, plist_t item, uint32_t n) { - if (node && PLIST_ARRAY == plist_get_node_type(node)) - { - node_attach(node, item); - _plist_array_post_insert(node, item, -1); + if (!PLIST_IS_ARRAY(node) || !item || n >= INT_MAX) { + PLIST_ERR("invalid argument passed to %s (node=%p, item=%p, n=%u)\n", __func__, node, item, n); + return; + } + node_t it = (node_t)item; + if (it->parent != NULL) { + assert(it->parent == NULL && "item already has a parent; use plist_copy() or detach first"); + PLIST_ERR("%s: item already has a parent; use plist_copy() or detach first\n", __func__); + return; } - return; + plist_t old_item = plist_array_get_item(node, n); + if (!old_item) return; + + int idx = node_detach((node_t)node, (node_t)old_item); + if (idx < 0) { + PLIST_ERR("%s: Failed to detach old item (err=%d)\n", __func__, idx); + return; + } + + int r = node_insert((node_t)node, (unsigned)idx, (node_t)item); + if (r != NODE_ERR_SUCCESS) { + int rb = node_insert((node_t)node, (unsigned)idx, (node_t)old_item); + if (rb == NODE_ERR_SUCCESS) { + _plist_array_post_set(node, old_item, idx); // restore cache correctly + PLIST_ERR("%s: failed to insert replacement (idx=%d err=%d); rollback succeeded\n", __func__, idx, r); + } else { + PLIST_ERR("%s: insert failed (err=%d) and rollback failed (err=%d); array now missing element at idx=%d\n", __func__, r, rb, idx); + } + return; + } + + _plist_array_post_set(node, item, idx); // update cache + plist_free_node((node_t)old_item); } -PLIST_API void plist_array_insert_item(plist_t node, plist_t item, uint32_t n) +void plist_array_append_item(plist_t node, plist_t item) { - if (node && PLIST_ARRAY == plist_get_node_type(node) && n < INT_MAX) - { - node_insert(node, n, item); - _plist_array_post_insert(node, item, (long)n); + if (!PLIST_IS_ARRAY(node) || !item) { + PLIST_ERR("invalid argument passed to %s (node=%p, item=%p)\n", __func__, node, item); + return; + } + node_t it = (node_t)item; + if (it->parent != NULL) { + assert(it->parent == NULL && "item already has a parent; use plist_copy() or detach first"); + PLIST_ERR("%s: item already has a parent; use plist_copy() or detach first\n", __func__); + return; + } + + int r = node_attach((node_t)node, (node_t)item); + if (r != NODE_ERR_SUCCESS) { + PLIST_ERR("%s: failed to append item (err=%d)\n", __func__, r); + return; } - return; + _plist_array_post_insert(node, item, -1); } -PLIST_API void plist_array_remove_item(plist_t node, uint32_t n) +void plist_array_insert_item(plist_t node, plist_t item, uint32_t n) +{ + if (!PLIST_IS_ARRAY(node) || !item || n >= INT_MAX) { + PLIST_ERR("invalid argument passed to %s (node=%p, item=%p, n=%u)\n", __func__, node, item, n); + return; + } + node_t it = (node_t)item; + if (it->parent != NULL) { + assert(it->parent == NULL && "item already has a parent; use plist_copy() or detach first"); + PLIST_ERR("%s: item already has a parent; use plist_copy() or detach first\n", __func__); + return; + } + + int r = node_insert((node_t)node, n, (node_t)item); + if (r != NODE_ERR_SUCCESS) { + PLIST_ERR("%s: Failed to insert item at index %u (err=%d)\n", __func__, n, r); + return; + } + _plist_array_post_insert(node, item, (long)n); +} + +void plist_array_remove_item(plist_t node, uint32_t n) { if (node && PLIST_ARRAY == plist_get_node_type(node) && n < INT_MAX) { plist_t old_item = plist_array_get_item(node, n); if (old_item) { - ptrarray_t* pa = ((plist_data_t)((node_t*)node)->data)->hashtable; + ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable; if (pa) { ptr_array_remove(pa, n); } plist_free(old_item); } } - return; } -PLIST_API void plist_array_item_remove(plist_t node) +void plist_array_item_remove(plist_t node) { plist_t father = plist_get_parent(node); if (PLIST_ARRAY == plist_get_node_type(father)) { - int n = node_child_position(father, node); + int n = node_child_position((node_t)father, (node_t)node); if (n < 0) return; - ptrarray_t* pa = ((plist_data_t)((node_t*)father)->data)->hashtable; + ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)father)->data)->hashtable; if (pa) { ptr_array_remove(pa, n); } @@ -580,206 +1149,284 @@ PLIST_API void plist_array_item_remove(plist_t node) } } -PLIST_API void plist_array_new_iter(plist_t node, plist_array_iter *iter) +typedef struct { + node_t cur; +} plist_array_iter_private; + +void plist_array_new_iter(plist_t node, plist_array_iter *iter) { - if (iter) - { - *iter = malloc(sizeof(node_t*)); - *((node_t**)(*iter)) = node_first_child(node); - } - return; + if (!iter) return; + *iter = NULL; + if (!PLIST_IS_ARRAY(node)) return; + + plist_array_iter_private* it = (plist_array_iter_private*)malloc(sizeof(*it)); + if (!it) return; + it->cur = node_first_child((node_t)node); + *iter = (plist_array_iter)it; } -PLIST_API void plist_array_next_item(plist_t node, plist_array_iter iter, plist_t *item) +void plist_array_next_item(plist_t node, plist_array_iter iter, plist_t *item) { - node_t** iter_node = (node_t**)iter; + if (item) *item = NULL; + if (!iter) return; + if (!PLIST_IS_ARRAY(node)) return; - if (item) - { - *item = NULL; - } + plist_array_iter_private* it = (plist_array_iter_private*)iter; + node_t cur = it->cur; + if (!cur) return; - if (node && PLIST_ARRAY == plist_get_node_type(node) && *iter_node) - { - if (item) - { - *item = (plist_t)(*iter_node); - } - *iter_node = node_next_sibling(*iter_node); + if (item) { + *item = (plist_t)cur; } - return; + it->cur = node_next_sibling(cur); } -PLIST_API uint32_t plist_dict_get_size(plist_t node) +void plist_array_free_iter(plist_array_iter iter) +{ + free(iter); +} + +uint32_t plist_dict_get_size(plist_t node) { uint32_t ret = 0; if (node && PLIST_DICT == plist_get_node_type(node)) { - ret = node_n_children(node) / 2; + ret = node_n_children((node_t)node) / 2; } return ret; } -PLIST_API void plist_dict_new_iter(plist_t node, plist_dict_iter *iter) +typedef struct { + node_t cur; +} plist_dict_iter_private; + +void plist_dict_new_iter(plist_t node, plist_dict_iter *iter) { - if (iter) - { - *iter = malloc(sizeof(node_t*)); - *((node_t**)(*iter)) = node_first_child(node); - } - return; + if (!iter) return; + *iter = NULL; + if (!PLIST_IS_DICT(node)) return; + + plist_dict_iter_private* it = (plist_dict_iter_private*)malloc(sizeof(*it)); + if (!it) return; + it->cur = node_first_child((node_t)node); + *iter = (plist_dict_iter)it; } -PLIST_API void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val) +void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val) { - node_t** iter_node = (node_t**)iter; + if (key) *key = NULL; + if (val) *val = NULL; + if (!iter) return; + if (!PLIST_IS_DICT(node)) return; - if (key) - { - *key = NULL; + plist_dict_iter_private* it = (plist_dict_iter_private*)iter; + + node_t k = it->cur; + if (!k) return; + + if (!PLIST_IS_KEY((plist_t)k)) { + // malformed dict, terminate iteration + it->cur = NULL; + return; } - if (val) - { - *val = NULL; + + node_t v = node_next_sibling(k); + if (!v) { + // key without value, terminate iteration + it->cur = NULL; + return; } - if (node && PLIST_DICT == plist_get_node_type(node) && *iter_node) - { - if (key) - { - plist_get_key_val((plist_t)(*iter_node), key); - } - *iter_node = node_next_sibling(*iter_node); - if (val) - { - *val = (plist_t)(*iter_node); - } - *iter_node = node_next_sibling(*iter_node); + if (key) { + plist_get_key_val((plist_t)k, key); } - return; + if (val) { + *val = (plist_t)v; + } + it->cur = node_next_sibling(v); +} + +void plist_dict_free_iter(plist_dict_iter iter) +{ + free(iter); } -PLIST_API void plist_dict_get_item_key(plist_t node, char **key) +void plist_dict_get_item_key(plist_t node, char **key) { plist_t father = plist_get_parent(node); if (PLIST_DICT == plist_get_node_type(father)) { - plist_get_key_val( (plist_t) node_prev_sibling(node), key); + plist_get_key_val( (plist_t) node_prev_sibling((node_t)node), key); } } -PLIST_API plist_t plist_dict_item_get_key(plist_t node) +plist_t plist_dict_item_get_key(plist_t node) { plist_t ret = NULL; plist_t father = plist_get_parent(node); if (PLIST_DICT == plist_get_node_type(father)) { - ret = (plist_t)node_prev_sibling(node); + ret = (plist_t)node_prev_sibling((node_t)node); } return ret; } -PLIST_API plist_t plist_dict_get_item(plist_t node, const char* key) +plist_t plist_dict_get_item(plist_t node, const char* key) { plist_t ret = NULL; - - if (node && PLIST_DICT == plist_get_node_type(node)) - { - plist_data_t data = plist_get_data(node); - hashtable_t *ht = (hashtable_t*)data->hashtable; - if (ht) { - struct plist_data_s sdata; - sdata.strval = (char*)key; - sdata.length = strlen(key); - ret = (plist_t)hash_table_lookup(ht, &sdata); - } else { - plist_t current = NULL; - for (current = (plist_t)node_first_child(node); - current; - current = (plist_t)node_next_sibling(node_next_sibling(current))) - { - data = plist_get_data(current); - assert( PLIST_KEY == plist_get_node_type(current) ); - - if (data && !strcmp(key, data->strval)) - { - ret = (plist_t)node_next_sibling(current); - break; - } + if (!PLIST_IS_DICT(node) || !key) { + PLIST_ERR("invalid argument passed to %s (node=%p, key=%p)\n", __func__, node, key); + return NULL; + } + plist_data_t data = plist_get_data(node); + if (!data) { + PLIST_ERR("%s: invalid node\n", __func__); + return NULL; + } + size_t keylen = strlen(key); + hashtable_t *ht = (hashtable_t*)data->hashtable; + if (ht) { + struct plist_data_s sdata = { 0 }; + sdata.strval = (char*)key; + sdata.length = keylen; + return (plist_t)hash_table_lookup(ht, &sdata); + } else { + plist_t k = NULL; + for (k = (plist_t)node_first_child((node_t)node); k; ) { + plist_t v = (plist_t)node_next_sibling(k); + if (!v) break; + data = plist_get_data(k); + assert(PLIST_IS_KEY(k)); + if (!PLIST_IS_KEY(k) || !data || !data->strval) { + PLIST_ERR("invalid key node at %p\n", k); + break; + } + if (data->length == keylen && !memcmp(key, data->strval, keylen+1)) { + ret = v; + break; } + k = node_next_sibling(v); } } return ret; } -PLIST_API void plist_dict_set_item(plist_t node, const char* key, plist_t item) +void plist_dict_set_item(plist_t node, const char* key, plist_t item) { - if (node && PLIST_DICT == plist_get_node_type(node)) { - node_t* old_item = plist_dict_get_item(node, key); - plist_t key_node = NULL; - if (old_item) { - int idx = plist_free_node(old_item); - assert(idx >= 0); - if (idx < 0) { - return; - } else { - node_insert(node, idx, item); + if (!PLIST_IS_DICT(node) || !key || !item) { + PLIST_ERR("invalid argument passed to %s (node=%p, key=%p, item=%p)\n", __func__, node, key, item); + return; + } + node_t it = (node_t)item; + if (it->parent != NULL) { + assert(it->parent == NULL && "item already has a parent"); + PLIST_ERR("%s: item already has a parent\n", __func__); + return; + } + + hashtable_t *ht = (hashtable_t*)((plist_data_t)((node_t)node)->data)->hashtable; + + plist_t old_item = plist_dict_get_item(node, key); + plist_t key_node = NULL; + + if (old_item) { + // --- REPLACE EXISTING VALUE --- + node_t old_val = (node_t)old_item; + node_t old_key = node_prev_sibling(old_val); + if (!old_key) { + PLIST_ERR("%s: corrupt dict (value without key)\n", __func__); + return; + } + if (!PLIST_IS_KEY((plist_t)old_key)) { + PLIST_ERR("%s: corrupt dict ('key' node is not PLIST_KEY\n", __func__); + return; + } + + // detach old value (do NOT free yet) + int idx = node_detach((node_t)node, old_val); + if (idx < 0) { + PLIST_ERR("%s: failed to detach old value (err=%d)\n", __func__, idx); + return; + } + + // insert new value at same position + int r = node_insert((node_t)node, (unsigned)idx, (node_t)item); + if (r != NODE_ERR_SUCCESS) { + // rollback: reinsert old value + int rb = node_insert((node_t)node, (unsigned)idx, old_val); + if (rb == NODE_ERR_SUCCESS && ht) { + hash_table_insert(ht, ((node_t)old_key)->data, old_item); } - key_node = node_prev_sibling(item); - } else { - key_node = plist_new_key(key); - node_attach(node, key_node); - node_attach(node, item); + PLIST_ERR("%s: failed to replace dict value (err=%d)\n", __func__, r); + return; } + key_node = old_key; - hashtable_t *ht = ((plist_data_t)((node_t*)node)->data)->hashtable; + // update hash table if (ht) { - /* store pointer to item in hash table */ - hash_table_insert(ht, (plist_data_t)((node_t*)key_node)->data, item); - } else { - if (((node_t*)node)->count > 500) { - /* make new hash table */ - ht = hash_table_new(dict_key_hash, dict_key_compare, NULL); - /* calculate the hashes for all entries we have so far */ - plist_t current = NULL; - for (current = (plist_t)node_first_child(node); - ht && current; - current = (plist_t)node_next_sibling(node_next_sibling(current))) - { - hash_table_insert(ht, ((node_t*)current)->data, node_next_sibling(current)); - } - ((plist_data_t)((node_t*)node)->data)->hashtable = ht; + hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item); + } + + // now it’s safe to free old value + plist_free_node(old_val); + } else { + // --- INSERT NEW KEY/VALUE PAIR --- + key_node = plist_new_key(key); + if (!key_node) return; + + int r = node_attach((node_t)node, (node_t)key_node); + if (r != NODE_ERR_SUCCESS) { + plist_free_node((node_t)key_node); + PLIST_ERR("%s: failed to attach dict key (err=%d)\n", __func__, r); + return; + } + r = node_attach((node_t)node, (node_t)item); + if (r != NODE_ERR_SUCCESS) { + // rollback key insertion + node_detach((node_t)node, (node_t)key_node); + plist_free_node((node_t)key_node); + PLIST_ERR("%s: failed to attach dict value (err=%d)\n", __func__, r); + return; + } + + if (ht) { + // store pointer to item in hash table + hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item); + } else if (((node_t)node)->count > 500) { + // make new hash table + ht = hash_table_new(dict_key_hash, dict_key_compare, NULL); + // calculate the hashes for all entries we have so far + plist_t current = NULL; + for (current = (plist_t)node_first_child((node_t)node); + ht && current; + current = (plist_t)node_next_sibling(node_next_sibling((node_t)current))) + { + hash_table_insert(ht, ((node_t)current)->data, node_next_sibling((node_t)current)); } + ((plist_data_t)((node_t)node)->data)->hashtable = ht; } } - return; } -PLIST_API void plist_dict_insert_item(plist_t node, const char* key, plist_t item) -{ - plist_dict_set_item(node, key, item); -} - -PLIST_API void plist_dict_remove_item(plist_t node, const char* key) +void plist_dict_remove_item(plist_t node, const char* key) { if (node && PLIST_DICT == plist_get_node_type(node)) { plist_t old_item = plist_dict_get_item(node, key); if (old_item) { - plist_t key_node = node_prev_sibling(old_item); - hashtable_t* ht = ((plist_data_t)((node_t*)node)->data)->hashtable; + plist_t key_node = node_prev_sibling((node_t)old_item); + hashtable_t* ht = (hashtable_t*)((plist_data_t)((node_t)node)->data)->hashtable; if (ht) { - hash_table_remove(ht, ((node_t*)key_node)->data); + hash_table_remove(ht, ((node_t)key_node)->data); } plist_free(key_node); plist_free(old_item); } } - return; } -PLIST_API void plist_dict_merge(plist_t *target, plist_t source) +void plist_dict_merge(plist_t *target, plist_t source) { if (!target || !*target || (plist_get_node_type(*target) != PLIST_DICT) || !source || (plist_get_node_type(source) != PLIST_DICT)) return; @@ -800,10 +1447,199 @@ PLIST_API void plist_dict_merge(plist_t *target, plist_t source) free(key); key = NULL; } while (1); - free(it); -} - -PLIST_API plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v) + free(it); +} + +uint8_t plist_dict_get_bool(plist_t dict, const char *key) +{ + uint8_t bval = 0; + uint64_t uintval = 0; + const char *strval = NULL; + uint64_t strsz = 0; + plist_t node = plist_dict_get_item(dict, key); + if (!node) { + return 0; + } + switch (plist_get_node_type(node)) { + case PLIST_BOOLEAN: + plist_get_bool_val(node, &bval); + break; + case PLIST_INT: + plist_get_uint_val(node, &uintval); + bval = (uintval) ? 1 : 0; + break; + case PLIST_STRING: + strval = plist_get_string_ptr(node, NULL); + if (strval) { + if (strcmp(strval, "true")) { + bval = 1; + } else if (strcmp(strval, "false")) { + bval = 0; + } else { + PLIST_ERR("%s: invalid string '%s' for string to boolean conversion\n", __func__, strval); + } + } + break; + case PLIST_DATA: + strval = (const char*)plist_get_data_ptr(node, &strsz); + if (strval) { + if (strsz == 1) { + bval = (strval[0]) ? 1 : 0; + } else { + PLIST_ERR("%s: invalid size %" PRIu64 " for data to boolean conversion\n", __func__, strsz); + } + } + break; + default: + break; + } + return bval; +} + +int64_t plist_dict_get_int(plist_t dict, const char *key) +{ + int64_t intval = 0; + const char *strval = NULL; + uint64_t strsz = 0; + plist_t node = plist_dict_get_item(dict, key); + if (!node) { + return intval; + } + switch (plist_get_node_type(node)) { + case PLIST_INT: + plist_get_int_val(node, &intval); + break; + case PLIST_STRING: + strval = plist_get_string_ptr(node, NULL); + if (strval) { + intval = strtoll(strval, NULL, 0); + } + break; + case PLIST_DATA: + strval = (const char*)plist_get_data_ptr(node, &strsz); + if (strval) { + if (strsz == 8) { + intval = le64toh(*(int64_t*)strval); + } else if (strsz == 4) { + intval = le32toh(*(int32_t*)strval); + } else if (strsz == 2) { + intval = le16toh(*(int16_t*)strval); + } else if (strsz == 1) { + intval = strval[0]; + } else { + PLIST_ERR("%s: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz); + } + } + break; + default: + break; + } + return intval; +} + + +uint64_t plist_dict_get_uint(plist_t dict, const char *key) +{ + uint64_t uintval = 0; + const char *strval = NULL; + uint64_t strsz = 0; + plist_t node = plist_dict_get_item(dict, key); + if (!node) { + return uintval; + } + switch (plist_get_node_type(node)) { + case PLIST_INT: + plist_get_uint_val(node, &uintval); + break; + case PLIST_STRING: + strval = plist_get_string_ptr(node, NULL); + if (strval) { + uintval = strtoull(strval, NULL, 0); + } + break; + case PLIST_DATA: + strval = (const char*)plist_get_data_ptr(node, &strsz); + if (strval) { + if (strsz == 8) { + uintval = le64toh(*(uint64_t*)strval); + } else if (strsz == 4) { + uintval = le32toh(*(uint32_t*)strval); + } else if (strsz == 2) { + uintval = le16toh(*(uint16_t*)strval); + } else if (strsz == 1) { + uintval = strval[0]; + } else { + PLIST_ERR("%s: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz); + } + } + break; + default: + break; + } + return uintval; +} + +plist_err_t plist_dict_copy_item(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); + if (!node) { + return PLIST_ERR_INVALID_ARG; + } + plist_dict_set_item(target_dict, key, plist_copy(node)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_bool(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { + return PLIST_ERR_INVALID_ARG; + } + uint8_t bval = plist_dict_get_bool(source_dict, (alt_source_key) ? alt_source_key : key); + plist_dict_set_item(target_dict, key, plist_new_bool(bval)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_int(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { + return PLIST_ERR_INVALID_ARG; + } + int64_t i64val = plist_dict_get_int(source_dict, (alt_source_key) ? alt_source_key : key); + plist_dict_set_item(target_dict, key, plist_new_int(i64val)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_uint(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { + return PLIST_ERR_INVALID_ARG; + } + uint64_t u64val = plist_dict_get_uint(source_dict, (alt_source_key) ? alt_source_key : key); + plist_dict_set_item(target_dict, key, plist_new_uint(u64val)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_data(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); + if (!PLIST_IS_DATA(node)) { + return PLIST_ERR_INVALID_ARG; + } + plist_dict_set_item(target_dict, key, plist_copy(node)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_string(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); + if (!PLIST_IS_STRING(node)) { + return PLIST_ERR_INVALID_ARG; + } + plist_dict_set_item(target_dict, key, plist_copy(node)); + return PLIST_ERR_SUCCESS; +} + +plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v) { plist_t current = plist; plist_type type = PLIST_NONE; @@ -827,7 +1663,7 @@ PLIST_API plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v) return current; } -PLIST_API plist_t plist_access_path(plist_t plist, uint32_t length, ...) +plist_t plist_access_path(plist_t plist, uint32_t length, ...) { plist_t ret = NULL; va_list v; @@ -842,10 +1678,11 @@ static void plist_get_type_and_value(plist_t node, plist_type * type, void *valu { plist_data_t data = NULL; - if (!node) + if (!node || !type || !value || !length) return; data = plist_get_data(node); + if (!data) return; *type = data->type; *length = data->length; @@ -855,7 +1692,7 @@ static void plist_get_type_and_value(plist_t node, plist_type * type, void *valu case PLIST_BOOLEAN: *((char *) value) = data->boolval; break; - case PLIST_UINT: + case PLIST_INT: case PLIST_UID: *((uint64_t *) value) = data->intval; break; @@ -866,9 +1703,17 @@ static void plist_get_type_and_value(plist_t node, plist_type * type, void *valu case PLIST_KEY: case PLIST_STRING: *((char **) value) = strdup(data->strval); + if (!*((char **) value)) { + PLIST_ERR("%s: strdup failed\n", __func__); + return; + } break; case PLIST_DATA: *((uint8_t **) value) = (uint8_t *) malloc(*length * sizeof(uint8_t)); + if (!*((uint8_t **) value)) { + PLIST_ERR("%s: malloc failed\n", __func__); + return; + } memcpy(*((uint8_t **) value), data->buff, *length * sizeof(uint8_t)); break; case PLIST_ARRAY: @@ -878,12 +1723,12 @@ static void plist_get_type_and_value(plist_t node, plist_type * type, void *valu } } -PLIST_API plist_t plist_get_parent(plist_t node) +plist_t plist_get_parent(plist_t node) { - return node ? (plist_t) ((node_t*) node)->parent : NULL; + return node ? (plist_t) ((node_t) node)->parent : NULL; } -PLIST_API plist_type plist_get_node_type(plist_t node) +plist_type plist_get_node_type(plist_t node) { if (node) { @@ -894,7 +1739,7 @@ PLIST_API plist_type plist_get_node_type(plist_t node) return PLIST_NONE; } -PLIST_API void plist_get_key_val(plist_t node, char **val) +void plist_get_key_val(plist_t node, char **val) { if (!node || !val) return; @@ -908,7 +1753,7 @@ PLIST_API void plist_get_key_val(plist_t node, char **val) assert(length == strlen(*val)); } -PLIST_API void plist_get_string_val(plist_t node, char **val) +void plist_get_string_val(plist_t node, char **val) { if (!node || !val) return; @@ -922,7 +1767,7 @@ PLIST_API void plist_get_string_val(plist_t node, char **val) assert(length == strlen(*val)); } -PLIST_API const char* plist_get_string_ptr(plist_t node, uint64_t* length) +const char* plist_get_string_ptr(plist_t node, uint64_t* length) { if (!node) return NULL; @@ -935,7 +1780,7 @@ PLIST_API const char* plist_get_string_ptr(plist_t node, uint64_t* length) return (const char*)data->strval; } -PLIST_API void plist_get_bool_val(plist_t node, uint8_t * val) +void plist_get_bool_val(plist_t node, uint8_t * val) { if (!node || !val) return; @@ -947,19 +1792,24 @@ PLIST_API void plist_get_bool_val(plist_t node, uint8_t * val) assert(length == sizeof(uint8_t)); } -PLIST_API void plist_get_uint_val(plist_t node, uint64_t * val) +void plist_get_uint_val(plist_t node, uint64_t * val) { if (!node || !val) return; plist_type type = plist_get_node_type(node); uint64_t length = 0; - if (PLIST_UINT != type) + if (PLIST_INT != type) return; plist_get_type_and_value(node, &type, (void *) val, &length); assert(length == sizeof(uint64_t) || length == 16); } -PLIST_API void plist_get_uid_val(plist_t node, uint64_t * val) +void plist_get_int_val(plist_t node, int64_t * val) +{ + plist_get_uint_val(node, (uint64_t*)val); +} + +void plist_get_uid_val(plist_t node, uint64_t * val) { if (!node || !val) return; @@ -971,7 +1821,7 @@ PLIST_API void plist_get_uid_val(plist_t node, uint64_t * val) assert(length == sizeof(uint64_t)); } -PLIST_API void plist_get_real_val(plist_t node, double *val) +void plist_get_real_val(plist_t node, double *val) { if (!node || !val) return; @@ -983,7 +1833,7 @@ PLIST_API void plist_get_real_val(plist_t node, double *val) assert(length == sizeof(double)); } -PLIST_API void plist_get_data_val(plist_t node, char **val, uint64_t * length) +void plist_get_data_val(plist_t node, char **val, uint64_t * length) { if (!node || !val || !length) return; @@ -993,7 +1843,7 @@ PLIST_API void plist_get_data_val(plist_t node, char **val, uint64_t * length) plist_get_type_and_value(node, &type, (void *) val, length); } -PLIST_API const char* plist_get_data_ptr(plist_t node, uint64_t* length) +const char* plist_get_data_ptr(plist_t node, uint64_t* length) { if (!node || !length) return NULL; @@ -1005,7 +1855,7 @@ PLIST_API const char* plist_get_data_ptr(plist_t node, uint64_t* length) return (const char*)data->buff; } -PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec) +void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec) { if (!node) return; @@ -1019,7 +1869,24 @@ PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec) if (sec) *sec = (int32_t)val; if (usec) - *usec = (int32_t)fabs((val - (int64_t)val) * 1000000); + { + val = fabs((val - (int64_t)val) * 1000000); + *usec = (int32_t)val; + } +} + +void plist_get_unix_date_val(plist_t node, int64_t *sec) +{ + if (!node || !sec) + return; + plist_type type = plist_get_node_type(node); + uint64_t length = 0; + double val = 0; + if (PLIST_DATE != type) + return; + plist_get_type_and_value(node, &type, (void *) &val, &length); + assert(length == sizeof(double)); + *sec = (int64_t)val + MAC_EPOCH; } int plist_data_compare(const void *a, const void *b) @@ -1027,85 +1894,77 @@ int plist_data_compare(const void *a, const void *b) plist_data_t val_a = NULL; plist_data_t val_b = NULL; - if (!a || !b) - return FALSE; + if (a == b) + return TRUE; - if (!((node_t*) a)->data || !((node_t*) b)->data) + if (!a || !b) return FALSE; val_a = plist_get_data((plist_t) a); val_b = plist_get_data((plist_t) b); + if (val_a == NULL && val_b == NULL) + return TRUE; + + if (val_a == NULL || val_b == NULL) + return FALSE; + if (val_a->type != val_b->type) return FALSE; switch (val_a->type) { case PLIST_BOOLEAN: - case PLIST_UINT: + case PLIST_NULL: + case PLIST_INT: case PLIST_REAL: case PLIST_DATE: case PLIST_UID: - if (val_a->length != val_b->length) - return FALSE; - if (val_a->intval == val_b->intval) //it is an union so this is sufficient - return TRUE; - else - return FALSE; + return val_a->length == val_b->length + && val_a->intval == val_b->intval; // it is a union so this is sufficient case PLIST_KEY: case PLIST_STRING: - if (!strcmp(val_a->strval, val_b->strval)) - return TRUE; - else - return FALSE; + if (!val_a->strval || !val_b->strval) + return val_a->strval == val_b->strval; + return strcmp(val_a->strval, val_b->strval) == 0; - case PLIST_DATA: + case PLIST_DATA: { if (val_a->length != val_b->length) return FALSE; - if (!memcmp(val_a->buff, val_b->buff, val_a->length)) + if (val_a->length == 0) return TRUE; - else - return FALSE; + return memcmp(val_a->buff, val_b->buff, val_a->length) == 0; + } case PLIST_ARRAY: case PLIST_DICT: //compare pointer - if (a == b) - return TRUE; - else - return FALSE; - break; + return a == b; + default: - break; + return FALSE; } - return FALSE; } -PLIST_API char plist_compare_node_value(plist_t node_l, plist_t node_r) +char plist_compare_node_value(plist_t node_l, plist_t node_r) { return plist_data_compare(node_l, node_r); } -static void plist_set_element_val(plist_t node, plist_type type, const void *value, uint64_t length) +static plist_err_t plist_set_element_val(plist_t node, plist_type type, const void *value, uint64_t length) { - //free previous allocated buffer + //free previous allocated data plist_data_t data = plist_get_data(node); - assert(data); // a node should always have data attached + if (!data) { // a node should always have data attached + PLIST_ERR("%s: Failed to allocate plist data\n", __func__); + return PLIST_ERR_NO_MEM; + } - switch (data->type) - { - case PLIST_KEY: - case PLIST_STRING: - free(data->strval); - data->strval = NULL; - break; - case PLIST_DATA: - free(data->buff); - data->buff = NULL; - break; - default: - break; + if (node_first_child((node_t)node)) { + int r = plist_free_children((node_t)node); + if (r < 0) return PLIST_ERR_UNKNOWN; } + _plist_free_data(data); //now handle value @@ -1117,7 +1976,7 @@ static void plist_set_element_val(plist_t node, plist_type type, const void *val case PLIST_BOOLEAN: data->boolval = *((char *) value); break; - case PLIST_UINT: + case PLIST_INT: case PLIST_UID: data->intval = *((uint64_t *) value); break; @@ -1128,9 +1987,17 @@ static void plist_set_element_val(plist_t node, plist_type type, const void *val case PLIST_KEY: case PLIST_STRING: data->strval = strdup((char *) value); + if (!data->strval) { + PLIST_ERR("%s: strdup failed\n", __func__); + return PLIST_ERR_NO_MEM; + } break; case PLIST_DATA: data->buff = (uint8_t *) malloc(length); + if (!data->buff) { + PLIST_ERR("%s: malloc failed\n", __func__); + return PLIST_ERR_NO_MEM; + } memcpy(data->buff, value, length); break; case PLIST_ARRAY: @@ -1138,9 +2005,10 @@ static void plist_set_element_val(plist_t node, plist_type type, const void *val default: break; } + return PLIST_ERR_SUCCESS; } -PLIST_API void plist_set_key_val(plist_t node, const char *val) +void plist_set_key_val(plist_t node, const char *val) { plist_t father = plist_get_parent(node); plist_t item = plist_dict_get_item(father, val); @@ -1150,43 +2018,54 @@ PLIST_API void plist_set_key_val(plist_t node, const char *val) plist_set_element_val(node, PLIST_KEY, val, strlen(val)); } -PLIST_API void plist_set_string_val(plist_t node, const char *val) +void plist_set_string_val(plist_t node, const char *val) { plist_set_element_val(node, PLIST_STRING, val, strlen(val)); } -PLIST_API void plist_set_bool_val(plist_t node, uint8_t val) +void plist_set_bool_val(plist_t node, uint8_t val) { plist_set_element_val(node, PLIST_BOOLEAN, &val, sizeof(uint8_t)); } -PLIST_API void plist_set_uint_val(plist_t node, uint64_t val) +void plist_set_uint_val(plist_t node, uint64_t val) { - plist_set_element_val(node, PLIST_UINT, &val, sizeof(uint64_t)); + plist_set_element_val(node, PLIST_INT, &val, (val > INT64_MAX) ? sizeof(uint64_t)*2 : sizeof(uint64_t)); } -PLIST_API void plist_set_uid_val(plist_t node, uint64_t val) +void plist_set_int_val(plist_t node, int64_t val) +{ + plist_set_element_val(node, PLIST_INT, &val, sizeof(uint64_t)); +} + +void plist_set_uid_val(plist_t node, uint64_t val) { plist_set_element_val(node, PLIST_UID, &val, sizeof(uint64_t)); } -PLIST_API void plist_set_real_val(plist_t node, double val) +void plist_set_real_val(plist_t node, double val) { plist_set_element_val(node, PLIST_REAL, &val, sizeof(double)); } -PLIST_API void plist_set_data_val(plist_t node, const char *val, uint64_t length) +void plist_set_data_val(plist_t node, const char *val, uint64_t length) { plist_set_element_val(node, PLIST_DATA, val, length); } -PLIST_API void plist_set_date_val(plist_t node, int32_t sec, int32_t usec) +void plist_set_date_val(plist_t node, int32_t sec, int32_t usec) { double val = (double)sec + (double)usec / 1000000; - plist_set_element_val(node, PLIST_DATE, &val, sizeof(struct timeval)); + plist_set_element_val(node, PLIST_DATE, &val, sizeof(double)); +} + +void plist_set_unix_date_val(plist_t node, int64_t sec) +{ + double val = (double)(sec - MAC_EPOCH); + plist_set_element_val(node, PLIST_DATE, &val, sizeof(double)); } -PLIST_API int plist_bool_val_is_true(plist_t boolnode) +int plist_bool_val_is_true(plist_t boolnode) { if (!PLIST_IS_BOOLEAN(boolnode)) { return 0; @@ -1196,23 +2075,58 @@ PLIST_API int plist_bool_val_is_true(plist_t boolnode) return (bv == 1); } -PLIST_API int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval) +int plist_int_val_is_negative(plist_t intnode) +{ + if (!PLIST_IS_INT(intnode)) { + return 0; + } + plist_data_t data = plist_get_data(intnode); + if (data->length == 16) { + return 0; + } + if ((int64_t)data->intval < 0) { + return 1; + } + return 0; +} + +int plist_int_val_compare(plist_t uintnode, int64_t cmpval) { - if (!PLIST_IS_UINT(uintnode)) { + if (!PLIST_IS_INT(uintnode)) { + return -1; + } + int64_t uintval = 0; + plist_get_int_val(uintnode, &uintval); + if (uintval == cmpval) { + return 0; + } + + if (uintval < cmpval) { + return -1; + } + + return 1; +} + +int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval) +{ + if (!PLIST_IS_INT(uintnode)) { return -1; } uint64_t uintval = 0; plist_get_uint_val(uintnode, &uintval); if (uintval == cmpval) { return 0; - } else if (uintval < cmpval) { + } + + if (uintval < cmpval) { return -1; - } else { - return 1; } + + return 1; } -PLIST_API int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval) +int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval) { if (!PLIST_IS_UID(uidnode)) { return -1; @@ -1221,14 +2135,16 @@ PLIST_API int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval) plist_get_uid_val(uidnode, &uidval); if (uidval == cmpval) { return 0; - } else if (uidval < cmpval) { + } + + if (uidval < cmpval) { return -1; - } else { - return 1; } + + return 1; } -PLIST_API int plist_real_val_compare(plist_t realnode, double cmpval) +int plist_real_val_compare(plist_t realnode, double cmpval) { if (!PLIST_IS_REAL(realnode)) { return -1; @@ -1241,42 +2157,71 @@ PLIST_API int plist_real_val_compare(plist_t realnode, double cmpval) double diff = fabs(a - b); if (a == b) { return 0; - } else if (a == 0 || b == 0 || (abs_a + abs_b < DBL_MIN)) { + } + + if (a == 0 || b == 0 || (abs_a + abs_b < DBL_MIN)) { if (diff < (DBL_EPSILON * DBL_MIN)) { return 0; - } else if (a < b) { + } + + if (a < b) { return -1; } } else { if ((diff / fmin(abs_a + abs_b, DBL_MAX)) < DBL_EPSILON) { return 0; - } else if (a < b) { + } + + if (a < b) { return -1; } } return 1; } -PLIST_API int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec) +int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec) { if (!PLIST_IS_DATE(datenode)) { return -1; } - int32_t sec = 0; - int32_t usec = 0; - plist_get_date_val(datenode, &sec, &usec); + plist_data_t data = plist_get_data(datenode); + assert(data->length == sizeof(double)); + double val = data->realval; + int32_t sec = (int32_t)val; + val = fabs((val - (int64_t)val) * 1000000); + int32_t usec = (int32_t)val; uint64_t dateval = ((int64_t)sec << 32) | usec; uint64_t cmpval = ((int64_t)cmpsec << 32) | cmpusec; if (dateval == cmpval) { return 0; - } else if (dateval < cmpval) { + } + + if (dateval < cmpval) { + return -1; + } + + return 1; +} + +int plist_unix_date_val_compare(plist_t datenode, int64_t cmpval) +{ + if (!PLIST_IS_DATE(datenode)) { return -1; - } else { - return 1; } + int64_t dateval = 0; + plist_get_unix_date_val(datenode, &dateval); + if (dateval == cmpval) { + return 0; + } + + if (dateval < cmpval) { + return -1; + } + + return 1; } -PLIST_API int plist_string_val_compare(plist_t strnode, const char* cmpval) +int plist_string_val_compare(plist_t strnode, const char* cmpval) { if (!PLIST_IS_STRING(strnode)) { return -1; @@ -1285,7 +2230,7 @@ PLIST_API int plist_string_val_compare(plist_t strnode, const char* cmpval) return strcmp(data->strval, cmpval); } -PLIST_API int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n) +int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n) { if (!PLIST_IS_STRING(strnode)) { return -1; @@ -1294,7 +2239,7 @@ PLIST_API int plist_string_val_compare_with_size(plist_t strnode, const char* cm return strncmp(data->strval, cmpval, n); } -PLIST_API int plist_string_val_contains(plist_t strnode, const char* substr) +int plist_string_val_contains(plist_t strnode, const char* substr) { if (!PLIST_IS_STRING(strnode)) { return 0; @@ -1303,7 +2248,7 @@ PLIST_API int plist_string_val_contains(plist_t strnode, const char* substr) return (strstr(data->strval, substr) != NULL); } -PLIST_API int plist_key_val_compare(plist_t keynode, const char* cmpval) +int plist_key_val_compare(plist_t keynode, const char* cmpval) { if (!PLIST_IS_KEY(keynode)) { return -1; @@ -1312,7 +2257,7 @@ PLIST_API int plist_key_val_compare(plist_t keynode, const char* cmpval) return strcmp(data->strval, cmpval); } -PLIST_API int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n) +int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n) { if (!PLIST_IS_KEY(keynode)) { return -1; @@ -1321,7 +2266,7 @@ PLIST_API int plist_key_val_compare_with_size(plist_t keynode, const char* cmpva return strncmp(data->strval, cmpval, n); } -PLIST_API int plist_key_val_contains(plist_t keynode, const char* substr) +int plist_key_val_contains(plist_t keynode, const char* substr) { if (!PLIST_IS_KEY(keynode)) { return 0; @@ -1330,7 +2275,7 @@ PLIST_API int plist_key_val_contains(plist_t keynode, const char* substr) return (strstr(data->strval, substr) != NULL); } -PLIST_API int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n) +int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n) { if (!PLIST_IS_DATA(datanode)) { return -1; @@ -1338,13 +2283,16 @@ PLIST_API int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, si plist_data_t data = plist_get_data(datanode); if (data->length < n) { return -1; - } else if (data->length > n) { + } + + if (data->length > n) { return 1; } + return memcmp(data->buff, cmpval, n); } -PLIST_API int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* cmpval, size_t n) +int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* cmpval, size_t n) { if (!PLIST_IS_DATA(datanode)) { return -1; @@ -1356,7 +2304,7 @@ PLIST_API int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* return memcmp(data->buff, cmpval, n); } -PLIST_API int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n) +int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n) { if (!PLIST_IS_DATA(datanode)) { return -1; @@ -1364,3 +2312,187 @@ PLIST_API int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, s plist_data_t data = plist_get_data(datanode); return (memmem(data->buff, data->length, cmpval, n) != NULL); } + +extern void plist_xml_set_debug(int debug); +extern void plist_bin_set_debug(int debug); +extern void plist_json_set_debug(int debug); +extern void plist_ostep_set_debug(int debug); + +void plist_set_debug(int debug) +{ +#if DEBUG + plist_debug = debug; +#endif + plist_xml_set_debug(debug); + plist_bin_set_debug(debug); + plist_json_set_debug(debug); + plist_ostep_set_debug(debug); +} + +void plist_sort(plist_t plist) +{ + if (!plist) { + return; + } + if (PLIST_IS_ARRAY(plist)) { + uint32_t n = plist_array_get_size(plist); + uint32_t i = 0; + for (i = 0; i < n; i++) { + plist_sort(plist_array_get_item(plist, i)); + } + } else if (PLIST_IS_DICT(plist)) { + node_t node = (node_t)plist; + node_t ch; + if (!node_first_child(node)) { + return; + } + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + ch = node_next_sibling(ch); + plist_sort((plist_t)ch); + } + #define KEY_DATA(x) (x->data) + #define NEXT_KEY(x) (x->next->next) + #define KEY_STRVAL(x) ((plist_data_t)(KEY_DATA(x)))->strval + int swapped = 0; + do { + swapped = 0; + node_t lptr = NULL; + node_t cur_key = node_first_child((node_t)plist); + + while (NEXT_KEY(cur_key) != lptr) { + node_t next_key = NEXT_KEY(cur_key); + if (strcmp(KEY_STRVAL(cur_key), KEY_STRVAL(next_key)) > 0) { + node_t cur_val = cur_key->next; + node_t next_val = next_key->next; + // we need to swap 2 consecutive nodes with the 2 after them + // a -> b -> [c] -> [d] -> [e] -> [f] -> g -> h + // cur next + // swapped: + // a -> b -> [e] -> [f] -> [c] -> [d] -> g -> h + // next cur + node_t tmp_prev = cur_key->prev; + node_t tmp_next = next_val->next; + cur_key->prev = next_val; + cur_val->next = tmp_next; + next_val->next = cur_key; + next_key->prev = tmp_prev; + if (tmp_prev) { + tmp_prev->next = next_key; + } else { + ((node_t)plist)->children->begin = next_key; + } + if (tmp_next) { + tmp_next->prev = cur_val; + } else { + ((node_t)plist)->children->end = cur_val; + } + cur_key = next_key; + swapped = 1; + } + cur_key = NEXT_KEY(cur_key); + } + lptr = cur_key; + } while (swapped); + } +} + +plist_err_t plist_write_to_string(plist_t plist, char **output, uint32_t* length, plist_format_t format, plist_write_options_t options) +{ + plist_err_t err = PLIST_ERR_UNKNOWN; + switch (format) { + case PLIST_FORMAT_XML: + err = plist_to_xml(plist, output, length); + break; + case PLIST_FORMAT_JSON: + err = plist_to_json_with_options(plist, output, length, options); + break; + case PLIST_FORMAT_OSTEP: + err = plist_to_openstep_with_options(plist, output, length, options); + break; + case PLIST_FORMAT_PRINT: + err = plist_write_to_string_default(plist, output, length, options); + break; + case PLIST_FORMAT_LIMD: + err = plist_write_to_string_limd(plist, output, length, options); + break; + case PLIST_FORMAT_PLUTIL: + err = plist_write_to_string_plutil(plist, output, length, options); + break; + default: + // unsupported output format + err = PLIST_ERR_FORMAT; + break; + } + return err; +} + +plist_err_t plist_write_to_stream(plist_t plist, FILE *stream, plist_format_t format, plist_write_options_t options) +{ + if (!plist || !stream) { + return PLIST_ERR_INVALID_ARG; + } + plist_err_t err = PLIST_ERR_UNKNOWN; + char *output = NULL; + uint32_t length = 0; + switch (format) { + case PLIST_FORMAT_BINARY: + err = plist_to_bin(plist, &output, &length); + break; + case PLIST_FORMAT_XML: + err = plist_to_xml(plist, &output, &length); + break; + case PLIST_FORMAT_JSON: + err = plist_to_json_with_options(plist, &output, &length, options); + break; + case PLIST_FORMAT_OSTEP: + err = plist_to_openstep_with_options(plist, &output, &length, options); + break; + case PLIST_FORMAT_PRINT: + err = plist_write_to_stream_default(plist, stream, options); + break; + case PLIST_FORMAT_LIMD: + err = plist_write_to_stream_limd(plist, stream, options); + break; + case PLIST_FORMAT_PLUTIL: + err = plist_write_to_stream_plutil(plist, stream, options); + break; + default: + // unsupported output format + err = PLIST_ERR_FORMAT; + break; + } + if (output && err == PLIST_ERR_SUCCESS) { + if (fwrite(output, 1, length, stream) < length) { + err = PLIST_ERR_IO; + } + free(output); + } + return err; +} + +plist_err_t plist_write_to_file(plist_t plist, const char* filename, plist_format_t format, plist_write_options_t options) +{ + if (!plist || !filename) { + return PLIST_ERR_INVALID_ARG; + } + FILE* f = fopen(filename, "wb"); + if (!f) { + return PLIST_ERR_IO; + } + plist_err_t err = plist_write_to_stream(plist, f, format, options); + fclose(f); + return err; +} + +void plist_print(plist_t plist) +{ + plist_write_to_stream(plist, stdout, PLIST_FORMAT_PRINT, PLIST_OPT_PARTIAL_DATA); +} + +const char* libplist_version() +{ +#ifndef PACKAGE_VERSION +#error PACKAGE_VERSION is not defined! +#endif + return PACKAGE_VERSION; +} |
