Add a return value to plist_to_* and plist_from_* functions

This way it can be easier determined why an import/export operation failed
instead of just having a NULL result.
diff --git a/include/plist/plist.h b/include/plist/plist.h
index 0f69d40..21fd8bd 100644
--- a/include/plist/plist.h
+++ b/include/plist/plist.h
@@ -117,6 +117,15 @@
         PLIST_NONE      /**< No type */
     } plist_type;
 
+    typedef enum
+    {
+        PLIST_ERR_SUCCESS      =  0,  /**< operation successful */
+        PLIST_ERR_INVALID_ARG  = -1,  /**< one or more of the parameters are invalid */
+        PLIST_ERR_FORMAT       = -2,  /**< the plist contains nodes not compatible with the output format */
+        PLIST_ERR_PARSE        = -3,  /**< parsing of the input format failed */
+        PLIST_ERR_NO_MEM       = -4,  /**< not enough memory to handle the operation */
+        PLIST_ERR_UNKNOWN      = -255 /**< an unspecified error occurred */
+    } plist_err_t;
 
     /********************************************
      *                                          *
@@ -655,9 +664,10 @@
      * @param plist_xml a pointer to a C-string. This function allocates the memory,
      *            caller is responsible for freeing it. Data is UTF-8 encoded.
      * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer.
+     * @return PLIST_ERR_SUCCESS on success or a #plist_error on failure
      * @note Use plist_mem_free() to free the allocated memory.
      */
-    void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length);
+    plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length);
 
     /**
      * Export the #plist_t structure to binary format.
@@ -666,9 +676,10 @@
      * @param plist_bin a pointer to a char* buffer. This function allocates the memory,
      *            caller is responsible for freeing it.
      * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer.
+     * @return PLIST_ERR_SUCCESS on success or a #plist_error on failure
      * @note Use plist_mem_free() to free the allocated memory.
      */
-    void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length);
+    plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length);
 
     /**
      * Import the #plist_t structure from XML format.
@@ -676,8 +687,9 @@
      * @param plist_xml a pointer to the xml buffer.
      * @param length length of the buffer to read.
      * @param plist a pointer to the imported plist.
+     * @return PLIST_ERR_SUCCESS on success or a #plist_error on failure
      */
-    void plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist);
+    plist_err_t plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist);
 
     /**
      * Import the #plist_t structure from binary format.
@@ -685,8 +697,9 @@
      * @param plist_bin a pointer to the xml buffer.
      * @param length length of the buffer to read.
      * @param plist a pointer to the imported plist.
+     * @return PLIST_ERR_SUCCESS on success or a #plist_error on failure
      */
-    void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist);
+    plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist);
 
     /**
      * Import the #plist_t structure from memory data.
@@ -696,8 +709,9 @@
      * @param plist_data a pointer to the memory buffer containing plist data.
      * @param length length of the buffer to read.
      * @param plist a pointer to the imported plist.
+     * @return PLIST_ERR_SUCCESS on success or a #plist_error on failure
      */
