xref: /freebsd/usr.bin/m4/gnum4.c (revision 88497f0c68b1469cbf8560b8c4a022dbc509ceae)
1*88497f0cSBaptiste Daroussin /* $OpenBSD: gnum4.c,v 1.46 2014/07/10 14:12:31 espie 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  */
2756ca2b35SJuli Mallett #include <sys/cdefs.h>
2856ca2b35SJuli Mallett __FBSDID("$FreeBSD$");
2956ca2b35SJuli Mallett 
30e3d86717SJuli Mallett /*
31e3d86717SJuli Mallett  * functions needed to support gnu-m4 extensions, including a fake freezing
32e3d86717SJuli Mallett  */
33e3d86717SJuli Mallett 
34e3d86717SJuli Mallett #include <sys/param.h>
35e3d86717SJuli Mallett #include <sys/types.h>
36e3d86717SJuli Mallett #include <sys/wait.h>
37e3d86717SJuli Mallett #include <ctype.h>
38a841e1ebSBaptiste Daroussin #include <err.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 <errno.h>
46e3d86717SJuli Mallett #include <unistd.h>
47e3d86717SJuli Mallett #include "mdef.h"
48e3d86717SJuli Mallett #include "stdd.h"
49e3d86717SJuli Mallett #include "extern.h"
50e3d86717SJuli Mallett 
51e3d86717SJuli Mallett 
52e3d86717SJuli Mallett int mimic_gnu = 0;
53e3d86717SJuli Mallett 
54e3d86717SJuli Mallett /*
55e3d86717SJuli Mallett  * Support for include path search
566bccea7cSRebecca Cran  * First search in the current directory.
57e3d86717SJuli Mallett  * If not found, and the path is not absolute, include path kicks in.
58e3d86717SJuli Mallett  * First, -I options, in the order found on the command line.
59e3d86717SJuli Mallett  * Then M4PATH env variable
60e3d86717SJuli Mallett  */
61e3d86717SJuli Mallett 
62eccad222SEd Schouten static struct path_entry {
63e3d86717SJuli Mallett 	char *name;
64e3d86717SJuli Mallett 	struct path_entry *next;
65e3d86717SJuli Mallett } *first, *last;
66e3d86717SJuli Mallett 
67e3d86717SJuli Mallett static struct path_entry *new_path_entry(const char *);
68e3d86717SJuli Mallett static void ensure_m4path(void);
69e3d86717SJuli Mallett static struct input_file *dopath(struct input_file *, const char *);
70e3d86717SJuli Mallett 
71e3d86717SJuli Mallett static struct path_entry *
72bd2bfb58SJuli Mallett new_path_entry(const char *dirname)
73e3d86717SJuli Mallett {
74e3d86717SJuli Mallett 	struct path_entry *n;
75e3d86717SJuli Mallett 
76e3d86717SJuli Mallett 	n = malloc(sizeof(struct path_entry));
77e3d86717SJuli Mallett 	if (!n)
78e3d86717SJuli Mallett 		errx(1, "out of memory");
79e3d86717SJuli Mallett 	n->name = strdup(dirname);
80e3d86717SJuli Mallett 	if (!n->name)
81e3d86717SJuli Mallett 		errx(1, "out of memory");
82e3d86717SJuli Mallett 	n->next = 0;
83e3d86717SJuli Mallett 	return n;
84e3d86717SJuli Mallett }
85e3d86717SJuli Mallett 
86e3d86717SJuli Mallett void
87bd2bfb58SJuli Mallett addtoincludepath(const char *dirname)
88e3d86717SJuli Mallett {
89e3d86717SJuli Mallett 	struct path_entry *n;
90e3d86717SJuli Mallett 
91e3d86717SJuli Mallett 	n = new_path_entry(dirname);
92e3d86717SJuli Mallett 
93e3d86717SJuli Mallett 	if (last) {
94e3d86717SJuli Mallett 		last->next = n;
95e3d86717SJuli Mallett 		last = n;
96e3d86717SJuli Mallett 	}
97e3d86717SJuli Mallett 	else
98e3d86717SJuli Mallett 		last = first = n;
99e3d86717SJuli Mallett }
100e3d86717SJuli Mallett 
101e3d86717SJuli Mallett static void
102d1fea89cSJuli Mallett ensure_m4path(void)
103e3d86717SJuli Mallett {
104e3d86717SJuli Mallett 	static int envpathdone = 0;
105e3d86717SJuli Mallett 	char *envpath;
106e3d86717SJuli Mallett 	char *sweep;
107e3d86717SJuli Mallett 	char *path;
108e3d86717SJuli Mallett 
109e3d86717SJuli Mallett 	if (envpathdone)
110e3d86717SJuli Mallett 		return;
111e3d86717SJuli Mallett 	envpathdone = TRUE;
112e3d86717SJuli Mallett 	envpath = getenv("M4PATH");
113e3d86717SJuli Mallett 	if (!envpath)
114e3d86717SJuli Mallett 		return;
115e3d86717SJuli Mallett 	/* for portability: getenv result is read-only */
116e3d86717SJuli Mallett 	envpath = strdup(envpath);
117e3d86717SJuli Mallett 	if (!envpath)
118e3d86717SJuli Mallett 		errx(1, "out of memory");
119e3d86717SJuli Mallett 	for (sweep = envpath;
120e3d86717SJuli Mallett 	    (path = strsep(&sweep, ":")) != NULL;)
121e3d86717SJuli Mallett 	    addtoincludepath(path);
122e3d86717SJuli Mallett 	free(envpath);
123e3d86717SJuli Mallett }
124e3d86717SJuli Mallett 
125e3d86717SJuli Mallett static
126e3d86717SJuli Mallett struct input_file *
127bd2bfb58SJuli Mallett dopath(struct input_file *i, const char *filename)
128e3d86717SJuli Mallett {
129e3d86717SJuli Mallett 	char path[MAXPATHLEN];
130e3d86717SJuli Mallett 	struct path_entry *pe;
131e3d86717SJuli Mallett 	FILE *f;
132e3d86717SJuli Mallett 
133e3d86717SJuli Mallett 	for (pe = first; pe; pe = pe->next) {
134e3d86717SJuli Mallett 		snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
135a841e1ebSBaptiste Daroussin 		if ((f = fopen(path, "r")) != 0) {
136e3d86717SJuli Mallett 			set_input(i, f, path);
137e3d86717SJuli Mallett 			return i;
138e3d86717SJuli Mallett 		}
139e3d86717SJuli Mallett 	}
140e3d86717SJuli Mallett 	return NULL;
141e3d86717SJuli Mallett }
142e3d86717SJuli Mallett 
143e3d86717SJuli Mallett struct input_file *
144bd2bfb58SJuli Mallett fopen_trypath(struct input_file *i, const char *filename)
145e3d86717SJuli Mallett {
146e3d86717SJuli Mallett 	FILE *f;
147e3d86717SJuli Mallett 
148e3d86717SJuli Mallett 	f = fopen(filename, "r");
149e3d86717SJuli Mallett 	if (f != NULL) {
150e3d86717SJuli Mallett 		set_input(i, f, filename);
151e3d86717SJuli Mallett 		return i;
152e3d86717SJuli Mallett 	}
153e3d86717SJuli Mallett 	if (filename[0] == '/')
154e3d86717SJuli Mallett 		return NULL;
155e3d86717SJuli Mallett 
156e3d86717SJuli Mallett 	ensure_m4path();
157e3d86717SJuli Mallett 
158e3d86717SJuli Mallett 	return dopath(i, filename);
159e3d86717SJuli Mallett }
160e3d86717SJuli Mallett 
161e3d86717SJuli Mallett void
162bd2bfb58SJuli Mallett doindir(const char *argv[], int argc)
163e3d86717SJuli Mallett {
164a841e1ebSBaptiste Daroussin 	ndptr n;
165a841e1ebSBaptiste Daroussin 	struct macro_definition *p = NULL;
166e3d86717SJuli Mallett 
167a841e1ebSBaptiste Daroussin 	n = lookup(argv[2]);
168a841e1ebSBaptiste Daroussin 	if (n == NULL || (p = macro_getdef(n)) == NULL)
169a841e1ebSBaptiste Daroussin 		m4errx(1, "indir: undefined macro %s.", argv[2]);
170e3d86717SJuli Mallett 	argv[1] = p->defn;
171a841e1ebSBaptiste Daroussin 
172a841e1ebSBaptiste Daroussin 	eval(argv+1, argc-1, p->type, is_traced(n));
173e3d86717SJuli Mallett }
174e3d86717SJuli Mallett 
175e3d86717SJuli Mallett void
176bd2bfb58SJuli Mallett dobuiltin(const char *argv[], int argc)
177e3d86717SJuli Mallett {
178a841e1ebSBaptiste Daroussin 	ndptr p;
179a841e1ebSBaptiste Daroussin 
180e3d86717SJuli Mallett 	argv[1] = NULL;
181a841e1ebSBaptiste Daroussin 	p = macro_getbuiltin(argv[2]);
182a841e1ebSBaptiste Daroussin 	if (p != NULL)
183a841e1ebSBaptiste Daroussin 		eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p));
184e3d86717SJuli Mallett 	else
185a841e1ebSBaptiste Daroussin 		m4errx(1, "unknown builtin %s.", argv[2]);
186e3d86717SJuli Mallett }
187e3d86717SJuli Mallett 
188e3d86717SJuli Mallett 
189e3d86717SJuli Mallett /* We need some temporary buffer space, as pb pushes BACK and substitution
190e3d86717SJuli Mallett  * proceeds forward... */
191e3d86717SJuli Mallett static char *buffer;
192e3d86717SJuli Mallett static size_t bufsize = 0;
193e3d86717SJuli Mallett static size_t current = 0;
194e3d86717SJuli Mallett 
195e3d86717SJuli Mallett static void addchars(const char *, size_t);
196bd2bfb58SJuli Mallett static void addchar(int);
197e3d86717SJuli Mallett static char *twiddle(const char *);
198e3d86717SJuli Mallett static char *getstring(void);
199*88497f0cSBaptiste Daroussin static void exit_regerror(int, regex_t *, const char *);
200*88497f0cSBaptiste Daroussin static void do_subst(const char *, regex_t *, const char *, const char *,
201*88497f0cSBaptiste Daroussin     regmatch_t *);
202*88497f0cSBaptiste Daroussin static void do_regexpindex(const char *, regex_t *, const char *, regmatch_t *);
203*88497f0cSBaptiste Daroussin static void do_regexp(const char *, regex_t *, const char *, const char *,
204*88497f0cSBaptiste Daroussin     regmatch_t *);
205a841e1ebSBaptiste Daroussin static void add_sub(int, const char *, regex_t *, regmatch_t *);
206e3d86717SJuli Mallett static void add_replace(const char *, regex_t *, const char *, regmatch_t *);
207e3d86717SJuli Mallett #define addconstantstring(s) addchars((s), sizeof(s)-1)
208e3d86717SJuli Mallett 
209e3d86717SJuli Mallett static void
210bd2bfb58SJuli Mallett addchars(const char *c, size_t n)
211e3d86717SJuli Mallett {
212e3d86717SJuli Mallett 	if (n == 0)
213e3d86717SJuli Mallett 		return;
214e3d86717SJuli Mallett 	while (current + n > bufsize) {
215e3d86717SJuli Mallett 		if (bufsize == 0)
216e3d86717SJuli Mallett 			bufsize = 1024;
217e3d86717SJuli Mallett 		else
218e3d86717SJuli Mallett 			bufsize *= 2;
219a841e1ebSBaptiste Daroussin 		buffer = xrealloc(buffer, bufsize, NULL);
220e3d86717SJuli Mallett 	}
221e3d86717SJuli Mallett 	memcpy(buffer+current, c, n);
222e3d86717SJuli Mallett 	current += n;
223e3d86717SJuli Mallett }
224e3d86717SJuli Mallett 
225e3d86717SJuli Mallett static void
226bd2bfb58SJuli Mallett addchar(int c)
227e3d86717SJuli Mallett {
228e3d86717SJuli Mallett 	if (current +1 > bufsize) {
229e3d86717SJuli Mallett 		if (bufsize == 0)
230e3d86717SJuli Mallett 			bufsize = 1024;
231e3d86717SJuli Mallett 		else
232e3d86717SJuli Mallett 			bufsize *= 2;
233a841e1ebSBaptiste Daroussin 		buffer = xrealloc(buffer, bufsize, NULL);
234e3d86717SJuli Mallett 	}
235e3d86717SJuli Mallett 	buffer[current++] = c;
236e3d86717SJuli Mallett }
237e3d86717SJuli Mallett 
238e3d86717SJuli Mallett static char *
239d1fea89cSJuli Mallett getstring(void)
240e3d86717SJuli Mallett {
241e3d86717SJuli Mallett 	addchar('\0');
242e3d86717SJuli Mallett 	current = 0;
243e3d86717SJuli Mallett 	return buffer;
244e3d86717SJuli Mallett }
245e3d86717SJuli Mallett 
246e3d86717SJuli Mallett 
247e3d86717SJuli Mallett static void
248*88497f0cSBaptiste Daroussin exit_regerror(int er, regex_t *re, const char *source)
249e3d86717SJuli Mallett {
250e3d86717SJuli Mallett 	size_t	errlen;
251e3d86717SJuli Mallett 	char	*errbuf;
252e3d86717SJuli Mallett 
253e3d86717SJuli Mallett 	errlen = regerror(er, re, NULL, 0);
254a841e1ebSBaptiste Daroussin 	errbuf = xalloc(errlen,
255a841e1ebSBaptiste Daroussin 	    "malloc in regerror: %lu", (unsigned long)errlen);
256e3d86717SJuli Mallett 	regerror(er, re, errbuf, errlen);
257*88497f0cSBaptiste Daroussin 	m4errx(1, "regular expression error in %s: %s.", source, errbuf);
258e3d86717SJuli Mallett }
259e3d86717SJuli Mallett 
260e3d86717SJuli Mallett static void
261a841e1ebSBaptiste Daroussin add_sub(int n, const char *string, regex_t *re, regmatch_t *pm)
262e3d86717SJuli Mallett {
263a841e1ebSBaptiste Daroussin 	if (n > (int)re->re_nsub)
264a841e1ebSBaptiste Daroussin 		warnx("No subexpression %d", n);
265e3d86717SJuli Mallett 	/* Subexpressions that did not match are
266e3d86717SJuli Mallett 	 * not an error.  */
267e3d86717SJuli Mallett 	else if (pm[n].rm_so != -1 &&
268e3d86717SJuli Mallett 	    pm[n].rm_eo != -1) {
269e3d86717SJuli Mallett 		addchars(string + pm[n].rm_so,
270e3d86717SJuli Mallett 			pm[n].rm_eo - pm[n].rm_so);
271e3d86717SJuli Mallett 	}
272e3d86717SJuli Mallett }
273e3d86717SJuli Mallett 
274e3d86717SJuli Mallett /* Add replacement string to the output buffer, recognizing special
275e3d86717SJuli Mallett  * constructs and replacing them with substrings of the original string.
276e3d86717SJuli Mallett  */
277e3d86717SJuli Mallett static void
278bd2bfb58SJuli Mallett add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
279e3d86717SJuli Mallett {
280e3d86717SJuli Mallett 	const char *p;
281e3d86717SJuli Mallett 
282e3d86717SJuli Mallett 	for (p = replace; *p != '\0'; p++) {
283e3d86717SJuli Mallett 		if (*p == '&' && !mimic_gnu) {
284e3d86717SJuli Mallett 			add_sub(0, string, re, pm);
285e3d86717SJuli Mallett 			continue;
286e3d86717SJuli Mallett 		}
287e3d86717SJuli Mallett 		if (*p == '\\') {
288e3d86717SJuli Mallett 			if (p[1] == '\\') {
289e3d86717SJuli Mallett 				addchar(p[1]);
290e3d86717SJuli Mallett 				p++;
291e3d86717SJuli Mallett 				continue;
292e3d86717SJuli Mallett 			}
293e3d86717SJuli Mallett 			if (p[1] == '&') {
294e3d86717SJuli Mallett 				if (mimic_gnu)
295e3d86717SJuli Mallett 					add_sub(0, string, re, pm);
296e3d86717SJuli Mallett 				else
297e3d86717SJuli Mallett 					addchar(p[1]);
298e3d86717SJuli Mallett 				p++;
299e3d86717SJuli Mallett 				continue;
300e3d86717SJuli Mallett 			}
301*88497f0cSBaptiste Daroussin 			if (isdigit((unsigned char)p[1])) {
302e3d86717SJuli Mallett 				add_sub(*(++p) - '0', string, re, pm);
303e3d86717SJuli Mallett 				continue;
304e3d86717SJuli Mallett 			}
305e3d86717SJuli Mallett 		}
306e3d86717SJuli Mallett 		addchar(*p);
307e3d86717SJuli Mallett 	}
308e3d86717SJuli Mallett }
309e3d86717SJuli Mallett 
310e3d86717SJuli Mallett static void
311*88497f0cSBaptiste Daroussin do_subst(const char *string, regex_t *re, const char *source,
312*88497f0cSBaptiste Daroussin     const char *replace, regmatch_t *pm)
313e3d86717SJuli Mallett {
314e3d86717SJuli Mallett 	int error;
315e3d86717SJuli Mallett 	int flags = 0;
316e3d86717SJuli Mallett 	const char *last_match = NULL;
317e3d86717SJuli Mallett 
318e3d86717SJuli Mallett 	while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
319e3d86717SJuli Mallett 		if (pm[0].rm_eo != 0) {
320e3d86717SJuli Mallett 			if (string[pm[0].rm_eo-1] == '\n')
321e3d86717SJuli Mallett 				flags = 0;
322e3d86717SJuli Mallett 			else
323e3d86717SJuli Mallett 				flags = REG_NOTBOL;
324e3d86717SJuli Mallett 		}
325e3d86717SJuli Mallett 
326e3d86717SJuli Mallett 		/* NULL length matches are special... We use the `vi-mode'
327e3d86717SJuli Mallett 		 * rule: don't allow a NULL-match at the last match
328e3d86717SJuli Mallett 		 * position.
329e3d86717SJuli Mallett 		 */
330e3d86717SJuli Mallett 		if (pm[0].rm_so == pm[0].rm_eo &&
331e3d86717SJuli Mallett 		    string + pm[0].rm_so == last_match) {
332e3d86717SJuli Mallett 			if (*string == '\0')
333e3d86717SJuli Mallett 				return;
334e3d86717SJuli Mallett 			addchar(*string);
335e3d86717SJuli Mallett 			if (*string++ == '\n')
336e3d86717SJuli Mallett 				flags = 0;
337e3d86717SJuli Mallett 			else
338e3d86717SJuli Mallett 				flags = REG_NOTBOL;
339e3d86717SJuli Mallett 			continue;
340e3d86717SJuli Mallett 		}
341e3d86717SJuli Mallett 		last_match = string + pm[0].rm_so;
342e3d86717SJuli Mallett 		addchars(string, pm[0].rm_so);
343e3d86717SJuli Mallett 		add_replace(string, re, replace, pm);
344e3d86717SJuli Mallett 		string += pm[0].rm_eo;
345e3d86717SJuli Mallett 	}
346e3d86717SJuli Mallett 	if (error != REG_NOMATCH)
347*88497f0cSBaptiste Daroussin 		exit_regerror(error, re, source);
348e3d86717SJuli Mallett 	pbstr(string);
349e3d86717SJuli Mallett }
350e3d86717SJuli Mallett 
351e3d86717SJuli Mallett static void
352*88497f0cSBaptiste Daroussin do_regexp(const char *string, regex_t *re, const char *source,
353*88497f0cSBaptiste Daroussin     const char *replace, regmatch_t *pm)
354e3d86717SJuli Mallett {
355e3d86717SJuli Mallett 	int error;
356e3d86717SJuli Mallett 
357e3d86717SJuli Mallett 	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
358e3d86717SJuli Mallett 	case 0:
359e3d86717SJuli Mallett 		add_replace(string, re, replace, pm);
360e3d86717SJuli Mallett 		pbstr(getstring());
361e3d86717SJuli Mallett 		break;
362e3d86717SJuli Mallett 	case REG_NOMATCH:
363e3d86717SJuli Mallett 		break;
364e3d86717SJuli Mallett 	default:
365*88497f0cSBaptiste Daroussin 		exit_regerror(error, re, source);
366e3d86717SJuli Mallett 	}
367e3d86717SJuli Mallett }
368e3d86717SJuli Mallett 
369e3d86717SJuli Mallett static void
370*88497f0cSBaptiste Daroussin do_regexpindex(const char *string, regex_t *re, const char *source,
371*88497f0cSBaptiste Daroussin     regmatch_t *pm)
372e3d86717SJuli Mallett {
373e3d86717SJuli Mallett 	int error;
374e3d86717SJuli Mallett 
375e3d86717SJuli Mallett 	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
376e3d86717SJuli Mallett 	case 0:
377e3d86717SJuli Mallett 		pbunsigned(pm[0].rm_so);
378e3d86717SJuli Mallett 		break;
379e3d86717SJuli Mallett 	case REG_NOMATCH:
380e3d86717SJuli Mallett 		pbnum(-1);
381e3d86717SJuli Mallett 		break;
382e3d86717SJuli Mallett 	default:
383*88497f0cSBaptiste Daroussin 		exit_regerror(error, re, source);
384e3d86717SJuli Mallett 	}
385e3d86717SJuli Mallett }
386e3d86717SJuli Mallett 
387e3d86717SJuli Mallett /* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
388e3d86717SJuli Mallett  * says. So we twiddle with the regexp before passing it to regcomp.
389e3d86717SJuli Mallett  */
390e3d86717SJuli Mallett static char *
391bd2bfb58SJuli Mallett twiddle(const char *p)
392e3d86717SJuli Mallett {
393a841e1ebSBaptiste Daroussin 	/* + at start of regexp is a normal character for Gnu m4 */
394a841e1ebSBaptiste Daroussin 	if (*p == '^') {
395a841e1ebSBaptiste Daroussin 		addchar(*p);
396a841e1ebSBaptiste Daroussin 		p++;
397a841e1ebSBaptiste Daroussin 	}
398a841e1ebSBaptiste Daroussin 	if (*p == '+') {
399a841e1ebSBaptiste Daroussin 		addchar('\\');
400a841e1ebSBaptiste Daroussin 	}
401e3d86717SJuli Mallett 	/* This could use strcspn for speed... */
402e3d86717SJuli Mallett 	while (*p != '\0') {
403e3d86717SJuli Mallett 		if (*p == '\\') {
404e3d86717SJuli Mallett 			switch(p[1]) {
405e3d86717SJuli Mallett 			case '(':
406e3d86717SJuli Mallett 			case ')':
407e3d86717SJuli Mallett 			case '|':
408e3d86717SJuli Mallett 				addchar(p[1]);
409e3d86717SJuli Mallett 				break;
410e3d86717SJuli Mallett 			case 'w':
411e3d86717SJuli Mallett 				addconstantstring("[_a-zA-Z0-9]");
412e3d86717SJuli Mallett 				break;
413e3d86717SJuli Mallett 			case 'W':
414e3d86717SJuli Mallett 				addconstantstring("[^_a-zA-Z0-9]");
415e3d86717SJuli Mallett 				break;
416e3d86717SJuli Mallett 			case '<':
417e3d86717SJuli Mallett 				addconstantstring("[[:<:]]");
418e3d86717SJuli Mallett 				break;
419e3d86717SJuli Mallett 			case '>':
420e3d86717SJuli Mallett 				addconstantstring("[[:>:]]");
421e3d86717SJuli Mallett 				break;
422e3d86717SJuli Mallett 			default:
423e3d86717SJuli Mallett 				addchars(p, 2);
424e3d86717SJuli Mallett 				break;
425e3d86717SJuli Mallett 			}
426e3d86717SJuli Mallett 			p+=2;
427e3d86717SJuli Mallett 			continue;
428e3d86717SJuli Mallett 		}
429e3d86717SJuli Mallett 		if (*p == '(' || *p == ')' || *p == '|')
430e3d86717SJuli Mallett 			addchar('\\');
431e3d86717SJuli Mallett 
432e3d86717SJuli Mallett 		addchar(*p);
433e3d86717SJuli Mallett 		p++;
434e3d86717SJuli Mallett 	}
435e3d86717SJuli Mallett 	return getstring();
436e3d86717SJuli Mallett }
437e3d86717SJuli Mallett 
438e3d86717SJuli Mallett /* patsubst(string, regexp, opt replacement) */
439e3d86717SJuli Mallett /* argv[2]: string
440e3d86717SJuli Mallett  * argv[3]: regexp
441e3d86717SJuli Mallett  * argv[4]: opt rep
442e3d86717SJuli Mallett  */
443e3d86717SJuli Mallett void
444bd2bfb58SJuli Mallett dopatsubst(const char *argv[], int argc)
445e3d86717SJuli Mallett {
446e3d86717SJuli Mallett 	if (argc <= 3) {
447e3d86717SJuli Mallett 		warnx("Too few arguments to patsubst");
448e3d86717SJuli Mallett 		return;
449e3d86717SJuli Mallett 	}
450a841e1ebSBaptiste Daroussin 	/* special case: empty regexp */
451a841e1ebSBaptiste Daroussin 	if (argv[3][0] == '\0') {
452a841e1ebSBaptiste Daroussin 		const char *s;
453a841e1ebSBaptiste Daroussin 		size_t len;
454a841e1ebSBaptiste Daroussin 		if (argc > 4 && argv[4])
455a841e1ebSBaptiste Daroussin 			len = strlen(argv[4]);
456a841e1ebSBaptiste Daroussin 		else
457a841e1ebSBaptiste Daroussin 			len = 0;
458a841e1ebSBaptiste Daroussin 		for (s = argv[2]; *s != '\0'; s++) {
459a841e1ebSBaptiste Daroussin 			addchars(argv[4], len);
460a841e1ebSBaptiste Daroussin 			addchar(*s);
461a841e1ebSBaptiste Daroussin 		}
462a841e1ebSBaptiste Daroussin 	} else {
463a841e1ebSBaptiste Daroussin 		int error;
464a841e1ebSBaptiste Daroussin 		regex_t re;
465a841e1ebSBaptiste Daroussin 		regmatch_t *pmatch;
466a841e1ebSBaptiste Daroussin 		int mode = REG_EXTENDED;
467*88497f0cSBaptiste Daroussin 		const char *source;
468a841e1ebSBaptiste Daroussin 		size_t l = strlen(argv[3]);
469a841e1ebSBaptiste Daroussin 
470a841e1ebSBaptiste Daroussin 		if (!mimic_gnu ||
471a841e1ebSBaptiste Daroussin 		    (argv[3][0] == '^') ||
472a841e1ebSBaptiste Daroussin 		    (l > 0 && argv[3][l-1] == '$'))
473a841e1ebSBaptiste Daroussin 			mode |= REG_NEWLINE;
474a841e1ebSBaptiste Daroussin 
475*88497f0cSBaptiste Daroussin 		source = mimic_gnu ? twiddle(argv[3]) : argv[3];
476*88497f0cSBaptiste Daroussin 		error = regcomp(&re, source, mode);
477e3d86717SJuli Mallett 		if (error != 0)
478*88497f0cSBaptiste Daroussin 			exit_regerror(error, &re, source);
479e3d86717SJuli Mallett 
480*88497f0cSBaptiste Daroussin 		pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t),
481*88497f0cSBaptiste Daroussin 		    NULL);
482*88497f0cSBaptiste Daroussin 		do_subst(argv[2], &re, source,
483a841e1ebSBaptiste Daroussin 		    argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch);
484e3d86717SJuli Mallett 		free(pmatch);
485e3d86717SJuli Mallett 		regfree(&re);
486e3d86717SJuli Mallett 	}
487a841e1ebSBaptiste Daroussin 	pbstr(getstring());
488a841e1ebSBaptiste Daroussin }
489e3d86717SJuli Mallett 
490e3d86717SJuli Mallett void
491bd2bfb58SJuli Mallett doregexp(const char *argv[], int argc)
492e3d86717SJuli Mallett {
493e3d86717SJuli Mallett 	int error;
494e3d86717SJuli Mallett 	regex_t re;
495e3d86717SJuli Mallett 	regmatch_t *pmatch;
496*88497f0cSBaptiste Daroussin 	const char *source;
497e3d86717SJuli Mallett 
498e3d86717SJuli Mallett 	if (argc <= 3) {
499e3d86717SJuli Mallett 		warnx("Too few arguments to regexp");
500e3d86717SJuli Mallett 		return;
501e3d86717SJuli Mallett 	}
502a841e1ebSBaptiste Daroussin 	/* special gnu case */
503a841e1ebSBaptiste Daroussin 	if (argv[3][0] == '\0' && mimic_gnu) {
504a841e1ebSBaptiste Daroussin 		if (argc == 4 || argv[4] == NULL)
505a841e1ebSBaptiste Daroussin 			return;
506a841e1ebSBaptiste Daroussin 		else
507a841e1ebSBaptiste Daroussin 			pbstr(argv[4]);
508a841e1ebSBaptiste Daroussin 	}
509*88497f0cSBaptiste Daroussin 	source = mimic_gnu ? twiddle(argv[3]) : argv[3];
510*88497f0cSBaptiste Daroussin 	error = regcomp(&re, source, REG_EXTENDED|REG_NEWLINE);
511e3d86717SJuli Mallett 	if (error != 0)
512*88497f0cSBaptiste Daroussin 		exit_regerror(error, &re, source);
513e3d86717SJuli Mallett 
514*88497f0cSBaptiste Daroussin 	pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t), NULL);
515a841e1ebSBaptiste Daroussin 	if (argc == 4 || argv[4] == NULL)
516*88497f0cSBaptiste Daroussin 		do_regexpindex(argv[2], &re, source, pmatch);
517e3d86717SJuli Mallett 	else
518*88497f0cSBaptiste Daroussin 		do_regexp(argv[2], &re, source, argv[4], pmatch);
519e3d86717SJuli Mallett 	free(pmatch);
520e3d86717SJuli Mallett 	regfree(&re);
521e3d86717SJuli Mallett }
522e3d86717SJuli Mallett 
523e3d86717SJuli Mallett void
524a841e1ebSBaptiste Daroussin doformat(const char *argv[], int argc)
525a841e1ebSBaptiste Daroussin {
526a841e1ebSBaptiste Daroussin 	const char *format = argv[2];
527a841e1ebSBaptiste Daroussin 	int pos = 3;
528a841e1ebSBaptiste Daroussin 	int left_padded;
529a841e1ebSBaptiste Daroussin 	long width;
530a841e1ebSBaptiste Daroussin 	size_t l;
531a841e1ebSBaptiste Daroussin 	const char *thisarg = NULL;
532a841e1ebSBaptiste Daroussin 	char temp[2];
533a841e1ebSBaptiste Daroussin 	long extra;
534a841e1ebSBaptiste Daroussin 
535a841e1ebSBaptiste Daroussin 	while (*format != 0) {
536a841e1ebSBaptiste Daroussin 		if (*format != '%') {
537a841e1ebSBaptiste Daroussin 			addchar(*format++);
538a841e1ebSBaptiste Daroussin 			continue;
539a841e1ebSBaptiste Daroussin 		}
540a841e1ebSBaptiste Daroussin 
541a841e1ebSBaptiste Daroussin 		format++;
542a841e1ebSBaptiste Daroussin 		if (*format == '%') {
543a841e1ebSBaptiste Daroussin 			addchar(*format++);
544a841e1ebSBaptiste Daroussin 			continue;
545a841e1ebSBaptiste Daroussin 		}
546a841e1ebSBaptiste Daroussin 		if (*format == 0) {
547a841e1ebSBaptiste Daroussin 			addchar('%');
548a841e1ebSBaptiste Daroussin 			break;
549a841e1ebSBaptiste Daroussin 		}
550a841e1ebSBaptiste Daroussin 
551a841e1ebSBaptiste Daroussin 		if (*format == '*') {
552a841e1ebSBaptiste Daroussin 			format++;
553a841e1ebSBaptiste Daroussin 			if (pos >= argc)
554a841e1ebSBaptiste Daroussin 				m4errx(1,
555a841e1ebSBaptiste Daroussin 				    "Format with too many format specifiers.");
556a841e1ebSBaptiste Daroussin 			width = strtol(argv[pos++], NULL, 10);
557a841e1ebSBaptiste Daroussin 		} else {
558a841e1ebSBaptiste Daroussin 			width = strtol(format, __DECONST(char **,&format), 10);
559a841e1ebSBaptiste Daroussin 		}
560a841e1ebSBaptiste Daroussin 		if (width < 0) {
561a841e1ebSBaptiste Daroussin 			left_padded = 1;
562a841e1ebSBaptiste Daroussin 			width = -width;
563a841e1ebSBaptiste Daroussin 		} else {
564a841e1ebSBaptiste Daroussin 			left_padded = 0;
565a841e1ebSBaptiste Daroussin 		}
566a841e1ebSBaptiste Daroussin 		if (*format == '.') {
567a841e1ebSBaptiste Daroussin 			format++;
568a841e1ebSBaptiste Daroussin 			if (*format == '*') {
569a841e1ebSBaptiste Daroussin 				format++;
570a841e1ebSBaptiste Daroussin 				if (pos >= argc)
571a841e1ebSBaptiste Daroussin 					m4errx(1,
572a841e1ebSBaptiste Daroussin 					    "Format with too many format specifiers.");
573a841e1ebSBaptiste Daroussin 				extra = strtol(argv[pos++], NULL, 10);
574a841e1ebSBaptiste Daroussin 			} else {
575a841e1ebSBaptiste Daroussin 				extra = strtol(format, __DECONST(char **, &format), 10);
576a841e1ebSBaptiste Daroussin 			}
577a841e1ebSBaptiste Daroussin 		} else {
578a841e1ebSBaptiste Daroussin 			extra = LONG_MAX;
579a841e1ebSBaptiste Daroussin 		}
580a841e1ebSBaptiste Daroussin 		if (pos >= argc)
581a841e1ebSBaptiste Daroussin 			m4errx(1, "Format with too many format specifiers.");
582a841e1ebSBaptiste Daroussin 		switch(*format) {
583a841e1ebSBaptiste Daroussin 		case 's':
584a841e1ebSBaptiste Daroussin 			thisarg = argv[pos++];
585a841e1ebSBaptiste Daroussin 			break;
586a841e1ebSBaptiste Daroussin 		case 'c':
587a841e1ebSBaptiste Daroussin 			temp[0] = strtoul(argv[pos++], NULL, 10);
588a841e1ebSBaptiste Daroussin 			temp[1] = 0;
589a841e1ebSBaptiste Daroussin 			thisarg = temp;
590a841e1ebSBaptiste Daroussin 			break;
591a841e1ebSBaptiste Daroussin 		default:
592a841e1ebSBaptiste Daroussin 			m4errx(1, "Unsupported format specification: %s.",
593a841e1ebSBaptiste Daroussin 			    argv[2]);
594a841e1ebSBaptiste Daroussin 		}
595a841e1ebSBaptiste Daroussin 		format++;
596a841e1ebSBaptiste Daroussin 		l = strlen(thisarg);
597a841e1ebSBaptiste Daroussin 		if ((long)l > extra)
598a841e1ebSBaptiste Daroussin 			l = extra;
599a841e1ebSBaptiste Daroussin 		if (!left_padded) {
600a841e1ebSBaptiste Daroussin 			while ((long)l < width--)
601a841e1ebSBaptiste Daroussin 				addchar(' ');
602a841e1ebSBaptiste Daroussin 		}
603a841e1ebSBaptiste Daroussin 		addchars(thisarg, l);
604a841e1ebSBaptiste Daroussin 		if (left_padded) {
605a841e1ebSBaptiste Daroussin 			while ((long)l < width--)
606a841e1ebSBaptiste Daroussin 				addchar(' ');
607a841e1ebSBaptiste Daroussin 		}
608a841e1ebSBaptiste Daroussin 	}
609a841e1ebSBaptiste Daroussin 	pbstr(getstring());
610a841e1ebSBaptiste Daroussin }
611a841e1ebSBaptiste Daroussin 
612a841e1ebSBaptiste Daroussin void
613bd2bfb58SJuli Mallett doesyscmd(const char *cmd)
614e3d86717SJuli Mallett {
615e3d86717SJuli Mallett 	int p[2];
616e3d86717SJuli Mallett 	pid_t pid, cpid;
617a841e1ebSBaptiste Daroussin 	char *argv[4];
618e3d86717SJuli Mallett 	int cc;
619e3d86717SJuli Mallett 	int status;
620e3d86717SJuli Mallett 
621e3d86717SJuli Mallett 	/* Follow gnu m4 documentation: first flush buffers. */
622e3d86717SJuli Mallett 	fflush(NULL);
623e3d86717SJuli Mallett 
624a841e1ebSBaptiste Daroussin 	argv[0] = __DECONST(char *, "sh");
625a841e1ebSBaptiste Daroussin 	argv[1] = __DECONST(char *, "-c");
626a841e1ebSBaptiste Daroussin 	argv[2] = __DECONST(char *, cmd);
627a841e1ebSBaptiste Daroussin 	argv[3] = NULL;
628a841e1ebSBaptiste Daroussin 
629e3d86717SJuli Mallett 	/* Just set up standard output, share stderr and stdin with m4 */
630e3d86717SJuli Mallett 	if (pipe(p) == -1)
631e3d86717SJuli Mallett 		err(1, "bad pipe");
632e3d86717SJuli Mallett 	switch(cpid = fork()) {
633e3d86717SJuli Mallett 	case -1:
634e3d86717SJuli Mallett 		err(1, "bad fork");
635e3d86717SJuli Mallett 		/* NOTREACHED */
636e3d86717SJuli Mallett 	case 0:
637e3d86717SJuli Mallett 		(void) close(p[0]);
638e3d86717SJuli Mallett 		(void) dup2(p[1], 1);
639e3d86717SJuli Mallett 		(void) close(p[1]);
640a841e1ebSBaptiste Daroussin 		execv(_PATH_BSHELL, argv);
641e3d86717SJuli Mallett 		exit(1);
642e3d86717SJuli Mallett 	default:
643e3d86717SJuli Mallett 		/* Read result in two stages, since m4's buffer is
644e3d86717SJuli Mallett 		 * pushback-only. */
645e3d86717SJuli Mallett 		(void) close(p[1]);
646e3d86717SJuli Mallett 		do {
647e3d86717SJuli Mallett 			char result[BUFSIZE];
648e3d86717SJuli Mallett 			cc = read(p[0], result, sizeof result);
649e3d86717SJuli Mallett 			if (cc > 0)
650e3d86717SJuli Mallett 				addchars(result, cc);
651e3d86717SJuli Mallett 		} while (cc > 0 || (cc == -1 && errno == EINTR));
652e3d86717SJuli Mallett 
653e3d86717SJuli Mallett 		(void) close(p[0]);
654e3d86717SJuli Mallett 		while ((pid = wait(&status)) != cpid && pid >= 0)
655e3d86717SJuli Mallett 			continue;
656e3d86717SJuli Mallett 		pbstr(getstring());
657e3d86717SJuli Mallett 	}
658e3d86717SJuli Mallett }
659a841e1ebSBaptiste Daroussin 
660a841e1ebSBaptiste Daroussin void
661a841e1ebSBaptiste Daroussin getdivfile(const char *name)
662a841e1ebSBaptiste Daroussin {
663a841e1ebSBaptiste Daroussin 	FILE *f;
664a841e1ebSBaptiste Daroussin 	int c;
665a841e1ebSBaptiste Daroussin 
666a841e1ebSBaptiste Daroussin 	f = fopen(name, "r");
667a841e1ebSBaptiste Daroussin 	if (!f)
668a841e1ebSBaptiste Daroussin 		return;
669a841e1ebSBaptiste Daroussin 
670a841e1ebSBaptiste Daroussin 	while ((c = getc(f))!= EOF)
671a841e1ebSBaptiste Daroussin 		putc(c, active);
672a841e1ebSBaptiste Daroussin 	(void) fclose(f);
673a841e1ebSBaptiste Daroussin }
674