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
35typedef unsigned long Key_t;
36
37typedef 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
47typedef struct State_s
48{
49	unsigned int	size;
50	unsigned long	serial;
51	char*		locale;
52	Cache_t**	cache;
53} State_t;
54
55static State_t	matchstate;
56
57/*
58 * flush the cache
59 */
60
61static void
62flushcache(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
78regex_t*
79regcache(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