-    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);
 
     /**
      * Test if in-memory plist data is binary or XML
diff --git a/src/bplist.c b/src/bplist.c
index a6e6ded..57ec151 100644
--- a/src/bplist.c
+++ b/src/bplist.c
@@ -774,7 +774,7 @@
     return plist;
 }
 
-PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist)
+PLIST_API plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist)
 {
     bplist_trailer_t *trailer = NULL;
     uint8_t offset_size = 0;
@@ -786,20 +786,28 @@
     const char *start_data = NULL;
     const char *end_data = NULL;
 
+    if (!plist) {
+        return PLIST_ERR_INVALID_ARG;
+    }
+    *plist = NULL;
+    if (!plist_bin || length == 0) {
+        return PLIST_ERR_INVALID_ARG;
+    }
+
     //first check we have enough data
     if (!(length >= BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE + sizeof(bplist_trailer_t))) {
         PLIST_BIN_ERR("plist data is to small to hold a binary plist\n");
-        return;
+        return PLIST_ERR_PARSE;
     }
     //check that plist_bin in actually a plist
     if (memcmp(plist_bin, BPLIST_MAGIC, BPLIST_MAGIC_SIZE) != 0) {
         PLIST_BIN_ERR("bplist magic mismatch\n");
-        return;
+        return PLIST_ERR_PARSE;
     }
     //check for known version
     if (memcmp(plist_bin + BPLIST_MAGIC_SIZE, BPLIST_VERSION, BPLIST_VERSION_SIZE) != 0) {
         PLIST_BIN_ERR("unsupported binary plist version '%.2s\n", plist_bin+BPLIST_MAGIC_SIZE);
-        return;
+        return PLIST_ERR_PARSE;
     }
 
     start_data = plist_bin + BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE;
@@ -816,37 +824,37 @@
 
     if (num_objects == 0) {
         PLIST_BIN_ERR("number of objects must be larger than 0\n");
-        return;
+        return PLIST_ERR_PARSE;
     }
 
     if (offset_size == 0) {
         PLIST_BIN_ERR("offset size in trailer must be larger than 0\n");
-        return;
+        return PLIST_ERR_PARSE;
     }
 
     if (ref_size == 0) {
         PLIST_BIN_ERR("object reference size in trailer must be larger than 0\n");
-        return;
+        return PLIST_ERR_PARSE;
     }
 
     if (root_object >= num_objects) {
         PLIST_BIN_ERR("root object index (%" PRIu64 ") must be smaller than number of objects (%" PRIu64 ")\n", root_object, num_objects);
-        return;
+        return PLIST_ERR_PARSE;
     }
 
     if (offset_table < start_data || offset_table >= end_data) {
         PLIST_BIN_ERR("offset table offset points outside of valid range\n");
-        return;
+        return PLIST_ERR_PARSE;
     }
 
     if (uint64_mul_overflow(num_objects, offset_size, &offset_table_size)) {
         PLIST_BIN_ERR("integer overflow when calculating offset table size\n");
-        return;
+        return PLIST_ERR_PARSE;
     }
 
     if (offset_table_size > (uint64_t)(end_data - offset_table)) {
         PLIST_BIN_ERR("offset table points outside of valid range\n");
-        return;
+        return PLIST_ERR_PARSE;
     }
 
     struct bplist_data bplist;
@@ -861,12 +869,18 @@
 
     if (!bplist.used_indexes) {
         PLIST_BIN_ERR("failed to create array to hold used node indexes. Out of memory?\n");
-        return;
+        return PLIST_ERR_NO_MEM;
     }
 
     *plist = parse_bin_node_at_index(&bplist, root_object);
 
     ptr_array_free(bplist.used_indexes);
+
+    if (!*plist) {
+        return PLIST_ERR_PARSE;
+    }
+
+    return PLIST_ERR_SUCCESS;
 }
 
 static unsigned int plist_data_hash(const void* key)
@@ -1163,7 +1177,7 @@
   return ret;
 }
 
-PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length)
+PLIST_API plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length)
 {
     ptrarray_t* objects = NULL;
     hashtable_t* ref_table = NULL;
@@ -1181,13 +1195,21 @@
     uint64_t objects_len = 0;
 
     //check for valid input
-    if (!plist || !plist_bin || *plist_bin || !length)
-        return;
+    if (!plist || !plist_bin || !length) {
+        return PLIST_ERR_INVALID_ARG;
+    }
 
     //list of objects
     objects = ptr_array_new(4096);
+    if (!objects) {
+        return PLIST_ERR_NO_MEM;
+    }
     //hashtable to write only once same nodes
     ref_table = hash_table_new(plist_data_hash, plist_data_compare, free);
+    if (!ref_table) {
+        ptr_array_free(objects);
+        return PLIST_ERR_NO_MEM;
+    }
 
     //serialize plist
     ser_s.objects = objects;
@@ -1212,6 +1234,7 @@
         uint8_t bsize;
         switch (data->type)
         {
+        case PLIST_NULL:
         case PLIST_BOOLEAN:
             req += 1;
             break;
@@ -1286,6 +1309,11 @@
 
     //setup a dynamic bytes array to store bplist in
     bplist_buff = byte_array_new(req);
+    if (!bplist_buff) {
+        ptr_array_free(objects);
+        hash_table_destroy(ref_table);
+        return PLIST_ERR_NO_MEM;
+    }
 
     //set magic number and version
     byte_array_append(bplist_buff, BPLIST_MAGIC, BPLIST_MAGIC_SIZE);
@@ -1385,4 +1413,6 @@
 
     bplist_buff->data = NULL; // make sure we don't free the output buffer
     byte_array_free(bplist_buff);
+
+    return PLIST_ERR_SUCCESS;
 }
diff --git a/src/plist.c b/src/plist.c
index 5453176..61b2913 100644
--- a/src/plist.c
+++ b/src/plist.c
@@ -183,18 +183,22 @@
 }
 
 
-PLIST_API void plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist)
+PLIST_API plist_err_t plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist)
 {
-    if (length < 8) {
-        *plist = NULL;
-        return;
+    int res = -1;
+    if (!plist) {
+        return PLIST_ERR_INVALID_ARG;
     }
-
+    *plist = NULL;
+    if (!plist_data || length < 8) {
+        return PLIST_ERR_INVALID_ARG;
+    }
     if (plist_is_binary(plist_data, length)) {
-        plist_from_bin(plist_data, length, plist);
+        res = plist_from_bin(plist_data, length, plist);
     } else {
-        plist_from_xml(plist_data, length, plist);
+        res = plist_from_xml(plist_data, length, plist);
     }
+    return res;
 }
 
 plist_t plist_new_node(plist_data_t data)
diff --git a/src/xplist.c b/src/xplist.c
index 94006f1..3971622 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -143,7 +143,7 @@
 
     if (!node) {
         PLIST_XML_WRITE_ERR("Encountered invalid empty node in property list\n");
-        return -1;
+        return PLIST_ERR_INVALID_ARG;
     }
 
     node_data = plist_get_data(node);
@@ -238,9 +238,9 @@
         break;
     case PLIST_NULL:
         PLIST_XML_WRITE_ERR("PLIST_NULL type is not valid for XML format\n");
-        return -1;
+        return PLIST_ERR_FORMAT;
     default:
-        break;
+        return PLIST_ERR_UNKNOWN;
     }
 
     for (i = 0; i < depth; i++) {
@@ -377,7 +377,7 @@
         str_buf_append(*outbuf, ">", 1);
     }
     str_buf_append(*outbuf, "\n", 1);
-    return 0;
+    return PLIST_ERR_SUCCESS;
 }
 
 static void parse_date(const char *strval, struct TM *btime)
@@ -438,11 +438,11 @@
     return n;
 }
 
-static void node_estimate_size(node_t *node, uint64_t *size, uint32_t depth)
+static int node_estimate_size(node_t *node, uint64_t *size, uint32_t depth)
 {
     plist_data_t data;
     if (!node) {
-        return;
+        return PLIST_ERR_INVALID_ARG;
     }
     data = plist_get_data(node);
     if (node->children) {
@@ -511,28 +511,47 @@
             *size += 18; /* <key>CF$UID</key> */
             *size += (XPLIST_INT_LEN << 1) + 6;
             break;
