From 817749dd4e27768da64e3883fe04580cd22461a8 Mon Sep 17 00:00:00 2001
From: Nikias Bassen <nikias@gmx.li>
Date: Sun, 16 Sep 2012 06:03:57 +0200
Subject: [PATCH] implemented handling of UID keyed encoding type

---
 include/plist/plist.h | 28 ++++++++++++++++++++++++
 src/bplist.c          | 60 +++++++++++++++++++++++++++++++++++++++++++++++++--
 src/plist.c           | 27 +++++++++++++++++++++++
 src/xplist.c          | 55 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 168 insertions(+), 2 deletions(-)

diff --git a/include/plist/plist.h b/include/plist/plist.h
index 1b1ac52..546e108 100644
--- a/include/plist/plist.h
+++ b/include/plist/plist.h
@@ -83,6 +83,7 @@ extern "C"
         PLIST_DATE,	/**< Date, scalar type */
         PLIST_DATA,	/**< Binary data, scalar type */
         PLIST_KEY,	/**< Key in dictionaries (ASCII String), scalar type */
+        PLIST_UID,      /**< Special type used for 'keyed encoding' */
         PLIST_NONE	/**< No type */
     } plist_type;
 
@@ -166,6 +167,15 @@ extern "C"
     PLIST_API plist_t plist_new_date(int32_t sec, int32_t usec);
 
     /**
+     * Create a new plist_t type #PLIST_UID
+     *
+     * @param val the unsigned integer value
+     * @return the created item
+     * @sa #plist_type
+     */
+    PLIST_API plist_t plist_new_uid(uint64_t val);
+
+    /**
      * Destruct a plist_t node and all its children recursively
      *
      * @param plist the plist to free
@@ -416,6 +426,15 @@ extern "C"
      */
     PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec);
 
+    /**
+     * Get the value of a #PLIST_UID node.
+     * This function does nothing if node is not of type #PLIST_UID
+     *
+     * @param node the node
+     * @param val a pointer to a uint64_t variable.
+     */
+    PLIST_API void plist_get_uid_val(plist_t node, uint64_t * val);
+
 
     /********************************************
      *                                          *
@@ -496,6 +515,15 @@ extern "C"
      */
     PLIST_API void plist_set_date_val(plist_t node, int32_t sec, int32_t usec);
 
