From ea77df2daa09b9afdd32a7e17790e4a25e64f75b Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sun, 12 Mar 2023 14:31:17 +0900 Subject: [PATCH] [plist] Add parsing for bare dictionaries and arrays Requiring top-level {} or () for (usually) hand-written files is awkward and even a little error prone, and certainly ugly at times. With this, loaders that expect a particular format can specify the format a little more directly. --- include/QF/plist.h | 30 +++++++++++++++++++-- libs/ruamoko/rua_plist.c | 56 ++++++++++++++++++++++++++++++++++---- libs/util/plist.c | 58 +++++++++++++++++++++++++++++++++++++--- libs/util/quakefs.c | 10 +++---- 4 files changed, 137 insertions(+), 17 deletions(-) diff --git a/include/QF/plist.h b/include/QF/plist.h index 46179bdc5..5ec11f849 100644 --- a/include/QF/plist.h +++ b/include/QF/plist.h @@ -143,8 +143,34 @@ typedef struct plelement_s { \return Returns an object equivalent to the passed-in string. \note You are responsible for freeing the returned object. */ -plitem_t *PL_GetPropertyList (const char *string, - struct hashctx_s **hashctx); +plitem_t *PL_GetPropertyList (const char *string, struct hashctx_s **hashctx); + +/** Create a property list from a bare dictionary list. + + The input is treated as a list of dictionary key-value pairs without the + enclosing { or }. + + \param string dicitionary list string. + \param hashctx Hashlink chain to use when creating dictionaries (see + Hash_NewTable()). May be null. + + \return Returns a dictionary object. + \note You are responsible for freeing the returned object. +*/ +plitem_t *PL_GetDictionary (const char *string, struct hashctx_s **hashctx); + +/** Create a property list from a bare array list. + + The input is treated as a list of array values without the enclosing ( or ). + + \param string array list string. + \param hashctx Hashlink chain to use when creating dictionaries (see + Hash_NewTable()). May be null. + + \return Returns an array object. + \note You are responsible for freeing the returned object. +*/ +plitem_t *PL_GetArray (const char *string, struct hashctx_s **hashctx); /** Create a property list string from the in-memory representation. diff --git a/libs/ruamoko/rua_plist.c b/libs/ruamoko/rua_plist.c index 430971b05..304fe14b0 100644 --- a/libs/ruamoko/rua_plist.c +++ b/libs/ruamoko/rua_plist.c @@ -188,9 +188,9 @@ plist_retain (plist_resources_t *res, plitem_t *plitem) } static void -bi_PL_GetFromFile (progs_t *pr, void *_res) +bi_pl_getfromfile (progs_t *pr, plist_resources_t *res, + plitem_t *get (const char *, struct hashctx_s **)) { - plist_resources_t *res = _res; QFile *file = QFile_GetFile (pr, P_INT (pr, 0)); plitem_t *plitem; long offset; @@ -205,19 +205,61 @@ bi_PL_GetFromFile (progs_t *pr, void *_res) Qread (file, buf, len); buf[len] = 0; - plitem = PL_GetPropertyList (buf, pr->hashctx); + plitem = get (buf, pr->hashctx); free (buf); R_INT (pr) = plist_retain (res, plitem); } +static void +bi_pl_get (progs_t *pr, plist_resources_t *res, + plitem_t *get (const char *, struct hashctx_s **)) +{ + plitem_t *plitem = get (P_GSTRING (pr, 0), pr->hashctx); + + R_INT (pr) = plist_retain (res, plitem); +} + +static void +bi_PL_GetFromFile (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + bi_pl_getfromfile (pr, res, PL_GetPropertyList); +} + static void bi_PL_GetPropertyList (progs_t *pr, void *_res) { plist_resources_t *res = _res; - plitem_t *plitem = PL_GetPropertyList (P_GSTRING (pr, 0), pr->hashctx); + bi_pl_get (pr, res, PL_GetPropertyList); +} - R_INT (pr) = plist_retain (res, plitem); +static void +bi_PL_GetDictionaryFromFile (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + bi_pl_getfromfile (pr, res, PL_GetDictionary); +} + +static void +bi_PL_GetDictionary (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + bi_pl_get (pr, res, PL_GetDictionary); +} + +static void +bi_PL_GetArrayFromFile (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + bi_pl_getfromfile (pr, res, PL_GetArray); +} + +static void +bi_PL_GetArray (progs_t *pr, void *_res) +{ + plist_resources_t *res = _res; + bi_pl_get (pr, res, PL_GetArray); } static void @@ -481,6 +523,10 @@ plist_compare (const void *k1, const void *k2, void *unused) static builtin_t builtins[] = { bi(PL_GetFromFile, 1, p(ptr)), bi(PL_GetPropertyList, 1, p(string)), + bi(PL_GetDictionary, 1, p(string)), + bi(PL_GetDictionaryFromFile, 1, p(string)), + bi(PL_GetArray, 1, p(string)), + bi(PL_GetArrayFromFile, 1, p(string)), bi(PL_WritePropertyList, 1, p(ptr)), bi(PL_Type, 1, p(ptr)), bi(PL_Line, 1, p(ptr)), diff --git a/libs/util/plist.c b/libs/util/plist.c index ff1287773..475380dc3 100644 --- a/libs/util/plist.c +++ b/libs/util/plist.c @@ -936,8 +936,9 @@ pl_parsepropertylistitem (pldata_t *pl) } } -VISIBLE plitem_t * -PL_GetPropertyList (const char *string, hashctx_t **hashctx) +static plitem_t * +pl_parseitem (const char *string, hashctx_t **hashctx, + plitem_t *(*parse) (pldata_t *)) { plitem_t *newpl = NULL; @@ -951,7 +952,7 @@ PL_GetPropertyList (const char *string, hashctx_t **hashctx) .hashctx = hashctx, }; - if (!(newpl = pl_parsepropertylistitem (&pl))) { + if (!(newpl = parse (&pl))) { if (pl.errmsg) { Sys_Printf ("plist: %d,%d: %s\n", pl.line, pl.pos - pl.line_start, pl.errmsg->str); @@ -962,6 +963,57 @@ PL_GetPropertyList (const char *string, hashctx_t **hashctx) return newpl; } +VISIBLE plitem_t * +PL_GetPropertyList (const char *string, hashctx_t **hashctx) +{ + return pl_parseitem (string, hashctx, pl_parsepropertylistitem); +} + + +static plitem_t * +pl_getdictionary (pldata_t *pl) +{ + plitem_t *dict = PL_NewDictionary (pl->hashctx); + dict->line = pl->line; + + while (pl_skipspace (pl, 1)) { + if (!pl_parsekeyvalue (pl, dict, 1)) { + PL_Free (dict); + return NULL; + } + } + + return dict; +} + +VISIBLE plitem_t * +PL_GetDictionary (const char *string, hashctx_t **hashctx) +{ + return pl_parseitem (string, hashctx, pl_getdictionary); +} + +static plitem_t * +pl_getarray (pldata_t *pl) +{ + plitem_t *array = PL_NewArray (); + array->line = pl->line; + + while (pl_skipspace (pl, 1)) { + if (!pl_parsevalue (pl, array, 1)) { + PL_Free (array); + return NULL; + } + } + + return array; +} + +VISIBLE plitem_t * +PL_GetArray (const char *string, hashctx_t **hashctx) +{ + return pl_parseitem (string, hashctx, pl_getarray); +} + static void write_tabs (dstring_t *dstr, int num) { diff --git a/libs/util/quakefs.c b/libs/util/quakefs.c index bc3b511fa..6e4db10eb 100644 --- a/libs/util/quakefs.c +++ b/libs/util/quakefs.c @@ -700,18 +700,14 @@ qfs_load_config (void) goto no_config; len = Qfilesize (f); - buf = malloc (len + 3); // +3 for { } and \0 + buf = malloc (len + 1); // +1 for nul - Qread (f, buf + 1, len); + Qread (f, buf, len); Qclose (f); - // convert the config file to a plist dictionary - buf[0] = '{'; - buf[len + 1] = '}'; - buf[len + 2] = 0; if (qfs_gd_plist) PL_Free (qfs_gd_plist); - qfs_gd_plist = PL_GetPropertyList (buf, 0); + qfs_gd_plist = PL_GetDictionary (buf, 0); free (buf); if (qfs_gd_plist && PL_Type (qfs_gd_plist) == QFDictionary) return; // done