mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 23:32:09 +00:00
"finish" qfplist
This is an imperfect revision of history.
This commit is contained in:
parent
2a36c74120
commit
aadf6ebf6a
2 changed files with 314 additions and 69 deletions
|
@ -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_
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue