[util] Make plist mostly null-safe

The main purpose is to allow fluent-style:
    const char *targetname = PL_String (PL_ObjectForKey (entity, "targetname"));
    if (targetname && !PL_ObjectForKey (targets, targetname)) {
        PL_D_AddObject (targets, targetname, entity);
    }

[note: the above is iffy due to ownership of entity, but the code from
which the above comes works around the issue]
This commit is contained in:
Bill Currie 2021-03-03 18:01:35 +09:00
parent 60348ab458
commit e3762d8f38
2 changed files with 113 additions and 91 deletions

View file

@ -136,14 +136,14 @@ char *PL_WritePropertyList (const plitem_t *pl);
/** Retrieve the type of an object.
\param item The object
\param item The object. Must not be null.
\return the type of the object
*/
pltype_t PL_Type (const plitem_t *item) __attribute__((pure));
/** Retrieve the line number of an object.
\param item The object
\param item The object. Must not be null.
\return the line number on which the object began, or 0 if not from a
string
*/
@ -152,15 +152,16 @@ int PL_Line (const plitem_t *item) __attribute__((pure));
/** Retrieve the data size from a binary object.
\param binary The binary object
\return the size in bytes of the binary object 0 if binary isn't a binary
object.
\return the size in bytes of the binary object 0 if \a binary isn't a
binary object (includes if \a binary is null).
*/
size_t PL_BinarySize (const plitem_t *binary) __attribute__((pure));
/** Retrieve the data from a binary object.
\param binary The binary object
\return pointer to the actual data or NULL if binary isn't a binary object.
\return pointer to the actual data or NULL if \b binary isn't a binary
object (includes if \a binary is null).
\note You are NOT responsible for freeing the returned object. It will
be destroyed when its container is.
*/
@ -170,7 +171,7 @@ const void *PL_BinaryData (const plitem_t *binary) __attribute__((pure));
\param string The string object
\return pointer to the actual string value or NULL if string isn't a
string.
string (includes if \a string is null).
\note You are NOT responsible for freeing the returned object. It will
be destroyed when its container is.
*/
@ -180,8 +181,8 @@ const char *PL_String (const plitem_t *string) __attribute__((pure));
\param dict The dictionary to retrieve a value from
\param key The unique key associated with the value
\return the value associated with the key, or NULL if not found or dict
isn't a dictionary.
\return the value associated with the key, or NULL if not found or \a dict
isn't a dictionary (includes if \a dict is null).
\note You are NOT responsible for freeing the returned object. It will
be destroyed when its container is.
*/
@ -191,8 +192,8 @@ plitem_t *PL_ObjectForKey (const plitem_t *dict, const char *key);
\param dict The Dictionary to remove the value from
\param key The unique key associated with the value to be removed
\return the value associated with the key, or NULL if not found or dict
isn't a dictionary.
\return the value associated with the key, or NULL if not found or \a dict
isn't a dictionary (includes if \a dict is null).
\note You are responsible for freeing the returned object.
*/
plitem_t *PL_RemoveObjectForKey (const plitem_t *dict, const char *key);
@ -202,19 +203,18 @@ plitem_t *PL_RemoveObjectForKey (const plitem_t *dict, const char *key);
\param dict The dictionary to get the key from
\param index The index of the key
\return the key at the specified index, or NULL if index is out of range or
dict is not a dictionaly
isn't an array.
dict is not a dictionary (includes if \a dict is null).
\note You are NOT responsible for freeing the returned object. It will
be destroyed when its container is.
*/
const char *PL_KeyAtIndex (const plitem_t *array, int index) __attribute__((pure));
const char *PL_KeyAtIndex (const plitem_t *dict, int index) __attribute__((pure));
/** Retrieve a value from an array object.
\param array The array to get the value from
\param index The index within the array to retrieve
\return the value at the specified index, or NULL if index is out of range
or array is not an array.
\return the value at the specified index, or NULL if \a index is out of
range or \a array is not an array (includes in \a array is null).
\note You are NOT responsible for freeing the returned object. It will
be destroyed when its container is.
*/
@ -223,7 +223,8 @@ plitem_t *PL_ObjectAtIndex (const plitem_t *array, int index) __attribute__((pur
/** Retrieve a list of all keys in a dictionary.
\param dict The dictionary to list
\return an Array containing Strings or NULL if dict isn't a dictionary
\return an Array containing Strings or NULL if \a dict isn't a dictionary
(includes if \a dict is null).
\note You are responsible for freeing this array.
*/
plitem_t *PL_D_AllKeys (const plitem_t *dict);
@ -232,7 +233,8 @@ plitem_t *PL_D_AllKeys (const plitem_t *dict);
\param dict The dictionary to get the number of keys of.
\return Returns the number of keys in the dictionary.
\return Returns the number of keys in the dictionary or 0 if \a dict isn't
a dictionary (includes if \a dict is null).
*/
int PL_D_NumKeys (const plitem_t *dict) __attribute__((pure));
@ -242,7 +244,8 @@ int PL_D_NumKeys (const plitem_t *dict) __attribute__((pure));
\param key The key of the key/value pair to be added to the dictionary
\param value The value of the key/value pair to be added to the dictionary
\return true on success, false on failure
\return true on success, false on failure (\a dict is null or not a
dictionary)
\note the dictionary becomes the owner of the value.
*/
@ -253,7 +256,8 @@ qboolean PL_D_AddObject (plitem_t *dict, const char *key, plitem_t *value);
\param array The array to which the item will be added
\param item The item to be added to the array
\return true on success, false on failure
\return true on success, false on failure (\a array is null or not an
array)
\note the array becomes the owner of the added item.
*/
@ -263,7 +267,8 @@ qboolean PL_A_AddObject (plitem_t *array, plitem_t *item);
\param array The array from which to get the number of objects
\return number of objects in the array
\return number of objects in the array or 0 if \a array is null or not
an array.
*/
int PL_A_NumObjects (const plitem_t *array) __attribute__((pure));
@ -273,7 +278,8 @@ int PL_A_NumObjects (const plitem_t *array) __attribute__((pure));
\param item The item to be added to the array
\param index The location at which to insert the item into the array
\return true on success, false on failure
\return true on success, false on failure (\a array is null or not an
array).
\note the array becomes the owner of the added item.
*/
@ -285,7 +291,7 @@ qboolean PL_A_InsertObjectAtIndex (plitem_t *array, plitem_t *item, int index);
\param array The array from which to remove the value
\param index The index within the array to remove
\return the value associated with the index, or NULL if not found or array
isn't an array.
is noll or an array.
\note You are responsible for freeing the returned object.
*/
plitem_t *PL_RemoveObjectAtIndex (plitem_t *array, int index);
@ -323,7 +329,7 @@ plitem_t *PL_NewString (const char *str);
/** Free a property list object.
This function takes care of freeing any referenced property list data, so
call it only on top-level objects.
call it only on top-level objects. Safe to call with a null argument.
\param item the property list object to be freed
*/

View file

@ -132,11 +132,11 @@ init_quotables (void)
quotable_bitmap[*c / 8] &= ~(1 << (*c % 8));
}
static plitem_t *PL_ParsePropertyListItem (pldata_t *);
static qboolean PL_SkipSpace (pldata_t *);
static char *PL_ParseQuotedString (pldata_t *);
static char *PL_ParseUnquotedString (pldata_t *);
static char *PL_ParseData (pldata_t *, int *);
static plitem_t *pl_parsepropertylistitem (pldata_t *);
static qboolean pl_skipspace (pldata_t *);
static char *pl_parsequotedstring (pldata_t *);
static char *pl_parseunquotedstring (pldata_t *);
static char *pl_parsedata (pldata_t *, int *);
static const char *
dict_get_key (const void *i, void *unused)
@ -156,7 +156,7 @@ dict_free (void *i, void *unused)
}
static plitem_t *
PL_NewItem (pltype_t type)
pl_newitem (pltype_t type)
{
plitem_t *item = calloc (1, sizeof (plitem_t));
item->type = type;
@ -166,7 +166,7 @@ PL_NewItem (pltype_t type)
VISIBLE plitem_t *
PL_NewDictionary (hashlink_t **hashlinks)
{
plitem_t *item = PL_NewItem (QFDictionary);
plitem_t *item = pl_newitem (QFDictionary);
pldict_t *dict = malloc (sizeof (pldict_t));
dict->tab = Hash_NewTable (1021, dict_get_key, dict_free, NULL, hashlinks);
DARRAY_INIT (&dict->keys, 8);
@ -177,7 +177,7 @@ PL_NewDictionary (hashlink_t **hashlinks)
VISIBLE plitem_t *
PL_NewArray (void)
{
plitem_t *item = PL_NewItem (QFArray);
plitem_t *item = pl_newitem (QFArray);
plarray_t *array = calloc (1, sizeof (plarray_t));
item->data = array;
return item;
@ -186,7 +186,7 @@ PL_NewArray (void)
VISIBLE plitem_t *
PL_NewData (void *data, size_t size)
{
plitem_t *item = PL_NewItem (QFBinary);
plitem_t *item = pl_newitem (QFBinary);
plbinary_t *bin = malloc (sizeof (plbinary_t));
item->data = bin;
bin->data = data;
@ -197,7 +197,7 @@ PL_NewData (void *data, size_t size)
static plitem_t *
new_string (char *str, int line)
{
plitem_t *item = PL_NewItem (QFString);
plitem_t *item = pl_newitem (QFString);
item->data = str;
item->line = line;
return item;
@ -215,6 +215,9 @@ PL_Free (plitem_t *item)
pldict_t *dict;
plarray_t *array;
if (!item) {
return;
}
switch (item->type) {
case QFDictionary:
dict = item->data;
@ -252,51 +255,54 @@ PL_Free (plitem_t *item)
VISIBLE size_t
PL_BinarySize (const plitem_t *binary)
{
plbinary_t *bin = (plbinary_t *) binary->data;
if (binary->type != QFBinary)
if (!binary || binary->type != QFBinary) {
return 0;
}
plbinary_t *bin = (plbinary_t *) binary->data;
return bin->size;
}
VISIBLE const void *
PL_BinaryData (const plitem_t *binary)
{
plbinary_t *bin = (plbinary_t *) binary->data;
if (binary->type != QFBinary)
if (!binary || binary->type != QFBinary) {
return 0;
}
plbinary_t *bin = (plbinary_t *) binary->data;
return bin->data;
}
VISIBLE const char *
PL_String (const plitem_t *string)
{
if (string->type != QFString)
if (!string || string->type != QFString) {
return NULL;
}
return string->data;
}
VISIBLE plitem_t *
PL_ObjectForKey (const plitem_t *item, const char *key)
{
pldict_t *dict = (pldict_t *) item->data;
dictkey_t *k;
if (item->type != QFDictionary)
if (!item || item->type != QFDictionary) {
return NULL;
}
k = (dictkey_t *) Hash_Find (dict->tab, key);
pldict_t *dict = (pldict_t *) item->data;
dictkey_t *k = (dictkey_t *) Hash_Find (dict->tab, key);
return k ? k->value : NULL;
}
VISIBLE const char *
PL_KeyAtIndex (const plitem_t *item, int index)
{
pldict_t *dict = (pldict_t *) item->data;
if (item->type != QFDictionary)
if (!item || item->type != QFDictionary) {
return NULL;
}
pldict_t *dict = (pldict_t *) item->data;
if (index < 0 || (size_t) index >= dict->keys.size) {
return NULL;
}
@ -307,13 +313,14 @@ PL_KeyAtIndex (const plitem_t *item, int index)
VISIBLE plitem_t *
PL_RemoveObjectForKey (const plitem_t *item, const char *key)
{
if (!item || item->type != QFDictionary) {
return NULL;
}
pldict_t *dict = (pldict_t *) item->data;
dictkey_t *k;
plitem_t *value;
if (item->type != QFDictionary)
return NULL;
k = (dictkey_t *) Hash_Del (dict->tab, key);
if (!k)
return NULL;
@ -332,13 +339,14 @@ PL_RemoveObjectForKey (const plitem_t *item, const char *key)
VISIBLE plitem_t *
PL_D_AllKeys (const plitem_t *item)
{
if (!item || item->type != QFDictionary) {
return NULL;
}
pldict_t *dict = (pldict_t *) item->data;
dictkey_t *current;
plitem_t *array;
if (item->type != QFDictionary)
return NULL;
if (!(array = PL_NewArray ()))
return NULL;
@ -353,32 +361,35 @@ PL_D_AllKeys (const plitem_t *item)
VISIBLE int
PL_D_NumKeys (const plitem_t *item)
{
pldict_t *dict = (pldict_t *) item->data;
if (item->type != QFDictionary)
if (!item || item->type != QFDictionary) {
return 0;
}
pldict_t *dict = (pldict_t *) item->data;
return Hash_NumElements (dict->tab);
}
VISIBLE plitem_t *
PL_ObjectAtIndex (const plitem_t *array, int index)
{
plarray_t *arr = (plarray_t *) array->data;
if (array->type != QFArray)
if (!array || array->type != QFArray) {
return NULL;
}
plarray_t *arr = (plarray_t *) array->data;
return index >= 0 && index < arr->numvals ? arr->values[index] : NULL;
}
VISIBLE qboolean
PL_D_AddObject (plitem_t *item, const char *key, plitem_t *value)
{
if (!item || item->type != QFDictionary || !value) {
return false;
}
pldict_t *dict = (pldict_t *) item->data;
dictkey_t *k;
if (item->type != QFDictionary)
return false;
if ((k = Hash_Find (dict->tab, key))) {
PL_Free ((plitem_t *) k->value);
k->value = value;
@ -400,10 +411,11 @@ PL_D_AddObject (plitem_t *item, const char *key, plitem_t *value)
VISIBLE qboolean
PL_A_InsertObjectAtIndex (plitem_t *array, plitem_t *item, int index)
{
plarray_t *arr;
if (array->type != QFArray)
if (!array || array->type != QFArray || !item) {
return false;
}
plarray_t *arr;
arr = (plarray_t *)array->data;
@ -442,20 +454,22 @@ PL_A_AddObject (plitem_t *array, plitem_t *item)
VISIBLE int
PL_A_NumObjects (const plitem_t *array)
{
if (array->type != QFArray)
if (!array || array->type != QFArray) {
return 0;
}
return ((plarray_t *) array->data)->numvals;
}
VISIBLE plitem_t *
PL_RemoveObjectAtIndex (plitem_t *array, int index)
{
if (!array || array->type != QFArray) {
return 0;
}
plarray_t *arr;
plitem_t *item;
if (array->type != QFArray)
return 0;
arr = (plarray_t *)array->data;
if (index < 0 || index >= arr->numvals)
@ -472,7 +486,7 @@ PL_RemoveObjectAtIndex (plitem_t *array, int index)
}
static qboolean
PL_SkipSpace (pldata_t *pl)
pl_skipspace (pldata_t *pl)
{
while (pl->pos < pl->end) {
char c = pl->ptr[pl->pos];
@ -545,7 +559,7 @@ make_byte (byte h, byte l)
}
static char *
PL_ParseData (pldata_t *pl, int *len)
pl_parsedata (pldata_t *pl, int *len)
{
unsigned start = ++pl->pos;
int nibbles = 0, i;
@ -578,7 +592,7 @@ PL_ParseData (pldata_t *pl, int *len)
}
static char *
PL_ParseQuotedString (pldata_t *pl)
pl_parsequotedstring (pldata_t *pl)
{
unsigned int start = ++pl->pos;
unsigned int escaped = 0;
@ -731,7 +745,7 @@ PL_ParseQuotedString (pldata_t *pl)
}
static char *
PL_ParseUnquotedString (pldata_t *pl)
pl_parseunquotedstring (pldata_t *pl)
{
unsigned int start = pl->pos;
char *str;
@ -747,11 +761,11 @@ PL_ParseUnquotedString (pldata_t *pl)
}
static plitem_t *
PL_ParsePropertyListItem (pldata_t *pl)
pl_parsepropertylistitem (pldata_t *pl)
{
plitem_t *item = NULL;
if (!PL_SkipSpace (pl))
if (!pl_skipspace (pl))
return NULL;
switch (pl->ptr[pl->pos]) {
@ -762,16 +776,16 @@ PL_ParsePropertyListItem (pldata_t *pl)
pl->pos++;
while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != '}') {
while (pl_skipspace (pl) && pl->ptr[pl->pos] != '}') {
plitem_t *key;
plitem_t *value;
if (!(key = PL_ParsePropertyListItem (pl))) {
if (!(key = pl_parsepropertylistitem (pl))) {
PL_Free (item);
return NULL;
}
if (!(PL_SkipSpace (pl))) {
if (!(pl_skipspace (pl))) {
PL_Free (key);
PL_Free (item);
return NULL;
@ -793,13 +807,13 @@ PL_ParsePropertyListItem (pldata_t *pl)
pl->pos++;
// If there is no value, lose the key
if (!(value = PL_ParsePropertyListItem (pl))) {
if (!(value = pl_parsepropertylistitem (pl))) {
PL_Free (key);
PL_Free (item);
return NULL;
}
if (!(PL_SkipSpace (pl))) {
if (!(pl_skipspace (pl))) {
PL_Free (key);
PL_Free (value);
PL_Free (item);
@ -842,15 +856,15 @@ PL_ParsePropertyListItem (pldata_t *pl)
pl->pos++;
while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != ')') {
while (pl_skipspace (pl) && pl->ptr[pl->pos] != ')') {
plitem_t *value;
if (!(value = PL_ParsePropertyListItem (pl))) {
if (!(value = pl_parsepropertylistitem (pl))) {
PL_Free (item);
return NULL;
}
if (!(PL_SkipSpace (pl))) {
if (!(pl_skipspace (pl))) {
PL_Free (value);
PL_Free (item);
return NULL;
@ -879,7 +893,7 @@ PL_ParsePropertyListItem (pldata_t *pl)
case '<': {
int len;
char *str = PL_ParseData (pl, &len);
char *str = pl_parsedata (pl, &len);
if (!str) {
return NULL;
@ -892,7 +906,7 @@ PL_ParsePropertyListItem (pldata_t *pl)
case '"': {
int line = pl->line;
char *str = PL_ParseQuotedString (pl);
char *str = pl_parsequotedstring (pl);
if (!str) {
return NULL;
@ -903,7 +917,7 @@ PL_ParsePropertyListItem (pldata_t *pl)
default: {
int line = pl->line;
char *str = PL_ParseUnquotedString (pl);
char *str = pl_parseunquotedstring (pl);
if (!str) {
return NULL;
@ -931,7 +945,7 @@ PL_GetPropertyList (const char *string, hashlink_t **hashlinks)
pl->va_ctx = va_create_context (4);
pl->hashlinks = hashlinks;
if ((newpl = PL_ParsePropertyListItem (pl))) {
if ((newpl = pl_parsepropertylistitem (pl))) {
va_destroy_context (pl->va_ctx);
free (pl);
return newpl;
@ -1111,10 +1125,12 @@ PL_WritePropertyList (const plitem_t *pl)
{
dstring_t *dstr = dstring_newstr ();
if (!quotable_bitmap[0])
init_quotables ();
write_item (dstr, pl, 0);
write_string_len (dstr, "\n", 1);
if (pl) {
if (!quotable_bitmap[0])
init_quotables ();
write_item (dstr, pl, 0);
write_string_len (dstr, "\n", 1);
}
return dstring_freeze (dstr);
}