xref: /freebsd/usr.bin/m4/gnum4.c (revision 56ca2b35a469002f18c8b7d807778614ba2072fe)
1e3d86717SJuli Mallett /* $OpenBSD: gnum4.c,v 1.16 2002/02/16 21:27:48 millert Exp $ */
2e3d86717SJuli Mallett 
3e3d86717SJuli Mallett /*
4e3d86717SJuli Mallett  * Copyright (c) 1999 Marc Espie
5e3d86717SJuli Mallett  *
6e3d86717SJuli Mallett  * Redistribution and use in source and binary forms, with or without
7e3d86717SJuli Mallett  * modification, are permitted provided that the following conditions
8e3d86717SJuli Mallett  * are met:
9e3d86717SJuli Mallett  * 1. Redistributions of source code must retain the above copyright
10e3d86717SJuli Mallett  *    notice, this list of conditions and the following disclaimer.
11e3d86717SJuli Mallett  * 2. Redistributions in binary form must reproduce the above copyright
12e3d86717SJuli Mallett  *    notice, this list of conditions and the following disclaimer in the
13e3d86717SJuli Mallett  *    documentation and/or other materials provided with the distribution.
14e3d86717SJuli Mallett  *
15e3d86717SJuli Mallett  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16e3d86717SJuli Mallett  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17e3d86717SJuli Mallett  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18e3d86717SJuli Mallett  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19e3d86717SJuli Mallett  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20e3d86717SJuli Mallett  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21e3d86717SJuli Mallett  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22e3d86717SJuli Mallett  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23e3d86717SJuli Mallett  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24e3d86717SJuli Mallett  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25e3d86717SJuli Mallett  * SUCH DAMAGE.
26e3d86717SJuli Mallett  */
27e3d86717SJuli Mallett 
2856ca2b35SJuli Mallett #include <sys/cdefs.h>
2956ca2b35SJuli Mallett __FBSDID("$FreeBSD$");
3056ca2b35SJuli Mallett 
31e3d86717SJuli Mallett /*
32e3d86717SJuli Mallett  * functions needed to support gnu-m4 extensions, including a fake freezing
33e3d86717SJuli Mallett  */
34e3d86717SJuli Mallett 
35e3d86717SJuli Mallett #include <sys/param.h>
36e3d86717SJuli Mallett #include <sys/types.h>
37e3d86717SJuli Mallett #include <sys/wait.h>
38e3d86717SJuli Mallett #include <ctype.h>
39e3d86717SJuli Mallett #include <paths.h>
40e3d86717SJuli Mallett #include <regex.h>
41e3d86717SJuli Mallett #include <stddef.h>
42e3d86717SJuli Mallett #include <stdlib.h>
43e3d86717SJuli Mallett #include <stdio.h>
44e3d86717SJuli Mallett #include <string.h>
45e3d86717SJuli Mallett #include <err.h>
46e3d86717SJuli Mallett #include <errno.h>
47e3d86717SJuli Mallett #include <unistd.h>
48e3d86717SJuli Mallett #include "mdef.h"
49e3d86717SJuli Mallett #include "stdd.h"
50e3d86717SJuli Mallett #include "extern.h"
51e3d86717SJuli Mallett 
52e3d86717SJuli Mallett 
53e3d86717SJuli Mallett int mimic_gnu = 0;
54e3d86717SJuli Mallett 
55e3d86717SJuli Mallett /*
56e3d86717SJuli Mallett  * Support for include path search
57e3d86717SJuli Mallett  * First search in the the current directory.
58e3d86717SJuli Mallett  * If not found, and the path is not absolute, include path kicks in.
59e3d86717SJuli Mallett  * First, -I options, in the order found on the command line.
60e3d86717SJuli Mallett  * Then M4PATH env variable
61e3d86717SJuli Mallett  */
62e3d86717SJuli Mallett 
63e3d86717SJuli Mallett struct path_entry {
64e3d86717SJuli Mallett 	char *name;
65e3d86717SJuli Mallett 	struct path_entry *next;
66e3d86717SJuli Mallett } *first, *last;
67e3d86717SJuli Mallett 
68e3d86717SJuli Mallett static struct path_entry *new_path_entry(const char *);
69e3d86717SJuli Mallett static void ensure_m4path(void);
70e3d86717SJuli Mallett static struct input_file *dopath(struct input_file *, const char *);
71e3d86717SJuli Mallett 
72e3d86717SJuli Mallett static struct path_entry *
73e3d86717SJuli Mallett new_path_entry(dirname)
74e3d86717SJuli Mallett 	const char *dirname;
75e3d86717SJuli Mallett {
76e3d86717SJuli Mallett 	struct path_entry *n;
77e3d86717SJuli Mallett 
78e3d86717SJuli Mallett 	n = malloc(sizeof(struct path_entry));
79e3d86717SJuli Mallett 	if (!n)
80e3d86717SJuli Mallett 		errx(1, "out of memory");
81e3d86717SJuli Mallett 	n->name = strdup(dirname);
82e3d86717SJuli Mallett 	if (!n->name)
83e3d86717SJuli Mallett 		errx(1, "out of memory");
84e3d86717SJuli Mallett 	n->next = 0;
85e3d86717SJuli Mallett 	return n;
86e3d86717SJuli Mallett }
87e3d86717SJuli Mallett 
88e3d86717SJuli Mallett void
89e3d86717SJuli Mallett addtoincludepath(dirname)
90e3d86717SJuli Mallett 	const char *dirname;
91e3d86717SJuli Mallett {
92e3d86717SJuli Mallett 	struct path_entry *n;
93e3d86717SJuli Mallett 
94e3d86717SJuli Mallett 	n = new_path_entry(dirname);
95e3d86717SJuli Mallett 
96e3d86717SJuli Mallett 	if (last) {
97e3d86717SJuli Mallett 		last->next = n;
98e3d86717SJuli Mallett 		last = n;
99e3d86717SJuli Mallett 	}
100e3d86717SJuli Mallett 	else
101e3d86717SJuli Mallett 		last = first = n;
102e3d86717SJuli Mallett }
103e3d86717SJuli Mallett 
104e3d86717SJuli Mallett static void
105e3d86717SJuli Mallett ensure_m4path()
106e3d86717SJuli Mallett {
107e3d86717SJuli Mallett 	static int envpathdone = 0;
108e3d86717SJuli Mallett 	char *envpath;
109e3d86717SJuli Mallett 	char *sweep;
110e3d86717SJuli Mallett 	char *path;
111e3d86717SJuli Mallett 
112e3d86717SJuli Mallett 	if (envpathdone)
113e3d86717SJuli Mallett 		return;
114e3d86717SJuli Mallett 	envpathdone = TRUE;
115e3d86717SJuli Mallett 	envpath = getenv("M4PATH");
116e3d86717SJuli Mallett 	if (!envpath)
117e3d86717SJuli Mallett 		return;
118e3d86717SJuli Mallett 	/* for portability: getenv result is read-only */
119e3d86717SJuli Mallett 	envpath = strdup(envpath);
120e3d86717SJuli Mallett 	if (!envpath)
121e3d86717SJuli Mallett 		errx(1, "out of memory");
122e3d86717SJuli Mallett 	for (sweep = envpath;
123e3d86717SJuli Mallett 	    (path = strsep(&sweep, ":")) != NULL;)
124e3d86717SJuli Mallett 	    addtoincludepath(path);
125e3d86717SJuli Mallett 	free(envpath);
126e3d86717SJuli Mallett }
127e3d86717SJuli Mallett 
128e3d86717SJuli Mallett static
129e3d86717SJuli Mallett struct input_file *
130e3d86717SJuli Mallett dopath(i, filename)
131e3d86717SJuli Mallett 	struct input_file *i;
132e3d86717SJuli Mallett 	const char *filename;
133e3d86717SJuli Mallett {
134e3d86717SJuli Mallett 	char path[MAXPATHLEN];
135e3d86717SJuli Mallett 	struct path_entry *pe;
136e3d86717SJuli Mallett 	FILE *f;
137e3d86717SJuli Mallett 
138e3d86717SJuli Mallett 	for (pe = first; pe; pe = pe->next) {
139e3d86717SJuli Mallett 		snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
140e3d86717SJuli Mallett 		if ((f = fopen(path, "r")) != 0) {
141e3d86717SJuli Mallett 			set_input(i, f, path);
142e3d86717SJuli Mallett 			return i;
143e3d86717SJuli Mallett 		}
144e3d86717SJuli Mallett 	}
145e3d86717SJuli Mallett 	return NULL;
146e3d86717SJuli Mallett }
147e3d86717SJuli Mallett 
148e3d86717SJuli Mallett struct input_file *
149e3d86717SJuli Mallett fopen_trypath(i, filename)
150e3d86717SJuli Mallett 	struct input_file *i;
151e3d86717SJuli Mallett 	const char *filename;
152e3d86717SJuli Mallett {
153e3d86717SJuli Mallett 	FILE *f;
154e3d86717SJuli Mallett 
155e3d86717SJuli Mallett 	f = fopen(filename, "r");
156e3d86717SJuli Mallett 	if (f != NULL) {
157e3d86717SJuli Mallett 		set_input(i, f, filename);
158e3d86717SJuli Mallett 		return i;
159e3d86717SJuli Mallett 	}
160e3d86717SJuli Mallett 	if (filename[0] == '/')
161e3d86717SJuli Mallett 		return NULL;
162e3d86717SJuli Mallett 
163e3d86717SJuli Mallett 	ensure_m4path();
164e3d86717SJuli Mallett 
165e3d86717SJuli Mallett 	return dopath(i, filename);
166e3d86717SJuli Mallett }
167e3d86717SJuli Mallett 
168e3d86717SJuli Mallett void
169e3d86717SJuli Mallett doindir(argv, argc)
170e3d86717SJuli Mallett 	const char *argv[];
171e3d86717SJuli Mallett 	int argc;
172e3d86717SJuli Mallett {
173e3d86717SJuli Mallett 	ndptr p;
174e3d86717SJuli Mallett 
175e3d86717SJuli Mallett 	p = lookup(argv[2]);
176e3d86717SJuli Mallett 	if (p == NULL)
177e3d86717SJuli Mallett 		errx(1, "undefined macro %s", argv[2]);
178e3d86717SJuli Mallett 	argv[1] = p->defn;
179e3d86717SJuli Mallett 	eval(argv+1, argc-1, p->type);
180e3d86717SJuli Mallett }
181e3d86717SJuli Mallett 
182e3d86717SJuli Mallett void
183e3d86717SJuli Mallett dobuiltin(argv, argc)
184e3d86717SJuli Mallett 	const char *argv[];
185e3d86717SJuli Mallett 	int argc;
186e3d86717SJuli Mallett {
187e3d86717SJuli Mallett 	int n;
188e3d86717SJuli Mallett 	argv[1] = NULL;
189e3d86717SJuli Mallett 	n = builtin_type(argv[2]);
190e3d86717SJuli Mallett 	if (n != -1)
191e3d86717SJuli Mallett 		eval(argv+1, argc-1, n);
192e3d86717SJuli Mallett 	else
193e3d86717SJuli Mallett 		errx(1, "unknown builtin %s", argv[2]);
194e3d86717SJuli Mallett }
195e3d86717SJuli Mallett 
196e3d86717SJuli Mallett 
197e3d86717SJuli Mallett /* We need some temporary buffer space, as pb pushes BACK and substitution
198e3d86717SJuli Mallett  * proceeds forward... */
199e3d86717SJuli Mallett static char *buffer;
200e3d86717SJuli Mallett static size_t bufsize = 0;
201e3d86717SJuli Mallett static size_t current = 0;
202e3d86717SJuli Mallett 
203e3d86717SJuli Mallett static void addchars(const char *, size_t);
204e3d86717SJuli Mallett static void addchar(char);
205e3d86717SJuli Mallett static char *twiddle(const char *);
206e3d86717SJuli Mallett static char *getstring(void);
207e3d86717SJuli Mallett static void exit_regerror(int, regex_t *);
208e3d86717SJuli Mallett static void do_subst(const char *, regex_t *, const char *, regmatch_t *);
209e3d86717SJuli Mallett static void do_regexpindex(const char *, regex_t *, regmatch_t *);
210e3d86717SJuli Mallett static void do_regexp(const char *, regex_t *, const char *, regmatch_t *);
211e3d86717SJuli Mallett static void add_sub(int, const char *, regex_t *, regmatch_t *);
212e3d86717SJuli Mallett static void add_replace(const char *, regex_t *, const char *, regmatch_t *);
213e3d86717SJuli Mallett #define addconstantstring(s) addchars((s), sizeof(s)-1)
214e3d86717SJuli Mallett 
215e3d86717SJuli Mallett static void
216e3d86717SJuli Mallett addchars(c, n)
217e3d86717SJuli Mallett 	const char *c;
218e3d86717SJuli Mallett 	size_t n;
219e3d86717SJuli Mallett {
220e3d86717SJuli Mallett 	if (n == 0)
221e3d86717SJuli Mallett 		return;
222e3d86717SJuli Mallett 	while (current + n > bufsize) {
223e3d86717SJuli Mallett 		if (bufsize == 0)
224e3d86717SJuli Mallett 			bufsize = 1024;
225e3d86717SJuli Mallett 		else
226e3d86717SJuli Mallett 			bufsize *= 2;
227e3d86717SJuli Mallett 		buffer = realloc(buffer, bufsize);
228e3d86717SJuli Mallett 		if (buffer == NULL)
229e3d86717SJuli Mallett 			errx(1, "out of memory");
230e3d86717SJuli Mallett 	}
231e3d86717SJuli Mallett 	memcpy(buffer+current, c, n);
232e3d86717SJuli Mallett 	current += n;
233e3d86717SJuli Mallett }
234e3d86717SJuli Mallett 
235e3d86717SJuli Mallett static void
236e3d86717SJuli Mallett addchar(c)
237e3d86717SJuli Mallett 	char c;
238e3d86717SJuli Mallett {
239e3d86717SJuli Mallett 	if (current +1 > bufsize) {
240e3d86717SJuli Mallett 		if (bufsize == 0)
241e3d86717SJuli Mallett 			bufsize = 1024;
242e3d86717SJuli Mallett 		else
243e3d86717SJuli Mallett 			bufsize *= 2;
244e3d86717SJuli Mallett 		buffer = realloc(buffer, bufsize);
245e3d86717SJuli Mallett 		if (buffer == NULL)
246e3d86717SJuli Mallett 			errx(1, "out of memory");
247e3d86717SJuli Mallett 	}
248e3d86717SJuli Mallett 	buffer[current++] = c;
249e3d86717SJuli Mallett }
250e3d86717SJuli Mallett 
251e3d86717SJuli Mallett static char *
252e3d86717SJuli Mallett getstring()
253e3d86717SJuli Mallett {
254e3d86717SJuli Mallett 	addchar('\0');
255e3d86717SJuli Mallett 	current = 0;
256e3d86717SJuli Mallett 	return buffer;
257e3d86717SJuli Mallett }
258e3d86717SJuli Mallett 
259e3d86717SJuli Mallett 
260e3d86717SJuli Mallett static void
261e3d86717SJuli Mallett exit_regerror(er, re)
262e3d86717SJuli Mallett 	int er;
263e3d86717SJuli Mallett 	regex_t *re;
264e3d86717SJuli Mallett {
265e3d86717SJuli Mallett 	size_t 	errlen;
266e3d86717SJuli Mallett 	char 	*errbuf;
267e3d86717SJuli Mallett 
268e3d86717SJuli Mallett 	errlen = regerror(er, re, NULL, 0);
269e3d86717SJuli Mallett 	errbuf = xalloc(errlen);
270e3d86717SJuli Mallett 	regerror(er, re, errbuf, errlen);
271e3d86717SJuli Mallett 	errx(1, "regular expression error: %s", errbuf);
272e3d86717SJuli Mallett }
273e3d86717SJuli Mallett 
274e3d86717SJuli Mallett static void
275e3d86717SJuli Mallett add_sub(n, string, re, pm)
276e3d86717SJuli Mallett 	int n;
277e3d86717SJuli Mallett 	const char *string;
278e3d86717SJuli Mallett 	regex_t *re;
279e3d86717SJuli Mallett 	regmatch_t *pm;
280e3d86717SJuli Mallett {
281e3d86717SJuli Mallett 	if (n > re->re_nsub)
282e3d86717SJuli Mallett 		warnx("No subexpression %d", n);
283e3d86717SJuli Mallett 	/* Subexpressions that did not match are
284e3d86717SJuli Mallett 	 * not an error.  */
285e3d86717SJuli Mallett 	else if (pm[n].rm_so != -1 &&
286e3d86717SJuli Mallett 	    pm[n].rm_eo != -1) {
287e3d86717SJuli Mallett 		addchars(string + pm[n].rm_so,
288e3d86717SJuli Mallett 			pm[n].rm_eo - pm[n].rm_so);
289e3d86717SJuli Mallett 	}
290e3d86717SJuli Mallett }
291e3d86717SJuli Mallett 
292e3d86717SJuli Mallett /* Add replacement string to the output buffer, recognizing special
293e3d86717SJuli Mallett  * constructs and replacing them with substrings of the original string.
294e3d86717SJuli Mallett  */
295e3d86717SJuli Mallett static void
296e3d86717SJuli Mallett add_replace(string, re, replace, pm)
297e3d86717SJuli Mallett 	const char *string;
298e3d86717SJuli Mallett 	regex_t *re;
299e3d86717SJuli Mallett 	const char *replace;
300e3d86717SJuli Mallett 	regmatch_t *pm;
301e3d86717SJuli Mallett {
302e3d86717SJuli Mallett 	const char *p;
303e3d86717SJuli Mallett 
304e3d86717SJuli Mallett 	for (p = replace; *p != '\0'; p++) {
305e3d86717SJuli Mallett 		if (*p == '&' && !mimic_gnu) {
306e3d86717SJuli Mallett 			add_sub(0, string, re, pm);
307e3d86717SJuli Mallett 			continue;
308e3d86717SJuli Mallett 		}
309e3d86717SJuli Mallett 		if (*p == '\\') {
310e3d86717SJuli Mallett 			if (p[1] == '\\') {
311e3d86717SJuli Mallett 				addchar(p[1]);
312e3d86717SJuli Mallett 				p++;
313e3d86717SJuli Mallett 				continue;
314e3d86717SJuli Mallett 			}
315e3d86717SJuli Mallett 			if (p[1] == '&') {
316e3d86717SJuli Mallett 				if (mimic_gnu)
317e3d86717SJuli Mallett 					add_sub(0, string, re, pm);
318e3d86717SJuli Mallett 				else
319e3d86717SJuli Mallett 					addchar(p[1]);
320e3d86717SJuli Mallett 				p++;
321e3d86717SJuli Mallett 				continue;
322e3d86717SJuli Mallett 			}
323e3d86717SJuli Mallett 			if (isdigit(p[1])) {
324e3d86717SJuli Mallett 				add_sub(*(++p) - '0', string, re, pm);
325e3d86717SJuli Mallett 				continue;
326e3d86717SJuli Mallett 			}
327e3d86717SJuli Mallett 		}
328e3d86717SJuli Mallett 	    	addchar(*p);
329e3d86717SJuli Mallett 	}
330e3d86717SJuli Mallett }
331e3d86717SJuli Mallett 
332e3d86717SJuli Mallett static void
333e3d86717SJuli Mallett do_subst(string, re, replace, pm)
334e3d86717SJuli Mallett 	const char *string;
335e3d86717SJuli Mallett 	regex_t *re;
336e3d86717SJuli Mallett 	const char *replace;
337e3d86717SJuli Mallett 	regmatch_t *pm;
338e3d86717SJuli Mallett {
339e3d86717SJuli Mallett 	int error;
340e3d86717SJuli Mallett 	int flags = 0;
341e3d86717SJuli Mallett 	const char *last_match = NULL;
342e3d86717SJuli Mallett 
343e3d86717SJuli Mallett 	while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
344e3d86717SJuli Mallett 		if (pm[0].rm_eo != 0) {
345e3d86717SJuli Mallett 			if (string[pm[0].rm_eo-1] == '\n')
346e3d86717SJuli Mallett 				flags = 0;
347e3d86717SJuli Mallett 			else
348e3d86717SJuli Mallett 				flags = REG_NOTBOL;
349e3d86717SJuli Mallett 		}
350e3d86717SJuli Mallett 
351e3d86717SJuli Mallett 		/* NULL length matches are special... We use the `vi-mode'
352e3d86717SJuli Mallett 		 * rule: don't allow a NULL-match at the last match
353e3d86717SJuli Mallett 		 * position.
354e3d86717SJuli Mallett 		 */
355e3d86717SJuli Mallett 		if (pm[0].rm_so == pm[0].rm_eo &&
356e3d86717SJuli Mallett 		    string + pm[0].rm_so == last_match) {
357e3d86717SJuli Mallett 			if (*string == '\0')
358e3d86717SJuli Mallett 				return;
359e3d86717SJuli Mallett 			addchar(*string);
360e3d86717SJuli Mallett 			if (*string++ == '\n')
361e3d86717SJuli Mallett 				flags = 0;
362e3d86717SJuli Mallett 			else
363e3d86717SJuli Mallett 				flags = REG_NOTBOL;
364e3d86717SJuli Mallett 			continue;
365e3d86717SJuli Mallett 		}
366e3d86717SJuli Mallett 		last_match = string + pm[0].rm_so;
367e3d86717SJuli Mallett 		addchars(string, pm[0].rm_so);
368e3d86717SJuli Mallett 		add_replace(string, re, replace, pm);
369e3d86717SJuli Mallett 		string += pm[0].rm_eo;
370e3d86717SJuli Mallett 	}
371e3d86717SJuli Mallett 	if (error != REG_NOMATCH)
372e3d86717SJuli Mallett 		exit_regerror(error, re);
373e3d86717SJuli Mallett 	pbstr(string);
374e3d86717SJuli Mallett }
375e3d86717SJuli Mallett 
376e3d86717SJuli Mallett static void
377e3d86717SJuli Mallett do_regexp(string, re, replace, pm)
378e3d86717SJuli Mallett 	const char *string;
379e3d86717SJuli Mallett 	regex_t *re;
380e3d86717SJuli Mallett 	const char *replace;
381e3d86717SJuli Mallett 	regmatch_t *pm;
382e3d86717SJuli Mallett {
383e3d86717SJuli Mallett 	int error;
384e3d86717SJuli Mallett 
385e3d86717SJuli Mallett 	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
386e3d86717SJuli Mallett 	case 0:
387e3d86717SJuli Mallett 		add_replace(string, re, replace, pm);
388e3d86717SJuli Mallett 		pbstr(getstring());
389e3d86717SJuli Mallett 		break;
390e3d86717SJuli Mallett 	case REG_NOMATCH:
391e3d86717SJuli Mallett 		break;
392e3d86717SJuli Mallett 	default:
393e3d86717SJuli Mallett 		exit_regerror(error, re);
394e3d86717SJuli Mallett 	}
395e3d86717SJuli Mallett }
396e3d86717SJuli Mallett 
397e3d86717SJuli Mallett static void
398e3d86717SJuli Mallett do_regexpindex(string, re, pm)
399e3d86717SJuli Mallett 	const char *string;
400e3d86717SJuli Mallett 	regex_t *re;
401e3d86717SJuli Mallett 	regmatch_t *pm;
402e3d86717SJuli Mallett {
403e3d86717SJuli Mallett 	int error;
404e3d86717SJuli Mallett 
405e3d86717SJuli Mallett 	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
406e3d86717SJuli Mallett 	case 0:
407e3d86717SJuli Mallett 		pbunsigned(pm[0].rm_so);
408e3d86717SJuli Mallett 		break;
409e3d86717SJuli Mallett 	case REG_NOMATCH:
410e3d86717SJuli Mallett 		pbnum(-1);
411e3d86717SJuli Mallett 		break;
412e3d86717SJuli Mallett 	default:
413e3d86717SJuli Mallett 		exit_regerror(error, re);
414e3d86717SJuli Mallett 	}
415e3d86717SJuli Mallett }
416e3d86717SJuli Mallett 
417e3d86717SJuli Mallett /* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
418e3d86717SJuli Mallett  * says. So we twiddle with the regexp before passing it to regcomp.
419e3d86717SJuli Mallett  */
420e3d86717SJuli Mallett static char *
421e3d86717SJuli Mallett twiddle(p)
422e3d86717SJuli Mallett 	const char *p;
423e3d86717SJuli Mallett {
424e3d86717SJuli Mallett 	/* This could use strcspn for speed... */
425e3d86717SJuli Mallett 	while (*p != '\0') {
426e3d86717SJuli Mallett 		if (*p == '\\') {
427e3d86717SJuli Mallett 			switch(p[1]) {
428e3d86717SJuli Mallett 			case '(':
429e3d86717SJuli Mallett 			case ')':
430e3d86717SJuli Mallett 			case '|':
431e3d86717SJuli Mallett 				addchar(p[1]);
432e3d86717SJuli Mallett 				break;
433e3d86717SJuli Mallett 			case 'w':
434e3d86717SJuli Mallett 				addconstantstring("[_a-zA-Z0-9]");
435e3d86717SJuli Mallett 				break;
436e3d86717SJuli Mallett 			case 'W':
437e3d86717SJuli Mallett 				addconstantstring("[^_a-zA-Z0-9]");
438e3d86717SJuli Mallett 				break;
439e3d86717SJuli Mallett 			case '<':
440e3d86717SJuli Mallett 				addconstantstring("[[:<:]]");
441e3d86717SJuli Mallett 				break;
442e3d86717SJuli Mallett 			case '>':
443e3d86717SJuli Mallett 				addconstantstring("[[:>:]]");
444e3d86717SJuli Mallett 				break;
445e3d86717SJuli Mallett 			default:
446e3d86717SJuli Mallett 				addchars(p, 2);
447e3d86717SJuli Mallett 				break;
448e3d86717SJuli Mallett 			}
449e3d86717SJuli Mallett 			p+=2;
450e3d86717SJuli Mallett 			continue;
451e3d86717SJuli Mallett 		}
452e3d86717SJuli Mallett 		if (*p == '(' || *p == ')' || *p == '|')
453e3d86717SJuli Mallett 			addchar('\\');
454e3d86717SJuli Mallett 
455e3d86717SJuli Mallett 		addchar(*p);
456e3d86717SJuli Mallett 		p++;
457e3d86717SJuli Mallett 	}
458e3d86717SJuli Mallett 	return getstring();
459e3d86717SJuli Mallett }
460e3d86717SJuli Mallett 
461e3d86717SJuli Mallett /* patsubst(string, regexp, opt replacement) */
462e3d86717SJuli Mallett /* argv[2]: string
463e3d86717SJuli Mallett  * argv[3]: regexp
464e3d86717SJuli Mallett  * argv[4]: opt rep
465e3d86717SJuli Mallett  */
466e3d86717SJuli Mallett void
467e3d86717SJuli Mallett dopatsubst(argv, argc)
468e3d86717SJuli Mallett 	const char *argv[];
469e3d86717SJuli Mallett 	int argc;
470e3d86717SJuli Mallett {
471e3d86717SJuli Mallett 	int error;
472e3d86717SJuli Mallett 	regex_t re;
473e3d86717SJuli Mallett 	regmatch_t *pmatch;
474e3d86717SJuli Mallett 
475e3d86717SJuli Mallett 	if (argc <= 3) {
476e3d86717SJuli Mallett 		warnx("Too few arguments to patsubst");
477e3d86717SJuli Mallett 		return;
478e3d86717SJuli Mallett 	}
479e3d86717SJuli Mallett 	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
480e3d86717SJuli Mallett 	    REG_NEWLINE | REG_EXTENDED);
481e3d86717SJuli Mallett 	if (error != 0)
482e3d86717SJuli Mallett 		exit_regerror(error, &re);
483e3d86717SJuli Mallett 
484e3d86717SJuli Mallett 	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1));
485e3d86717SJuli Mallett 	do_subst(argv[2], &re,
486e3d86717SJuli Mallett 	    argc != 4 && argv[4] != NULL ? argv[4] : "", pmatch);
487e3d86717SJuli Mallett 	pbstr(getstring());
488e3d86717SJuli Mallett 	free(pmatch);
489e3d86717SJuli Mallett 	regfree(&re);
490e3d86717SJuli Mallett }
491e3d86717SJuli Mallett 
492e3d86717SJuli Mallett void
493e3d86717SJuli Mallett doregexp(argv, argc)
494e3d86717SJuli Mallett 	const char *argv[];
495e3d86717SJuli Mallett 	int argc;
496e3d86717SJuli Mallett {
497e3d86717SJuli Mallett 	int error;
498e3d86717SJuli Mallett 	regex_t re;
499e3d86717SJuli Mallett 	regmatch_t *pmatch;
500e3d86717SJuli Mallett 
501e3d86717SJuli Mallett 	if (argc <= 3) {
502e3d86717SJuli Mallett 		warnx("Too few arguments to regexp");
503e3d86717SJuli Mallett 		return;
504e3d86717SJuli Mallett 	}
505e3d86717SJuli Mallett 	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
506e3d86717SJuli Mallett 	    REG_EXTENDED);
507e3d86717SJuli Mallett 	if (error != 0)
508e3d86717SJuli Mallett 		exit_regerror(error, &re);
509e3d86717SJuli Mallett 
510e3d86717SJuli Mallett 	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1));
511e3d86717SJuli Mallett 	if (argv[4] == NULL || argc == 4)
512e3d86717SJuli Mallett 		do_regexpindex(argv[2], &re, pmatch);
513e3d86717SJuli Mallett 	else
514e3d86717SJuli Mallett 		do_regexp(argv[2], &re, argv[4], pmatch);
515e3d86717SJuli Mallett 	free(pmatch);
516e3d86717SJuli Mallett 	regfree(&re);
517e3d86717SJuli Mallett }
518e3d86717SJuli Mallett 
519e3d86717SJuli Mallett void
520e3d86717SJuli Mallett doesyscmd(cmd)
521e3d86717SJuli Mallett 	const char *cmd;
522e3d86717SJuli Mallett {
523e3d86717SJuli Mallett 	int p[2];
524e3d86717SJuli Mallett 	pid_t pid, cpid;
525e3d86717SJuli Mallett 	char *argv[4];
526e3d86717SJuli Mallett 	int cc;
527e3d86717SJuli Mallett 	int status;
528e3d86717SJuli Mallett 
529e3d86717SJuli Mallett 	/* Follow gnu m4 documentation: first flush buffers. */
530e3d86717SJuli Mallett 	fflush(NULL);
531e3d86717SJuli Mallett 
532e3d86717SJuli Mallett 	argv[0] = "sh";
533e3d86717SJuli Mallett 	argv[1] = "-c";
534e3d86717SJuli Mallett 	argv[2] = (char *)cmd;
535e3d86717SJuli Mallett 	argv[3] = NULL;
536e3d86717SJuli Mallett 
537e3d86717SJuli Mallett 	/* Just set up standard output, share stderr and stdin with m4 */
538e3d86717SJuli Mallett 	if (pipe(p) == -1)
539e3d86717SJuli Mallett 		err(1, "bad pipe");
540e3d86717SJuli Mallett 	switch(cpid = fork()) {
541e3d86717SJuli Mallett 	case -1:
542e3d86717SJuli Mallett 		err(1, "bad fork");
543e3d86717SJuli Mallett 		/* NOTREACHED */
544e3d86717SJuli Mallett 	case 0:
545e3d86717SJuli Mallett 		(void) close(p[0]);
546e3d86717SJuli Mallett 		(void) dup2(p[1], 1);
547e3d86717SJuli Mallett 		(void) close(p[1]);
548e3d86717SJuli Mallett 		execv(_PATH_BSHELL, argv);
549e3d86717SJuli Mallett 		exit(1);
550e3d86717SJuli Mallett 	default:
551e3d86717SJuli Mallett 		/* Read result in two stages, since m4's buffer is
552e3d86717SJuli Mallett 		 * pushback-only. */
553e3d86717SJuli Mallett 		(void) close(p[1]);
554e3d86717SJuli Mallett 		do {
555e3d86717SJuli Mallett 			char result[BUFSIZE];
556e3d86717SJuli Mallett 			cc = read(p[0], result, sizeof result);
557e3d86717SJuli Mallett 			if (cc > 0)
558e3d86717SJuli Mallett 				addchars(result, cc);
559e3d86717SJuli Mallett 		} while (cc > 0 || (cc == -1 && errno == EINTR));
560e3d86717SJuli Mallett 
561e3d86717SJuli Mallett 		(void) close(p[0]);
562e3d86717SJuli Mallett 		while ((pid = wait(&status)) != cpid && pid >= 0)
563e3d86717SJuli Mallett 			continue;
564e3d86717SJuli Mallett 		pbstr(getstring());
565e3d86717SJuli Mallett 	}
566e3d86717SJuli Mallett }
567