summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2026-04-27 01:45:33 +0200
committerGravatar Nikias Bassen2026-04-27 01:45:33 +0200
commitd35b31d0a2661b6346a1592a5eb7b70e66b2a141 (patch)
treea65156248dfd4df7c9301daf2a786a3469586147
parentdddb76d74a646a7ec41cfa6b9f7772830b7acbd2 (diff)
downloadlibplist-d35b31d0a2661b6346a1592a5eb7b70e66b2a141.tar.gz
libplist-d35b31d0a2661b6346a1592a5eb7b70e66b2a141.tar.bz2
Add error handling to all modification functions
Convert all array/dict modification functions from void to plist_err_t return type: - plist_array_set_item: replace at index n - plist_array_append_item: append to end - plist_array_insert_item: insert at position n - plist_array_remove_item: remove item at index n - plist_array_item_remove: remove item from its array parent - plist_dict_set_item: replace or insert key/value - plist_dict_remove_item: remove key/value pair - plist_dict_merge: merge source dict into target Returns: - PLIST_ERR_SUCCESS on success - PLIST_ERR_INVALID_ARG for invalid arguments (NULL, wrong type, out of range, etc.) - PLIST_ERR_NO_MEM on memory allocation failure - PLIST_ERR_UNKNOWN on unexpected internal errors Header documentation updated with full error code semantics for each function.
-rw-r--r--include/plist/plist.h81
-rw-r--r--src/plist.c174
2 files changed, 163 insertions, 92 deletions
diff --git a/include/plist/plist.h b/include/plist/plist.h
index 94930b8..e720aac 100644
--- a/include/plist/plist.h
+++ b/include/plist/plist.h
@@ -347,43 +347,77 @@ extern "C"
*
* @param node the node of type #PLIST_ARRAY
* @param item the new item at index n. The array is responsible for freeing item when it is no longer needed.
- * @param n the index of the item to get. Range is [0, array_size[. Assert if n is not in range.
+ * @param n the index of the item to get. Range is [0, array_size[.
+ *
+ * @return
+ * - PLIST_ERR_SUCCESS on success.
+ * - PLIST_ERR_INVALID_ARG if node is not an array, item is NULL, item is already
+ * attached to a parent, or n is outside the valid range.
+ * - PLIST_ERR_NO_MEM if replacing the item fails due to memory allocation failure
+ * and the original array element was restored successfully.
+ * - PLIST_ERR_UNKNOWN if an unexpected internal error occurred, including failure
+ * to restore the original array element after insertion of the replacement failed.
*/
- PLIST_API void plist_array_set_item(plist_t node, plist_t item, uint32_t n);
+ PLIST_API plist_err_t plist_array_set_item(plist_t node, plist_t item, uint32_t n);
/**
* Append a new item at the end of a #PLIST_ARRAY node.
*
* @param node the node of type #PLIST_ARRAY
* @param item the new item. The array is responsible for freeing item when it is no longer needed.
+ *
+ * @return
+ * - PLIST_ERR_SUCCESS on success.
+ * - PLIST_ERR_INVALID_ARG if node is not an array, item is NULL, or item is
+ * already attached to a parent.
+ * - PLIST_ERR_NO_MEM if memory allocation failed while appending the item.
+ * - PLIST_ERR_UNKNOWN if an unexpected internal error occurred.
*/
- PLIST_API void plist_array_append_item(plist_t node, plist_t item);
+ PLIST_API plist_err_t plist_array_append_item(plist_t node, plist_t item);
/**
* Insert a new item at position n in a #PLIST_ARRAY node.
*
* @param node the node of type #PLIST_ARRAY
* @param item the new item to insert. The array is responsible for freeing item when it is no longer needed.
- * @param n The position at which the node will be stored. Range is [0, array_size[. Assert if n is not in range.
+ * @param n The position at which the node will be stored. Range is [0, array_size[.
+ *
+ * @return
+ * - PLIST_ERR_SUCCESS on success.
+ * - PLIST_ERR_INVALID_ARG if node is not an array, item is NULL, item is already
+ * attached to a parent, or n is outside the valid insertion range.
+ * - PLIST_ERR_NO_MEM if memory allocation failed while inserting the item.
+ * - PLIST_ERR_UNKNOWN if an unexpected internal error occurred.
*/
- PLIST_API void plist_array_insert_item(plist_t node, plist_t item, uint32_t n);
+ PLIST_API plist_err_t plist_array_insert_item(plist_t node, plist_t item, uint32_t n);
/**
* Remove an existing position in a #PLIST_ARRAY node.
* Removed position will be freed using #plist_free.
*
* @param node the node of type #PLIST_ARRAY
- * @param n The position to remove. Range is [0, array_size[. Assert if n is not in range.
+ * @param n The position to remove. Range is [0, array_size[.
+ *
+ * @return
+ * - PLIST_ERR_SUCCESS on success.
+ * - PLIST_ERR_INVALID_ARG if node is not an array or n is outside the valid range.
+ * - PLIST_ERR_UNKNOWN if an unexpected internal error occurred while removing
+ * the item.
*/
- PLIST_API void plist_array_remove_item(plist_t node, uint32_t n);
+ PLIST_API plist_err_t plist_array_remove_item(plist_t node, uint32_t n);
/**
* Remove a node that is a child node of a #PLIST_ARRAY node.
* node will be freed using #plist_free.
*
* @param node The node to be removed from its #PLIST_ARRAY parent.
+ *
+ * @return
+ * - PLIST_ERR_SUCCESS on success.
+ * - PLIST_ERR_INVALID_ARG if item is NULL or not a child of a #PLIST_ARRAY
+ * - PLIST_ERR_UNKNOWN if an unexpected internal error occurred.
*/
- PLIST_API void plist_array_item_remove(plist_t node);
+ PLIST_API plist_err_t plist_array_item_remove(plist_t item);
/**
* Create an iterator of a #PLIST_ARRAY node.
@@ -489,17 +523,32 @@ extern "C"
* @param node the node of type #PLIST_DICT
* @param item the new item associated to key
* @param key the identifier of the item to set.
+ *
+ * @return
+ * - PLIST_ERR_SUCCESS on success.
+ * - PLIST_ERR_INVALID_ARG if node is not a dictionary, key is NULL, item is NULL,
+ * or item is already attached to a parent.
+ * - PLIST_ERR_NO_MEM if memory allocation failed while creating or inserting the
+ * key/value pair.
+ * - PLIST_ERR_UNKNOWN if an unexpected internal error occurred.
*/
- PLIST_API void plist_dict_set_item(plist_t node, const char* key, plist_t item);
+ PLIST_API plist_err_t plist_dict_set_item(plist_t node, const char* key, plist_t item);
/**
* Remove an existing position in a #PLIST_DICT node.
* Removed position will be freed using #plist_free
*
* @param node the node of type #PLIST_DICT
- * @param key The identifier of the item to remove. Assert if identifier is not present.
+ * @param key The identifier of the item to remove
+ *
+ * @return
+ * - PLIST_ERR_SUCCESS on success.
+ * - PLIST_ERR_INVALID_ARG if node is not a dictionary, key is NULL, or no item
+ * with the given key exists.
+ * - PLIST_ERR_UNKNOWN if an unexpected internal error occurred while removing
+ * the item.
*/
- PLIST_API void plist_dict_remove_item(plist_t node, const char* key);
+ PLIST_API plist_err_t plist_dict_remove_item(plist_t node, const char* key);
/**
* Merge a dictionary into another. This will add all key/value pairs
@@ -508,8 +557,16 @@ extern "C"
*
* @param target pointer to an existing node of type #PLIST_DICT
* @param source node of type #PLIST_DICT that should be merged into target
+ *
+ * @return
+ * - PLIST_ERR_SUCCESS on success.
+ * - PLIST_ERR_INVALID_ARG if target is NULL, source is NULL, source is not a
+ * dictionary, or *target is not NULL and does not point to a dictionary.
+ * - PLIST_ERR_NO_MEM if memory allocation failed while creating the target
+ * dictionary or copying items from source.
+ * - PLIST_ERR_UNKNOWN if an unexpected internal error occurred.
*/
- PLIST_API void plist_dict_merge(plist_t *target, plist_t source);
+ PLIST_API plist_err_t plist_dict_merge(plist_t *target, plist_t source);
/**
* Get a boolean value from a given #PLIST_DICT entry.
diff --git a/src/plist.c b/src/plist.c
index 2ad1b0a..7def2b6 100644
--- a/src/plist.c
+++ b/src/plist.c
@@ -953,8 +953,7 @@ plist_t plist_copy(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))
- {
+ if (PLIST_IS_ARRAY(node)) {
ret = node_n_children((node_t)node);
}
return ret;
@@ -963,8 +962,7 @@ uint32_t plist_array_get_size(plist_t node)
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)
- {
+ if (PLIST_IS_ARRAY(node) && n < INT_MAX) {
ptrarray_t *pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable;
if (pa) {
ret = (plist_t)ptr_array_index(pa, n);
@@ -977,10 +975,9 @@ plist_t plist_array_get_item(plist_t node, uint32_t n)
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((node_t)father, (node_t)node);
+ plist_t parent = plist_get_parent(node);
+ if (PLIST_IS_ARRAY(parent)) {
+ return node_child_position((node_t)parent, (node_t)node);
}
return UINT_MAX;
}
@@ -1039,25 +1036,25 @@ static void _plist_array_post_set(plist_t node, plist_t item, long n)
}
}
-void plist_array_set_item(plist_t node, plist_t item, uint32_t n)
+plist_err_t plist_array_set_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;
+ return PLIST_ERR_INVALID_ARG;
}
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_ERR_INVALID_ARG;
}
plist_t old_item = plist_array_get_item(node, n);
- if (!old_item) return;
+ if (!old_item) return PLIST_ERR_INVALID_ARG;
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;
+ return PLIST_ERR_UNKNOWN;
}
int r = node_insert((node_t)node, (unsigned)idx, (node_t)item);
@@ -1066,87 +1063,99 @@ void plist_array_set_item(plist_t node, plist_t item, uint32_t n)
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);
+ return (r == NODE_ERR_NO_MEM) ? PLIST_ERR_NO_MEM : PLIST_ERR_UNKNOWN;
} 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_ERR_UNKNOWN;
}
- return;
}
_plist_array_post_set(node, item, idx); // update cache
plist_free_node((node_t)old_item);
+
+ return PLIST_ERR_SUCCESS;
}
-void plist_array_append_item(plist_t node, plist_t item)
+plist_err_t plist_array_append_item(plist_t node, plist_t item)
{
if (!PLIST_IS_ARRAY(node) || !item) {
PLIST_ERR("invalid argument passed to %s (node=%p, item=%p)\n", __func__, node, item);
- return;
+ return PLIST_ERR_INVALID_ARG;
}
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_ERR_INVALID_ARG;
}
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_ERR_UNKNOWN;
}
_plist_array_post_insert(node, item, -1);
+
+ return PLIST_ERR_SUCCESS;
}
-void plist_array_insert_item(plist_t node, plist_t item, uint32_t n)
+plist_err_t 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;
+ return PLIST_ERR_INVALID_ARG;
}
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_ERR_INVALID_ARG;
}
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;
+ return PLIST_ERR_UNKNOWN;
}
_plist_array_post_insert(node, item, (long)n);
+
+ return PLIST_ERR_SUCCESS;
}
-void plist_array_remove_item(plist_t node, uint32_t n)
+plist_err_t 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 = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable;
- if (pa) {
- ptr_array_remove(pa, n);
- }
- plist_free(old_item);
- }
+ if (!PLIST_IS_ARRAY(node) || n >= INT_MAX || n > plist_array_get_size(node)) {
+ PLIST_ERR("invalid argument passed to %s (node=%p, n=%u)\n", __func__, node, n);
+ return PLIST_ERR_INVALID_ARG;
+ }
+
+ plist_t old_item = plist_array_get_item(node, n);
+ if (!old_item) {
+ PLIST_ERR("item not found at index %u\n", n);
+ return PLIST_ERR_INVALID_ARG;
+ }
+ 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_ERR_SUCCESS;
}
-void plist_array_item_remove(plist_t node)
+plist_err_t plist_array_item_remove(plist_t item)
{
- plist_t father = plist_get_parent(node);
- if (PLIST_ARRAY == plist_get_node_type(father))
- {
- int n = node_child_position((node_t)father, (node_t)node);
- if (n < 0) return;
- ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)father)->data)->hashtable;
+ plist_t parent = plist_get_parent(item);
+ if (PLIST_IS_ARRAY(parent)) {
+ int n = node_child_position((node_t)parent, (node_t)item);
+ if (n < 0) return PLIST_ERR_INVALID_ARG;
+ ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)parent)->data)->hashtable;
if (pa) {
ptr_array_remove(pa, n);
}
- plist_free(node);
+ plist_free(item);
}
+ return PLIST_ERR_SUCCESS;
}
typedef struct {
@@ -1189,8 +1198,7 @@ void plist_array_free_iter(plist_array_iter iter)
uint32_t plist_dict_get_size(plist_t node)
{
uint32_t ret = 0;
- if (node && PLIST_DICT == plist_get_node_type(node))
- {
+ if (PLIST_IS_DICT(node)) {
ret = node_n_children((node_t)node) / 2;
}
return ret;
@@ -1253,9 +1261,8 @@ void plist_dict_free_iter(plist_dict_iter iter)
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_t parent = plist_get_parent(node);
+ if (PLIST_IS_DICT(parent)) {
plist_get_key_val( (plist_t) node_prev_sibling((node_t)node), key);
}
}
@@ -1263,9 +1270,8 @@ void plist_dict_get_item_key(plist_t node, char **key)
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))
- {
+ plist_t parent = plist_get_parent(node);
+ if (PLIST_IS_DICT(parent)) {
ret = (plist_t)node_prev_sibling((node_t)node);
}
return ret;
@@ -1311,17 +1317,17 @@ plist_t plist_dict_get_item(plist_t node, const char* key)
return ret;
}
-void plist_dict_set_item(plist_t node, const char* key, plist_t item)
+plist_err_t plist_dict_set_item(plist_t node, const char* key, plist_t 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;
+ return PLIST_ERR_INVALID_ARG;
}
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;
+ return PLIST_ERR_INVALID_ARG;
}
hashtable_t *ht = (hashtable_t*)((plist_data_t)((node_t)node)->data)->hashtable;
@@ -1335,18 +1341,18 @@ void plist_dict_set_item(plist_t node, const char* key, plist_t item)
node_t old_key = node_prev_sibling(old_val);
if (!old_key) {
PLIST_ERR("%s: corrupt dict (value without key)\n", __func__);
- return;
+ return PLIST_ERR_UNKNOWN;
}
if (!PLIST_IS_KEY((plist_t)old_key)) {
PLIST_ERR("%s: corrupt dict ('key' node is not PLIST_KEY\n", __func__);
- return;
+ return PLIST_ERR_UNKNOWN;
}
// 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;
+ return PLIST_ERR_UNKNOWN;
}
// insert new value at same position
@@ -1358,7 +1364,7 @@ void plist_dict_set_item(plist_t node, const char* key, plist_t item)
hash_table_insert(ht, ((node_t)old_key)->data, old_item);
}
PLIST_ERR("%s: failed to replace dict value (err=%d)\n", __func__, r);
- return;
+ return PLIST_ERR_UNKNOWN;
}
key_node = old_key;
@@ -1372,13 +1378,13 @@ void plist_dict_set_item(plist_t node, const char* key, plist_t item)
} else {
// --- INSERT NEW KEY/VALUE PAIR ---
key_node = plist_new_key(key);
- if (!key_node) return;
+ if (!key_node) return PLIST_ERR_NO_MEM;
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;
+ return PLIST_ERR_UNKNOWN;
}
r = node_attach((node_t)node, (node_t)item);
if (r != NODE_ERR_SUCCESS) {
@@ -1386,7 +1392,7 @@ void plist_dict_set_item(plist_t node, const char* key, plist_t item)
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;
+ return PLIST_ERR_UNKNOWN;
}
if (ht) {
@@ -1406,37 +1412,44 @@ void plist_dict_set_item(plist_t node, const char* key, plist_t item)
((plist_data_t)((node_t)node)->data)->hashtable = ht;
}
}
+ return PLIST_ERR_SUCCESS;
}
-void plist_dict_remove_item(plist_t node, const char* key)
+plist_err_t 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((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);
- }
- plist_free(key_node);
- plist_free(old_item);
- }
+ if (!PLIST_IS_DICT(node) || !key) {
+ PLIST_ERR("invalid argument passed to %s (node=%p, key=%p)\n", __func__, node, key);
+ return PLIST_ERR_INVALID_ARG;
+ }
+
+ plist_t old_item = plist_dict_get_item(node, key);
+ if (!old_item) {
+ PLIST_ERR("item not found for key '%s'\n", key);
+ return PLIST_ERR_INVALID_ARG;
+ }
+
+ 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);
}
+ plist_free(key_node);
+ plist_free(old_item);
+
+ return PLIST_ERR_SUCCESS;
}
-void plist_dict_merge(plist_t *target, plist_t source)
+plist_err_t 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;
+ if (!target || !PLIST_IS_DICT(*target) || !PLIST_IS_DICT(source))
+ return PLIST_ERR_INVALID_ARG;
char* key = NULL;
plist_dict_iter it = NULL;
plist_t subnode = NULL;
plist_dict_new_iter(source, &it);
if (!it)
- return;
+ return PLIST_ERR_NO_MEM;
do {
plist_dict_next_item(source, it, &key, &subnode);
@@ -1448,6 +1461,7 @@ void plist_dict_merge(plist_t *target, plist_t source)
key = NULL;
} while (1);
free(it);
+ return PLIST_ERR_SUCCESS;
}
uint8_t plist_dict_get_bool(plist_t dict, const char *key)
@@ -2010,8 +2024,8 @@ static plist_err_t plist_set_element_val(plist_t node, plist_type type, const vo
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);
+ plist_t parent = plist_get_parent(node);
+ plist_t item = plist_dict_get_item(parent, val);
if (item) {
return;
}