1*fdb905e2SBaptiste Daroussin /* $OpenBSD: gnum4.c,v 1.52 2017/08/21 21:41:13 deraadt Exp $ */
2e3d86717SJuli Mallett
31de7b4b8SPedro F. Giffuni /*-
41de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause
51de7b4b8SPedro F. Giffuni *
6e3d86717SJuli Mallett * Copyright (c) 1999 Marc Espie
7e3d86717SJuli Mallett *
8e3d86717SJuli Mallett * Redistribution and use in source and binary forms, with or without
9e3d86717SJuli Mallett * modification, are permitted provided that the following conditions
10e3d86717SJuli Mallett * are met:
11e3d86717SJuli Mallett * 1. Redistributions of source code must retain the above copyright
12e3d86717SJuli Mallett * notice, this list of conditions and the following disclaimer.
13e3d86717SJuli Mallett * 2. Redistributions in binary form must reproduce the above copyright
14e3d86717SJuli Mallett * notice, this list of conditions and the following disclaimer in the
15e3d86717SJuli Mallett * documentation and/or other materials provided with the distribution.
16e3d86717SJuli Mallett *
17e3d86717SJuli Mallett * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18e3d86717SJuli Mallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19e3d86717SJuli Mallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20e3d86717SJuli Mallett * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21e3d86717SJuli Mallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22e3d86717SJuli Mallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23e3d86717SJuli Mallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24e3d86717SJuli Mallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25e3d86717SJuli Mallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26e3d86717SJuli Mallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27e3d86717SJuli Mallett * SUCH DAMAGE.
28e3d86717SJuli Mallett */
2956ca2b35SJuli Mallett #include <sys/cdefs.h>
30e3d86717SJuli Mallett /*
31e3d86717SJuli Mallett * functions needed to support gnu-m4 extensions, including a fake freezing
32e3d86717SJuli Mallett */
33e3d86717SJuli Mallett
34e3d86717SJuli Mallett #include <sys/types.h>
35e3d86717SJuli Mallett #include <sys/wait.h>
36e3d86717SJuli Mallett #include <ctype.h>
37a841e1ebSBaptiste Daroussin #include <err.h>
38e3d86717SJuli Mallett #include <paths.h>
39e3d86717SJuli Mallett #include <regex.h>
40c560b67cSBaptiste Daroussin #include <stdarg.h>
41e3d86717SJuli Mallett #include <stddef.h>
42e3d86717SJuli Mallett #include <stdlib.h>
4331129d4fSBaptiste Daroussin #include <stdint.h>
44e3d86717SJuli Mallett #include <stdio.h>
45e3d86717SJuli Mallett #include <string.h>
46e3d86717SJuli Mallett #include <errno.h>
47e3d86717SJuli Mallett #include <unistd.h>
4831129d4fSBaptiste Daroussin #include <limits.h>
49e3d86717SJuli Mallett #include "mdef.h"
50e3d86717SJuli Mallett #include "stdd.h"
51e3d86717SJuli Mallett #include "extern.h"
52e3d86717SJuli Mallett
53e3d86717SJuli Mallett
54e3d86717SJuli Mallett int mimic_gnu = 0;
55e3d86717SJuli Mallett
56e3d86717SJuli Mallett /*
57e3d86717SJuli Mallett * Support for include path search
586bccea7cSRebecca Cran * First search in the current directory.
59e3d86717SJuli Mallett * If not found, and the path is not absolute, include path kicks in.
60e3d86717SJuli Mallett * First, -I options, in the order found on the command line.
61e3d86717SJuli Mallett * Then M4PATH env variable
62e3d86717SJuli Mallett */
63e3d86717SJuli Mallett
64eccad222SEd Schouten static struct path_entry {
65e3d86717SJuli Mallett char *name;
66e3d86717SJuli Mallett struct path_entry *next;
67e3d86717SJuli Mallett } *first, *last;
68e3d86717SJuli Mallett
69e3d86717SJuli Mallett static struct path_entry *new_path_entry(const char *);
70e3d86717SJuli Mallett static void ensure_m4path(void);
71e3d86717SJuli Mallett static struct input_file *dopath(struct input_file *, const char *);
72e3d86717SJuli Mallett
73e3d86717SJuli Mallett static struct path_entry *
new_path_entry(const char * dirname)74bd2bfb58SJuli Mallett new_path_entry(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");
8131129d4fSBaptiste Daroussin n->name = xstrdup(dirname);
82e3d86717SJuli Mallett n->next = 0;
83e3d86717SJuli Mallett return n;
84e3d86717SJuli Mallett }
85e3d86717SJuli Mallett
86e3d86717SJuli Mallett void
addtoincludepath(const char * dirname)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
ensure_m4path(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 */
11631129d4fSBaptiste Daroussin envpath = xstrdup(envpath);
117e3d86717SJuli Mallett for (sweep = envpath;
118e3d86717SJuli Mallett (path = strsep(&sweep, ":")) != NULL;)
119e3d86717SJuli Mallett addtoincludepath(path);
120e3d86717SJuli Mallett free(envpath);
121e3d86717SJuli Mallett }
122e3d86717SJuli Mallett
123e3d86717SJuli Mallett static
124e3d86717SJuli Mallett struct input_file *
dopath(struct input_file * i,const char * filename)125bd2bfb58SJuli Mallett dopath(struct input_file *i, const char *filename)
126e3d86717SJuli Mallett {
12731129d4fSBaptiste Daroussin char path[PATH_MAX];
128e3d86717SJuli Mallett struct path_entry *pe;
129e3d86717SJuli Mallett FILE *f;
130e3d86717SJuli Mallett
131e3d86717SJuli Mallett for (pe = first; pe; pe = pe->next) {
132e3d86717SJuli Mallett snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
1331b56693fSMarcelo Araujo if ((f = fopen(path, "r")) != NULL) {
134e3d86717SJuli Mallett set_input(i, f, path);
135e3d86717SJuli Mallett return i;
136e3d86717SJuli Mallett }
137e3d86717SJuli Mallett }
138e3d86717SJuli Mallett return NULL;
139e3d86717SJuli Mallett }
140e3d86717SJuli Mallett
141e3d86717SJuli Mallett struct input_file *
fopen_trypath(struct input_file * i,const char * filename)142bd2bfb58SJuli Mallett fopen_trypath(struct input_file *i, const char *filename)
143e3d86717SJuli Mallett {
144e3d86717SJuli Mallett FILE *f;
145e3d86717SJuli Mallett
146e3d86717SJuli Mallett f = fopen(filename, "r");
147e3d86717SJuli Mallett if (f != NULL) {
148e3d86717SJuli Mallett set_input(i, f, filename);
149e3d86717SJuli Mallett return i;
150e3d86717SJuli Mallett }
151e3d86717SJuli Mallett if (filename[0] == '/')
152e3d86717SJuli Mallett return NULL;
153e3d86717SJuli Mallett
154e3d86717SJuli Mallett ensure_m4path();
155e3d86717SJuli Mallett
156e3d86717SJuli Mallett return dopath(i, filename);
157e3d86717SJuli Mallett }
158e3d86717SJuli Mallett
159e3d86717SJuli Mallett void
doindir(const char * argv[],int argc)160bd2bfb58SJuli Mallett doindir(const char *argv[], int argc)
161e3d86717SJuli Mallett {
162a841e1ebSBaptiste Daroussin ndptr n;
163a841e1ebSBaptiste Daroussin struct macro_definition *p = NULL;
164e3d86717SJuli Mallett
165a841e1ebSBaptiste Daroussin n = lookup(argv[2]);
166a841e1ebSBaptiste Daroussin if (n == NULL || (p = macro_getdef(n)) == NULL)
167a841e1ebSBaptiste Daroussin m4errx(1, "indir: undefined macro %s.", argv[2]);
168e3d86717SJuli Mallett argv[1] = p->defn;
169a841e1ebSBaptiste Daroussin
170a841e1ebSBaptiste Daroussin eval(argv+1, argc-1, p->type, is_traced(n));
171e3d86717SJuli Mallett }
172e3d86717SJuli Mallett
173e3d86717SJuli Mallett void
dobuiltin(const char * argv[],int argc)174bd2bfb58SJuli Mallett dobuiltin(const char *argv[], int argc)
175e3d86717SJuli Mallett {
176a841e1ebSBaptiste Daroussin ndptr p;
177a841e1ebSBaptiste Daroussin
178e3d86717SJuli Mallett argv[1] = NULL;
179a841e1ebSBaptiste Daroussin p = macro_getbuiltin(argv[2]);
180a841e1ebSBaptiste Daroussin if (p != NULL)
181a841e1ebSBaptiste Daroussin eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p));
182e3d86717SJuli Mallett else
183a841e1ebSBaptiste Daroussin m4errx(1, "unknown builtin %s.", argv[2]);
184e3d86717SJuli Mallett }
185e3d86717SJuli Mallett
186e3d86717SJuli Mallett
187e3d86717SJuli Mallett /* We need some temporary buffer space, as pb pushes BACK and substitution
188e3d86717SJuli Mallett * proceeds forward... */
189e3d86717SJuli Mallett static char *buffer;
190e3d86717SJuli Mallett static size_t bufsize = 0;
191e3d86717SJuli Mallett static size_t current = 0;
192e3d86717SJuli Mallett
193e3d86717SJuli Mallett static void addchars(const char *, size_t);
194bd2bfb58SJuli Mallett static void addchar(int);
195e3d86717SJuli Mallett static char *twiddle(const char *);
196e3d86717SJuli Mallett static char *getstring(void);
19788497f0cSBaptiste Daroussin static void exit_regerror(int, regex_t *, const char *);
19888497f0cSBaptiste Daroussin static void do_subst(const char *, regex_t *, const char *, const char *,
19988497f0cSBaptiste Daroussin regmatch_t *);
20088497f0cSBaptiste Daroussin static void do_regexpindex(const char *, regex_t *, const char *, regmatch_t *);
20188497f0cSBaptiste Daroussin static void do_regexp(const char *, regex_t *, const char *, const char *,
20288497f0cSBaptiste Daroussin regmatch_t *);
203a841e1ebSBaptiste Daroussin static void add_sub(int, const char *, regex_t *, regmatch_t *);
204e3d86717SJuli Mallett static void add_replace(const char *, regex_t *, const char *, regmatch_t *);
205e3d86717SJuli Mallett #define addconstantstring(s) addchars((s), sizeof(s)-1)
206e3d86717SJuli Mallett
207e3d86717SJuli Mallett static void
addchars(const char * c,size_t n)208bd2bfb58SJuli Mallett addchars(const char *c, size_t n)
209e3d86717SJuli Mallett {
210e3d86717SJuli Mallett if (n == 0)
211e3d86717SJuli Mallett return;
212e3d86717SJuli Mallett while (current + n > bufsize) {
213e3d86717SJuli Mallett if (bufsize == 0)
214e3d86717SJuli Mallett bufsize = 1024;
21531129d4fSBaptiste Daroussin else if (bufsize <= SIZE_MAX/2) {
216e3d86717SJuli Mallett bufsize *= 2;
21731129d4fSBaptiste Daroussin } else {
21831129d4fSBaptiste Daroussin errx(1, "size overflow");
21931129d4fSBaptiste Daroussin }
220a841e1ebSBaptiste Daroussin buffer = xrealloc(buffer, bufsize, NULL);
221e3d86717SJuli Mallett }
222e3d86717SJuli Mallett memcpy(buffer+current, c, n);
223e3d86717SJuli Mallett current += n;
224e3d86717SJuli Mallett }
225e3d86717SJuli Mallett
226e3d86717SJuli Mallett static void
addchar(int c)227bd2bfb58SJuli Mallett addchar(int c)
228e3d86717SJuli Mallett {
229e3d86717SJuli Mallett if (current +1 > bufsize) {
230e3d86717SJuli Mallett if (bufsize == 0)
231e3d86717SJuli Mallett bufsize = 1024;
232e3d86717SJuli Mallett else
233e3d86717SJuli Mallett bufsize *= 2;
234a841e1ebSBaptiste Daroussin buffer = xrealloc(buffer, bufsize, NULL);
235e3d86717SJuli Mallett }
236e3d86717SJuli Mallett buffer[current++] = c;
237e3d86717SJuli Mallett }
238e3d86717SJuli Mallett
239e3d86717SJuli Mallett static char *
getstring(void)240d1fea89cSJuli Mallett getstring(void)
241e3d86717SJuli Mallett {
242e3d86717SJuli Mallett addchar('\0');
243e3d86717SJuli Mallett current = 0;
244e3d86717SJuli Mallett return buffer;
245e3d86717SJuli Mallett }
246e3d86717SJuli Mallett
247e3d86717SJuli Mallett
248e3d86717SJuli Mallett static void
exit_regerror(int er,regex_t * re,const char * source)24988497f0cSBaptiste Daroussin exit_regerror(int er, regex_t *re, const char *source)
250e3d86717SJuli Mallett {
251e3d86717SJuli Mallett size_t errlen;
252e3d86717SJuli Mallett char *errbuf;
253e3d86717SJuli Mallett
254e3d86717SJuli Mallett errlen = regerror(er, re, NULL, 0);
255a841e1ebSBaptiste Daroussin errbuf = xalloc(errlen,
256a841e1ebSBaptiste Daroussin "malloc in regerror: %lu", (unsigned long)errlen);
257e3d86717SJuli Mallett regerror(er, re, errbuf, errlen);
25888497f0cSBaptiste Daroussin m4errx(1, "regular expression error in %s: %s.", source, errbuf);
259e3d86717SJuli Mallett }
260e3d86717SJuli Mallett
261c560b67cSBaptiste Daroussin /* warnx() plus check to see if we need to change exit code or exit .
262c560b67cSBaptiste Daroussin * -E flag functionality.
263c560b67cSBaptiste Daroussin */
264c560b67cSBaptiste Daroussin void
m4_warnx(const char * fmt,...)265c560b67cSBaptiste Daroussin m4_warnx(const char *fmt, ...)
266c560b67cSBaptiste Daroussin {
267c560b67cSBaptiste Daroussin va_list ap;
268c560b67cSBaptiste Daroussin
269c560b67cSBaptiste Daroussin va_start(ap, fmt);
270c560b67cSBaptiste Daroussin warnx(fmt, ap);
271c560b67cSBaptiste Daroussin va_end(ap);
272c560b67cSBaptiste Daroussin
273c560b67cSBaptiste Daroussin if (fatal_warns)
274c560b67cSBaptiste Daroussin exit(1);
275c560b67cSBaptiste Daroussin if (error_warns)
276c560b67cSBaptiste Daroussin exit_code = 1;
277c560b67cSBaptiste Daroussin }
278c560b67cSBaptiste Daroussin
279e3d86717SJuli Mallett static void
add_sub(int n,const char * string,regex_t * re,regmatch_t * pm)280a841e1ebSBaptiste Daroussin add_sub(int n, const char *string, regex_t *re, regmatch_t *pm)
281e3d86717SJuli Mallett {
282a841e1ebSBaptiste Daroussin if (n > (int)re->re_nsub)
283c560b67cSBaptiste Daroussin m4_warnx("No subexpression %d", n);
284e3d86717SJuli Mallett /* Subexpressions that did not match are
285e3d86717SJuli Mallett * not an error. */
286e3d86717SJuli Mallett else if (pm[n].rm_so != -1 &&
287e3d86717SJuli Mallett pm[n].rm_eo != -1) {
288e3d86717SJuli Mallett addchars(string + pm[n].rm_so,
289e3d86717SJuli Mallett pm[n].rm_eo - pm[n].rm_so);
290e3d86717SJuli Mallett }
291e3d86717SJuli Mallett }
292e3d86717SJuli Mallett
293e3d86717SJuli Mallett /* Add replacement string to the output buffer, recognizing special
294e3d86717SJuli Mallett * constructs and replacing them with substrings of the original string.
295e3d86717SJuli Mallett */
296e3d86717SJuli Mallett static void
add_replace(const char * string,regex_t * re,const char * replace,regmatch_t * pm)297bd2bfb58SJuli Mallett add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
298e3d86717SJuli Mallett {
299e3d86717SJuli Mallett const char *p;
300e3d86717SJuli Mallett
301e3d86717SJuli Mallett for (p = replace; *p != '\0'; p++) {
302e3d86717SJuli Mallett if (*p == '&' && !mimic_gnu) {
303e3d86717SJuli Mallett add_sub(0, string, re, pm);
304e3d86717SJuli Mallett continue;
305e3d86717SJuli Mallett }
306e3d86717SJuli Mallett if (*p == '\\') {
307e3d86717SJuli Mallett if (p[1] == '\\') {
308e3d86717SJuli Mallett addchar(p[1]);
309e3d86717SJuli Mallett p++;
310e3d86717SJuli Mallett continue;
311e3d86717SJuli Mallett }
312e3d86717SJuli Mallett if (p[1] == '&') {
313e3d86717SJuli Mallett if (mimic_gnu)
314e3d86717SJuli Mallett add_sub(0, string, re, pm);
315e3d86717SJuli Mallett else
316e3d86717SJuli Mallett addchar(p[1]);
317e3d86717SJuli Mallett p++;
318e3d86717SJuli Mallett continue;
319e3d86717SJuli Mallett }
32088497f0cSBaptiste Daroussin if (isdigit((unsigned char)p[1])) {
321e3d86717SJuli Mallett add_sub(*(++p) - '0', string, re, pm);
322e3d86717SJuli Mallett continue;
323e3d86717SJuli Mallett }
324e3d86717SJuli Mallett }
325e3d86717SJuli Mallett addchar(*p);
326e3d86717SJuli Mallett }
327e3d86717SJuli Mallett }
328e3d86717SJuli Mallett
329e3d86717SJuli Mallett static void
do_subst(const char * string,regex_t * re,const char * source,const char * replace,regmatch_t * pm)33088497f0cSBaptiste Daroussin do_subst(const char *string, regex_t *re, const char *source,
33188497f0cSBaptiste Daroussin const char *replace, regmatch_t *pm)
332e3d86717SJuli Mallett {
333e3d86717SJuli Mallett int error;
334e3d86717SJuli Mallett int flags = 0;
335e3d86717SJuli Mallett const char *last_match = NULL;
336e3d86717SJuli Mallett
337e3d86717SJuli Mallett while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
338e3d86717SJuli Mallett if (pm[0].rm_eo != 0) {
339e3d86717SJuli Mallett if (string[pm[0].rm_eo-1] == '\n')
340e3d86717SJuli Mallett flags = 0;
341e3d86717SJuli Mallett else
342e3d86717SJuli Mallett flags = REG_NOTBOL;
343e3d86717SJuli Mallett }
344e3d86717SJuli Mallett
345e3d86717SJuli Mallett /* NULL length matches are special... We use the `vi-mode'
346e3d86717SJuli Mallett * rule: don't allow a NULL-match at the last match
347e3d86717SJuli Mallett * position.
348e3d86717SJuli Mallett */
349e3d86717SJuli Mallett if (pm[0].rm_so == pm[0].rm_eo &&
350e3d86717SJuli Mallett string + pm[0].rm_so == last_match) {
351e3d86717SJuli Mallett if (*string == '\0')
352e3d86717SJuli Mallett return;
353e3d86717SJuli Mallett addchar(*string);
354e3d86717SJuli Mallett if (*string++ == '\n')
355e3d86717SJuli Mallett flags = 0;
356e3d86717SJuli Mallett else
357e3d86717SJuli Mallett flags = REG_NOTBOL;
358e3d86717SJuli Mallett continue;
359e3d86717SJuli Mallett }
360e3d86717SJuli Mallett last_match = string + pm[0].rm_so;
361e3d86717SJuli Mallett addchars(string, pm[0].rm_so);
362e3d86717SJuli Mallett add_replace(string, re, replace, pm);
363e3d86717SJuli Mallett string += pm[0].rm_eo;
364e3d86717SJuli Mallett }
365e3d86717SJuli Mallett if (error != REG_NOMATCH)
36688497f0cSBaptiste Daroussin exit_regerror(error, re, source);
367e3d86717SJuli Mallett pbstr(string);
368e3d86717SJuli Mallett }
369e3d86717SJuli Mallett
370e3d86717SJuli Mallett static void
do_regexp(const char * string,regex_t * re,const char * source,const char * replace,regmatch_t * pm)37188497f0cSBaptiste Daroussin do_regexp(const char *string, regex_t *re, const char *source,
37288497f0cSBaptiste Daroussin const char *replace, regmatch_t *pm)
373e3d86717SJuli Mallett {
374e3d86717SJuli Mallett int error;
375e3d86717SJuli Mallett
376e3d86717SJuli Mallett switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
377e3d86717SJuli Mallett case 0:
378e3d86717SJuli Mallett add_replace(string, re, replace, pm);
379e3d86717SJuli Mallett pbstr(getstring());
380e3d86717SJuli Mallett break;
381e3d86717SJuli Mallett case REG_NOMATCH:
382e3d86717SJuli Mallett break;
383e3d86717SJuli Mallett default:
38488497f0cSBaptiste Daroussin exit_regerror(error, re, source);
385e3d86717SJuli Mallett }
386e3d86717SJuli Mallett }
387e3d86717SJuli Mallett
388e3d86717SJuli Mallett static void
do_regexpindex(const char * string,regex_t * re,const char * source,regmatch_t * pm)38988497f0cSBaptiste Daroussin do_regexpindex(const char *string, regex_t *re, const char *source,
39088497f0cSBaptiste Daroussin regmatch_t *pm)
391e3d86717SJuli Mallett {
392e3d86717SJuli Mallett int error;
393e3d86717SJuli Mallett
394e3d86717SJuli Mallett switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
395e3d86717SJuli Mallett case 0:
396e3d86717SJuli Mallett pbunsigned(pm[0].rm_so);
397e3d86717SJuli Mallett break;
398e3d86717SJuli Mallett case REG_NOMATCH:
399e3d86717SJuli Mallett pbnum(-1);
400e3d86717SJuli Mallett break;
401e3d86717SJuli Mallett default:
40288497f0cSBaptiste Daroussin exit_regerror(error, re, source);
403e3d86717SJuli Mallett }
404e3d86717SJuli Mallett }
405e3d86717SJuli Mallett
406e3d86717SJuli Mallett /* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
407e3d86717SJuli Mallett * says. So we twiddle with the regexp before passing it to regcomp.
408e3d86717SJuli Mallett */
409e3d86717SJuli Mallett static char *
twiddle(const char * p)410bd2bfb58SJuli Mallett twiddle(const char *p)
411e3d86717SJuli Mallett {
412a841e1ebSBaptiste Daroussin /* + at start of regexp is a normal character for Gnu m4 */
413a841e1ebSBaptiste Daroussin if (*p == '^') {
414a841e1ebSBaptiste Daroussin addchar(*p);
415a841e1ebSBaptiste Daroussin p++;
416a841e1ebSBaptiste Daroussin }
417a841e1ebSBaptiste Daroussin if (*p == '+') {
418a841e1ebSBaptiste Daroussin addchar('\\');
419a841e1ebSBaptiste Daroussin }
420e3d86717SJuli Mallett /* This could use strcspn for speed... */
421e3d86717SJuli Mallett while (*p != '\0') {
422e3d86717SJuli Mallett if (*p == '\\') {
423e3d86717SJuli Mallett switch(p[1]) {
424e3d86717SJuli Mallett case '(':
425e3d86717SJuli Mallett case ')':
426e3d86717SJuli Mallett case '|':
427e3d86717SJuli Mallett addchar(p[1]);
428e3d86717SJuli Mallett break;
429e3d86717SJuli Mallett case 'w':
430e3d86717SJuli Mallett addconstantstring("[_a-zA-Z0-9]");
431e3d86717SJuli Mallett break;
432e3d86717SJuli Mallett case 'W':
433e3d86717SJuli Mallett addconstantstring("[^_a-zA-Z0-9]");
434e3d86717SJuli Mallett break;
435e3d86717SJuli Mallett case '<':
436e3d86717SJuli Mallett addconstantstring("[[:<:]]");
437e3d86717SJuli Mallett break;
438e3d86717SJuli Mallett case '>':
439e3d86717SJuli Mallett addconstantstring("[[:>:]]");
440e3d86717SJuli Mallett break;
441e3d86717SJuli Mallett default:
442e3d86717SJuli Mallett addchars(p, 2);
443e3d86717SJuli Mallett break;
444e3d86717SJuli Mallett }
445e3d86717SJuli Mallett p+=2;
446e3d86717SJuli Mallett continue;
447e3d86717SJuli Mallett }
448e3d86717SJuli Mallett if (*p == '(' || *p == ')' || *p == '|')
449e3d86717SJuli Mallett addchar('\\');
450e3d86717SJuli Mallett
451e3d86717SJuli Mallett addchar(*p);
452e3d86717SJuli Mallett p++;
453e3d86717SJuli Mallett }
454e3d86717SJuli Mallett return getstring();
455e3d86717SJuli Mallett }
456e3d86717SJuli Mallett
457e3d86717SJuli Mallett /* patsubst(string, regexp, opt replacement) */
458e3d86717SJuli Mallett /* argv[2]: string
459e3d86717SJuli Mallett * argv[3]: regexp
460e3d86717SJuli Mallett * argv[4]: opt rep
461e3d86717SJuli Mallett */
462e3d86717SJuli Mallett void
dopatsubst(const char * argv[],int argc)463bd2bfb58SJuli Mallett dopatsubst(const char *argv[], int argc)
464e3d86717SJuli Mallett {
465e3d86717SJuli Mallett if (argc <= 3) {
466c560b67cSBaptiste Daroussin m4_warnx("Too few arguments to patsubst");
467e3d86717SJuli Mallett return;
468e3d86717SJuli Mallett }
469a841e1ebSBaptiste Daroussin /* special case: empty regexp */
470a841e1ebSBaptiste Daroussin if (argv[3][0] == '\0') {
471a841e1ebSBaptiste Daroussin const char *s;
472a841e1ebSBaptiste Daroussin size_t len;
473a841e1ebSBaptiste Daroussin if (argc > 4 && argv[4])
474a841e1ebSBaptiste Daroussin len = strlen(argv[4]);
475a841e1ebSBaptiste Daroussin else
476a841e1ebSBaptiste Daroussin len = 0;
477a841e1ebSBaptiste Daroussin for (s = argv[2]; *s != '\0'; s++) {
478a841e1ebSBaptiste Daroussin addchars(argv[4], len);
479a841e1ebSBaptiste Daroussin addchar(*s);
480a841e1ebSBaptiste Daroussin }
481a841e1ebSBaptiste Daroussin } else {
482a841e1ebSBaptiste Daroussin int error;
483a841e1ebSBaptiste Daroussin regex_t re;
484a841e1ebSBaptiste Daroussin regmatch_t *pmatch;
485a841e1ebSBaptiste Daroussin int mode = REG_EXTENDED;
48688497f0cSBaptiste Daroussin const char *source;
487a841e1ebSBaptiste Daroussin size_t l = strlen(argv[3]);
488a841e1ebSBaptiste Daroussin
489a841e1ebSBaptiste Daroussin if (!mimic_gnu ||
490a841e1ebSBaptiste Daroussin (argv[3][0] == '^') ||
491a841e1ebSBaptiste Daroussin (l > 0 && argv[3][l-1] == '$'))
492a841e1ebSBaptiste Daroussin mode |= REG_NEWLINE;
493a841e1ebSBaptiste Daroussin
49488497f0cSBaptiste Daroussin source = mimic_gnu ? twiddle(argv[3]) : argv[3];
49588497f0cSBaptiste Daroussin error = regcomp(&re, source, mode);
496e3d86717SJuli Mallett if (error != 0)
49788497f0cSBaptiste Daroussin exit_regerror(error, &re, source);
498e3d86717SJuli Mallett
49988497f0cSBaptiste Daroussin pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t),
50088497f0cSBaptiste Daroussin NULL);
50188497f0cSBaptiste Daroussin do_subst(argv[2], &re, source,
502a841e1ebSBaptiste Daroussin argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch);
503e3d86717SJuli Mallett free(pmatch);
504e3d86717SJuli Mallett regfree(&re);
505e3d86717SJuli Mallett }
506a841e1ebSBaptiste Daroussin pbstr(getstring());
507a841e1ebSBaptiste Daroussin }
508e3d86717SJuli Mallett
509e3d86717SJuli Mallett void
doregexp(const char * argv[],int argc)510bd2bfb58SJuli Mallett doregexp(const char *argv[], int argc)
511e3d86717SJuli Mallett {
512e3d86717SJuli Mallett int error;
513e3d86717SJuli Mallett regex_t re;
514e3d86717SJuli Mallett regmatch_t *pmatch;
51588497f0cSBaptiste Daroussin const char *source;
516e3d86717SJuli Mallett
517e3d86717SJuli Mallett if (argc <= 3) {
518c560b67cSBaptiste Daroussin m4_warnx("Too few arguments to regexp");
519e3d86717SJuli Mallett return;
520e3d86717SJuli Mallett }
521a841e1ebSBaptiste Daroussin /* special gnu case */
522a841e1ebSBaptiste Daroussin if (argv[3][0] == '\0' && mimic_gnu) {
523a841e1ebSBaptiste Daroussin if (argc == 4 || argv[4] == NULL)
524a841e1ebSBaptiste Daroussin return;
525a841e1ebSBaptiste Daroussin else
526a841e1ebSBaptiste Daroussin pbstr(argv[4]);
527a841e1ebSBaptiste Daroussin }
52888497f0cSBaptiste Daroussin source = mimic_gnu ? twiddle(argv[3]) : argv[3];
52988497f0cSBaptiste Daroussin error = regcomp(&re, source, REG_EXTENDED|REG_NEWLINE);
530e3d86717SJuli Mallett if (error != 0)
53188497f0cSBaptiste Daroussin exit_regerror(error, &re, source);
532e3d86717SJuli Mallett
53388497f0cSBaptiste Daroussin pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t), NULL);
534a841e1ebSBaptiste Daroussin if (argc == 4 || argv[4] == NULL)
53588497f0cSBaptiste Daroussin do_regexpindex(argv[2], &re, source, pmatch);
536e3d86717SJuli Mallett else
53788497f0cSBaptiste Daroussin do_regexp(argv[2], &re, source, argv[4], pmatch);
538e3d86717SJuli Mallett free(pmatch);
539e3d86717SJuli Mallett regfree(&re);
540e3d86717SJuli Mallett }
541e3d86717SJuli Mallett
542e3d86717SJuli Mallett void
doformat(const char * argv[],int argc)543a841e1ebSBaptiste Daroussin doformat(const char *argv[], int argc)
544a841e1ebSBaptiste Daroussin {
545a841e1ebSBaptiste Daroussin const char *format = argv[2];
546a841e1ebSBaptiste Daroussin int pos = 3;
547a841e1ebSBaptiste Daroussin int left_padded;
548a841e1ebSBaptiste Daroussin long width;
549a841e1ebSBaptiste Daroussin size_t l;
550a841e1ebSBaptiste Daroussin const char *thisarg = NULL;
551a841e1ebSBaptiste Daroussin char temp[2];
552a841e1ebSBaptiste Daroussin long extra;
553a841e1ebSBaptiste Daroussin
554a841e1ebSBaptiste Daroussin while (*format != 0) {
555a841e1ebSBaptiste Daroussin if (*format != '%') {
556a841e1ebSBaptiste Daroussin addchar(*format++);
557a841e1ebSBaptiste Daroussin continue;
558a841e1ebSBaptiste Daroussin }
559a841e1ebSBaptiste Daroussin
560a841e1ebSBaptiste Daroussin format++;
561a841e1ebSBaptiste Daroussin if (*format == '%') {
562a841e1ebSBaptiste Daroussin addchar(*format++);
563a841e1ebSBaptiste Daroussin continue;
564a841e1ebSBaptiste Daroussin }
565a841e1ebSBaptiste Daroussin if (*format == 0) {
566a841e1ebSBaptiste Daroussin addchar('%');
567a841e1ebSBaptiste Daroussin break;
568a841e1ebSBaptiste Daroussin }
569a841e1ebSBaptiste Daroussin
570a841e1ebSBaptiste Daroussin if (*format == '*') {
571a841e1ebSBaptiste Daroussin format++;
572a841e1ebSBaptiste Daroussin if (pos >= argc)
573a841e1ebSBaptiste Daroussin m4errx(1,
574a841e1ebSBaptiste Daroussin "Format with too many format specifiers.");
575a841e1ebSBaptiste Daroussin width = strtol(argv[pos++], NULL, 10);
576a841e1ebSBaptiste Daroussin } else {
577a841e1ebSBaptiste Daroussin width = strtol(format, __DECONST(char **,&format), 10);
578a841e1ebSBaptiste Daroussin }
579a841e1ebSBaptiste Daroussin if (width < 0) {
580a841e1ebSBaptiste Daroussin left_padded = 1;
581a841e1ebSBaptiste Daroussin width = -width;
582a841e1ebSBaptiste Daroussin } else {
583a841e1ebSBaptiste Daroussin left_padded = 0;
584a841e1ebSBaptiste Daroussin }
585a841e1ebSBaptiste Daroussin if (*format == '.') {
586a841e1ebSBaptiste Daroussin format++;
587a841e1ebSBaptiste Daroussin if (*format == '*') {
588a841e1ebSBaptiste Daroussin format++;
589a841e1ebSBaptiste Daroussin if (pos >= argc)
590a841e1ebSBaptiste Daroussin m4errx(1,
591a841e1ebSBaptiste Daroussin "Format with too many format specifiers.");
592a841e1ebSBaptiste Daroussin extra = strtol(argv[pos++], NULL, 10);
593a841e1ebSBaptiste Daroussin } else {
594a841e1ebSBaptiste Daroussin extra = strtol(format, __DECONST(char **, &format), 10);
595a841e1ebSBaptiste Daroussin }
596a841e1ebSBaptiste Daroussin } else {
597a841e1ebSBaptiste Daroussin extra = LONG_MAX;
598a841e1ebSBaptiste Daroussin }
599a841e1ebSBaptiste Daroussin if (pos >= argc)
600a841e1ebSBaptiste Daroussin m4errx(1, "Format with too many format specifiers.");
601a841e1ebSBaptiste Daroussin switch(*format) {
602a841e1ebSBaptiste Daroussin case 's':
603a841e1ebSBaptiste Daroussin thisarg = argv[pos++];
604a841e1ebSBaptiste Daroussin break;
605a841e1ebSBaptiste Daroussin case 'c':
606a841e1ebSBaptiste Daroussin temp[0] = strtoul(argv[pos++], NULL, 10);
607a841e1ebSBaptiste Daroussin temp[1] = 0;
608a841e1ebSBaptiste Daroussin thisarg = temp;
609a841e1ebSBaptiste Daroussin break;
610a841e1ebSBaptiste Daroussin default:
611a841e1ebSBaptiste Daroussin m4errx(1, "Unsupported format specification: %s.",
612a841e1ebSBaptiste Daroussin argv[2]);
613a841e1ebSBaptiste Daroussin }
614a841e1ebSBaptiste Daroussin format++;
615a841e1ebSBaptiste Daroussin l = strlen(thisarg);
616a841e1ebSBaptiste Daroussin if ((long)l > extra)
617a841e1ebSBaptiste Daroussin l = extra;
618a841e1ebSBaptiste Daroussin if (!left_padded) {
619a841e1ebSBaptiste Daroussin while ((long)l < width--)
620a841e1ebSBaptiste Daroussin addchar(' ');
621a841e1ebSBaptiste Daroussin }
622a841e1ebSBaptiste Daroussin addchars(thisarg, l);
623a841e1ebSBaptiste Daroussin if (left_padded) {
624a841e1ebSBaptiste Daroussin while ((long)l < width--)
625a841e1ebSBaptiste Daroussin addchar(' ');
626a841e1ebSBaptiste Daroussin }
627a841e1ebSBaptiste Daroussin }
628a841e1ebSBaptiste Daroussin pbstr(getstring());
629a841e1ebSBaptiste Daroussin }
630a841e1ebSBaptiste Daroussin
631a841e1ebSBaptiste Daroussin void
doesyscmd(const char * cmd)632bd2bfb58SJuli Mallett doesyscmd(const char *cmd)
633e3d86717SJuli Mallett {
634e3d86717SJuli Mallett int p[2];
635*fdb905e2SBaptiste Daroussin pid_t cpid;
636a841e1ebSBaptiste Daroussin char *argv[4];
637e3d86717SJuli Mallett int cc;
638e3d86717SJuli Mallett int status;
639e3d86717SJuli Mallett
640e3d86717SJuli Mallett /* Follow gnu m4 documentation: first flush buffers. */
641e3d86717SJuli Mallett fflush(NULL);
642e3d86717SJuli Mallett
643a841e1ebSBaptiste Daroussin argv[0] = __DECONST(char *, "sh");
644a841e1ebSBaptiste Daroussin argv[1] = __DECONST(char *, "-c");
645a841e1ebSBaptiste Daroussin argv[2] = __DECONST(char *, cmd);
646a841e1ebSBaptiste Daroussin argv[3] = NULL;
647a841e1ebSBaptiste Daroussin
648e3d86717SJuli Mallett /* Just set up standard output, share stderr and stdin with m4 */
649e3d86717SJuli Mallett if (pipe(p) == -1)
650e3d86717SJuli Mallett err(1, "bad pipe");
651e3d86717SJuli Mallett switch(cpid = fork()) {
652e3d86717SJuli Mallett case -1:
653e3d86717SJuli Mallett err(1, "bad fork");
654e3d86717SJuli Mallett /* NOTREACHED */
655e3d86717SJuli Mallett case 0:
656e3d86717SJuli Mallett (void) close(p[0]);
657e3d86717SJuli Mallett (void) dup2(p[1], 1);
658e3d86717SJuli Mallett (void) close(p[1]);
659a841e1ebSBaptiste Daroussin execv(_PATH_BSHELL, argv);
660e3d86717SJuli Mallett exit(1);
661e3d86717SJuli Mallett default:
662e3d86717SJuli Mallett /* Read result in two stages, since m4's buffer is
663e3d86717SJuli Mallett * pushback-only. */
664e3d86717SJuli Mallett (void) close(p[1]);
665e3d86717SJuli Mallett do {
666e3d86717SJuli Mallett char result[BUFSIZE];
667e3d86717SJuli Mallett cc = read(p[0], result, sizeof result);
668e3d86717SJuli Mallett if (cc > 0)
669e3d86717SJuli Mallett addchars(result, cc);
670e3d86717SJuli Mallett } while (cc > 0 || (cc == -1 && errno == EINTR));
671e3d86717SJuli Mallett
672e3d86717SJuli Mallett (void) close(p[0]);
673*fdb905e2SBaptiste Daroussin while (waitpid(cpid, &status, 0) == -1) {
674*fdb905e2SBaptiste Daroussin if (errno != EINTR)
675*fdb905e2SBaptiste Daroussin break;
676*fdb905e2SBaptiste Daroussin }
677e3d86717SJuli Mallett pbstr(getstring());
678e3d86717SJuli Mallett }
679e3d86717SJuli Mallett }
680a841e1ebSBaptiste Daroussin
681a841e1ebSBaptiste Daroussin void
getdivfile(const char * name)682a841e1ebSBaptiste Daroussin getdivfile(const char *name)
683a841e1ebSBaptiste Daroussin {
684a841e1ebSBaptiste Daroussin FILE *f;
685a841e1ebSBaptiste Daroussin int c;
686a841e1ebSBaptiste Daroussin
687a841e1ebSBaptiste Daroussin f = fopen(name, "r");
688a841e1ebSBaptiste Daroussin if (!f)
689a841e1ebSBaptiste Daroussin return;
690a841e1ebSBaptiste Daroussin
691a841e1ebSBaptiste Daroussin while ((c = getc(f))!= EOF)
692a841e1ebSBaptiste Daroussin putc(c, active);
693a841e1ebSBaptiste Daroussin (void) fclose(f);
694a841e1ebSBaptiste Daroussin }
695