mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-27 06:34:11 +00:00
Change PL_D_AddObject() so that it replaces values instead of stacking them.
Add PL_D_AllKeys(), which returns a list of all keys in a dictionary. Rename PL_FreeItem() to PL_Free() -- what was I smoking? Document some of the property list system for doxygen.
This commit is contained in:
parent
91fdd90674
commit
d1b4c5df72
2 changed files with 145 additions and 37 deletions
|
@ -37,6 +37,37 @@
|
||||||
inrange((ch), '0', '9') ? ((ch) - 0x30) \
|
inrange((ch), '0', '9') ? ((ch) - 0x30) \
|
||||||
: (inrange((ch), 'a', 'f') ? ((ch) - 0x57) : ((ch) - 0x37))
|
: (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 <FF00> is valid, <F00> isn't.
|
||||||
|
|
||||||
|
Property lists may contain C-style or BCPL-style comments.
|
||||||
|
*/
|
||||||
typedef enum {QFDictionary, QFArray, QFBinary, QFString} pltype_t; // possible types
|
typedef enum {QFDictionary, QFArray, QFBinary, QFString} pltype_t; // possible types
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -78,10 +109,52 @@ typedef struct plbinary_s plbinary_t;
|
||||||
|
|
||||||
struct hashtab_s;
|
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 *);
|
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 *);
|
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);
|
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_D_AddObject (plitem_t *, plitem_t *, plitem_t *);
|
||||||
plitem_t *PL_A_AddObject (plitem_t *, plitem_t *);
|
plitem_t *PL_A_AddObject (plitem_t *, plitem_t *);
|
||||||
plitem_t *PL_A_InsertObjectAtIndex (plitem_t *, plitem_t *, int ind);
|
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_NewData (void *, int);
|
||||||
plitem_t *PL_NewString (const char *);
|
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
|
typedef struct pldata_s { // Unparsed property list string
|
||||||
const char *ptr;
|
const char *ptr;
|
||||||
|
|
|
@ -57,7 +57,7 @@ dict_free (void *i, void *unused)
|
||||||
{
|
{
|
||||||
dictkey_t *item = (dictkey_t *) i;
|
dictkey_t *item = (dictkey_t *) i;
|
||||||
free (item->key);
|
free (item->key);
|
||||||
PL_FreeItem (item->value); // Make descended stuff get freed
|
PL_Free (item->value); // Make descended stuff get freed
|
||||||
free (item);
|
free (item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ PL_NewString (const char *str)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PL_FreeItem (plitem_t *item)
|
PL_Free (plitem_t *item)
|
||||||
{
|
{
|
||||||
switch (item->type) {
|
switch (item->type) {
|
||||||
case QFDictionary:
|
case QFDictionary:
|
||||||
|
@ -124,7 +124,7 @@ PL_FreeItem (plitem_t *item)
|
||||||
int i = ((plarray_t *) item->data)->numvals;
|
int i = ((plarray_t *) item->data)->numvals;
|
||||||
|
|
||||||
while (i-- > 0) {
|
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 (((plarray_t *) item->data)->values);
|
||||||
free (item->data);
|
free (item->data);
|
||||||
|
@ -156,6 +156,29 @@ PL_ObjectForKey (plitem_t *item, const char *key)
|
||||||
return k ? k->value : NULL;
|
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 *
|
plitem_t *
|
||||||
PL_ObjectAtIndex (plitem_t *item, int index)
|
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)
|
if (key->type != QFString)
|
||||||
return NULL;
|
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)
|
if (!k)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
k->key = strdup ((char *) key->data);
|
k->key = strdup ((char *) key->data);
|
||||||
k->value = value;
|
k->value = value; // FIXME: see above
|
||||||
|
|
||||||
Hash_Add ((hashtab_t *)dict->data, k);
|
Hash_Add ((hashtab_t *)dict->data, k);
|
||||||
|
}
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,42 +493,42 @@ PL_ParsePropertyListItem (pldata_t *pl)
|
||||||
plitem_t *value;
|
plitem_t *value;
|
||||||
|
|
||||||
if (!(key = PL_ParsePropertyListItem (pl))) {
|
if (!(key = PL_ParsePropertyListItem (pl))) {
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(PL_SkipSpace (pl))) {
|
if (!(PL_SkipSpace (pl))) {
|
||||||
PL_FreeItem (key);
|
PL_Free (key);
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key->type != QFString) {
|
if (key->type != QFString) {
|
||||||
pl->error = "Key is not a string";
|
pl->error = "Key is not a string";
|
||||||
PL_FreeItem (key);
|
PL_Free (key);
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pl->ptr[pl->pos] != '=') {
|
if (pl->ptr[pl->pos] != '=') {
|
||||||
pl->error = "Unexpected character (expected '=')";
|
pl->error = "Unexpected character (expected '=')";
|
||||||
PL_FreeItem (key);
|
PL_Free (key);
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
pl->pos++;
|
pl->pos++;
|
||||||
|
|
||||||
// If there is no value, lose the key
|
// If there is no value, lose the key
|
||||||
if (!(value = PL_ParsePropertyListItem (pl))) {
|
if (!(value = PL_ParsePropertyListItem (pl))) {
|
||||||
PL_FreeItem (key);
|
PL_Free (key);
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(PL_SkipSpace (pl))) {
|
if (!(PL_SkipSpace (pl))) {
|
||||||
PL_FreeItem (key);
|
PL_Free (key);
|
||||||
PL_FreeItem (value);
|
PL_Free (value);
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,25 +536,25 @@ PL_ParsePropertyListItem (pldata_t *pl)
|
||||||
pl->pos++;
|
pl->pos++;
|
||||||
} else if (pl->ptr[pl->pos] != '}') {
|
} else if (pl->ptr[pl->pos] != '}') {
|
||||||
pl->error = "Unexpected character (wanted ';' or '}')";
|
pl->error = "Unexpected character (wanted ';' or '}')";
|
||||||
PL_FreeItem (key);
|
PL_Free (key);
|
||||||
PL_FreeItem (value);
|
PL_Free (value);
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the key/value pair to the dictionary
|
// Add the key/value pair to the dictionary
|
||||||
if (!PL_D_AddObject (item, key, value)) {
|
if (!PL_D_AddObject (item, key, value)) {
|
||||||
PL_FreeItem (key);
|
PL_Free (key);
|
||||||
PL_FreeItem (value);
|
PL_Free (value);
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
PL_FreeItem (key);
|
PL_Free (key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pl->pos >= pl->end) { // Catch the error
|
if (pl->pos >= pl->end) { // Catch the error
|
||||||
pl->error = "Unexpected end of string when parsing dictionary";
|
pl->error = "Unexpected end of string when parsing dictionary";
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
pl->pos++;
|
pl->pos++;
|
||||||
|
@ -546,13 +574,13 @@ PL_ParsePropertyListItem (pldata_t *pl)
|
||||||
plitem_t *value;
|
plitem_t *value;
|
||||||
|
|
||||||
if (!(value = PL_ParsePropertyListItem (pl))) {
|
if (!(value = PL_ParsePropertyListItem (pl))) {
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(PL_SkipSpace (pl))) {
|
if (!(PL_SkipSpace (pl))) {
|
||||||
PL_FreeItem (value);
|
PL_Free (value);
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,15 +588,15 @@ PL_ParsePropertyListItem (pldata_t *pl)
|
||||||
pl->pos++;
|
pl->pos++;
|
||||||
} else if (pl->ptr[pl->pos] != ')') {
|
} else if (pl->ptr[pl->pos] != ')') {
|
||||||
pl->error = "Unexpected character (wanted ',' or ')')";
|
pl->error = "Unexpected character (wanted ',' or ')')";
|
||||||
PL_FreeItem (value);
|
PL_Free (value);
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PL_A_AddObject (item, value)) {
|
if (!PL_A_AddObject (item, value)) {
|
||||||
pl->error = "Unexpected character (too many items in array)";
|
pl->error = "Unexpected character (too many items in array)";
|
||||||
PL_FreeItem (value);
|
PL_Free (value);
|
||||||
PL_FreeItem (item);
|
PL_Free (item);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue