"finish" qfplist

This is an imperfect revision of history.
This commit is contained in:
Bill Currie 2004-11-11 11:18:00 +00:00 committed by Jeff Teunissen
parent 2a36c74120
commit aadf6ebf6a
2 changed files with 314 additions and 69 deletions

View file

@ -29,18 +29,20 @@
#ifndef __QF_qfplist_h_ #ifndef __QF_qfplist_h_
#define __QF_qfplist_h_ #define __QF_qfplist_h_
#include "QF/qtypes.h" /** \addtogroup utils */
//@{
// Ugly defines for fast checking and conversion from char to number /** \defgroup qfplist Property lists
#define inrange(ch,min,max) ((ch) >= (min) && (ch) <= (max)) */
#define char2num(ch) \ //@{
inrange((ch), '0', '9') ? ((ch) - 0x30) \
: (inrange((ch), 'a', 'f') ? ((ch) - 0x57) : ((ch) - 0x37)) #include "QF/qtypes.h"
/** /**
There are four types of data that can be stored in a property list: 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). QFDictionary A list of values, each associated with a key (a C
string).
QFArray A list of indexed values QFArray A list of indexed values
QFString A string. QFString A string.
QFBinary Random binary data. The parser doesn't load these yet. QFBinary Random binary data. The parser doesn't load these yet.
@ -64,9 +66,9 @@ inrange((ch), '0', '9') ? ((ch) - 0x30) \
<!-- in the following paragram, the \< and \> are just < and >. the \ is <!-- in the following paragram, the \< and \> are just < and >. the \ is
for doxygen --> for doxygen -->
QFBinary data (though not loaded currently) is hex-encoded and contained QFBinary data is hex-encoded and contained within angle brackets, \< \>.
within angle brackets, \< \>. The length of the encoded data must be an even The length of the encoded data must be an even number, so while \<FF00\>
number, so while \<FF00\> is valid, \<F00\> isn't. is valid, \<F00\> isn't.
Property lists may contain C-style or BCPL-style comments. Property lists may contain C-style or BCPL-style comments.
*/ */
@ -111,63 +113,79 @@ typedef struct plbinary_s plbinary_t;
struct hashtab_s; struct hashtab_s;
/** /** Create an in-memory representation of the contents of a property list.
\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. \param string the saved plist, as read from a file.
\return Returns an object equivalent to the passed-in string. \return Returns an object equivalent to the passed-in string.
You are responsible for freeing the object returned. \note You are responsible for freeing the object returned.
*/ */
plitem_t *PL_GetPropertyList (const char *); plitem_t *PL_GetPropertyList (const char *string);
/** /** Create a property list string from the in-memory representation.
\fn plitem_t *PL_ObjectForKey (plitem_t *dict, const char *key)
\brief Retrieve a value from a dictionary object. \param pl the in-memory representation
\return the text representation of the property list
\note You are responsible for freeing the string returned.
*/
char *PL_WritePropertyList (plitem_t *pl);
/** Retrieve the type of an object.
\param item The object
\return the type of the object
*/
pltype_t PL_Type (plitem_t *item);
/** Retrieve a string from a string object.
\param string The string object
\return pointer to the actual string value or NULL if string isn't a
string.
\note You are NOT responsible for freeing the returned object. It will
be destroyed when its container is.
*/
const char *PL_String (plitem_t *string);
/** Retrieve a value from a dictionary object.
\param dict The dictionary to retrieve a value from \param dict The dictionary to retrieve a value from
\param key The unique key associated with the value \param key The unique key associated with the value
\return the value associated with the key, or NULL if not found or dict
\return You are NOT responsible for freeing the returned object. It will isn't a dictionary.
\note You are NOT responsible for freeing the returned object. It will
be destroyed when its container is. be destroyed when its container is.
*/ */
plitem_t *PL_ObjectForKey (plitem_t *, const char *); plitem_t *PL_ObjectForKey (plitem_t *dict, const char *key);
/** /** Retrieve a value from an array object.
\fn plitem_t *PL_ObjectAtIndex (plitem_t *array, int index)
\brief Retrieve a value from an array object.
\param array The array to get the value from \param array The array to get the value from
\param index The index within the array to retrieve \param index The index within the array to retrieve
\return the value associated with the key, or NULL if not found or array
\return You are NOT responsible for freeing the returned object. It will isn't an array.
\note You are NOT responsible for freeing the returned object. It will
be destroyed when its container is. be destroyed when its container is.
*/ */
plitem_t *PL_ObjectAtIndex (plitem_t *, int); plitem_t *PL_ObjectAtIndex (plitem_t *array, int index);
/** /** Retrieve a list of all keys in a dictionary.
\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 \param dict The dictionary to list
\return an Array containing Strings or NULL if dict isn't a dictionary
\return Returns an Array containing Strings. You are responsible for \note You are responsible for freeing this array.
freeing this array.
*/ */
plitem_t *PL_D_AllKeys (plitem_t *); plitem_t *PL_D_AllKeys (plitem_t *dict);
/** /** Retrieve the number of keys in a dictionary.
\brief Retrieve the number of keys in a dictionalry
\param dict The dictionary to get the number of keys of. \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.
*/ */
int PL_D_NumKeys (plitem_t *); int PL_D_NumKeys (plitem_t *dict);
/** /** Add a key/value pair to a dictionary.
\fn qboolean PL_D_AddObject (plitem_t *dict, plitem_t *key, plitem_t *value)
\param dict The dictionary to add the key/value pair to \param dict The dictionary to add the key/value pair to
\param key The key of the key/value pair to be added to the dictionary \param key The key of the key/value pair to be added to the dictionary
@ -175,54 +193,54 @@ int PL_D_NumKeys (plitem_t *);
\return true on success, false on failure \return true on success, false on failure
Note: the dictionary becomes the owner of both the key and \note the dictionary becomes the owner of both the key and the value.
the value.
*/ */
qboolean PL_D_AddObject (plitem_t *, plitem_t *, plitem_t *); qboolean PL_D_AddObject (plitem_t *dict, plitem_t *key, plitem_t *value);
/** Add an item to an array.
/**
\fn qboolean PL_A_AddObject (plitem_t *array, plitem_t *item)
\param array The array to add the item to \param array The array to add the item to
\param item The item to be added to the array \param item The item to be added to the array
\return true on success, false on failure \return true on success, false on failure
Note: the array becomes the owner of the added item. \note the array becomes the owner of the added item.
*/ */
qboolean PL_A_AddObject (plitem_t *, plitem_t *); qboolean PL_A_AddObject (plitem_t *array, plitem_t *item);
/** Retrieve the number of items in an array.
/**
\param array The array to get the number of objects in \param array The array to get the number of objects in
\return number of objects in the array \return number of objects in the array
*/ */
int PL_A_NumObjects (plitem_t *array); int PL_A_NumObjects (plitem_t *array);
/** /** Insert an item into an array before the specified location.
\fn qboolean PL_A_InsertObjectAtIndex (plitem_t *array, plitem_t *item, int index)
\param array The array to add the item to \param array The array to add the item to
\param item The item to be added to the array \param item The item to be added to the array
\param index The location at which to insert the item into 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
Note: the array becomes the owner of the added item. \note the array becomes the owner of the added item.
*/ */
qboolean PL_A_InsertObjectAtIndex (plitem_t *, plitem_t *, int); qboolean PL_A_InsertObjectAtIndex (plitem_t *array, plitem_t *item, int index);
plitem_t *PL_NewDictionary (void); plitem_t *PL_NewDictionary (void);
plitem_t *PL_NewArray (void); 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 *);
/** /** Free a property list object.
\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 This function takes care of freeing any referenced property list data, so
only call it on top-level objects. only call it on top-level objects.
\param item the property list object to be freed
*/ */
void PL_Free (plitem_t *); void PL_Free (plitem_t *item);
typedef struct pldata_s { // Unparsed property list string typedef struct pldata_s { // Unparsed property list string
const char *ptr; const char *ptr;
@ -232,12 +250,7 @@ typedef struct pldata_s { // Unparsed property list string
const char *error; const char *error;
} pldata_t; } pldata_t;
/* //@}
Internal prototypes //@}
static plist_t *PL_ParsePropertyList (pldata_t *);
static qboolean PL_SkipSpace (pldata_t *);
static char *PL_ParseQuotedString (pldata_t *);
static char *PL_ParseUnquotedString (pldata_t *);
*/
#endif // __QF_qfplist_h_ #endif // __QF_qfplist_h_

View file

@ -35,15 +35,41 @@ static __attribute__ ((unused)) const char rcsid[] =
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "QF/dstring.h"
#include "QF/hash.h" #include "QF/hash.h"
#include "QF/qfplist.h" #include "QF/qfplist.h"
#include "QF/qtypes.h" #include "QF/qtypes.h"
#include "QF/sys.h" #include "QF/sys.h"
// Ugly defines for fast checking and conversion from char to number
#define inrange(ch,min,max) ((ch) >= (min) && (ch) <= (max))
#define char2num(ch) \
inrange((ch), '0', '9') ? ((ch) - 0x30) \
: (inrange((ch), 'a', 'f') ? ((ch) - 0x57) : ((ch) - 0x37))
static byte quotable_bitmap[32];
static inline int
is_quotable (byte x)
{
return quotable_bitmap[x / 8] & (1 << (x % 8));
}
static void
init_quotables (void)
{
const char *unquotables = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz!#$%&*+-./:?@|~_^";
const byte *c;
memset (quotable_bitmap, ~0, sizeof (quotable_bitmap));
for (c = unquotables; *c; c++)
quotable_bitmap[*c / 8] &= ~(1 << (*c % 8));
}
static plitem_t *PL_ParsePropertyListItem (pldata_t *); static plitem_t *PL_ParsePropertyListItem (pldata_t *);
static qboolean PL_SkipSpace (pldata_t *); static qboolean PL_SkipSpace (pldata_t *);
static char *PL_ParseQuotedString (pldata_t *); static char *PL_ParseQuotedString (pldata_t *);
static char *PL_ParseUnquotedString (pldata_t *); static char *PL_ParseUnquotedString (pldata_t *);
static char *PL_ParseData (pldata_t *, int *);
static const char * static const char *
dict_get_key (void *i, void *unused) dict_get_key (void *i, void *unused)
@ -143,6 +169,14 @@ PL_Free (plitem_t *item)
free (item); free (item);
} }
const char *
PL_String (plitem_t *string)
{
if (string->type != QFString)
return NULL;
return string->data;
}
plitem_t * plitem_t *
PL_ObjectForKey (plitem_t *dict, const char *key) PL_ObjectForKey (plitem_t *dict, const char *key)
{ {
@ -159,22 +193,23 @@ PL_ObjectForKey (plitem_t *dict, const char *key)
plitem_t * plitem_t *
PL_D_AllKeys (plitem_t *dict) PL_D_AllKeys (plitem_t *dict)
{ {
void **list; void **list, **l;
dictkey_t *current; dictkey_t *current;
plitem_t *array; plitem_t *array;
if (dict->type != QFDictionary) if (dict->type != QFDictionary)
return NULL; return NULL;
if (!(list = Hash_GetList ((hashtab_t *) dict->data))) if (!(l = list = Hash_GetList ((hashtab_t *) dict->data)))
return NULL; return NULL;
if (!(array = PL_NewArray ())) if (!(array = PL_NewArray ()))
return NULL; return NULL;
while ((current = (dictkey_t *) *list++)) { while ((current = (dictkey_t *) *l++)) {
PL_A_AddObject (array, PL_NewString (current->key)); PL_A_AddObject (array, PL_NewString (current->key));
} }
free (list);
return array; return array;
} }
@ -333,6 +368,53 @@ PL_SkipSpace (pldata_t *pl)
return false; return false;
} }
static inline byte
to_hex (byte a)
{
if (a >= '0' && a <= '9')
return a - '0';
if (a >= 'A' && a <= 'F')
return a - 'A' + 10;
return a - 'a' + 10;
}
static inline byte
make_byte (byte h, byte l)
{
return (to_hex (h) << 4) | to_hex (l);
}
static char *
PL_ParseData (pldata_t *pl, int *len)
{
unsigned int start = ++pl->pos;
int nibbles = 0, i;
char *str;
while (pl->pos < pl->end) {
if (isxdigit (pl->ptr[pl->pos])) {
nibbles++;
continue;
}
if (pl->ptr[pl->pos] == '>') {
if (nibbles & 1) {
pl->error = "invalid data, missing nibble";
return NULL;
}
*len = nibbles / 2;
str = malloc (*len);
for (i = 0; i < *len; i++)
str[i] = make_byte (pl->ptr[start + i * 2],
pl->ptr[start + i * 2 + 1]);
return str;
}
pl->error = "invalid character in data";
return NULL;
}
pl->error = "Reached end of string while parsing data";
return NULL;
}
static char * static char *
PL_ParseQuotedString (pldata_t *pl) PL_ParseQuotedString (pldata_t *pl)
{ {
@ -478,7 +560,7 @@ PL_ParseUnquotedString (pldata_t *pl)
char *str; char *str;
while (pl->pos < pl->end) { while (pl->pos < pl->end) {
if (!isalnum ((byte) pl->ptr[pl->pos]) && pl->ptr[pl->pos] != '_') if (is_quotable (pl->ptr[pl->pos]))
break; break;
pl->pos++; pl->pos++;
} }
@ -622,9 +704,16 @@ PL_ParsePropertyListItem (pldata_t *pl)
return item; return item;
} }
case '<': case '<': {
pl->error = "Unexpected character in string (binary data unsupported)"; int len;
char *str = PL_ParseData (pl, &len);
if (!str) {
return NULL; return NULL;
} else {
return PL_NewData (str, len);
}
}
case '"': { case '"': {
char *str = PL_ParseQuotedString (pl); char *str = PL_ParseQuotedString (pl);
@ -654,6 +743,9 @@ PL_GetPropertyList (const char *string)
pldata_t *pl = calloc (1, sizeof (pldata_t)); pldata_t *pl = calloc (1, sizeof (pldata_t));
plitem_t *newpl = NULL; plitem_t *newpl = NULL;
if (!quotable_bitmap[0])
init_quotables ();
pl->ptr = string; pl->ptr = string;
pl->pos = 0; pl->pos = 0;
pl->end = strlen (string); pl->end = strlen (string);
@ -670,3 +762,143 @@ PL_GetPropertyList (const char *string)
return NULL; return NULL;
} }
} }
static void
write_tabs (dstring_t *dstr, int num)
{
int len = strlen (dstr->str);
dstr->size = len + num + 1;
dstring_adjust (dstr);
memset (dstr->str + len, '\t', num);
dstr->str[len + num] = 0;
}
static void
write_string (dstring_t *dstr, const char *str)
{
const char *s;
char c;
for (s = str; *s && !is_quotable (*s); s++)
;
if (!*s) {
dstring_appendstr (dstr, str);
return;
}
dstring_appendstr (dstr, "\"");
while (*str) {
for (s = str; (*s && isascii ((byte) *s) && isprint ((byte) *s)
&& *s != '\\' && *s != '\"'); s++)
;
dstring_appendsubstr (dstr, str, s - str);
if (*s) {
switch (*s) {
case '\"':
case '\\':
c = *s;
break;
case '\n':
c = 'n';
break;
case '\a':
c = 'a';
break;
case '\b':
c = 'b';
break;
case '\f':
c = 'f';
break;
case '\r':
c = 'r';
break;
case '\t':
c = 't';
break;
case '\v':
c = 'v';
break;
default:
c = 0;
dasprintf (dstr, "\\%03o", (byte) *s);
break;
}
if (c)
dasprintf (dstr, "\\%c", c);
s++;
}
str = s;
}
dstring_appendstr (dstr, "\"");
}
static void
write_item (dstring_t *dstr, plitem_t *item, int level)
{
void **list, **l;
dictkey_t *current;
plarray_t *array;
plbinary_t *binary;
int i;
switch (item->type) {
case QFDictionary:
dstring_appendstr (dstr, "{\n");
l = list = Hash_GetList ((hashtab_t *) item->data);
while ((current = (dictkey_t *) *l++)) {
write_tabs (dstr, level + 1);
write_string (dstr, current->key);
dstring_appendstr (dstr, " = ");
write_item (dstr, current->value, level + 1);
dstring_appendstr (dstr, ";\n");
}
free (list);
write_tabs (dstr, level);
dstring_appendstr (dstr, "}");
break;
case QFArray:
dstring_appendstr (dstr, "(\n");
array = (plarray_t *) item->data;
for (i = 0; i < array->numvals; i++) {
write_tabs (dstr, level + 1);
write_item (dstr, array->values[i], level + 1);
if (i < array->numvals - 1)
dstring_appendstr (dstr, ",\n");
}
dstring_appendstr (dstr, "\n");
write_tabs (dstr, level);
dstring_appendstr (dstr, ")");
break;
case QFBinary:
dstring_appendstr (dstr, "<");
binary = (plbinary_t *) item->data;
for (i = 0; i < (int) binary->size; i++)
dasprintf (dstr, "%02X", ((char *) binary->data)[i]);
dstring_appendstr (dstr, ">");
break;
case QFString:
write_string (dstr, item->data);
break;
default:
break;
}
}
char *
PL_WritePropertyList (plitem_t *pl)
{
dstring_t *dstr = dstring_newstr ();
if (!quotable_bitmap[0])
init_quotables ();
write_item (dstr, pl, 0);
dstring_appendstr (dstr, "\n");
return dstring_freeze (dstr);
}
pltype_t
PL_Type (plitem_t *item)
{
return item->type;
}