1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
18*4887Schin *                  David Korn <dgk@research.att.com>                   *
19*4887Schin *                   Phong Vo <kpv@research.att.com>                    *
20*4887Schin *                                                                      *
21*4887Schin ***********************************************************************/
22*4887Schin #pragma prototyped
23*4887Schin 
24*4887Schin /*
25*4887Schin  * regcomp() regex_t cache
26*4887Schin  * AT&T Research
27*4887Schin  */
28*4887Schin 
29*4887Schin #include <ast.h>
30*4887Schin #include <regex.h>
31*4887Schin 
32*4887Schin #define CACHE		8		/* # cached re's		*/
33*4887Schin #define MAXPAT		256		/* max pattern length + 1	*/
34*4887Schin 
35*4887Schin #define KEEP		01
36*4887Schin #define DROP		02
37*4887Schin 
38*4887Schin typedef struct Cache_s
39*4887Schin {
40*4887Schin 	regex_t		re;
41*4887Schin 	unsigned long	serial;
42*4887Schin 	regflags_t	reflags;
43*4887Schin 	int		flags;
44*4887Schin 	char		pattern[MAXPAT];
45*4887Schin } Cache_t;
46*4887Schin 
47*4887Schin static struct State_s
48*4887Schin {
49*4887Schin 	Cache_t*	cache[CACHE];
50*4887Schin 	unsigned long	serial;
51*4887Schin 	char*		locale;
52*4887Schin } matchstate;
53*4887Schin 
54*4887Schin /*
55*4887Schin  * flush the cache
56*4887Schin  */
57*4887Schin 
58*4887Schin static void
59*4887Schin flushcache(void)
60*4887Schin {
61*4887Schin 	register int		i;
62*4887Schin 
63*4887Schin 	for (i = 0; i < elementsof(matchstate.cache); i++)
64*4887Schin 		if (matchstate.cache[i] && matchstate.cache[i]->flags)
65*4887Schin 		{
66*4887Schin 			matchstate.cache[i]->flags = 0;
67*4887Schin 			regfree(&matchstate.cache[i]->re);
68*4887Schin 		}
69*4887Schin }
70*4887Schin 
71*4887Schin /*
72*4887Schin  * return regcomp() compiled re for pattern and reflags
73*4887Schin  */
74*4887Schin 
75*4887Schin regex_t*
76*4887Schin regcache(const char* pattern, regflags_t reflags, int* status)
77*4887Schin {
78*4887Schin 	register Cache_t*	cp;
79*4887Schin 	register int		i;
80*4887Schin 	char*			s;
81*4887Schin 	int			empty;
82*4887Schin 	int			unused;
83*4887Schin 	int			old;
84*4887Schin 
85*4887Schin 	/*
86*4887Schin 	 * 0 pattern flushes the cache
87*4887Schin 	 */
88*4887Schin 
89*4887Schin 	if (!pattern)
90*4887Schin 	{
91*4887Schin 		flushcache();
92*4887Schin 		if (status)
93*4887Schin 			*status = 0;
94*4887Schin 		return 0;
95*4887Schin 	}
96*4887Schin 
97*4887Schin 	/*
98*4887Schin 	 * flush the cache if the locale changed
99*4887Schin 	 * the ast setlocale() intercept maintains
100*4887Schin 	 * persistent setlocale() return values
101*4887Schin 	 */
102*4887Schin 
103*4887Schin 	if ((s = setlocale(LC_CTYPE, NiL)) != matchstate.locale)
104*4887Schin 	{
105*4887Schin 		matchstate.locale = s;
106*4887Schin 		flushcache();
107*4887Schin 	}
108*4887Schin 
109*4887Schin 	/*
110*4887Schin 	 * check if the pattern is in the cache
111*4887Schin 	 */
112*4887Schin 
113*4887Schin 	empty = unused = -1;
114*4887Schin 	old = 0;
115*4887Schin 	for (i = 0; i < elementsof(matchstate.cache); i++)
116*4887Schin 		if (!matchstate.cache[i])
117*4887Schin 			empty = i;
118*4887Schin 		else if (!(matchstate.cache[i]->flags & KEEP))
119*4887Schin 		{
120*4887Schin 			if (matchstate.cache[i]->flags)
121*4887Schin 			{
122*4887Schin 				matchstate.cache[i]->flags = 0;
123*4887Schin 				regfree(&matchstate.cache[i]->re);
124*4887Schin 			}
125*4887Schin 			unused = i;
126*4887Schin 		}
127*4887Schin 		else if (streq(matchstate.cache[i]->pattern, pattern) && matchstate.cache[i]->reflags == reflags)
128*4887Schin 			break;
129*4887Schin 		else if (!matchstate.cache[old] || matchstate.cache[old]->serial > matchstate.cache[i]->serial)
130*4887Schin 			old = i;
131*4887Schin 	if (i >= elementsof(matchstate.cache))
132*4887Schin 	{
133*4887Schin 		if (unused < 0)
134*4887Schin 		{
135*4887Schin 			if (empty < 0)
136*4887Schin 				unused = old;
137*4887Schin 			else
138*4887Schin 				unused = empty;
139*4887Schin 		}
140*4887Schin 		if (!(cp = matchstate.cache[unused]) && !(cp = matchstate.cache[unused] = newof(0, Cache_t, 1, 0)))
141*4887Schin 		{
142*4887Schin 			if (status)
143*4887Schin 				*status = REG_ESPACE;
144*4887Schin 			return 0;
145*4887Schin 		}
146*4887Schin 		if (cp->flags)
147*4887Schin 		{
148*4887Schin 			cp->flags = 0;
149*4887Schin 			regfree(&cp->re);
150*4887Schin 		}
151*4887Schin 		cp->reflags = reflags;
152*4887Schin 		if (strlen(pattern) < sizeof(cp->pattern))
153*4887Schin 		{
154*4887Schin 			strcpy(cp->pattern, pattern);
155*4887Schin 			pattern = (const char*)cp->pattern;
156*4887Schin 			cp->flags = KEEP;
157*4887Schin 		}
158*4887Schin 		else
159*4887Schin 			cp->flags = DROP;
160*4887Schin 		if (i = regcomp(&cp->re, pattern, cp->reflags))
161*4887Schin 		{
162*4887Schin 			if (status)
163*4887Schin 				*status = i;
164*4887Schin 			cp->flags = 0;
165*4887Schin 			return 0;
166*4887Schin 		}
167*4887Schin 	}
168*4887Schin 	else
169*4887Schin 		cp = matchstate.cache[i];
170*4887Schin 	cp->serial = ++matchstate.serial;
171*4887Schin 	if (status)
172*4887Schin 		*status = 0;
173*4887Schin 	return &cp->re;
174*4887Schin }
175