blob: e696f70d836add06e114cfe2eb0b40ceb8d8a54c [file] [log] [blame]
/*
* plist.c
* Builds plist XML structures
*
* Copyright (c) 2009-2019 Nikias Bassen, All Rights Reserved.
* Copyright (c) 2010-2015 Martin Szulecki, All Rights Reserved.
* Copyright (c) 2008 Zach C., All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE 1
#include <string.h>
#include "plist.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <limits.h>
#include <float.h>
#include <ctype.h>
#ifdef WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
#include <node.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);
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_init(void)
{
plist_bin_init();
plist_xml_init();
plist_json_init();
plist_ostep_init();
}
static void internal_plist_deinit(void)
{
plist_bin_deinit();
plist_xml_deinit();
plist_json_deinit();
plist_ostep_deinit();
}
#ifdef WIN32
typedef volatile struct {
LONG lock;
int state;
} thread_once_t;
static thread_once_t init_once = {0, 0};
static thread_once_t deinit_once = {0, 0};
static 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);
}
#else
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
static pthread_once_t deinit_once = PTHREAD_ONCE_INIT;
#define thread_once pthread_once
#endif
#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR
#if defined(__llvm__) || defined(__GNUC__)
#define HAVE_ATTRIBUTE_CONSTRUCTOR
#endif
#endif
#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR
static void __attribute__((constructor)) libplist_initialize(void)
{
thread_once(&init_once, internal_plist_init);
}
static void __attribute__((destructor)) libplist_deinitialize(void)
{
thread_once(&deinit_once, internal_plist_deinit);
}
#elif defined(WIN32)
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;
}
#else
#warning No compiler support for constructor/destructor attributes, some features might not be available.
#endif
#ifndef HAVE_MEMMEM
// see https://sourceware.org/legacy-ml/libc-alpha/2007-12/msg00000.html
#ifndef _LIBC
# define __builtin_expect(expr, val) (expr)
#endif
#undef memmem
/* Return the first occurrence of NEEDLE in HAYSTACK. */
void* memmem(const void* haystack, size_t haystack_len, const void* needle, size_t needle_len)
{
/* not really Rabin-Karp, just using additive hashing */
char* haystack_ = (char*)haystack;
char* needle_ = (char*)needle;
int hash = 0; /* this is the static hash value of the needle */
int hay_hash = 0; /* rolling hash over the haystack */
char* last;
size_t i;
if (haystack_len < needle_len)
return NULL;
if (!needle_len)
return haystack_;
/* initialize hashes */
for (i = needle_len; i; --i) {
hash += *needle_++;
hay_hash += *haystack_++;
}
/* iterate over the haystack */
haystack_ = (char*)haystack;
needle_ = (char*)needle;
last = haystack_+(haystack_len - needle_len + 1);
for (; haystack_ < last; ++haystack_) {
if (__builtin_expect(hash == hay_hash, 0)
&& *haystack_ == *needle_ /* prevent calling memcmp, was a optimization from existing glibc */
&& !memcmp (haystack_, needle_, needle_len)) {
return haystack_;
}
/* roll the hash */
hay_hash -= *haystack_;
hay_hash += *(haystack_+needle_len);
}
return NULL;
}
#endif
PLIST_API int plist_is_binary(const char *plist_data, uint32_t length)
{
if (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 plist_err_t plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist)
{
int res = -1;
if (!plist) {
return PLIST_ERR_INVALID_ARG;
}
*plist = NULL;
if (!plist_data || length == 0) {
return PLIST_ERR_INVALID_ARG;
}
if (plist_is_binary(plist_data, length)) {
res = plist_from_bin(plist_data, length, plist);
} else {
uint32_t pos = 0;
int is_json = 0;
int is_xml = 0;
/* skip whitespace */
SKIP_WS(plist_data, pos, length);
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 (plist_data[pos] == '"') {
/* still could be both */
pos++;
do {
FIND_NEXT(plist_data, pos, length, '"');
if (plist_data[pos-1] != '\\') {
break;
}
pos++;
} while (pos < length);
if (plist_data[pos] == '"') {
pos++;
SKIP_WS(plist_data, pos, length);
if (plist_data[pos] == ':') {
/* this is definitely json */
is_json = 1;
}
}
}
}
if (is_xml) {
res = plist_from_xml(plist_data, length, plist);
} else if (is_json) {
res = plist_from_json(plist_data, length, plist);
} else {
res = plist_from_openstep(plist_data, length, plist);
}
}
return res;
}
plist_t plist_new_node(plist_data_t data)
{
return (plist_t) node_create(NULL, data);
}
plist_data_t plist_get_data(plist_t node)
{
if (!node)
return NULL;
return ((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;
}
static unsigned int dict_key_hash(const void *data)
{
plist_data_t keydata = (plist_data_t)data;
unsigned int hash = 5381;
size_t i;
char *str = keydata->strval;
for (i = 0; i < keydata->length; str++, i++) {
hash = ((hash << 5) + hash) + *str;
}
return hash;
}
static int dict_key_compare(const void* a, const void* b)
{
plist_data_t data_a = (plist_data_t)a;
plist_data_t data_b = (plist_data_t)b;
if (data_a->strval == NULL || data_b->strval == NULL) {
return FALSE;
}
if (data_a->length != data_b->length) {
return FALSE;
}
return (strcmp(data_a->strval, data_b->strval) == 0) ? TRUE : FALSE;
}
void plist_free_data(plist_data_t data)
{
if (data)
{
switch (data->type)
{
case PLIST_KEY:
case PLIST_STRING:
free(data->strval);
break;
case PLIST_DATA:
free(data->buff);
break;
case PLIST_ARRAY:
ptr_array_free(data->hashtable);
break;
case PLIST_DICT:
hash_table_destroy(data->hashtable);
break;
default:
break;
}
free(data);
}
}
static int plist_free_node(node_t* node)
{
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;
node_t *ch;
for (ch = node_first_child(node); ch; ) {
node_t *next = node_next_sibling(ch);
plist_free_node(ch);
ch = next;
}
node_destroy(node);
return node_index;
}
PLIST_API plist_t plist_new_dict(void)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_DICT;
return plist_new_node(data);
}
PLIST_API plist_t plist_new_array(void)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_ARRAY;
return plist_new_node(data);
}
//These nodes should not be handled by users
static plist_t plist_new_key(const char *val)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_KEY;
data->strval = strdup(val);
data->length = strlen(val);
return plist_new_node(data);
}
PLIST_API plist_t plist_new_string(const char *val)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_STRING;
data->strval = strdup(val);
data->length = strlen(val);
return plist_new_node(data);
}
PLIST_API plist_t plist_new_bool(uint8_t val)
{
plist_data_t data = plist_new_plist_data();
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_data_t data = plist_new_plist_data();
data->type = PLIST_UINT;
data->intval = val;
data->length = sizeof(uint64_t);
return plist_new_node(data);
}
PLIST_API plist_t plist_new_uid(uint64_t val)
{
plist_data_t data = plist_new_plist_data();
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_data_t data = plist_new_plist_data();
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_data_t data = plist_new_plist_data();
data->type = PLIST_DATA;
data->buff = (uint8_t *) malloc(length);
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_data_t data = plist_new_plist_data();
data->type = PLIST_DATE;
data->realval = (double)sec + (double)usec / 1000000;
data->length = sizeof(double);
return plist_new_node(data);
}
PLIST_API plist_t plist_new_null(void)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_NULL;
data->intval = 0;
data->length = 0;
return plist_new_node(data);
}
PLIST_API void plist_free(plist_t plist)
{
if (plist)
{
plist_free_node(plist);
}
}
PLIST_API void plist_mem_free(void* ptr)
{
if (ptr)
{
free(ptr);
}
}
static plist_t plist_copy_node(node_t *node)
{
plist_type node_type = PLIST_NONE;
plist_t newnode = NULL;
plist_data_t data = plist_get_data(node);
plist_data_t newdata = plist_new_plist_data();
assert(data); // plist should always have data
assert(newdata);
memcpy(newdata, data, sizeof(struct plist_data_s));
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);
break;
case PLIST_KEY:
case PLIST_STRING:
newdata->strval = strdup(data->strval);
break;
case PLIST_ARRAY:
if (data->hashtable) {
ptrarray_t* pa = ptr_array_new(((ptrarray_t*)data->hashtable)->capacity);
assert(pa);
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);
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) {
case PLIST_ARRAY:
if (newdata->hashtable) {
ptr_array_add((ptrarray_t*)newdata->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);
}
break;
default:
break;
}
node_index++;
}
return newnode;
}
PLIST_API plist_t plist_copy(plist_t node)
{
return node ? plist_copy_node(node) : NULL;
}
PLIST_API 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);
}
return ret;
}
PLIST_API 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;
if (pa) {
ret = (plist_t)ptr_array_index(pa, n);
} else {
ret = (plist_t)node_nth_child(node, n);
}
}
return ret;
}
PLIST_API 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 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;
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;
}
}
}
PLIST_API void plist_array_set_item(plist_t node, plist_t item, 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)
{
int idx = plist_free_node(old_item);
assert(idx >= 0);
if (idx < 0) {
return;
}
node_insert(node, idx, item);
ptrarray_t* pa = ((plist_data_t)((node_t*)node)->data)->hashtable;
if (pa) {
ptr_array_set(pa, item, idx);
}
}
}
}
PLIST_API void plist_array_append_item(plist_t node, plist_t item)
{
if (node && PLIST_ARRAY == plist_get_node_type(node))
{
node_attach(node, item);
_plist_array_post_insert(node, item, -1);
}
}
PLIST_API void plist_array_insert_item(plist_t node, plist_t item, uint32_t n)
{
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);
}
}
PLIST_API 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;
if (pa) {
ptr_array_remove(pa, n);
}
plist_free(old_item);
}
}
}
PLIST_API 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);
if (n < 0) return;
ptrarray_t* pa = ((plist_data_t)((node_t*)father)->data)->hashtable;
if (pa) {
ptr_array_remove(pa, n);
}
plist_free(node);
}
}
PLIST_API 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);
}
}
PLIST_API 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 (node && PLIST_ARRAY == plist_get_node_type(node) && *iter_node)
{
if (item)
{
*item = (plist_t)(*iter_node);
}
*iter_node = node_next_sibling(*iter_node);
}
}
PLIST_API 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;
}
return ret;
}
PLIST_API 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);
}
}
PLIST_API 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 (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);
}
}
PLIST_API 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_API 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);
}
return ret;
}
PLIST_API 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;
}
}
}
}
return ret;
}
PLIST_API 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;
}
node_insert(node, idx, item);
key_node = node_prev_sibling(item);
} else {
key_node = plist_new_key(key);
node_attach(node, key_node);
node_attach(node, item);
}
hashtable_t *ht = ((plist_data_t)((node_t*)node)->data)->hashtable;
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;
}
}
}
}
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)
{
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;
if (ht) {
hash_table_remove(ht, ((node_t*)key_node)->data);
}
plist_free(key_node);
plist_free(old_item);
}
}
}
PLIST_API 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;
char* key = NULL;
plist_dict_iter it = NULL;
plist_t subnode = NULL;
plist_dict_new_iter(source, &it);
if (!it)
return;
do {
plist_dict_next_item(source, it, &key, &subnode);
if (!key)
break;
plist_dict_set_item(*target, key, plist_copy(subnode));
free(key);
key = NULL;
} while (1);
free(it);
}
PLIST_API plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v)
{
plist_t current = plist;
plist_type type = PLIST_NONE;
uint32_t i = 0;
for (i = 0; i < length && current; i++)
{
type = plist_get_node_type(current);
if (type == PLIST_ARRAY)
{
uint32_t n = va_arg(v, uint32_t);
current = plist_array_get_item(current, n);
}
else if (type == PLIST_DICT)
{
const char* key = va_arg(v, const char*);
current = plist_dict_get_item(current, key);
}
}
return current;
}
PLIST_API plist_t plist_access_path(plist_t plist, uint32_t length, ...)
{
plist_t ret = NULL;
va_list v;
va_start(v, length);
ret = plist_access_pathv(plist, length, v);
va_end(v);
return ret;
}
static void plist_get_type_and_value(plist_t node, plist_type * type, void *value, uint64_t * length)
{
plist_data_t data = NULL;
if (!node)
return;
data = plist_get_data(node);
*type = data->type;
*length = data->length;
switch (*type)
{
case PLIST_BOOLEAN:
*((char *) value) = data->boolval;
break;
case PLIST_UINT:
case PLIST_UID:
*((uint64_t *) value) = data->intval;
break;
case PLIST_REAL:
case PLIST_DATE:
*((double *) value) = data->realval;
break;
case PLIST_KEY:
case PLIST_STRING:
*((char **) value) = strdup(data->strval);
break;
case PLIST_DATA:
*((uint8_t **) value) = (uint8_t *) malloc(*length * sizeof(uint8_t));
memcpy(*((uint8_t **) value), data->buff, *length * sizeof(uint8_t));
break;
case PLIST_ARRAY:
case PLIST_DICT:
default:
break;
}
}
PLIST_API plist_t plist_get_parent(plist_t node)
{
return node ? (plist_t) ((node_t*) node)->parent : NULL;
}
PLIST_API plist_type plist_get_node_type(plist_t node)
{
if (node)
{
plist_data_t data = plist_get_data(node);
if (data)
return data->type;
}
return PLIST_NONE;
}
PLIST_API void plist_get_key_val(plist_t node, char **val)
{
if (!node || !val)
return;
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_KEY != type)
return;
plist_get_type_and_value(node, &type, (void *) val, &length);
if (!*val)
return;
assert(length == strlen(*val));
}
PLIST_API void plist_get_string_val(plist_t node, char **val)
{
if (!node || !val)
return;
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_STRING != type)
return;
plist_get_type_and_value(node, &type, (void *) val, &length);
if (!*val)
return;
assert(length == strlen(*val));
}
PLIST_API const char* plist_get_string_ptr(plist_t node, uint64_t* length)
{
if (!node)
return NULL;
plist_type type = plist_get_node_type(node);
if (PLIST_STRING != type)
return NULL;
plist_data_t data = plist_get_data(node);
if (length)
*length = data->length;
return (const char*)data->strval;
}
PLIST_API void plist_get_bool_val(plist_t node, uint8_t * val)
{
if (!node || !val)
return;
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_BOOLEAN != type)
return;
plist_get_type_and_value(node, &type, (void *) val, &length);
assert(length == sizeof(uint8_t));
}
PLIST_API 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)
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)
{
if (!node || !val)
return;
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_UID != type)
return;
plist_get_type_and_value(node, &type, (void *) val, &length);
assert(length == sizeof(uint64_t));
}
PLIST_API void plist_get_real_val(plist_t node, double *val)
{
if (!node || !val)
return;
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_REAL != type)
return;
plist_get_type_and_value(node, &type, (void *) val, &length);
assert(length == sizeof(double));
}
PLIST_API void plist_get_data_val(plist_t node, char **val, uint64_t * length)
{
if (!node || !val || !length)
return;
plist_type type = plist_get_node_type(node);
if (PLIST_DATA != type)
return;
plist_get_type_and_value(node, &type, (void *) val, length);
}
PLIST_API const char* plist_get_data_ptr(plist_t node, uint64_t* length)
{
if (!node || !length)
return NULL;
plist_type type = plist_get_node_type(node);
if (PLIST_DATA != type)
return NULL;
plist_data_t data = plist_get_data(node);
*length = data->length;
return (const char*)data->buff;
}
PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec)
{
if (!node)
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));
if (sec)
*sec = (int32_t)val;
if (usec)
{
val = fabs((val - (int64_t)val) * 1000000);
*usec = (int32_t)val;
}
}
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 (!((node_t*) a)->data || !((node_t*) b)->data)
return FALSE;
val_a = plist_get_data((plist_t) a);
val_b = plist_get_data((plist_t) b);
if (val_a->type != val_b->type)
return FALSE;
switch (val_a->type)
{
case PLIST_BOOLEAN:
case PLIST_UINT:
case PLIST_REAL:
case PLIST_DATE:
case PLIST_UID:
if (val_a->length != val_b->length)
return FALSE;
return val_a->intval == val_b->intval; //it is an union so this is sufficient
case PLIST_KEY:
case PLIST_STRING:
return strcmp(val_a->strval, val_b->strval) == 0;
case PLIST_DATA:
if (val_a->length != val_b->length)
return FALSE;
return memcmp(val_a->buff, val_b->buff, val_a->length) == 0;
case PLIST_ARRAY:
case PLIST_DICT:
//compare pointer
return a == b;
default:
break;
}
return FALSE;
}
PLIST_API 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)
{
//free previous allocated buffer
plist_data_t data = plist_get_data(node);
assert(data); // a node should always have data attached
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;
}
//now handle value
data->type = type;
data->length = length;
switch (type)
{
case PLIST_BOOLEAN:
data->boolval = *((char *) value);
break;
case PLIST_UINT:
case PLIST_UID:
data->intval = *((uint64_t *) value);
break;
case PLIST_REAL:
case PLIST_DATE:
data->realval = *((double *) value);
break;
case PLIST_KEY:
case PLIST_STRING:
data->strval = strdup((char *) value);
break;
case PLIST_DATA:
data->buff = (uint8_t *) malloc(length);
memcpy(data->buff, value, length);
break;
case PLIST_ARRAY:
case PLIST_DICT:
default:
break;
}
}
PLIST_API 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);
if (item) {
return;
}
plist_set_element_val(node, PLIST_KEY, val, strlen(val));
}
PLIST_API 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)
{
plist_set_element_val(node, PLIST_BOOLEAN, &val, sizeof(uint8_t));
}
PLIST_API void plist_set_uint_val(plist_t node, uint64_t val)
{
plist_set_element_val(node, PLIST_UINT, &val, sizeof(uint64_t));
}
PLIST_API 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)
{
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)
{
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)
{
double val = (double)sec + (double)usec / 1000000;
plist_set_element_val(node, PLIST_DATE, &val, sizeof(struct timeval));
}
PLIST_API int plist_bool_val_is_true(plist_t boolnode)
{
if (!PLIST_IS_BOOLEAN(boolnode)) {
return 0;
}
uint8_t bv = 0;
plist_get_bool_val(boolnode, &bv);
return (bv == 1);
}
PLIST_API int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval)
{
if (!PLIST_IS_UINT(uintnode)) {
return -1;
}
uint64_t uintval = 0;
plist_get_uint_val(uintnode, &uintval);
if (uintval == cmpval) {
return 0;
}
if (uintval < cmpval) {
return -1;
}
return 1;
}
PLIST_API int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval)
{
if (!PLIST_IS_UID(uidnode)) {
return -1;
}
uint64_t uidval = 0;
plist_get_uid_val(uidnode, &uidval);
if (uidval == cmpval) {
return 0;
}
if (uidval < cmpval) {
return -1;
}
return 1;
}
PLIST_API int plist_real_val_compare(plist_t realnode, double cmpval)
{
if (!PLIST_IS_REAL(realnode)) {
return -1;
}
double a = 0;
double b = cmpval;
plist_get_real_val(realnode, &a);
double abs_a = fabs(a);
double abs_b = fabs(b);
double diff = fabs(a - b);
if (a == b) {
return 0;
}
if (a == 0 || b == 0 || (abs_a + abs_b < DBL_MIN)) {
if (diff < (DBL_EPSILON * DBL_MIN)) {
return 0;
}
if (a < b) {
return -1;
}
} else {
if ((diff / fmin(abs_a + abs_b, DBL_MAX)) < DBL_EPSILON) {
return 0;
}
if (a < b) {
return -1;
}
}
return 1;
}
PLIST_API 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);
uint64_t dateval = ((int64_t)sec << 32) | usec;
uint64_t cmpval = ((int64_t)cmpsec << 32) | cmpusec;
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)
{
if (!PLIST_IS_STRING(strnode)) {
return -1;
}
plist_data_t data = plist_get_data(strnode);
return strcmp(data->strval, cmpval);
}
PLIST_API int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n)
{
if (!PLIST_IS_STRING(strnode)) {
return -1;
}
plist_data_t data = plist_get_data(strnode);
return strncmp(data->strval, cmpval, n);
}
PLIST_API int plist_string_val_contains(plist_t strnode, const char* substr)
{
if (!PLIST_IS_STRING(strnode)) {
return 0;
}
plist_data_t data = plist_get_data(strnode);
return (strstr(data->strval, substr) != NULL);
}
PLIST_API int plist_key_val_compare(plist_t keynode, const char* cmpval)
{
if (!PLIST_IS_KEY(keynode)) {
return -1;
}
plist_data_t data = plist_get_data(keynode);
return strcmp(data->strval, cmpval);
}
PLIST_API int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n)
{
if (!PLIST_IS_KEY(keynode)) {
return -1;
}
plist_data_t data = plist_get_data(keynode);
return strncmp(data->strval, cmpval, n);
}
PLIST_API int plist_key_val_contains(plist_t keynode, const char* substr)
{
if (!PLIST_IS_KEY(keynode)) {
return 0;
}
plist_data_t data = plist_get_data(keynode);
return (strstr(data->strval, substr) != NULL);
}
PLIST_API int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n)
{
if (!PLIST_IS_DATA(datanode)) {
return -1;
}
plist_data_t data = plist_get_data(datanode);
if (data->length < n) {
return -1;
}
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)
{
if (!PLIST_IS_DATA(datanode)) {
return -1;
}
plist_data_t data = plist_get_data(datanode);
if (data->length < n) {
return -1;
}
return memcmp(data->buff, cmpval, n);
}
PLIST_API int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n)
{
if (!PLIST_IS_DATA(datanode)) {
return -1;
}
plist_data_t data = plist_get_data(datanode);
return (memmem(data->buff, data->length, cmpval, n) != NULL);
}