+    /**
+     * Set the value of a node.
+     * Forces type of node to #PLIST_UID
+     *
+     * @param node the node
+     * @param val the unsigned integer value
+     */
+    PLIST_API void plist_set_uid_val(plist_t node, uint64_t val);
+
 
     /********************************************
      *                                          *
diff --git a/src/bplist.c b/src/bplist.c
index eff44fc..7893ff5 100644
--- a/src/bplist.c
+++ b/src/bplist.c
@@ -63,7 +63,8 @@ enum
     BPLIST_DATA = 0x40,
     BPLIST_STRING = 0x50,
     BPLIST_UNICODE = 0x60,
-    BPLIST_UID = 0x70,
+    BPLIST_UNK_0x70 = 0x70,
+    BPLIST_UID = 0x80,
     BPLIST_ARRAY = 0xA0,
     BPLIST_SET = 0xC0,
     BPLIST_DICT = 0xD0,
@@ -375,6 +376,31 @@ static plist_t parse_array_node(char *bnode, uint64_t size, uint32_t ref_size)
     return node_create(NULL, data);
 }
 
+static plist_t parse_uid_node(char *bnode, uint8_t size, char **next_object)
+{
+    plist_data_t data = plist_new_plist_data();
+
+    size = 1 << size;			// make length less misleading
+    switch (size)
+    {
+    case sizeof(uint8_t):
+    case sizeof(uint16_t):
+    case sizeof(uint32_t):
+    case sizeof(uint64_t):
+        memcpy(&data->intval, bnode, size);
+        data->intval = UINT_TO_HOST(&data->intval, size);
+        break;
+    default:
+        free(data);
+        return NULL;
+    };
+
+    *next_object = bnode + size;
+    data->type = PLIST_UID;
+    data->length = sizeof(uint64_t);
+
+    return node_create(NULL, data);
+}
 
 
 static plist_t parse_bin_node(char *object, uint8_t dict_size, char **next_object)
@@ -464,7 +490,7 @@ static plist_t parse_bin_node(char *object, uint8_t dict_size, char **next_objec
         }
         return parse_unicode_node(object, size);
 
-    case BPLIST_UID:
+    case BPLIST_UNK_0x70:
     case BPLIST_ARRAY:
         if (0x0F == size)
         {
@@ -476,6 +502,9 @@ static plist_t parse_bin_node(char *object, uint8_t dict_size, char **next_objec
         }
         return parse_array_node(object, size, dict_size);
 
+    case BPLIST_UID:
+        return parse_uid_node(object, size, next_object);
+
     case BPLIST_SET:
     case BPLIST_DICT:
         if (0x0F == size)
@@ -528,6 +557,9 @@ static void* copy_plist_data(const void* src)
         dstdata->buff = (uint8_t*) malloc(sizeof(uint8_t) * srcdata->length * 2);
         memcpy(dstdata->buff, srcdata->buff, sizeof(uint8_t) * srcdata->length * 2);
         break;
+    case PLIST_UID:
+        dstdata->intval = srcdata->intval;
+        break;
     default:
         break;
     }
@@ -672,6 +704,7 @@ static unsigned int plist_data_hash(const void* key)
     case PLIST_BOOLEAN:
     case PLIST_UINT:
     case PLIST_REAL:
+    case PLIST_UID:
         buff = (char *) &data->intval;	//works also for real as we use an union
         size = 8;
         break;
@@ -916,6 +949,26 @@ static void write_dict(bytearray_t * bplist, node_t* node, hashtable_t* ref_tabl
 
 }
 
+static void write_uid(bytearray_t * bplist, uint64_t val)
+{
+    uint64_t size = get_needed_bytes(val);
+    uint8_t *buff = NULL;
+    //do not write 3bytes int node
+    if (size == 3)
+        size++;
+
+#if PLIST_BYTE_ORDER == PLIST_BIG_ENDIAN
+    val = val << ((sizeof(uint64_t) - size) * 8);
+#endif
+
+    buff = (uint8_t *) malloc(sizeof(uint8_t) + size);
+    buff[0] = BPLIST_UID | Log2(size);
+    memcpy(buff + 1, &val, size);
+    byte_convert(buff + 1, size);
+    byte_array_append(bplist, buff, sizeof(uint8_t) + size);
+    free(buff);
+}
+
 static int is_ascii_string(char* s, int len)
 {
   int ret = 1, i = 0;
@@ -1074,6 +1127,9 @@ void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length)
         case PLIST_DATE:
             write_date(bplist_buff, data->timeval.tv_sec + (double) data->timeval.tv_usec / 1000000);
             break;
+        case PLIST_UID:
+            write_uid(bplist_buff, data->intval);
+            break;
         default:
             break;
         }
diff --git a/src/plist.c b/src/plist.c
index 2b31fdd..ee79cf4 100644
--- a/src/plist.c
+++ b/src/plist.c
@@ -138,6 +138,15 @@ plist_t plist_new_uint(uint64_t val)
     return plist_new_node(data);
 }
 
+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_t plist_new_real(double val)
 {
     plist_data_t data = plist_new_plist_data();
@@ -490,6 +499,7 @@ static void plist_get_type_and_value(plist_t node, plist_type * type, void *valu
         *((char *) value) = data->boolval;
         break;
     case PLIST_UINT:
+    case PLIST_UID:
         *((uint64_t *) value) = data->intval;
         break;
     case PLIST_REAL:
@@ -567,6 +577,15 @@ void plist_get_uint_val(plist_t node, uint64_t * val)
     assert(length == sizeof(uint64_t));
 }
 
+void plist_get_uid_val(plist_t node, uint64_t * val)
+{
+    plist_type type = plist_get_node_type(node);
+    uint64_t length = 0;
+    if (PLIST_UID == type)
+        plist_get_type_and_value(node, &type, (void *) val, &length);
+    assert(length == sizeof(uint64_t));
+}
+
 void plist_get_real_val(plist_t node, double *val)
 {
     plist_type type = plist_get_node_type(node);
@@ -617,6 +636,7 @@ int plist_data_compare(const void *a, const void *b)
     case PLIST_BOOLEAN:
     case PLIST_UINT:
     case PLIST_REAL:
+    case PLIST_UID:
         if (val_a->intval == val_b->intval)	//it is an union so this is sufficient
             return TRUE;
         else
@@ -690,6 +710,7 @@ static void plist_set_element_val(plist_t node, plist_type type, const void *val
         data->boolval = *((char *) value);
         break;
     case PLIST_UINT:
+    case PLIST_UID:
         data->intval = *((uint64_t *) value);
         break;
     case PLIST_REAL:
@@ -728,6 +749,7 @@ void plist_set_type(plist_t node, plist_type type)
             data->length = sizeof(uint8_t);
             break;
         case PLIST_UINT:
+        case PLIST_UID:
             data->length = sizeof(uint64_t);
             break;
         case PLIST_REAL:
@@ -763,6 +785,11 @@ void plist_set_uint_val(plist_t node, uint64_t val)
     plist_set_element_val(node, PLIST_UINT, &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));
+}
+
 void plist_set_real_val(plist_t node, double val)
 {
     plist_set_element_val(node, PLIST_REAL, &val, sizeof(double));
diff --git a/src/xplist.c b/src/xplist.c
index ba312a1..b551179 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -126,6 +126,25 @@ static xmlDocPtr new_xml_plist(void)
     return plist_xml;
 }
 
+static struct node_t* new_key_node(const char* name)
+{
+    plist_data_t data = plist_new_plist_data();
+    data->type = PLIST_KEY;
+    int size = strlen(name);
+    data->strval = strdup(name);
+    data->length = size;
+    return node_create(NULL, data);
+}
+
+static struct node_t* new_uint_node(uint64_t uint)
+{
+    plist_data_t data = plist_new_plist_data();
+    data->type = PLIST_UINT;
+    data->intval = uint;
+    data->length = sizeof(uint64_t);
+    return node_create(NULL, data);
+}
+
 static void node_to_xml(node_t* node, void *xml_struct)
 {
     struct xml_node *xstruct = NULL;
@@ -133,6 +152,7 @@ static void node_to_xml(node_t* node, void *xml_struct)
 
     xmlNodePtr child_node = NULL;
     char isStruct = FALSE;
+    char isUIDNode = FALSE;
 
     const xmlChar *tag = NULL;
     char *val = NULL;
@@ -214,6 +234,15 @@ static void node_to_xml(node_t* node, void *xml_struct)
             }
         }
         break;
+    case PLIST_UID:
+        // special case for keyed encoding
+        tag = XPLIST_DICT;
+        isStruct = TRUE;
+        isUIDNode = TRUE;
+        node_data->type = PLIST_DICT;
+        node_attach(node, new_key_node("CF$UID"));
+        node_attach(node, new_uint_node(node_data->intval));
+        break;
     default:
         break;
     }
@@ -256,6 +285,17 @@ static void node_to_xml(node_t* node, void *xml_struct)
             xmlNodeAddContent(child_node, BAD_CAST("\t"));
         }
     }
+    if (isUIDNode)
+    {
+        unsigned int num = node_n_children(node);
+        unsigned int i;
+        for (i = num; i > 0; i--) {
+            node_t* ch = node_nth_child(node, i-1);
+            node_detach(node, ch);
+            node_destroy(ch);
+        }
+        node_data->type = PLIST_UID;
+    }
 
     return;
 }
@@ -406,6 +446,21 @@ static void xml_to_node(xmlNodePtr xml_node, plist_t * plist_node)
         {
             data->type = PLIST_DICT;
             xml_to_node(node, &subnode);
+            if (plist_get_node_type(subnode) == PLIST_DICT) {
+                if (plist_dict_get_size(subnode) == 1) {
+                    plist_t uid = plist_dict_get_item(subnode, "CF$UID");
+                    if (uid) {
+                        uint64_t val = 0;
+                        plist_get_uint_val(uid, &val);
+                        plist_dict_remove_item(subnode, "CF$UID");
+                        plist_data_t nodedata = plist_get_data((node_t*)subnode);
+                        free(nodedata->buff);
+                        nodedata->type = PLIST_UID;
+                        nodedata->length = sizeof(uint64_t);
+                        nodedata->intval = val;
+                    } 
+                }
+            }
             continue;
         }
     }
