diff --git a/include/QF/Makefile.am b/include/QF/Makefile.am index 3765722ae..6372769c5 100644 --- a/include/QF/Makefile.am +++ b/include/QF/Makefile.am @@ -4,9 +4,10 @@ includedir = $(prefix)/include/QF include_HEADERS = bspfile.h cbuf.h cdaudio.h checksum.h clip_hull.h cmd.h \ console.h crc.h csqc.h cvar.h dstring.h draw.h gcc_attr.h gib_buffer.h \ gib_builtin.h gib_function.h gib_init.h gib_parse.h gib_process.h \ - gib_thread.h gib_vars.h hash.h hl.h idparse.h in_event.h info.h input.h \ - joystick.h keys.h link.h locs.h mathlib.h mdfour.h model.h modelgen.h \ - msg.h pak.h pakfile.h pcx.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 screen.h sizebuf.h skin.h sound.h spritegn.h sys.h teamplay.h \ - texture.h tga.h uint32.h va.h ver_check.h vid.h wad.h zone.h + gib_regex.h gib_thread.h gib_vars.h hash.h hl.h idparse.h in_event.h \ + info.h input.h joystick.h keys.h link.h locs.h mathlib.h mdfour.h \ + model.h modelgen.h msg.h pak.h pakfile.h pcx.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 screen.h sizebuf.h skin.h \ + sound.h spritegn.h sys.h teamplay.h texture.h tga.h uint32.h va.h \ + ver_check.h vid.h wad.h zone.h diff --git a/include/QF/gib_regex.h b/include/QF/gib_regex.h new file mode 100644 index 000000000..140123065 --- /dev/null +++ b/include/QF/gib_regex.h @@ -0,0 +1,45 @@ +/* + #FILENAME# + + #DESCRIPTION# + + Copyright (C) 2002 #AUTHOR# + + Author: #AUTHOR# + Date: #DATE# + + 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$ + +*/ + +#include "regex.h" + +typedef struct gib_regex_s { + char *regex; + regex_t comp; + int cflags; +} gib_regex_t; + +void GIB_Regex_Init (void); +regex_t *GIB_Regex_Compile (const char *regex, int cflags); +const char *GIB_Regex_Error (void); +int GIB_Regex_Translate_Options (const char *opstr); +unsigned int GIB_Regex_Apply_Match (regmatch_t match[10], dstring_t *dstr, unsigned int ofs, const char *replace); diff --git a/libs/gib/Makefile.am b/libs/gib/Makefile.am index e5eb6d92d..80ef3b79d 100644 --- a/libs/gib/Makefile.am +++ b/libs/gib/Makefile.am @@ -7,4 +7,4 @@ lib_LTLIBRARIES= libQFgib.la libQFgib_la_LDFLAGS= -version-info 1:0:0 libQFgib_la_SOURCES= \ gib_buffer.c gib_builtin.c gib_function.c gib_parse.c gib_process.c \ - gib_thread.c gib_vars.c gib_init.c ops.c exp.c regex.c + gib_regex.c gib_thread.c gib_vars.c gib_init.c ops.c exp.c regex.c diff --git a/libs/gib/gib_builtin.c b/libs/gib/gib_builtin.c index fc5c1df13..65b31cd8b 100644 --- a/libs/gib/gib_builtin.c +++ b/libs/gib/gib_builtin.c @@ -58,6 +58,7 @@ static const char rcsid[] = #include "QF/gib_buffer.h" #include "QF/gib_function.h" #include "QF/gib_vars.h" +#include "QF/gib_regex.h" #include "QF/gib_thread.h" #include "regex.h" @@ -495,127 +496,67 @@ GIB_String_Findsub_f (void) else GIB_Return ("-1"); } - -inline unsigned int -GIB_Regex_Apply_Match (regmatch_t match[10], dstring_t *dstr, unsigned int ofs, const char *replace) -{ - int i, start, len, sub, rlen = strlen(replace); - char *matched; - - start = match[0].rm_so+ofs; - len = match[0].rm_eo - match[0].rm_so; - - // Save matched pattern space - matched = calloc (len + 1, sizeof(char)); - memcpy (matched, dstr->str+start, match[0].rm_eo - match[0].rm_so); - - dstring_replace (dstr, start, len, replace, rlen); - for (i = start; i < start+rlen; i++) { - if (dstr->str[i] == '\\') { - if (dstr->str[i+1] == '&') { - dstring_snip (dstr, i, 1); - rlen--; - continue; - } - if (isdigit ((byte) dstr->str[i+1])) { - if (i && dstr->str[i-1] == '\\') { // Escaped, not a true back reference - dstring_snip (dstr, i, 1); - rlen--; - continue; - } - sub = dstr->str[i+1] - '0'; - if (match[sub].rm_so != -1) { - dstring_replace (dstr, i, 2, matched+match[sub].rm_so, match[sub].rm_eo - match[sub].rm_so); - rlen += match[sub].rm_eo - match[sub].rm_so - 2; - } else { - dstring_snip (dstr, i, 2); - rlen -= 2; - } - } - } else if (dstr->str[i] == '&') { - dstring_replace (dstr, i, 1, matched, len); - rlen += strlen(matched) - 1; - } - } - free (matched); - return rlen + match[0].rm_so; -} void GIB_Regex_Match_f (void) { regex_t *reg; - int res; - char errbuf[1024]; - if (GIB_Argc() != 3) { - GIB_USAGE ("string regex"); + if (GIB_Argc() != 4) { + GIB_USAGE ("string regex options"); return; } - reg = calloc (1, sizeof (regex_t)); - - if ((res = regcomp(reg, GIB_Argv(2), REG_NOSUB | REG_EXTENDED))) { - regerror(res, reg, errbuf, sizeof(errbuf)); - Cbuf_Error ("regex", "%s: %s", GIB_Argv(0), errbuf); - } else if (regexec(reg, GIB_Argv(1), 0, 0, 0)) + if (!(reg = GIB_Regex_Compile (GIB_Argv(2), REG_EXTENDED | GIB_Regex_Translate_Options (GIB_Argv(3))))) + Cbuf_Error ("regex", "%s: %s", GIB_Argv(0), GIB_Regex_Error ()); + else if (regexec(reg, GIB_Argv(1), 0, 0, 0)) GIB_Return ("0"); else GIB_Return ("1"); - regfree (reg); - free (reg); } void GIB_Regex_Replace_f (void) { regex_t *reg; - int res, ofs, len;//, e; - char errbuf[1024]; + int ofs, len;//, e; regmatch_t match[10]; - if (GIB_Argc() < 4 || GIB_Argc() > 5) { - GIB_USAGE ("string regex replacement [options]"); + if (GIB_Argc() != 5) { + GIB_USAGE ("string regex options replacement"); return; } ofs = 0; - len = strlen (GIB_Argv(3)); - reg = calloc (1, sizeof (regex_t)); + len = strlen (GIB_Argv(4)); - if ((res = regcomp(reg, GIB_Argv(2), REG_EXTENDED))) { - regerror(res, reg, errbuf, sizeof(errbuf)); - Cbuf_Error ("regex", "%s: %s", GIB_Argv(0), errbuf); - } else while (!regexec(reg, GIB_Argv(1)+ofs, 10, match, ofs > 0 ? REG_NOTBOL : 0) && match[0].rm_eo) { - - ofs += GIB_Regex_Apply_Match (match, GIB_Argd(1), ofs, GIB_Argv(3)); - } - regfree (reg); - free (reg); + if (!(reg = GIB_Regex_Compile (GIB_Argv(2), REG_EXTENDED | GIB_Regex_Translate_Options (GIB_Argv(3))))) + Cbuf_Error ("regex", "%s: %s", GIB_Argv(0), GIB_Regex_Error ()); + else if (strchr(GIB_Argv(3), 'g')) + while (!regexec(reg, GIB_Argv(1)+ofs, 10, match, ofs > 0 ? REG_NOTBOL : 0) && match[0].rm_eo) + ofs += GIB_Regex_Apply_Match (match, GIB_Argd(1), ofs, GIB_Argv(4)); + else if (!regexec(reg, GIB_Argv(1), 10, match, 0) && match[0].rm_eo) + GIB_Regex_Apply_Match (match, GIB_Argd(1), 0, GIB_Argv(4)); GIB_Return (GIB_Argv(1)); } - void GIB_Regex_Extract_f (void) { regex_t *reg; - char errbuf[1024]; regmatch_t *match; - int i, res; + int i; char o; if (GIB_Argc() < 4) { - GIB_USAGE ("string regex var1 [var2 var3 ...]"); + GIB_USAGE ("string regex options [var1 var2 var3 ...]"); return; } match = calloc (GIB_Argc() - 3, sizeof(regmatch_t)); - reg = calloc (1, sizeof (regex_t)); - if ((res = regcomp(reg, GIB_Argv(2), REG_EXTENDED))) { - regerror(res, reg, errbuf, sizeof(errbuf)); - Cbuf_Error ("regex", "%s: %s", GIB_Argv(0), errbuf); - } else if (!regexec(reg, GIB_Argv(1), GIB_Argc() - 3, match, 0) && match[0].rm_eo) { + if (!(reg = GIB_Regex_Compile (GIB_Argv(2), REG_EXTENDED | GIB_Regex_Translate_Options (GIB_Argv(3))))) + Cbuf_Error ("regex", "%s: %s", GIB_Argv(0), GIB_Regex_Error ()); + else if (!regexec(reg, GIB_Argv(1), GIB_Argc() - 3, match, 0) && match[0].rm_eo) { for (i = 0; i < GIB_Argc() - 3; i++) { if (match[i].rm_so != -1 && *GIB_Argv(i+3)) { o = GIB_Argv(1)[match[i].rm_eo]; @@ -627,8 +568,6 @@ GIB_Regex_Extract_f (void) GIB_Return (va("%lu", (unsigned long) match[0].rm_eo)); } else GIB_Return ("-1"); - regfree (reg); - free (reg); free (match); } diff --git a/libs/gib/gib_init.c b/libs/gib/gib_init.c index cfdc29e8e..b4aa3ac41 100644 --- a/libs/gib/gib_init.c +++ b/libs/gib/gib_init.c @@ -37,6 +37,7 @@ static const char rcsid[] = #include "QF/quakefs.h" #include "QF/gib_parse.h" #include "QF/gib_builtin.h" +#include "QF/gib_regex.h" #include "QF/cmd.h" #include "QF/sys.h" #include "QF/zone.h" @@ -83,6 +84,8 @@ GIB_Init (qboolean sandbox) Cmd_RemoveCommand ("exec"); Cmd_AddCommand ("exec", GIB_Exec_Override_f, "Execute a script file."); } + // Initialize regex cache + GIB_Regex_Init (); // Initialize builtins GIB_Builtin_Init (sandbox); } diff --git a/libs/gib/gib_regex.c b/libs/gib/gib_regex.c new file mode 100644 index 000000000..4e8c5814b --- /dev/null +++ b/libs/gib/gib_regex.c @@ -0,0 +1,174 @@ +/* + #FILENAME# + + #DESCRIPTION# + + Copyright (C) 2002 #AUTHOR# + + Author: #AUTHOR# + Date: #DATE# + + 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 + +*/ + +static const char rcsid[] = + "$Id$"; + +#include +#include +#include +#include + +#include "regex.h" +#include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/gib_regex.h" +#include "QF/qtypes.h" + +hashtab_t *gib_regexs; +static char errstr[1024]; + +const char * +GIB_Regex_Get_Key (void *ele, void *ptr) +{ + return ((gib_regex_t *) ele)->regex; +} + +void +GIB_Regex_Free (void *ele, void *ptr) +{ + regfree (&(((gib_regex_t *) ele)->comp)); + free (((gib_regex_t *) ele)->regex); + free (ele); +} + +void +GIB_Regex_Init (void) +{ + gib_regexs = Hash_NewTable (512, GIB_Regex_Get_Key, GIB_Regex_Free, 0); +} + +regex_t * +GIB_Regex_Compile (const char *regex, int cflags) +{ + static unsigned int num_regexs = 0; + gib_regex_t *reg; + int res; + + // Already compiled ? + if ((reg = Hash_Find (gib_regexs, regex))) { + // Did cflags change? + if (cflags != reg->cflags) { + // Try to recompile + reg->cflags = cflags; + if ((res = regcomp (&(reg->comp), regex, cflags))) { + // Blew up, remove from hash + regerror(res, &(reg->comp), errstr, sizeof(errstr)); + regfree (&(reg->comp)); + free (reg->regex); + free (Hash_Del (gib_regexs, regex)); + num_regexs--; + return 0; + } else + return &(reg->comp); + } + else + return &(reg->comp); + } else { + reg = calloc (1, sizeof (gib_regex_t)); + if ((res = regcomp (&(reg->comp), regex, cflags))) { + regerror(res, &(reg->comp), errstr, sizeof(errstr)); + regfree (&(reg->comp)); + free (reg); + return 0; + } else { + reg->cflags = cflags; + reg->regex = strdup (regex); + if (++num_regexs > 128) { + Hash_FlushTable (gib_regexs); + num_regexs = 0; + } + Hash_Add (gib_regexs, reg); + return &(reg->comp); + } + } +} + +const char * +GIB_Regex_Error (void) +{ + return errstr; +} + +int +GIB_Regex_Translate_Options (const char *opstr) +{ + int options = 0; + if (strchr (opstr, 'i')) + options |= REG_ICASE; + if (strchr (opstr, 'n')) + options |= REG_NEWLINE; + return options; +} + +unsigned int +GIB_Regex_Apply_Match (regmatch_t match[10], dstring_t *dstr, unsigned int ofs, const char *replace) +{ + int i, start, len, sub, rlen = strlen(replace); + char *matched; + + start = match[0].rm_so+ofs; + len = match[0].rm_eo - match[0].rm_so; + + // Save matched pattern space + matched = calloc (len + 1, sizeof(char)); + memcpy (matched, dstr->str+start, match[0].rm_eo - match[0].rm_so); + + dstring_replace (dstr, start, len, replace, rlen); + for (i = start; i < start+rlen; i++) { + if (dstr->str[i] == '\\') { + if (dstr->str[i+1] == '&') { + dstring_snip (dstr, i, 1); + rlen--; + continue; + } + if (isdigit ((byte) dstr->str[i+1])) { + if (i && dstr->str[i-1] == '\\') { // Escaped, not a true back reference + dstring_snip (dstr, i, 1); + rlen--; + continue; + } + sub = dstr->str[i+1] - '0'; + if (match[sub].rm_so != -1) { + dstring_replace (dstr, i, 2, matched+match[sub].rm_so, match[sub].rm_eo - match[sub].rm_so); + rlen += match[sub].rm_eo - match[sub].rm_so - 2; + } else { + dstring_snip (dstr, i, 2); + rlen -= 2; + } + } + } else if (dstr->str[i] == '&') { + dstring_replace (dstr, i, 1, matched, len); + rlen += strlen(matched) - 1; + } + } + free (matched); + return rlen + match[0].rm_so; +}