From 25484f415b2e00935e9d3ba8c88d1e43455f673a Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 2 Nov 2004 05:05:00 +0000 Subject: [PATCH] move the script parser from qfbsp into libQFutil and correct a spelling error This is an imperfect revision of history. --- include/QF/Makefile.am | 6 +- include/QF/script.h | 96 +++++++++++++++ libs/util/Makefile.am | 2 +- libs/util/script.c | 173 ++++++++++++++++++++++++++ tools/qfbsp/source/csg4.c | 2 +- tools/qfbsp/source/map.c | 247 ++++++++++++-------------------------- 6 files changed, 352 insertions(+), 174 deletions(-) create mode 100644 include/QF/script.h create mode 100644 libs/util/script.c diff --git a/include/QF/Makefile.am b/include/QF/Makefile.am index 705103a39..da3a35775 100644 --- a/include/QF/Makefile.am +++ b/include/QF/Makefile.am @@ -7,9 +7,9 @@ nobase_pkginclude_HEADERS = \ llist.h locs.h mathlib.h mdfour.h model.h modelgen.h msg.h object.h pak.h \ pakfile.h pcx.h png.h plugin.h pr_comp.h pr_debug.h pr_obj.h progs.h \ qargs.h qdefs.h qendian.h qfplist.h qtypes.h quakefs.h quakeio.h render.h \ - riff.h ruamoko.h screen.h sizebuf.h skin.h sound.h spritegn.h sys.h \ - teamplay.h tga.h uint32.h va.h ver_check.h vid.h view.h wad.h wadfile.h \ - zone.h \ + riff.h ruamoko.h screen.h script.h sizebuf.h skin.h sound.h spritegn.h \ + sys.h teamplay.h tga.h uint32.h va.h ver_check.h vid.h view.h wad.h \ + wadfile.h zone.h \ \ GL/ati.h GL/defines.h GL/extensions.h GL/funcs.h GL/qf_explosions.h \ GL/qf_funcs_list.h GL/qf_lightmap.h GL/qf_noisetextures.h \ diff --git a/include/QF/script.h b/include/QF/script.h new file mode 100644 index 000000000..3f7751959 --- /dev/null +++ b/include/QF/script.h @@ -0,0 +1,96 @@ +/* + Copyright (C) 1996-1997 Id Software, Inc. + + 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 the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + See file, 'COPYING', for details. +*/ +#ifndef __QF_script_h +#define __QF_script_h + +/** \addtogroup utils */ +//@{ + +/** \defgroup script Scripts + Line oriented script parsing. Multiple scripts being parsed at the same + time is supported. +*/ +//@{ + +#include "QF/qtypes.h" + +typedef struct script_s { + /// The current (or next when unget is true) token + struct dstring_s *token; + /// True if the last token has been pushed back. + qboolean unget; + /// current position within the script + const char *p; + /// name of the file being processed. used only for error reporting + const char *file; + /// line number of the file being processed. used only for error reporting + /// but updated internally. + int line; + /// if set, will be called instead of the internal error handler + void (*error)(struct script_s *script, const char *msg); + /// if set, multi line quoted tokens will be treated as errors + int no_quote_lines; +} script_t; + +/** Return a new script_t object. + \return A new, blank, script object. Use Script_Start() to initialize. +*/ +script_t *Script_New (void); + +/** Delete a script_t object. + \param script The script_t object to be deleted + Does not free the memory passed to Script_Start(). +*/ +void Script_Delete (script_t *script); + +/** Prepare a script_t object for parsing. + The caller is responsible for freeing the memory associated with file and + data when parsing is complete. + \param script The script_t object being parsed + \param file Name of the file being parsed. used only for error reporting + \param data The script to be parsed +*/ +void Script_Start (script_t *script, const char *file, const char *data); + +/** Check if a new token is available. + \param script The script_t object being parsed + \param crossline True to allow passing \n + \return True if a token is available, false if end of file + or end of line (if crossline is false) has been hit +*/ +qboolean Script_TokenAvailable (script_t *script, qboolean crossline); + +/** Get the next token. Generates an error and exits the program if no token + is available and crossline is false. + \param script The script_t object being parsed + \param crossline True to allow passing \n + \return True on success, false on failure (no token available) +*/ +qboolean Script_GetToken (script_t *script, qboolean crossline); + +/** Unget the current token. Only one level of unget is supported. + \param script The script_t object being parsed +*/ +void Script_UngetToken (script_t *script); + +//@} +//@} + +#endif//__QF_script_h diff --git a/libs/util/Makefile.am b/libs/util/Makefile.am index 9f4d79f2d..b450e1c88 100644 --- a/libs/util/Makefile.am +++ b/libs/util/Makefile.am @@ -38,7 +38,7 @@ libQFutil_la_SOURCES= \ bspfile.c buildnum.c cbuf.c checksum.c cmd.c crc.c cvar.c dstring.c \ fendian.c hash.c idparse.c info.c link.c llist.c \ mathlib.c mdfour.c msg.c pakfile.c plugin.c qargs.c qendian.c \ - qfplist.c quakefs.c quakeio.c riff.c sizebuf.c string.c sys.c \ + qfplist.c quakefs.c quakeio.c riff.c script.c sizebuf.c string.c sys.c \ va.c ver_check.c wad.c wadfile.c zone.c $(fnmatch) $(getopt) EXTRA_DIST= $(fnmatch_src) $(getopt_src) diff --git a/libs/util/script.c b/libs/util/script.c new file mode 100644 index 000000000..440a1dc14 --- /dev/null +++ b/libs/util/script.c @@ -0,0 +1,173 @@ +/* + Copyright (C) 1996-1997 Id Software, Inc. + + 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 the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + See file, 'COPYING', for details. +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static __attribute__ ((unused)) const char rcsid[] = + "$Id$"; + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include +#include +#include + +#include "QF/dstring.h" +#include "QF/script.h" + +static void __attribute__ ((format (printf, 2, 3), noreturn)) +script_error (script_t *script, const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + fprintf (stderr, "%s:%d: ", script->file, script->line); + vfprintf (stderr, fmt, args); + fprintf (stderr, "\n"); + va_end (args); + exit (1); +} + +script_t * +Script_New (void) +{ + script_t *script = calloc (1, sizeof (script_t)); + script->token = dstring_newstr (); + return script; +} + +void +Script_Delete (script_t *script) +{ + dstring_delete (script->token); + free (script); +} + +void +Script_Start (script_t *script, const char *file, const char *data) +{ + script->line = 1; + script->file = file; + script->p = data; + script->unget = false; +} + +qboolean +Script_TokenAvailable (script_t *script, qboolean crossline) +{ + if (script->unget) + return true; + skipspace: + while (isspace ((unsigned char) *script->p)) { + if (*script->p == '\n') { + if (!crossline) + return false; + script->line++; + } + script->p++; + } + if (!*script->p) + return false; + if (*script->p == 26 || *script->p == 4) { + // end of file characters + script->p++; + goto skipspace; + } + + if (script->p[0] == '/' && script->p[1] == '/') { + // comment field + while (*script->p && *script->p != '\n') + script->p++; + if (!*script->p) + return false; + if (!crossline) + return false; + goto skipspace; + } + return true; +} + +qboolean +Script_GetToken (script_t *script, qboolean crossline) +{ + const char *token_p; + + if (script->unget) { // is a token allready waiting? + script->unget = false; + return true; + } + + if (!Script_TokenAvailable (script, crossline)) { + if (!crossline) { + if (script->error) + script->error (script, "line is incomplete"); + else + script_error (script, "line is incomplete"); + } + return false; + } + + // copy token + if (*script->p == '"') { + int start_line = script->line; + script->p++; + token_p = script->p; + while (*script->p != '"') { + if (!*script->p) { + script->line = start_line; + if (script->error) + script->error (script, "EOF inside quoted token"); + else + script_error (script, "EOF inside quoted token"); + return false; + } + if (*script->p == '\n') { + if (script->no_quote_lines) { + if (script->error) + script->error (script, "EOL inside quoted token"); + else + script_error (script, "EOL inside quoted token"); + } + script->line++; + } + script->p++; + } + dstring_copysubstr (script->token, token_p, script->p - token_p); + script->p++; + } else { + token_p = script->p; + while (*script->p && !isspace ((unsigned char) *script->p)) + script->p++; + dstring_copysubstr (script->token, token_p, script->p - token_p); + } + + return true; +} + +void +Script_UngetToken (script_t *script) +{ + script->unget = true; +} diff --git a/tools/qfbsp/source/csg4.c b/tools/qfbsp/source/csg4.c index ac24a3d9d..db286801a 100644 --- a/tools/qfbsp/source/csg4.c +++ b/tools/qfbsp/source/csg4.c @@ -287,7 +287,7 @@ CopyFacesToOutside (brush_t *b) /* CSGFaces - Returns a list of surfaces containing aall of the faces + Returns a list of surfaces containing all of the faces */ surface_t * CSGFaces (brushset_t *bs) diff --git a/tools/qfbsp/source/map.c b/tools/qfbsp/source/map.c index 24c2f9cf3..7ecec92fd 100644 --- a/tools/qfbsp/source/map.c +++ b/tools/qfbsp/source/map.c @@ -36,6 +36,7 @@ static __attribute__ ((unused)) const char rcsid[] = #include "QF/dstring.h" #include "QF/quakefs.h" +#include "QF/script.h" #include "QF/sys.h" #include "QF/va.h" @@ -53,11 +54,7 @@ char miptex[MAX_MAP_TEXINFO][16]; int numdetailbrushes; -dstring_t token = {&dstring_default_mem}; -qboolean unget; -char *script_p; -const char *script_file; -int script_line; +script_t *map; static void __attribute__ ((format (printf, 1, 2), noreturn)) map_error (const char *fmt, ...) @@ -65,7 +62,7 @@ map_error (const char *fmt, ...) va_list args; va_start (args, fmt); - fprintf (stderr, "%s:%d: ", script_file, script_line); + fprintf (stderr, "%s:%d: ", map->file, map->line); vfprintf (stderr, fmt, args); fprintf (stderr, "\n"); va_end (args); @@ -133,95 +130,6 @@ FindTexinfo (texinfo_t *t) return i; } -static void -StartTokenParsing (char *data) -{ - script_line = 1; - script_p = data; - unget = false; -} - -static qboolean -TokenAvailable (qboolean crossline) -{ - if (unget) - return true; - skipspace: - while (isspace ((unsigned char) *script_p)) { - if (*script_p == '\n') { - if (!crossline) - return false; - script_line++; - } - script_p++; - } - if (!*script_p) - return false; - if (*script_p == 26 || *script_p == 4) { // end of file characters - script_p++; - goto skipspace; - } - - if (script_p[0] == '/' && script_p[1] == '/') { // comment field - while (*script_p && *script_p != '\n') - script_p++; - if (!*script_p) - return false; - if (!crossline) - return false; - goto skipspace; - } - return true; -} - -static qboolean -GetToken (qboolean crossline) -{ - char *token_p; - - if (unget) { // is a token allready waiting? - unget = false; - return true; - } - - if (!TokenAvailable (crossline)) { - if (!crossline) - map_error ("line is incomplete"); - return false; - } - - // copy token - if (*script_p == '"') { - script_p++; - token_p = script_p; - while (*script_p != '"') { - if (!*script_p) - map_error ("EOF inside quoted token"); - if (*script_p == '\n') - script_line++; - script_p++; - } - dstring_copysubstr (&token, token_p, script_p - token_p); - script_p++; - } else { - token_p = script_p; - while (*script_p && !isspace ((unsigned char) *script_p)) - script_p++; - dstring_copysubstr (&token, token_p, script_p - token_p); - } - - *token_p = 0; - - return true; -} - -static void -UngetToken (void) -{ - unget = true; -} - - entity_t *mapent; static void @@ -234,9 +142,9 @@ ParseEpair (void) e->next = mapent->epairs; mapent->epairs = e; - e->key = strdup (token.str); - GetToken (false); - e->value = strdup (token.str); + e->key = strdup (map->token->str); + Script_GetToken (map, false); + e->value = strdup (map->token->str); } vec3_t baseaxis[18] = { @@ -275,18 +183,18 @@ ParseVerts (int *n_verts) vec3_t *verts; int i; - if (token.str[0] != ':') + if (map->token->str[0] != ':') map_error ("parsing brush"); - *n_verts = atoi (token.str + 1); + *n_verts = atoi (map->token->str + 1); verts = malloc (sizeof (vec3_t) * *n_verts); for (i = 0; i < *n_verts; i++) { - GetToken (true); - verts[i][0] = atof (token.str); - GetToken (true); - verts[i][1] = atof (token.str); - GetToken (true); - verts[i][2] = atof (token.str); + Script_GetToken (map, true); + verts[i][0] = atof (map->token->str); + Script_GetToken (map, true); + verts[i][1] = atof (map->token->str); + Script_GetToken (map, true); + verts[i][2] = atof (map->token->str); } return verts; @@ -312,45 +220,45 @@ ParseBrush (void) b->next = mapent->brushes; mapent->brushes = b; - GetToken (true); - if (strcmp (token.str, "(") != 0) { + Script_GetToken (map, true); + if (strcmp (map->token->str, "(") != 0) { verts = ParseVerts (&n_verts); } else { - UngetToken (); + Script_UngetToken (map); } do { - if (!GetToken (true)) + if (!Script_GetToken (map, true)) break; - if (!strcmp (token.str, "}")) + if (!strcmp (map->token->str, "}")) break; if (verts) { int n_v, v; - n_v = atoi (token.str); - GetToken (false); + n_v = atoi (map->token->str); + Script_GetToken (map, false); for (i = 0; i < n_v; i++) { - GetToken (false); - v = atof (token.str); + Script_GetToken (map, false); + v = atof (map->token->str); if (i < 3) VectorCopy (verts[v], planepts[i]); } - GetToken (false); + Script_GetToken (map, false); } else { // read the three point plane definition for (i = 0; i < 3; i++) { if (i != 0) - GetToken (true); - if (strcmp (token.str, "(")) + Script_GetToken (map, true); + if (strcmp (map->token->str, "(")) map_error ("parsing brush"); for (j = 0; j < 3; j++) { - GetToken (false); - planepts[i][j] = atof (token.str); + Script_GetToken (map, false); + planepts[i][j] = atof (map->token->str); } - GetToken (false); - if (strcmp (token.str, ")")) + Script_GetToken (map, false); + if (strcmp (map->token->str, ")")) map_error ("parsing brush"); } } @@ -364,53 +272,53 @@ ParseBrush (void) // read the texturedef memset (&tx, 0, sizeof (tx)); - GetToken (false); - tx.miptex = FindMiptex (token.str); - GetToken (false); - if ((hltexdef = !strcmp (token.str, "["))) { + Script_GetToken (map, false); + tx.miptex = FindMiptex (map->token->str); + Script_GetToken (map, false); + if ((hltexdef = !strcmp (map->token->str, "["))) { // S vector - GetToken (false); - vecs[0][0] = atof (token.str); - GetToken (false); - vecs[0][1] = atof (token.str); - GetToken (false); - vecs[0][2] = atof (token.str); - GetToken (false); - vecs[0][3] = atof (token.str); - GetToken (false); - if (strcmp (token.str, "]")) + Script_GetToken (map, false); + vecs[0][0] = atof (map->token->str); + Script_GetToken (map, false); + vecs[0][1] = atof (map->token->str); + Script_GetToken (map, false); + vecs[0][2] = atof (map->token->str); + Script_GetToken (map, false); + vecs[0][3] = atof (map->token->str); + Script_GetToken (map, false); + if (strcmp (map->token->str, "]")) map_error ("missing ]"); - GetToken (false); - if (strcmp (token.str, "[")) + Script_GetToken (map, false); + if (strcmp (map->token->str, "[")) map_error ("missing ["); // T vector - GetToken (false); - vecs[1][0] = atof (token.str); - GetToken (false); - vecs[1][1] = atof (token.str); - GetToken (false); - vecs[1][2] = atof (token.str); - GetToken (false); - vecs[1][3] = atof (token.str); - GetToken (false); - if (strcmp (token.str, "]")) + Script_GetToken (map, false); + vecs[1][0] = atof (map->token->str); + Script_GetToken (map, false); + vecs[1][1] = atof (map->token->str); + Script_GetToken (map, false); + vecs[1][2] = atof (map->token->str); + Script_GetToken (map, false); + vecs[1][3] = atof (map->token->str); + Script_GetToken (map, false); + if (strcmp (map->token->str, "]")) map_error ("missing ]"); } else { - vecs[0][3] = atof (token.str); - GetToken (false); - vecs[1][3] = atof (token.str); + vecs[0][3] = atof (map->token->str); + Script_GetToken (map, false); + vecs[1][3] = atof (map->token->str); } - GetToken (false); - rotate = atof (token.str); - GetToken (false); - scale[0] = atof (token.str); - GetToken (false); - scale[1] = atof (token.str); + Script_GetToken (map, false); + rotate = atof (map->token->str); + Script_GetToken (map, false); + scale[0] = atof (map->token->str); + Script_GetToken (map, false); + scale[1] = atof (map->token->str); - while (TokenAvailable (false)) { - GetToken (false); - if (!strcmp (token.str, "detail")) + while (Script_TokenAvailable (map, false)) { + Script_GetToken (map, false); + if (!strcmp (map->token->str, "detail")) b->detail = 1; else map_error ("parse error"); @@ -435,7 +343,7 @@ ParseBrush (void) if (DotProduct (plane.normal, plane.normal) < 0.1) { printf ("WARNING: brush plane with no normal on line %d\n", - script_line); + map->line); continue; } @@ -509,10 +417,10 @@ ParseBrush (void) static qboolean ParseEntity (void) { - if (!GetToken (true)) + if (!Script_GetToken (map, true)) return false; - if (strcmp (token.str, "{")) + if (strcmp (map->token->str, "{")) map_error ("ParseEntity: { not found"); if (num_entities == MAX_MAP_ENTITIES) @@ -522,11 +430,11 @@ ParseEntity (void) num_entities++; do { - if (!GetToken (true)) + if (!Script_GetToken (map, true)) map_error ("ParseEntity: EOF without closing brace"); - if (!strcmp (token.str, "}")) + if (!strcmp (map->token->str, "}")) break; - if (!strcmp (token.str, "{")) + if (!strcmp (map->token->str, "{")) ParseBrush (); else ParseEpair (); @@ -572,15 +480,16 @@ LoadMapFile (const char *filename) buf[bytes] = 0; Qclose (file); - script_file = filename; - - StartTokenParsing (buf); + map = Script_New (); + Script_Start (map, filename, buf); num_entities = 0; while (ParseEntity ()) { } + Script_Delete (map); + map = 0; free (buf); qprintf ("--- LoadMapFile ---\n");