+        case PLIST_NULL:
+            PLIST_XML_WRITE_ERR("PLIST_NULL type is not valid for XML format\n");
+            return PLIST_ERR_FORMAT;
         default:
-            break;
+            PLIST_XML_WRITE_ERR("invalid node type encountered\n");
+            return PLIST_ERR_UNKNOWN;
         }
         *size += indent;
     }
+    return PLIST_ERR_SUCCESS;
 }
 
-PLIST_API void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length)
+PLIST_API plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length)
 {
     uint64_t size = 0;
-    node_estimate_size(plist, &size, 0);
+    int res;
+
+    if (!plist || !plist_xml || !length) {
+        return PLIST_ERR_INVALID_ARG;
+    }
+
+    res = node_estimate_size(plist, &size, 0);
+    if (res < 0) {
+        return res;
+    }
     size += sizeof(XML_PLIST_PROLOG) + sizeof(XML_PLIST_EPILOG) - 1;
 
     strbuf_t *outbuf = str_buf_new(size);
+    if (!outbuf) {
+        PLIST_XML_WRITE_ERR("Could not allocate output buffer");
+        return PLIST_ERR_NO_MEM;
+    }
 
     str_buf_append(outbuf, XML_PLIST_PROLOG, sizeof(XML_PLIST_PROLOG)-1);
 
-    if (node_to_xml(plist, &outbuf, 0) < 0) {
+    res = node_to_xml(plist, &outbuf, 0);
+    if (res < 0) {
         str_buf_free(outbuf);
         *plist_xml = NULL;
         *length = 0;
-        return;
+        return res;
     }
 
     str_buf_append(outbuf, XML_PLIST_EPILOG, sizeof(XML_PLIST_EPILOG));
@@ -542,6 +561,8 @@
 
     outbuf->data = NULL;
     str_buf_free(outbuf);
+
+    return PLIST_ERR_SUCCESS;
 }
 
 struct _parse_ctx {
@@ -932,8 +953,9 @@
     return str;
 }
 
-static void node_from_xml(parse_ctx ctx, plist_t *plist)
+static int node_from_xml(parse_ctx ctx, plist_t *plist)
 {
+    int res;
     char *tag = NULL;
     char *keyname = NULL;
     plist_t subnode = NULL;
@@ -1427,17 +1449,24 @@
     if (ctx->err) {
         plist_free(*plist);
         *plist = NULL;
+        res = PLIST_ERR_PARSE;
+    } else {
+        res = PLIST_ERR_SUCCESS;
     }
+    return res;
 }
 
-PLIST_API void plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist)
+PLIST_API plist_err_t plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist)
 {
+    if (!plist) {
+        return PLIST_ERR_INVALID_ARG;
+    }
+    *plist = NULL;
     if (!plist_xml || (length == 0)) {
-        *plist = NULL;
-        return;
+        return PLIST_ERR_INVALID_ARG;
     }
 
     struct _parse_ctx ctx = { plist_xml, plist_xml + length, 0 };
 
-    node_from_xml(&ctx, plist);
+    return node_from_xml(&ctx, plist);
 }