diff --git a/include/QF/qfplist.h b/include/QF/qfplist.h index 3f83742d4..257ef78dc 100644 --- a/include/QF/qfplist.h +++ b/include/QF/qfplist.h @@ -37,6 +37,37 @@ inrange((ch), '0', '9') ? ((ch) - 0x30) \ : (inrange((ch), 'a', 'f') ? ((ch) - 0x57) : ((ch) - 0x37)) +/** + There are four types of data that can be stored in a property list: + + QFDictionary A list of values, each associated with a key (a C string). + QFArray A list of indexed values + QFString A string. + QFBinary Random binary data. The parser doesn't load these yet. + + In textual form, a dictionary looks like: + + { + key = value; + } + + An array looks like: + + ( + value1, + value2 + ) + + An unquoted string may contain only alphanumeric characters and/or the + underscore character, '_'. Quoted strings may contain whitespace, C escape + sequences, and so on. The quote character is '"'. + + QFBinary data (though not loaded currently) is hex-encoded and contained + within angle brackets, < >. The length of the encoded data must be an even + number, so while is valid, isn't. + + Property lists may contain C-style or BCPL-style comments. +*/ typedef enum {QFDictionary, QFArray, QFBinary, QFString} pltype_t; // possible types /* @@ -78,10 +109,52 @@ typedef struct plbinary_s plbinary_t; struct hashtab_s; +/** + \fn plitem_t *PL_GetPropertyList (const char *string) + \brief Create an in-memory representation of the contents of a property list + + \param string the saved plist, as read from a file. + + \return Returns an object equivalent to the passed-in string. + You are responsible for freeing the object returned. +*/ plitem_t *PL_GetPropertyList (const char *); + +/** + \fn plitem_t *PL_ObjectForKey (plitem_t *dict, const char *key) + \brief Retrieve a value from a dictionary object. + + \param dict The dictionary to retrieve a value from + \param key The unique key associated with the value + + \return You are NOT responsible for freeing the returned object. It will + be destroyed when its container is. +*/ plitem_t *PL_ObjectForKey (plitem_t *, const char *); + +/** + \fn plitem_t *PL_ObjectAtIndex (plitem_t *array, int idx) + \brief Retrieve a value from an array object. + + \param array The array to get the value from + \param idx The index within the array to retrieve + + \return You are NOT responsible for freeing the returned object. It will + be destroyed when its container is. +*/ plitem_t *PL_ObjectAtIndex (plitem_t *, int); +/** + \fn plitem_t *PL_D_AllKeys (plitem_t *dict) + \brief Retrieve a list of all keys in a dictionary. + + \param dict The dictionary to list + + \return Returns an Array containing Strings. You are responsible for + freeing this array. +*/ +plitem_t *PL_D_AllKeys (plitem_t *); + plitem_t *PL_D_AddObject (plitem_t *, plitem_t *, plitem_t *); plitem_t *PL_A_AddObject (plitem_t *, plitem_t *); plitem_t *PL_A_InsertObjectAtIndex (plitem_t *, plitem_t *, int ind); @@ -91,7 +164,14 @@ plitem_t *PL_NewArray (void); plitem_t *PL_NewData (void *, int); plitem_t *PL_NewString (const char *); -void PL_FreeItem (struct plitem_s *); +/** + \fn void PL_Free (plitem_t *object) + \brief Free a property list object + + This function takes care of freeing any referenced property list data, so + only call it on top-level objects. +*/ +void PL_Free (plitem_t *); typedef struct pldata_s { // Unparsed property list string const char *ptr; diff --git a/libs/util/qfplist.c b/libs/util/qfplist.c index 0809eb80f..662ef094e 100644 --- a/libs/util/qfplist.c +++ b/libs/util/qfplist.c @@ -57,7 +57,7 @@ dict_free (void *i, void *unused) { dictkey_t *item = (dictkey_t *) i; free (item->key); - PL_FreeItem (item->value); // Make descended stuff get freed + PL_Free (item->value); // Make descended stuff get freed free (item); } @@ -113,7 +113,7 @@ PL_NewString (const char *str) } void -PL_FreeItem (plitem_t *item) +PL_Free (plitem_t *item) { switch (item->type) { case QFDictionary: @@ -124,7 +124,7 @@ PL_FreeItem (plitem_t *item) int i = ((plarray_t *) item->data)->numvals; while (i-- > 0) { - PL_FreeItem (((plarray_t *) item->data)->values[i]); + PL_Free (((plarray_t *) item->data)->values[i]); } free (((plarray_t *) item->data)->values); free (item->data); @@ -156,6 +156,29 @@ PL_ObjectForKey (plitem_t *item, const char *key) return k ? k->value : NULL; } +plitem_t * +PL_D_AllKeys (plitem_t *dict) +{ + void **list; + dictkey_t *current; + plitem_t *array; + + if (dict->type != QFDictionary) + return NULL; + + if (!(list = Hash_GetList ((hashtab_t *) dict->data))) + return NULL; + + if (!(array = PL_NewArray ())) + return NULL; + + while ((current = (dictkey_t *) *list++)) { + PL_A_AddObject (array, PL_NewString (current->key)); + } + + return array; +} + plitem_t * PL_ObjectAtIndex (plitem_t *item, int index) { @@ -178,15 +201,20 @@ PL_D_AddObject (plitem_t *dict, plitem_t *key, plitem_t *value) if (key->type != QFString) return NULL; - k = malloc (sizeof (dictkey_t)); + if ((k = Hash_Find ((hashtab_t *)dict->data, key->data))) { + PL_Free ((plitem_t *) k->value); + k->value = value; // FIXME: should this be a copy? + } else { + k = malloc (sizeof (dictkey_t)); - if (!k) - return NULL; + if (!k) + return NULL; - k->key = strdup ((char *) key->data); - k->value = value; + k->key = strdup ((char *) key->data); + k->value = value; // FIXME: see above - Hash_Add ((hashtab_t *)dict->data, k); + Hash_Add ((hashtab_t *)dict->data, k); + } return dict; } @@ -465,42 +493,42 @@ PL_ParsePropertyListItem (pldata_t *pl) plitem_t *value; if (!(key = PL_ParsePropertyListItem (pl))) { - PL_FreeItem (item); + PL_Free (item); return NULL; } if (!(PL_SkipSpace (pl))) { - PL_FreeItem (key); - PL_FreeItem (item); + PL_Free (key); + PL_Free (item); return NULL; } if (key->type != QFString) { pl->error = "Key is not a string"; - PL_FreeItem (key); - PL_FreeItem (item); + PL_Free (key); + PL_Free (item); return NULL; } if (pl->ptr[pl->pos] != '=') { pl->error = "Unexpected character (expected '=')"; - PL_FreeItem (key); - PL_FreeItem (item); + PL_Free (key); + PL_Free (item); return NULL; } pl->pos++; // If there is no value, lose the key if (!(value = PL_ParsePropertyListItem (pl))) { - PL_FreeItem (key); - PL_FreeItem (item); + PL_Free (key); + PL_Free (item); return NULL; } if (!(PL_SkipSpace (pl))) { - PL_FreeItem (key); - PL_FreeItem (value); - PL_FreeItem (item); + PL_Free (key); + PL_Free (value); + PL_Free (item); return NULL; } @@ -508,25 +536,25 @@ PL_ParsePropertyListItem (pldata_t *pl) pl->pos++; } else if (pl->ptr[pl->pos] != '}') { pl->error = "Unexpected character (wanted ';' or '}')"; - PL_FreeItem (key); - PL_FreeItem (value); - PL_FreeItem (item); + PL_Free (key); + PL_Free (value); + PL_Free (item); return NULL; } // Add the key/value pair to the dictionary if (!PL_D_AddObject (item, key, value)) { - PL_FreeItem (key); - PL_FreeItem (value); - PL_FreeItem (item); + PL_Free (key); + PL_Free (value); + PL_Free (item); return NULL; } - PL_FreeItem (key); + PL_Free (key); } if (pl->pos >= pl->end) { // Catch the error pl->error = "Unexpected end of string when parsing dictionary"; - PL_FreeItem (item); + PL_Free (item); return NULL; } pl->pos++; @@ -546,13 +574,13 @@ PL_ParsePropertyListItem (pldata_t *pl) plitem_t *value; if (!(value = PL_ParsePropertyListItem (pl))) { - PL_FreeItem (item); + PL_Free (item); return NULL; } if (!(PL_SkipSpace (pl))) { - PL_FreeItem (value); - PL_FreeItem (item); + PL_Free (value); + PL_Free (item); return NULL; } @@ -560,15 +588,15 @@ PL_ParsePropertyListItem (pldata_t *pl) pl->pos++; } else if (pl->ptr[pl->pos] != ')') { pl->error = "Unexpected character (wanted ',' or ')')"; - PL_FreeItem (value); - PL_FreeItem (item); + PL_Free (value); + PL_Free (item); return NULL; } if (!PL_A_AddObject (item, value)) { pl->error = "Unexpected character (too many items in array)"; - PL_FreeItem (value); - PL_FreeItem (item); + PL_Free (value); + PL_Free (item); return NULL; } }