regcache.c revision 34f9b3eef6fdadbda0a846aa4d68691ac40eace5
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2009 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* http://www.opensource.org/licenses/cpl1.0.txt *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Phong Vo <kpv@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* regcomp() regex_t cache
* AT&T Research
*/
#include <ast.h>
#include <regex.h>
#define CACHE 8 /* default # cached re's */
#define MAXPAT 256 /* max pattern length + 1 */
#define KEEP 01
#define DROP 02
typedef union Pattern_u
{
unsigned long key;
char buf[MAXPAT];
} Pattern_t;
typedef struct Cache_s
{
Pattern_t pattern;
regex_t re;
unsigned long serial;
regflags_t reflags;
int flags;
} Cache_t;
typedef struct State_s
{
unsigned int size;
unsigned long serial;
char* locale;
Cache_t** cache;
} State_t;
static State_t matchstate;
/*
* flush the cache
*/
static void
flushcache(void)
{
register int i;
for (i = matchstate.size; i--;)
if (matchstate.cache[i] && matchstate.cache[i]->flags)
{
matchstate.cache[i]->flags = 0;
regfree(&matchstate.cache[i]->re);
}
}
/*
* return regcomp() compiled re for pattern and reflags
*/
regex_t*
regcache(const char* pattern, regflags_t reflags, int* status)
{
register Cache_t* cp;
register int i;
char* s;
int empty;
int unused;
int old;
Pattern_t head;
/*
* 0 pattern flushes the cache and reflags>0 extends cache
*/
if (!pattern)
{
flushcache();
i = 0;
if (reflags > matchstate.size)
{
if (matchstate.cache = newof(matchstate.cache, Cache_t*, reflags, 0))
matchstate.size = reflags;
else
{
matchstate.size = 0;
i = 1;
}
}
if (status)
*status = i;
return 0;
}
if (!matchstate.cache)
{
if (!(matchstate.cache = newof(0, Cache_t*, CACHE, 0)))
return 0;
matchstate.size = CACHE;
}
/*
* flush the cache if the locale changed
* the ast setlocale() intercept maintains
* persistent setlocale() return values
*/
if ((s = setlocale(LC_CTYPE, NiL)) != matchstate.locale)
{
matchstate.locale = s;
flushcache();
}
/*
* check if the pattern is in the cache
*/
for (i = 0; i < sizeof(unsigned long) && pattern[i]; i++)
head.buf[i] = pattern[i];
for (; i < sizeof(unsigned long); i++)
head.buf[i] = 0;
empty = unused = -1;
old = 0;
for (i = matchstate.size; i--;)
if (!matchstate.cache[i])
empty = i;
else if (!(matchstate.cache[i]->flags & KEEP))
{
if (matchstate.cache[i]->flags)
{
matchstate.cache[i]->flags = 0;
regfree(&matchstate.cache[i]->re);
}
unused = i;
}
else if (matchstate.cache[i]->pattern.key == head.key && !strcmp(matchstate.cache[i]->pattern.buf, pattern) && matchstate.cache[i]->reflags == reflags)
break;
else if (!matchstate.cache[old] || matchstate.cache[old]->serial > matchstate.cache[i]->serial)
old = i;
if (i < 0)
{
if (unused < 0)
{
if (empty < 0)
unused = old;
else
unused = empty;
}
if (!(cp = matchstate.cache[unused]) && !(cp = matchstate.cache[unused] = newof(0, Cache_t, 1, 0)))
{
if (status)
*status = REG_ESPACE;
return 0;
}
if (cp->flags)
{
cp->flags = 0;
regfree(&cp->re);
}
cp->reflags = reflags;
if ((i = strlen(pattern)) < sizeof(cp->pattern.buf))
{
if (i < sizeof(unsigned long))
memset(cp->pattern.buf, 0, sizeof(unsigned long));
strcpy(cp->pattern.buf, pattern);
pattern = (const char*)cp->pattern.buf;
cp->flags = KEEP;
}
else
cp->flags = DROP;
if (i = regcomp(&cp->re, pattern, cp->reflags))
{
if (status)
*status = i;
cp->flags = 0;
return 0;
}
}
else
cp = matchstate.cache[i];
cp->serial = ++matchstate.serial;
if (status)
*status = 0;
return &cp->re;
}