xref: /illumos-gate/usr/src/contrib/ast/src/lib/libast/regex/regsubcomp.c (revision 16b76d3cb933ff92018a2a75594449010192eacb)
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  * posix regex ed(1) style substitute compile
26  */
27 
28 #include "reglib.h"
29 
30 static const regflags_t	submap[] =
31 {
32 	'g',	REG_SUB_ALL,
33 	'l',	REG_SUB_LOWER,
34 	'n',	REG_SUB_NUMBER,
35 	'p',	REG_SUB_PRINT,
36 	's',	REG_SUB_STOP,
37 	'u',	REG_SUB_UPPER,
38 	'w',	REG_SUB_WRITE|REG_SUB_LAST,
39 	0,	0
40 };
41 
42 int
43 regsubflags(regex_t* p, register const char* s, char** e, int delim, register const regflags_t* map, int* pm, regflags_t* pf)
44 {
45 	register int			c;
46 	register const regflags_t*	m;
47 	regflags_t			flags;
48 	int				minmatch;
49 	regdisc_t*			disc;
50 
51 	flags = pf ? *pf : 0;
52 	minmatch = pm ? *pm : 0;
53 	if (!map)
54 		map = submap;
55 	while (!(flags & REG_SUB_LAST))
56 	{
57 		if  (!(c = *s++) || c == delim)
58 		{
59 			s--;
60 			break;
61 		}
62 		else if (c >= '0' && c <= '9')
63 		{
64 			if (minmatch)
65 			{
66 				disc = p->env->disc;
67 				regfree(p);
68 				return fatal(disc, REG_EFLAGS, s - 1);
69 			}
70 			minmatch = c - '0';
71 			while (*s >= '0' && *s <= '9')
72 				minmatch = minmatch * 10 + *s++ - '0';
73 		}
74 		else
75 		{
76 			for (m = map; *m; m++)
77 				if (*m++ == c)
78 				{
79 					if (flags & *m)
80 					{
81 						disc = p->env->disc;
82 						regfree(p);
83 						return fatal(disc, REG_EFLAGS, s - 1);
84 					}
85 					flags |= *m--;
86 					break;
87 				}
88 			if (!*m)
89 			{
90 				s--;
91 				break;
92 			}
93 		}
94 	}
95 	if (pf)
96 		*pf = flags;
97 	if (pm)
98 		*pm = minmatch;
99 	if (e)
100 		*e = (char*)s;
101 	return 0;
102 }
103 
104 /*
105  * compile substitute rhs and optional flags
106  */
107 
108 int
109 regsubcomp(regex_t* p, register const char* s, const regflags_t* map, int minmatch, regflags_t flags)
110 {
111 	register regsub_t*	sub;
112 	register int		c;
113 	register int		d;
114 	register char*		t;
115 	register regsubop_t*	op;
116 	char*			e;
117 	const char*		r;
118 	int			sre;
119 	int			f;
120 	int			g;
121 	int			n;
122 	int			nops;
123 	const char*		o;
124 	regdisc_t*		disc;
125 
126 	disc = p->env->disc;
127 	if (p->env->flags & REG_NOSUB)
128 	{
129 		regfree(p);
130 		return fatal(disc, REG_BADPAT, NiL);
131 	}
132 	if (!(sub = (regsub_t*)alloc(p->env->disc, 0, sizeof(regsub_t) + strlen(s))) || !(sub->re_ops = (regsubop_t*)alloc(p->env->disc, 0, (nops = 8) * sizeof(regsubop_t))))
133 	{
134 		if (sub)
135 			alloc(p->env->disc, sub, 0);
136 		regfree(p);
137 		return fatal(disc, REG_ESPACE, s);
138 	}
139 	sub->re_buf = sub->re_end = 0;
140 	p->re_sub = sub;
141 	p->env->sub = 1;
142 	op = sub->re_ops;
143 	o = s;
144 	if (!(p->env->flags & REG_DELIMITED))
145 		d = 0;
146 	else
147 		switch (d = *(s - 1))
148 		{
149 		case '\\':
150 		case '\n':
151 		case '\r':
152 			regfree(p);
153 			return fatal(disc, REG_EDELIM, s);
154 		}
155 	sre = p->env->flags & REG_SHELL;
156 	t = sub->re_rhs;
157 	if (d)
158 	{
159 		r = s;
160 		for (;;)
161 		{
162 			if (!*s)
163 			{
164 				if (p->env->flags & REG_MUSTDELIM)
165 				{
166 					regfree(p);
167 					return fatal(disc, REG_EDELIM, r);
168 				}
169 				break;
170 			}
171 			else if (*s == d)
172 			{
173 				flags |= REG_SUB_FULL;
174 				s++;
175 				break;
176 			}
177 			else if (*s++ == '\\' && !*s++)
178 			{
179 				regfree(p);
180 				return fatal(disc, REG_EESCAPE, r);
181 			}
182 		}
183 		if (*s)
184 		{
185 			if (n = regsubflags(p, s, &e, d, map, &minmatch, &flags))
186 				return n;
187 			s = (const char*)e;
188 		}
189 		p->re_npat = s - o;
190 		s = r;
191 	}
192 	else
193 		p->re_npat = 0;
194 	op->op = f = g = flags & (REG_SUB_LOWER|REG_SUB_UPPER);
195 	op->off = 0;
196 	while ((c = *s++) != d)
197 	{
198 	again:
199 		if (!c)
200 		{
201 			p->re_npat = s - o - 1;
202 			break;
203 		}
204 		else if (c == '\\')
205 		{
206 			if (*s == c)
207 			{
208 				*t++ = *s++;
209 				continue;
210 			}
211 			if ((c = *s++) == d)
212 				goto again;
213 			if (!c)
214 			{
215 				regfree(p);
216 				return fatal(disc, REG_EESCAPE, s - 2);
217 			}
218 			if (c == '&')
219 			{
220 				*t++ = c;
221 				continue;
222 			}
223 		}
224 		else if (c == '&')
225 		{
226 			if (sre)
227 			{
228 				*t++ = c;
229 				continue;
230 			}
231 		}
232 		else
233 		{
234 			switch (op->op)
235 			{
236 			case REG_SUB_UPPER:
237 				if (islower(c))
238 					c = toupper(c);
239 				break;
240 			case REG_SUB_LOWER:
241 				if (isupper(c))
242 					c = tolower(c);
243 				break;
244 			case REG_SUB_UPPER|REG_SUB_LOWER:
245 				if (isupper(c))
246 					c = tolower(c);
247 				else if (islower(c))
248 					c = toupper(c);
249 				break;
250 			}
251 			*t++ = c;
252 			continue;
253 		}
254 		switch (c)
255 		{
256 		case 0:
257 			s--;
258 			continue;
259 		case '&':
260 			c = 0;
261 			break;
262 		case '0': case '1': case '2': case '3': case '4':
263 		case '5': case '6': case '7': case '8': case '9':
264 			c -= '0';
265 			if (isdigit(*s) && (p->env->flags & REG_MULTIREF))
266 				c = c * 10 + *s++ - '0';
267 			break;
268 		case 'l':
269 			if (c = *s)
270 			{
271 				s++;
272 				if (isupper(c))
273 					c = tolower(c);
274 				*t++ = c;
275 			}
276 			continue;
277 		case 'u':
278 			if (c = *s)
279 			{
280 				s++;
281 				if (islower(c))
282 					c = toupper(c);
283 				*t++ = c;
284 			}
285 			continue;
286 		case 'E':
287 			f = g;
288 		set:
289 			if ((op->len = (t - sub->re_rhs) - op->off) && (n = ++op - sub->re_ops) >= nops)
290 			{
291 				if (!(sub->re_ops = (regsubop_t*)alloc(p->env->disc, sub->re_ops, (nops *= 2) * sizeof(regsubop_t))))
292 				{
293 					regfree(p);
294 					return fatal(disc, REG_ESPACE, NiL);
295 				}
296 				op = sub->re_ops + n;
297 			}
298 			op->op = f;
299 			op->off = t - sub->re_rhs;
300 			continue;
301 		case 'L':
302 			g = f;
303 			f = REG_SUB_LOWER;
304 			goto set;
305 		case 'U':
306 			g = f;
307 			f = REG_SUB_UPPER;
308 			goto set;
309 		default:
310 			if (!sre)
311 			{
312 				*t++ = chresc(s - 2, &e);
313 				s = (const char*)e;
314 				continue;
315 			}
316 			s--;
317 			c = -1;
318 			break;
319 		}
320 		if (c > p->re_nsub)
321 		{
322 			regfree(p);
323 			return fatal(disc, REG_ESUBREG, s - 1);
324 		}
325 		if ((n = op - sub->re_ops) >= (nops - 2))
326 		{
327 			if (!(sub->re_ops = (regsubop_t*)alloc(p->env->disc, sub->re_ops, (nops *= 2) * sizeof(regsubop_t))))
328 			{
329 				regfree(p);
330 				return fatal(disc, REG_ESPACE, NiL);
331 			}
332 			op = sub->re_ops + n;
333 		}
334 		if (op->len = (t - sub->re_rhs) - op->off)
335 			op++;
336 		op->op = f;
337 		op->off = c;
338 		op->len = 0;
339 		op++;
340 		op->op = f;
341 		op->off = t - sub->re_rhs;
342 	}
343 	if ((op->len = (t - sub->re_rhs) - op->off) && (n = ++op - sub->re_ops) >= nops)
344 	{
345 		if (!(sub->re_ops = (regsubop_t*)alloc(p->env->disc, sub->re_ops, (nops *= 2) * sizeof(regsubop_t))))
346 		{
347 			regfree(p);
348 			return fatal(disc, REG_ESPACE, NiL);
349 		}
350 		op = sub->re_ops + n;
351 	}
352 	op->len = -1;
353 	sub->re_flags = flags;
354 	sub->re_min = minmatch;
355 	return 0;
356 }
357 
358 void
359 regsubfree(regex_t* p)
360 {
361 	Env_t*		env;
362 	regsub_t*	sub;
363 
364 	if (p && (env = p->env) && env->sub && (sub = p->re_sub))
365 	{
366 		env->sub = 0;
367 		p->re_sub = 0;
368 		if (!(env->disc->re_flags & REG_NOFREE))
369 		{
370 			if (sub->re_buf)
371 				alloc(env->disc, sub->re_buf, 0);
372 			if (sub->re_ops)
373 				alloc(env->disc, sub->re_ops, 0);
374 			alloc(env->disc, sub, 0);
375 		}
376 	}
377 }
378