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