From e1638eba8507c80a08d3812bcf6a9f571f71c7f7 Mon Sep 17 00:00:00 2001 From: Jeff Teunissen Date: Thu, 15 Feb 2001 17:18:45 +0000 Subject: [PATCH] (Finally!) commit the property list parser. --- include/qfplist.h | 102 +++++++++++ source/qfplist.c | 445 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 547 insertions(+) create mode 100644 include/qfplist.h create mode 100644 source/qfplist.c diff --git a/include/qfplist.h b/include/qfplist.h new file mode 100644 index 0000000..f42819b --- /dev/null +++ b/include/qfplist.h @@ -0,0 +1,102 @@ +/* + qfplist.h + + Property list management types and prototypes + + Copyright (C) 2000 Jeff Teunissen + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __qfplist_h_ +#define __qfplist_h_ + +#include "qtypes.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)) + +// Maximum number of items in an array +#define MAX_ARRAY_INDEX 128 + +typedef enum {QFDictionary, QFArray, QFBinary, QFString} pltype_t; // possible types + +struct plitem_s { + struct plitem_s *next; // Pointer to next item + pltype_t type; // Type + union shared { // Type-dependant data + struct dict_s *dict; + struct array_s *array; + void *binary; + char *string; + } data; +}; + +/* + Dictionaries +*/ +struct dict_s { + int numkeys; // Number of items in dictionary + struct dictkey_s *keys; +}; + +struct dictkey_s { + struct dictkey_s *next; + struct plitem_s *key; + struct plitem_s *value; +}; + +/* + Arrays +*/ +struct array_s { + int numvals; // Number of items in array + struct plitem_s *values[MAX_ARRAY_INDEX+1]; // Array data +}; + +// now that we've defined the structs, define their types so we can use them +typedef struct plitem_s plitem_t; +typedef struct dict_s dict_t; +typedef struct dictkey_s dictkey_t; +typedef struct array_s array_t; + +typedef struct pldata_s { // Unparsed property list string + const char *ptr; + unsigned int end; + unsigned int pos; + unsigned int line; + char *error; +} pldata_t; + +static plitem_t *PL_GetPropertyList (const char *); + +/* + 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 // __qfplist_h_ diff --git a/source/qfplist.c b/source/qfplist.c new file mode 100644 index 0000000..f7b7b5a --- /dev/null +++ b/source/qfplist.c @@ -0,0 +1,445 @@ +/* + qfplist.c + + Property list management + + Copyright (C) 2000 Jeff Teunissen + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "qfplist.h" +#include "qtypes.h" + +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 qboolean +PL_SkipSpace (pldata_t *pl) +{ + while (pl->pos < pl->end) { + char c = pl->ptr[pl->pos]; + + if (!isspace (c)) { + if (c == '/' && pl->pos < pl->end - 1) { // check for comments + if (pl->ptr[pl->pos + 1] == '/') { + pl->pos += 2; + + while (pl->pos < pl->end) { + c = pl->ptr[pl->pos]; + + if (c == '\n') + break; + pl->pos++; + } + if (pl->pos >= pl->end) { + pl->error = "Reached end of string in comment"; + return false; + } + } else if (pl->ptr[pl->pos + 1] == '*') { // "/*" comments + pl->pos += 2; + + while (pl->pos < pl->end) { + c = pl->ptr[pl->pos]; + + if (c == '\n') { + pl->line++; + } else if (c == '*' && pl->pos < pl->end - 1 + && pl->ptr[pl->pos+1] == '/') { + pl->pos++; + break; + } + pl->pos++; + } + if (pl->pos >= pl->end) { + pl->error = "Reached end of string in comment"; + return false; + } + } else { + return true; + } + } else { + return true; + } + } + if (c == '\n') { + pl->line++; + } + pl->pos++; + } + pl->error = "Reached end of string"; + return false; +} + +static char * +PL_ParseQuotedString (pldata_t *pl) +{ + unsigned int start = ++pl->pos; + unsigned int escaped = 0; + unsigned int shrink = 0; + qboolean hex = false; + char *str; + + while (pl->pos < pl->end) { + + char c = pl->ptr[pl->pos]; + + if (escaped) { + if (escaped == 1 && c == '0') { + escaped++; + hex = false; + } else if (escaped > 1) { + if (escaped == 2 && c == 'x') { + hex = true; + shrink++; + escaped++; + } else if (hex && isxdigit (c)) { + shrink++; + escaped++; + } else if (c >= '0' && c <= '7') { + shrink++; + escaped++; + } else { + pl->pos--; + escaped = 0; + } + } else { + escaped = 0; + } + } else { + if (c == '\\') { + escaped = 1; + shrink++; + } else if (c == '"') { + break; + } + } + + if (c == '\n') { + pl->line++; + } + + pl->pos++; + } + + if (pl->pos >= pl->end) { + pl->error = "Reached end of string while parsing quoted string"; + return NULL; + } + + if (pl->pos - start - shrink == 0) { + str = ""; + } else { + + char chars[pl->pos - start - shrink]; + unsigned int j; + unsigned int k; + + escaped = 0; + hex = false; + + for (j = start, k = 0; j < pl->pos; j++) { + + char c = pl->ptr[j]; + + if (escaped) { + if (escaped == 1 && c == '0') { + chars[k] = 0; + hex = false; + escaped++; + } else if (escaped > 1) { + if (escaped == 2 && c == 'x') { + hex = true; + escaped++; + } else if (hex && isxdigit (c)) { + chars[k] <<= 4; + chars[k] |= char2num (c); + escaped++; + } else if (inrange (c, '0', '7')) { + chars[k] <<= 3; + chars[k] |= (c - '0'); + escaped++; + } else { + escaped = 0; + j--; + k++; + } + } else { + escaped = 0; + switch (c) { + case 'a': + chars[k] = '\a'; + break; + case 'b': + chars[k] = '\b'; + break; + case 't': + chars[k] = '\t'; + break; + case 'r': + chars[k] = '\r'; + break; + case 'n': + chars[k] = '\n'; + break; + case 'v': + chars[k] = '\v'; + break; + case 'f': + chars[k] = '\f'; + break; + default: + chars[k] = c; + break; + } + k++; + } + } else { + chars[k] = c; + if (c == '\\') { + escaped = 1; + } else { + k++; + } + } + } + str = strncat (calloc ((pl->pos - start - shrink) + 1, 1), chars, pl->pos - start - shrink); + } + pl->pos++; + return str; +} + +static char * +PL_ParseUnquotedString (pldata_t *pl) +{ + unsigned int start = pl->pos; + char *str; + + while (pl->pos < pl->end) { + if (!isalnum (pl->ptr[pl->pos])) + break; + pl->pos++; + } + str = strncat (calloc ((pl->pos - start) + 1, 1), &pl->ptr[start], pl->pos - start); + return str; +} + +static plitem_t * +PL_ParsePropertyListItem (pldata_t *pl) +{ + plitem_t *item = NULL; + + if (!PL_SkipSpace (pl)) + return NULL; + + switch (pl->ptr[pl->pos]) { + case '{': { + dict_t *dict = calloc (1, sizeof (dict_t)); + + pl->pos++; + + while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != '}') { + plitem_t *key; + plitem_t *value; + + if (!(key = PL_ParsePropertyListItem (pl))) + return NULL; + if (!(PL_SkipSpace (pl))) { + free (key); + return NULL; + } + if (pl->ptr[pl->pos] != '=') { + pl->error = "Unexpected character (wanted '=')"; + free (key); + return NULL; + } + pl->pos++; + + // If there is no value, lose the key + if (!(value = PL_ParsePropertyListItem (pl))) { + free (key); + return NULL; + } + if (!(PL_SkipSpace (pl))) { + free (key); + free (value); + return NULL; + } + if (pl->ptr[pl->pos] == ';') { + pl->pos++; + } else if (pl->ptr[pl->pos] != '}') { + pl->error = "Unexpected character (wanted ';' or '}')"; + free (key); + free (value); + return NULL; + } + + // Add the key/value pair to the dict + if (!(dict->keys)) { // No keys, add one + dictkey_t *k = calloc (1, sizeof (dictkey_t)); + + if (!k) { + free (key); + free (value); + return NULL; + } + + k->key = key; + k->value = value; + + dict->keys = k; + } else { + dictkey_t *k; + + for (k = dict->keys; k; k = k->next) { // add to end + if (k->next == NULL) { + dictkey_t *k2 = calloc (1, sizeof (dictkey_t)); + + if (!k2) { + free (key); + free (value); + return NULL; + } + + k2->key = key; + k2->value = value; + k = k2; + break; + } + } + } + dict->numkeys++; + } + + if (pl->pos >= pl->end) { // Catch the error + pl->error = "Unexpected end of string when parsing dictionary"; + free (dict); + return NULL; + } + + pl->pos++; + + item = calloc (1, sizeof (plitem_t)); + item->type = QFDictionary; + item->data.dict = dict; + return item; + } + + case '(': { + array_t *a = calloc (1, sizeof (array_t)); + + pl->pos++; + + while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != ')') { + plitem_t *value; + + if (!(value = PL_ParsePropertyListItem (pl))) + return NULL; + if (!(PL_SkipSpace (pl))) { + free (value); + return NULL; + } + if (pl->ptr[pl->pos] == ',') { + pl->pos++; + } else if (pl->ptr[pl->pos] != ')') { + pl->error = "Unexpected character (wanted ',' or ')')"; + free (value); + return NULL; + } + if (a->numvals == MAX_ARRAY_INDEX) { + pl->error = "Unexpected character (too many items in array)"; + free (value); + return NULL; + } + a->values[a->numvals++] = value; + } + pl->pos++; + + item = calloc (1, sizeof (plitem_t)); + item->type = QFArray; + item->data.array = a; + return item; + } + + case '<': + pl->error = "Unexpected character in string (binary data unsupported)"; + return NULL; + + case '"': { + char *str = PL_ParseQuotedString (pl); + + if (!str) { + return NULL; + } else { + item = calloc (1, sizeof (plitem_t)); + item->type = QFString; + item->data.string = str; + return item; + } + } + + default: { + char *str = PL_ParseUnquotedString (pl); + + if (!str) { + return NULL; + } else { + item = calloc (1, sizeof (plitem_t)); + item->type = QFString; + item->data.string = str; + return item; + } + } + } // switch +} + +static plitem_t * +PL_GetPropertyList (const char *string) +{ + pldata_t *pl = calloc (1, sizeof (pldata_t)); + + pl->ptr = string; + pl->pos = 0; + pl->end = strlen (string); + pl->error = NULL; + pl->line = 1; + + return PL_ParsePropertyListItem (pl); +} + + + + + + + + +