"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_
#define __QF_qfplist_h_
#include "QF/qtypes.h"
/** \addtogroup utils */
//@{
// 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))
/** \defgroup qfplist Property lists
*/
//@{
#include "QF/qtypes.h"
/**
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
QFString A string.
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
for doxygen -->
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.
QFBinary data 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.
*/
@ -111,63 +113,79 @@ 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
/** 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.
\note You are responsible for freeing the object returned.
*/
plitem_t *PL_GetPropertyList (const char *);
plitem_t *PL_GetPropertyList (const char *string);
/**
\fn plitem_t *PL_ObjectForKey (plitem_t *dict, const char *key)
\brief Retrieve a value from a dictionary object.
/** Create a property list string from the in-memory representation.
\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 key The unique key associated with the value
\return You are NOT responsible for freeing the returned object. It will
\return the value associated with the key, or NULL if not found or dict
isn't a dictionary.
\note 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 *dict, const char *key);
/**
\fn plitem_t *PL_ObjectAtIndex (plitem_t *array, int index)
\brief Retrieve a value from an array object.
/** 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 You are NOT responsible for freeing the returned object. It will
\return the value associated with the key, or NULL if not found or array
isn't an array.
\note 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 *array, int index);
/**
\fn plitem_t *PL_D_AllKeys (plitem_t *dict)
\brief Retrieve a list of all keys in a dictionary.
/** 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.
\return an Array containing Strings or NULL if dict isn't a dictionary
\note You are responsible for freeing this array.
*/
plitem_t *PL_D_AllKeys (plitem_t *);
plitem_t *PL_D_AllKeys (plitem_t *dict);
/**
\brief Retrieve the number of keys in a dictionalry
/** Retrieve the number of keys in a dictionary.
\param dict The dictionary to get the number of keys of.
\return Returns the number of keys in the dictionary.
*/
int PL_D_NumKeys (plitem_t *);
int PL_D_NumKeys (plitem_t *dict);
/**
\fn qboolean PL_D_AddObject (plitem_t *dict, plitem_t *key, plitem_t *value)
/** Add a key/value pair to a dictionary.
\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
@ -175,54 +193,54 @@ int PL_D_NumKeys (plitem_t *);
\return true on success, false on failure
Note: the dictionary becomes the owner of both the key and
the value.
\note the dictionary becomes the owner of both the key and 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 item The item to be added to the array
\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
\return number of objects in the array
*/
int PL_A_NumObjects (plitem_t *array);
/**
\fn qboolean PL_A_InsertObjectAtIndex (plitem_t *array, plitem_t *item, int index)
/** Insert an item into an array before the specified location.
\param array The array to add the item to
\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
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_NewArray (void);
plitem_t *PL_NewData (void *, int);
plitem_t *PL_NewString (const char *);
/**
\fn void PL_Free (plitem_t *object)
\brief Free a property list object
/** Free a property list object.
This function takes care of freeing any referenced property list data, so
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
const char *ptr;
@ -232,12 +250,7 @@ typedef struct pldata_s { // Unparsed property list string
const char *error;
} 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_

View file

@ -35,15 +35,41 @@ static __attribute__ ((unused)) const char rcsid[] =
#include <stdlib.h>
#include <string.h>
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/qfplist.h"
#include "QF/qtypes.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 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 (void *i, void *unused)
@ -143,6 +169,14 @@ PL_Free (plitem_t *item)
free (item);
}
const char *
PL_String (plitem_t *string)
{
if (string->type != QFString)
return NULL;
return string->data;
}
plitem_t *
PL_ObjectForKey (plitem_t *dict, const char *key)
{
@ -159,22 +193,23 @@ PL_ObjectForKey (plitem_t *dict, const char *key)
plitem_t *
PL_D_AllKeys (plitem_t *dict)
{
void **list;
void **list, **l;
dictkey_t *current;
plitem_t *array;
if (dict->type != QFDictionary)
return NULL;
if (!(list = Hash_GetList ((hashtab_t *) dict->data)))
if (!(l = list = Hash_GetList ((hashtab_t *) dict->data)))
return NULL;
if (!(array = PL_NewArray ()))
return NULL;
while ((current = (dictkey_t *) *list++)) {
while ((current = (dictkey_t *) *l++)) {
PL_A_AddObject (array, PL_NewString (current->key));
}
free (list);
return array;
}
@ -333,6 +368,53 @@ PL_SkipSpace (pldata_t *pl)
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 *
PL_ParseQuotedString (pldata_t *pl)
{
@ -478,7 +560,7 @@ PL_ParseUnquotedString (pldata_t *pl)
char *str;
while (pl->pos < pl->end) {
if (!isalnum ((byte) pl->ptr[pl->pos]) && pl->ptr[pl->pos] != '_')
if (is_quotable (pl->ptr[pl->pos]))
break;
pl->pos++;
}
@ -622,9 +704,16 @@ PL_ParsePropertyListItem (pldata_t *pl)
return item;
}
case '<':
pl->error = "Unexpected character in string (binary data unsupported)";
return NULL;
case '<': {
int len;
char *str = PL_ParseData (pl, &len);
if (!str) {
return NULL;
} else {
return PL_NewData (str, len);
}
}
case '"': {
char *str = PL_ParseQuotedString (pl);
@ -654,6 +743,9 @@ PL_GetPropertyList (const char *string)
pldata_t *pl = calloc (1, sizeof (pldata_t));
plitem_t *newpl = NULL;
if (!quotable_bitmap[0])
init_quotables ();
pl->ptr = string;
pl->pos = 0;
pl->end = strlen (string);
@ -670,3 +762,143 @@ PL_GetPropertyList (const char *string)
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;
}