14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1985-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * Phong Vo <kpv@research.att.com> *
204887Schin * *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin
244887Schin /*
254887Schin * regcomp() regex_t cache
26*12068SRoger.Faulkner@Oracle.COM * at&t research
274887Schin */
284887Schin
294887Schin #include <ast.h>
304887Schin #include <regex.h>
314887Schin
328462SApril.Chin@Sun.COM #define CACHE 8 /* default # cached re's */
33*12068SRoger.Faulkner@Oracle.COM #define ROUND 64 /* pattern buffer size round */
344887Schin
35*12068SRoger.Faulkner@Oracle.COM typedef unsigned long Key_t;
368462SApril.Chin@Sun.COM
374887Schin typedef struct Cache_s
384887Schin {
39*12068SRoger.Faulkner@Oracle.COM char* pattern;
404887Schin regex_t re;
414887Schin unsigned long serial;
424887Schin regflags_t reflags;
43*12068SRoger.Faulkner@Oracle.COM int keep;
44*12068SRoger.Faulkner@Oracle.COM int size;
454887Schin } Cache_t;
464887Schin
478462SApril.Chin@Sun.COM typedef struct State_s
484887Schin {
498462SApril.Chin@Sun.COM unsigned int size;
504887Schin unsigned long serial;
514887Schin char* locale;
528462SApril.Chin@Sun.COM Cache_t** cache;
538462SApril.Chin@Sun.COM } State_t;
548462SApril.Chin@Sun.COM
558462SApril.Chin@Sun.COM static State_t matchstate;
564887Schin
574887Schin /*
584887Schin * flush the cache
594887Schin */
604887Schin
614887Schin static void
flushcache(void)624887Schin flushcache(void)
634887Schin {
644887Schin register int i;
654887Schin
668462SApril.Chin@Sun.COM for (i = matchstate.size; i--;)
67*12068SRoger.Faulkner@Oracle.COM if (matchstate.cache[i] && matchstate.cache[i]->keep)
684887Schin {
69*12068SRoger.Faulkner@Oracle.COM matchstate.cache[i]->keep = 0;
704887Schin regfree(&matchstate.cache[i]->re);
714887Schin }
724887Schin }
734887Schin
744887Schin /*
754887Schin * return regcomp() compiled re for pattern and reflags
764887Schin */
774887Schin
784887Schin regex_t*
regcache(const char * pattern,regflags_t reflags,int * status)794887Schin regcache(const char* pattern, regflags_t reflags, int* status)
804887Schin {
814887Schin register Cache_t* cp;
824887Schin register int i;
834887Schin char* s;
844887Schin int empty;
854887Schin int unused;
864887Schin int old;
87*12068SRoger.Faulkner@Oracle.COM Key_t key;
884887Schin
894887Schin /*
908462SApril.Chin@Sun.COM * 0 pattern flushes the cache and reflags>0 extends cache
914887Schin */
924887Schin
934887Schin if (!pattern)
944887Schin {
954887Schin flushcache();
968462SApril.Chin@Sun.COM i = 0;
978462SApril.Chin@Sun.COM if (reflags > matchstate.size)
988462SApril.Chin@Sun.COM {
998462SApril.Chin@Sun.COM if (matchstate.cache = newof(matchstate.cache, Cache_t*, reflags, 0))
1008462SApril.Chin@Sun.COM matchstate.size = reflags;
1018462SApril.Chin@Sun.COM else
1028462SApril.Chin@Sun.COM {
1038462SApril.Chin@Sun.COM matchstate.size = 0;
1048462SApril.Chin@Sun.COM i = 1;
1058462SApril.Chin@Sun.COM }
1068462SApril.Chin@Sun.COM }
1074887Schin if (status)
1088462SApril.Chin@Sun.COM *status = i;
1094887Schin return 0;
1104887Schin }
1118462SApril.Chin@Sun.COM if (!matchstate.cache)
1128462SApril.Chin@Sun.COM {
1138462SApril.Chin@Sun.COM if (!(matchstate.cache = newof(0, Cache_t*, CACHE, 0)))
1148462SApril.Chin@Sun.COM return 0;
1158462SApril.Chin@Sun.COM matchstate.size = CACHE;
1168462SApril.Chin@Sun.COM }
1174887Schin
1184887Schin /*
1194887Schin * flush the cache if the locale changed
1204887Schin * the ast setlocale() intercept maintains
1214887Schin * persistent setlocale() return values
1224887Schin */
1234887Schin
1244887Schin if ((s = setlocale(LC_CTYPE, NiL)) != matchstate.locale)
1254887Schin {
1264887Schin matchstate.locale = s;
1274887Schin flushcache();
1284887Schin }
1294887Schin
1304887Schin /*
1314887Schin * check if the pattern is in the cache
1324887Schin */
1334887Schin
134*12068SRoger.Faulkner@Oracle.COM for (i = 0; i < sizeof(key) && pattern[i]; i++)
135*12068SRoger.Faulkner@Oracle.COM ((char*)&key)[i] = pattern[i];
136*12068SRoger.Faulkner@Oracle.COM for (; i < sizeof(key); i++)
137*12068SRoger.Faulkner@Oracle.COM ((char*)&key)[i] = 0;
1384887Schin empty = unused = -1;
1394887Schin old = 0;
1408462SApril.Chin@Sun.COM for (i = matchstate.size; i--;)
1414887Schin if (!matchstate.cache[i])
1424887Schin empty = i;
143*12068SRoger.Faulkner@Oracle.COM else if (!matchstate.cache[i]->keep)
1444887Schin unused = i;
145*12068SRoger.Faulkner@Oracle.COM else if (*(Key_t*)matchstate.cache[i]->pattern == key && !strcmp(matchstate.cache[i]->pattern, pattern) && matchstate.cache[i]->reflags == reflags)
1464887Schin break;
1474887Schin else if (!matchstate.cache[old] || matchstate.cache[old]->serial > matchstate.cache[i]->serial)
1484887Schin old = i;
1498462SApril.Chin@Sun.COM if (i < 0)
1504887Schin {
1514887Schin if (unused < 0)
1524887Schin {
1534887Schin if (empty < 0)
1544887Schin unused = old;
1554887Schin else
1564887Schin unused = empty;
1574887Schin }
1584887Schin if (!(cp = matchstate.cache[unused]) && !(cp = matchstate.cache[unused] = newof(0, Cache_t, 1, 0)))
1594887Schin {
1604887Schin if (status)
1614887Schin *status = REG_ESPACE;
1624887Schin return 0;
1634887Schin }
164*12068SRoger.Faulkner@Oracle.COM if (cp->keep)
1654887Schin {
166*12068SRoger.Faulkner@Oracle.COM cp->keep = 0;
1674887Schin regfree(&cp->re);
1684887Schin }
169*12068SRoger.Faulkner@Oracle.COM if ((i = strlen(pattern) + 1) >= cp->size)
1704887Schin {
171*12068SRoger.Faulkner@Oracle.COM cp->size = roundof(i, ROUND);
172*12068SRoger.Faulkner@Oracle.COM if (!(cp->pattern = newof(cp->pattern, char, cp->size, 0)))
173*12068SRoger.Faulkner@Oracle.COM {
174*12068SRoger.Faulkner@Oracle.COM if (status)
175*12068SRoger.Faulkner@Oracle.COM *status = REG_ESPACE;
176*12068SRoger.Faulkner@Oracle.COM return 0;
177*12068SRoger.Faulkner@Oracle.COM }
1784887Schin }
179*12068SRoger.Faulkner@Oracle.COM strcpy(cp->pattern, pattern);
180*12068SRoger.Faulkner@Oracle.COM while (++i < sizeof(Key_t))
181*12068SRoger.Faulkner@Oracle.COM cp->pattern[i] = 0;
182*12068SRoger.Faulkner@Oracle.COM pattern = (const char*)cp->pattern;
183*12068SRoger.Faulkner@Oracle.COM if (i = regcomp(&cp->re, pattern, reflags))
1844887Schin {
1854887Schin if (status)
1864887Schin *status = i;
1874887Schin return 0;
1884887Schin }
189*12068SRoger.Faulkner@Oracle.COM cp->keep = 1;
190*12068SRoger.Faulkner@Oracle.COM cp->reflags = reflags;
1914887Schin }
1924887Schin else
1934887Schin cp = matchstate.cache[i];
1944887Schin cp->serial = ++matchstate.serial;
1954887Schin if (status)
1964887Schin *status = 0;
1974887Schin return &cp->re;
1984887Schin }
199