Created gib_regex.[ch] to manage regular expressions in GIB. Regexs are

now cached and only recompiled when compile flags change.  Changed regex
builtins to take an options string argument after the regular expression.
This commit is contained in:
Brian Koropoff 2002-11-19 06:24:29 +00:00
parent 1251cbda9e
commit 854f6d9054
6 changed files with 252 additions and 90 deletions

View file

@ -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

45
include/QF/gib_regex.h Normal file
View file

@ -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);

View file

@ -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

View file

@ -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"
@ -496,126 +497,66 @@ GIB_String_Findsub_f (void)
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);
}

View file

@ -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);
}

174
libs/gib/gib_regex.c Normal file
View file

@ -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 <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#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;
}