1283089Sbapt/* $OpenBSD: gnum4.c,v 1.50 2015/04/29 00:13:26 millert Exp $ */
290744Sjmallett
390744Sjmallett/*
490744Sjmallett * Copyright (c) 1999 Marc Espie
590744Sjmallett *
690744Sjmallett * Redistribution and use in source and binary forms, with or without
790744Sjmallett * modification, are permitted provided that the following conditions
890744Sjmallett * are met:
990744Sjmallett * 1. Redistributions of source code must retain the above copyright
1090744Sjmallett *    notice, this list of conditions and the following disclaimer.
1190744Sjmallett * 2. Redistributions in binary form must reproduce the above copyright
1290744Sjmallett *    notice, this list of conditions and the following disclaimer in the
1390744Sjmallett *    documentation and/or other materials provided with the distribution.
1490744Sjmallett *
1590744Sjmallett * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1690744Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1790744Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1890744Sjmallett * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1990744Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2090744Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2190744Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2290744Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2390744Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2490744Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2590744Sjmallett * SUCH DAMAGE.
2690744Sjmallett */
2795061Sjmallett#include <sys/cdefs.h>
2895061Sjmallett__FBSDID("$FreeBSD: releng/11.0/usr.bin/m4/gnum4.c 298190 2016-04-18 07:09:34Z araujo $");
2995061Sjmallett
30100014Sjmallett/*
3190744Sjmallett * functions needed to support gnu-m4 extensions, including a fake freezing
3290744Sjmallett */
3390744Sjmallett
3490744Sjmallett#include <sys/types.h>
3590744Sjmallett#include <sys/wait.h>
3690744Sjmallett#include <ctype.h>
37228063Sbapt#include <err.h>
3890744Sjmallett#include <paths.h>
3990744Sjmallett#include <regex.h>
4090744Sjmallett#include <stddef.h>
4190744Sjmallett#include <stdlib.h>
42283089Sbapt#include <stdint.h>
4390744Sjmallett#include <stdio.h>
4490744Sjmallett#include <string.h>
4590744Sjmallett#include <errno.h>
4690744Sjmallett#include <unistd.h>
47283089Sbapt#include <limits.h>
4890744Sjmallett#include "mdef.h"
4990744Sjmallett#include "stdd.h"
5090744Sjmallett#include "extern.h"
5190744Sjmallett
5290744Sjmallett
5390744Sjmallettint mimic_gnu = 0;
5490744Sjmallett
5590744Sjmallett/*
5690744Sjmallett * Support for include path search
57218909Sbrucec * First search in the current directory.
5890744Sjmallett * If not found, and the path is not absolute, include path kicks in.
5990744Sjmallett * First, -I options, in the order found on the command line.
6090744Sjmallett * Then M4PATH env variable
6190744Sjmallett */
6290744Sjmallett
63241777Sedstatic struct path_entry {
6490744Sjmallett	char *name;
6590744Sjmallett	struct path_entry *next;
6690744Sjmallett} *first, *last;
6790744Sjmallett
6890744Sjmallettstatic struct path_entry *new_path_entry(const char *);
6990744Sjmallettstatic void ensure_m4path(void);
7090744Sjmallettstatic struct input_file *dopath(struct input_file *, const char *);
7190744Sjmallett
7290744Sjmallettstatic struct path_entry *
7395887Sjmallettnew_path_entry(const char *dirname)
7490744Sjmallett{
7590744Sjmallett	struct path_entry *n;
7690744Sjmallett
7790744Sjmallett	n = malloc(sizeof(struct path_entry));
7890744Sjmallett	if (!n)
7990744Sjmallett		errx(1, "out of memory");
80283089Sbapt	n->name = xstrdup(dirname);
8190744Sjmallett	n->next = 0;
8290744Sjmallett	return n;
8390744Sjmallett}
84100014Sjmallett
85100014Sjmallettvoid
8695887Sjmallettaddtoincludepath(const char *dirname)
8790744Sjmallett{
8890744Sjmallett	struct path_entry *n;
8990744Sjmallett
9090744Sjmallett	n = new_path_entry(dirname);
9190744Sjmallett
9290744Sjmallett	if (last) {
9390744Sjmallett		last->next = n;
9490744Sjmallett		last = n;
9590744Sjmallett	}
9690744Sjmallett	else
9790744Sjmallett		last = first = n;
9890744Sjmallett}
9990744Sjmallett
10090744Sjmallettstatic void
10199939Sjmallettensure_m4path(void)
10290744Sjmallett{
10390744Sjmallett	static int envpathdone = 0;
10490744Sjmallett	char *envpath;
10590744Sjmallett	char *sweep;
10690744Sjmallett	char *path;
10790744Sjmallett
10890744Sjmallett	if (envpathdone)
10990744Sjmallett		return;
11090744Sjmallett	envpathdone = TRUE;
11190744Sjmallett	envpath = getenv("M4PATH");
112100014Sjmallett	if (!envpath)
11390744Sjmallett		return;
11490744Sjmallett	/* for portability: getenv result is read-only */
115283089Sbapt	envpath = xstrdup(envpath);
116100014Sjmallett	for (sweep = envpath;
11790744Sjmallett	    (path = strsep(&sweep, ":")) != NULL;)
11890744Sjmallett	    addtoincludepath(path);
11990744Sjmallett	free(envpath);
12090744Sjmallett}
12190744Sjmallett
12290744Sjmallettstatic
12390744Sjmallettstruct input_file *
12495887Sjmallettdopath(struct input_file *i, const char *filename)
12590744Sjmallett{
126283089Sbapt	char path[PATH_MAX];
12790744Sjmallett	struct path_entry *pe;
12890744Sjmallett	FILE *f;
12990744Sjmallett
13090744Sjmallett	for (pe = first; pe; pe = pe->next) {
13190744Sjmallett		snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
132298190Saraujo		if ((f = fopen(path, "r")) != NULL) {
13390744Sjmallett			set_input(i, f, path);
13490744Sjmallett			return i;
13590744Sjmallett		}
13690744Sjmallett	}
13790744Sjmallett	return NULL;
13890744Sjmallett}
13990744Sjmallett
14090744Sjmallettstruct input_file *
14195887Sjmallettfopen_trypath(struct input_file *i, const char *filename)
14290744Sjmallett{
14390744Sjmallett	FILE *f;
14490744Sjmallett
14590744Sjmallett	f = fopen(filename, "r");
14690744Sjmallett	if (f != NULL) {
14790744Sjmallett		set_input(i, f, filename);
14890744Sjmallett		return i;
14990744Sjmallett	}
15090744Sjmallett	if (filename[0] == '/')
15190744Sjmallett		return NULL;
15290744Sjmallett
15390744Sjmallett	ensure_m4path();
15490744Sjmallett
15590744Sjmallett	return dopath(i, filename);
15690744Sjmallett}
15790744Sjmallett
158100014Sjmallettvoid
15995887Sjmallettdoindir(const char *argv[], int argc)
16090744Sjmallett{
161228063Sbapt	ndptr n;
162228063Sbapt	struct macro_definition *p = NULL;
16390744Sjmallett
164228063Sbapt	n = lookup(argv[2]);
165228063Sbapt	if (n == NULL || (p = macro_getdef(n)) == NULL)
166228063Sbapt		m4errx(1, "indir: undefined macro %s.", argv[2]);
16790744Sjmallett	argv[1] = p->defn;
168228063Sbapt
169228063Sbapt	eval(argv+1, argc-1, p->type, is_traced(n));
17090744Sjmallett}
17190744Sjmallett
172100014Sjmallettvoid
17395887Sjmallettdobuiltin(const char *argv[], int argc)
17490744Sjmallett{
175228063Sbapt	ndptr p;
176228063Sbapt
17790744Sjmallett	argv[1] = NULL;
178228063Sbapt	p = macro_getbuiltin(argv[2]);
179228063Sbapt	if (p != NULL)
180228063Sbapt		eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p));
18190744Sjmallett	else
182228063Sbapt		m4errx(1, "unknown builtin %s.", argv[2]);
183100014Sjmallett}
18490744Sjmallett
18590744Sjmallett
18690744Sjmallett/* We need some temporary buffer space, as pb pushes BACK and substitution
18790744Sjmallett * proceeds forward... */
18890744Sjmallettstatic char *buffer;
18990744Sjmallettstatic size_t bufsize = 0;
19090744Sjmallettstatic size_t current = 0;
19190744Sjmallett
19290744Sjmallettstatic void addchars(const char *, size_t);
19395887Sjmallettstatic void addchar(int);
19490744Sjmallettstatic char *twiddle(const char *);
19590744Sjmallettstatic char *getstring(void);
196269162Sbaptstatic void exit_regerror(int, regex_t *, const char *);
197269162Sbaptstatic void do_subst(const char *, regex_t *, const char *, const char *,
198269162Sbapt    regmatch_t *);
199269162Sbaptstatic void do_regexpindex(const char *, regex_t *, const char *, regmatch_t *);
200269162Sbaptstatic void do_regexp(const char *, regex_t *, const char *, const char *,
201269162Sbapt    regmatch_t *);
202228063Sbaptstatic void add_sub(int, const char *, regex_t *, regmatch_t *);
20390744Sjmallettstatic void add_replace(const char *, regex_t *, const char *, regmatch_t *);
20490744Sjmallett#define addconstantstring(s) addchars((s), sizeof(s)-1)
20590744Sjmallett
206100014Sjmallettstatic void
20795887Sjmallettaddchars(const char *c, size_t n)
20890744Sjmallett{
20990744Sjmallett	if (n == 0)
21090744Sjmallett		return;
21190744Sjmallett	while (current + n > bufsize) {
21290744Sjmallett		if (bufsize == 0)
21390744Sjmallett			bufsize = 1024;
214283089Sbapt		else if (bufsize <= SIZE_MAX/2) {
21590744Sjmallett			bufsize *= 2;
216283089Sbapt		} else {
217283089Sbapt			errx(1, "size overflow");
218283089Sbapt		}
219228063Sbapt		buffer = xrealloc(buffer, bufsize, NULL);
22090744Sjmallett	}
22190744Sjmallett	memcpy(buffer+current, c, n);
22290744Sjmallett	current += n;
22390744Sjmallett}
22490744Sjmallett
225100014Sjmallettstatic void
22695887Sjmallettaddchar(int c)
22790744Sjmallett{
22890744Sjmallett	if (current +1 > bufsize) {
22990744Sjmallett		if (bufsize == 0)
23090744Sjmallett			bufsize = 1024;
23190744Sjmallett		else
23290744Sjmallett			bufsize *= 2;
233228063Sbapt		buffer = xrealloc(buffer, bufsize, NULL);
23490744Sjmallett	}
23590744Sjmallett	buffer[current++] = c;
23690744Sjmallett}
23790744Sjmallett
23890744Sjmallettstatic char *
23999939Sjmallettgetstring(void)
24090744Sjmallett{
24190744Sjmallett	addchar('\0');
24290744Sjmallett	current = 0;
24390744Sjmallett	return buffer;
24490744Sjmallett}
24590744Sjmallett
24690744Sjmallett
247100014Sjmallettstatic void
248269162Sbaptexit_regerror(int er, regex_t *re, const char *source)
24990744Sjmallett{
250228063Sbapt	size_t	errlen;
251228063Sbapt	char	*errbuf;
25290744Sjmallett
25390744Sjmallett	errlen = regerror(er, re, NULL, 0);
254228063Sbapt	errbuf = xalloc(errlen,
255228063Sbapt	    "malloc in regerror: %lu", (unsigned long)errlen);
25690744Sjmallett	regerror(er, re, errbuf, errlen);
257269162Sbapt	m4errx(1, "regular expression error in %s: %s.", source, errbuf);
25890744Sjmallett}
25990744Sjmallett
26090744Sjmallettstatic void
261228063Sbaptadd_sub(int n, const char *string, regex_t *re, regmatch_t *pm)
26290744Sjmallett{
263228063Sbapt	if (n > (int)re->re_nsub)
264228063Sbapt		warnx("No subexpression %d", n);
26590744Sjmallett	/* Subexpressions that did not match are
26690744Sjmallett	 * not an error.  */
26790744Sjmallett	else if (pm[n].rm_so != -1 &&
26890744Sjmallett	    pm[n].rm_eo != -1) {
26990744Sjmallett		addchars(string + pm[n].rm_so,
27090744Sjmallett			pm[n].rm_eo - pm[n].rm_so);
27190744Sjmallett	}
27290744Sjmallett}
27390744Sjmallett
27490744Sjmallett/* Add replacement string to the output buffer, recognizing special
27590744Sjmallett * constructs and replacing them with substrings of the original string.
27690744Sjmallett */
277100014Sjmallettstatic void
27895887Sjmallettadd_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
27990744Sjmallett{
28090744Sjmallett	const char *p;
28190744Sjmallett
28290744Sjmallett	for (p = replace; *p != '\0'; p++) {
28390744Sjmallett		if (*p == '&' && !mimic_gnu) {
28490744Sjmallett			add_sub(0, string, re, pm);
28590744Sjmallett			continue;
28690744Sjmallett		}
28790744Sjmallett		if (*p == '\\') {
28890744Sjmallett			if (p[1] == '\\') {
28990744Sjmallett				addchar(p[1]);
29090744Sjmallett				p++;
29190744Sjmallett				continue;
29290744Sjmallett			}
29390744Sjmallett			if (p[1] == '&') {
29490744Sjmallett				if (mimic_gnu)
29590744Sjmallett					add_sub(0, string, re, pm);
29690744Sjmallett				else
29790744Sjmallett					addchar(p[1]);
29890744Sjmallett				p++;
29990744Sjmallett				continue;
30090744Sjmallett			}
301269162Sbapt			if (isdigit((unsigned char)p[1])) {
30290744Sjmallett				add_sub(*(++p) - '0', string, re, pm);
30390744Sjmallett				continue;
30490744Sjmallett			}
30590744Sjmallett		}
306228063Sbapt		addchar(*p);
30790744Sjmallett	}
30890744Sjmallett}
30990744Sjmallett
310100014Sjmallettstatic void
311269162Sbaptdo_subst(const char *string, regex_t *re, const char *source,
312269162Sbapt    const char *replace, regmatch_t *pm)
31390744Sjmallett{
31490744Sjmallett	int error;
31590744Sjmallett	int flags = 0;
31690744Sjmallett	const char *last_match = NULL;
31790744Sjmallett
31890744Sjmallett	while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
31990744Sjmallett		if (pm[0].rm_eo != 0) {
32090744Sjmallett			if (string[pm[0].rm_eo-1] == '\n')
32190744Sjmallett				flags = 0;
32290744Sjmallett			else
32390744Sjmallett				flags = REG_NOTBOL;
32490744Sjmallett		}
32590744Sjmallett
326100014Sjmallett		/* NULL length matches are special... We use the `vi-mode'
32790744Sjmallett		 * rule: don't allow a NULL-match at the last match
328100014Sjmallett		 * position.
32990744Sjmallett		 */
330100014Sjmallett		if (pm[0].rm_so == pm[0].rm_eo &&
33190744Sjmallett		    string + pm[0].rm_so == last_match) {
33290744Sjmallett			if (*string == '\0')
33390744Sjmallett				return;
33490744Sjmallett			addchar(*string);
33590744Sjmallett			if (*string++ == '\n')
33690744Sjmallett				flags = 0;
33790744Sjmallett			else
33890744Sjmallett				flags = REG_NOTBOL;
33990744Sjmallett			continue;
34090744Sjmallett		}
34190744Sjmallett		last_match = string + pm[0].rm_so;
34290744Sjmallett		addchars(string, pm[0].rm_so);
34390744Sjmallett		add_replace(string, re, replace, pm);
34490744Sjmallett		string += pm[0].rm_eo;
34590744Sjmallett	}
34690744Sjmallett	if (error != REG_NOMATCH)
347269162Sbapt		exit_regerror(error, re, source);
34890744Sjmallett	pbstr(string);
34990744Sjmallett}
35090744Sjmallett
351100014Sjmallettstatic void
352269162Sbaptdo_regexp(const char *string, regex_t *re, const char *source,
353269162Sbapt    const char *replace, regmatch_t *pm)
35490744Sjmallett{
35590744Sjmallett	int error;
35690744Sjmallett
35790744Sjmallett	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
358100014Sjmallett	case 0:
35990744Sjmallett		add_replace(string, re, replace, pm);
36090744Sjmallett		pbstr(getstring());
36190744Sjmallett		break;
36290744Sjmallett	case REG_NOMATCH:
36390744Sjmallett		break;
36490744Sjmallett	default:
365269162Sbapt		exit_regerror(error, re, source);
36690744Sjmallett	}
36790744Sjmallett}
36890744Sjmallett
369100014Sjmallettstatic void
370269162Sbaptdo_regexpindex(const char *string, regex_t *re, const char *source,
371269162Sbapt    regmatch_t *pm)
37290744Sjmallett{
37390744Sjmallett	int error;
37490744Sjmallett
37590744Sjmallett	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
37690744Sjmallett	case 0:
37790744Sjmallett		pbunsigned(pm[0].rm_so);
37890744Sjmallett		break;
37990744Sjmallett	case REG_NOMATCH:
38090744Sjmallett		pbnum(-1);
38190744Sjmallett		break;
38290744Sjmallett	default:
383269162Sbapt		exit_regerror(error, re, source);
38490744Sjmallett	}
38590744Sjmallett}
38690744Sjmallett
38790744Sjmallett/* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
38890744Sjmallett * says. So we twiddle with the regexp before passing it to regcomp.
38990744Sjmallett */
39090744Sjmallettstatic char *
39195887Sjmalletttwiddle(const char *p)
39290744Sjmallett{
393228063Sbapt	/* + at start of regexp is a normal character for Gnu m4 */
394228063Sbapt	if (*p == '^') {
395228063Sbapt		addchar(*p);
396228063Sbapt		p++;
397228063Sbapt	}
398228063Sbapt	if (*p == '+') {
399228063Sbapt		addchar('\\');
400228063Sbapt	}
40190744Sjmallett	/* This could use strcspn for speed... */
40290744Sjmallett	while (*p != '\0') {
40390744Sjmallett		if (*p == '\\') {
40490744Sjmallett			switch(p[1]) {
40590744Sjmallett			case '(':
40690744Sjmallett			case ')':
40790744Sjmallett			case '|':
40890744Sjmallett				addchar(p[1]);
40990744Sjmallett				break;
41090744Sjmallett			case 'w':
41190744Sjmallett				addconstantstring("[_a-zA-Z0-9]");
41290744Sjmallett				break;
41390744Sjmallett			case 'W':
41490744Sjmallett				addconstantstring("[^_a-zA-Z0-9]");
41590744Sjmallett				break;
41690744Sjmallett			case '<':
41790744Sjmallett				addconstantstring("[[:<:]]");
41890744Sjmallett				break;
41990744Sjmallett			case '>':
42090744Sjmallett				addconstantstring("[[:>:]]");
42190744Sjmallett				break;
42290744Sjmallett			default:
42390744Sjmallett				addchars(p, 2);
42490744Sjmallett				break;
42590744Sjmallett			}
42690744Sjmallett			p+=2;
42790744Sjmallett			continue;
42890744Sjmallett		}
42990744Sjmallett		if (*p == '(' || *p == ')' || *p == '|')
43090744Sjmallett			addchar('\\');
43190744Sjmallett
43290744Sjmallett		addchar(*p);
43390744Sjmallett		p++;
43490744Sjmallett	}
43590744Sjmallett	return getstring();
43690744Sjmallett}
43790744Sjmallett
43890744Sjmallett/* patsubst(string, regexp, opt replacement) */
43990744Sjmallett/* argv[2]: string
44090744Sjmallett * argv[3]: regexp
44190744Sjmallett * argv[4]: opt rep
44290744Sjmallett */
44390744Sjmallettvoid
44495887Sjmallettdopatsubst(const char *argv[], int argc)
44590744Sjmallett{
44690744Sjmallett	if (argc <= 3) {
44790744Sjmallett		warnx("Too few arguments to patsubst");
44890744Sjmallett		return;
44990744Sjmallett	}
450228063Sbapt	/* special case: empty regexp */
451228063Sbapt	if (argv[3][0] == '\0') {
452228063Sbapt		const char *s;
453228063Sbapt		size_t len;
454228063Sbapt		if (argc > 4 && argv[4])
455228063Sbapt			len = strlen(argv[4]);
456228063Sbapt		else
457228063Sbapt			len = 0;
458228063Sbapt		for (s = argv[2]; *s != '\0'; s++) {
459228063Sbapt			addchars(argv[4], len);
460228063Sbapt			addchar(*s);
461228063Sbapt		}
462228063Sbapt	} else {
463228063Sbapt		int error;
464228063Sbapt		regex_t re;
465228063Sbapt		regmatch_t *pmatch;
466228063Sbapt		int mode = REG_EXTENDED;
467269162Sbapt		const char *source;
468228063Sbapt		size_t l = strlen(argv[3]);
469100014Sjmallett
470228063Sbapt		if (!mimic_gnu ||
471228063Sbapt		    (argv[3][0] == '^') ||
472228063Sbapt		    (l > 0 && argv[3][l-1] == '$'))
473228063Sbapt			mode |= REG_NEWLINE;
474228063Sbapt
475269162Sbapt		source = mimic_gnu ? twiddle(argv[3]) : argv[3];
476269162Sbapt		error = regcomp(&re, source, mode);
477228063Sbapt		if (error != 0)
478269162Sbapt			exit_regerror(error, &re, source);
479228063Sbapt
480269162Sbapt		pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t),
481269162Sbapt		    NULL);
482269162Sbapt		do_subst(argv[2], &re, source,
483228063Sbapt		    argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch);
484228063Sbapt		free(pmatch);
485228063Sbapt		regfree(&re);
486228063Sbapt	}
48790744Sjmallett	pbstr(getstring());
48890744Sjmallett}
48990744Sjmallett
49090744Sjmallettvoid
49195887Sjmallettdoregexp(const char *argv[], int argc)
49290744Sjmallett{
49390744Sjmallett	int error;
49490744Sjmallett	regex_t re;
49590744Sjmallett	regmatch_t *pmatch;
496269162Sbapt	const char *source;
49790744Sjmallett
49890744Sjmallett	if (argc <= 3) {
49990744Sjmallett		warnx("Too few arguments to regexp");
50090744Sjmallett		return;
50190744Sjmallett	}
502228063Sbapt	/* special gnu case */
503228063Sbapt	if (argv[3][0] == '\0' && mimic_gnu) {
504228063Sbapt		if (argc == 4 || argv[4] == NULL)
505228063Sbapt			return;
506228063Sbapt		else
507228063Sbapt			pbstr(argv[4]);
508228063Sbapt	}
509269162Sbapt	source = mimic_gnu ? twiddle(argv[3]) : argv[3];
510269162Sbapt	error = regcomp(&re, source, REG_EXTENDED|REG_NEWLINE);
51190744Sjmallett	if (error != 0)
512269162Sbapt		exit_regerror(error, &re, source);
513100014Sjmallett
514269162Sbapt	pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t), NULL);
515228063Sbapt	if (argc == 4 || argv[4] == NULL)
516269162Sbapt		do_regexpindex(argv[2], &re, source, pmatch);
51790744Sjmallett	else
518269162Sbapt		do_regexp(argv[2], &re, source, argv[4], pmatch);
51990744Sjmallett	free(pmatch);
52090744Sjmallett	regfree(&re);
52190744Sjmallett}
52290744Sjmallett
52390744Sjmallettvoid
524228063Sbaptdoformat(const char *argv[], int argc)
525228063Sbapt{
526228063Sbapt	const char *format = argv[2];
527228063Sbapt	int pos = 3;
528228063Sbapt	int left_padded;
529228063Sbapt	long width;
530228063Sbapt	size_t l;
531228063Sbapt	const char *thisarg = NULL;
532228063Sbapt	char temp[2];
533228063Sbapt	long extra;
534228063Sbapt
535228063Sbapt	while (*format != 0) {
536228063Sbapt		if (*format != '%') {
537228063Sbapt			addchar(*format++);
538228063Sbapt			continue;
539228063Sbapt		}
540228063Sbapt
541228063Sbapt		format++;
542228063Sbapt		if (*format == '%') {
543228063Sbapt			addchar(*format++);
544228063Sbapt			continue;
545228063Sbapt		}
546228063Sbapt		if (*format == 0) {
547228063Sbapt			addchar('%');
548228063Sbapt			break;
549228063Sbapt		}
550228063Sbapt
551228063Sbapt		if (*format == '*') {
552228063Sbapt			format++;
553228063Sbapt			if (pos >= argc)
554228063Sbapt				m4errx(1,
555228063Sbapt				    "Format with too many format specifiers.");
556228063Sbapt			width = strtol(argv[pos++], NULL, 10);
557228063Sbapt		} else {
558228063Sbapt			width = strtol(format, __DECONST(char **,&format), 10);
559228063Sbapt		}
560228063Sbapt		if (width < 0) {
561228063Sbapt			left_padded = 1;
562228063Sbapt			width = -width;
563228063Sbapt		} else {
564228063Sbapt			left_padded = 0;
565228063Sbapt		}
566228063Sbapt		if (*format == '.') {
567228063Sbapt			format++;
568228063Sbapt			if (*format == '*') {
569228063Sbapt				format++;
570228063Sbapt				if (pos >= argc)
571228063Sbapt					m4errx(1,
572228063Sbapt					    "Format with too many format specifiers.");
573228063Sbapt				extra = strtol(argv[pos++], NULL, 10);
574228063Sbapt			} else {
575228063Sbapt				extra = strtol(format, __DECONST(char **, &format), 10);
576228063Sbapt			}
577228063Sbapt		} else {
578228063Sbapt			extra = LONG_MAX;
579228063Sbapt		}
580228063Sbapt		if (pos >= argc)
581228063Sbapt			m4errx(1, "Format with too many format specifiers.");
582228063Sbapt		switch(*format) {
583228063Sbapt		case 's':
584228063Sbapt			thisarg = argv[pos++];
585228063Sbapt			break;
586228063Sbapt		case 'c':
587228063Sbapt			temp[0] = strtoul(argv[pos++], NULL, 10);
588228063Sbapt			temp[1] = 0;
589228063Sbapt			thisarg = temp;
590228063Sbapt			break;
591228063Sbapt		default:
592228063Sbapt			m4errx(1, "Unsupported format specification: %s.",
593228063Sbapt			    argv[2]);
594228063Sbapt		}
595228063Sbapt		format++;
596228063Sbapt		l = strlen(thisarg);
597228063Sbapt		if ((long)l > extra)
598228063Sbapt			l = extra;
599228063Sbapt		if (!left_padded) {
600228063Sbapt			while ((long)l < width--)
601228063Sbapt				addchar(' ');
602228063Sbapt		}
603228063Sbapt		addchars(thisarg, l);
604228063Sbapt		if (left_padded) {
605228063Sbapt			while ((long)l < width--)
606228063Sbapt				addchar(' ');
607228063Sbapt		}
608228063Sbapt	}
609228063Sbapt	pbstr(getstring());
610228063Sbapt}
611228063Sbapt
612228063Sbaptvoid
61395887Sjmallettdoesyscmd(const char *cmd)
61490744Sjmallett{
61590744Sjmallett	int p[2];
61690744Sjmallett	pid_t pid, cpid;
617228063Sbapt	char *argv[4];
61890744Sjmallett	int cc;
61990744Sjmallett	int status;
62090744Sjmallett
62190744Sjmallett	/* Follow gnu m4 documentation: first flush buffers. */
62290744Sjmallett	fflush(NULL);
62390744Sjmallett
624228063Sbapt	argv[0] = __DECONST(char *, "sh");
625228063Sbapt	argv[1] = __DECONST(char *, "-c");
626228063Sbapt	argv[2] = __DECONST(char *, cmd);
627228063Sbapt	argv[3] = NULL;
628228063Sbapt
62990744Sjmallett	/* Just set up standard output, share stderr and stdin with m4 */
63090744Sjmallett	if (pipe(p) == -1)
63190744Sjmallett		err(1, "bad pipe");
63290744Sjmallett	switch(cpid = fork()) {
63390744Sjmallett	case -1:
63490744Sjmallett		err(1, "bad fork");
63590744Sjmallett		/* NOTREACHED */
63690744Sjmallett	case 0:
63790744Sjmallett		(void) close(p[0]);
63890744Sjmallett		(void) dup2(p[1], 1);
63990744Sjmallett		(void) close(p[1]);
640228063Sbapt		execv(_PATH_BSHELL, argv);
64190744Sjmallett		exit(1);
64290744Sjmallett	default:
64390744Sjmallett		/* Read result in two stages, since m4's buffer is
64490744Sjmallett		 * pushback-only. */
64590744Sjmallett		(void) close(p[1]);
64690744Sjmallett		do {
64790744Sjmallett			char result[BUFSIZE];
64890744Sjmallett			cc = read(p[0], result, sizeof result);
64990744Sjmallett			if (cc > 0)
65090744Sjmallett				addchars(result, cc);
65190744Sjmallett		} while (cc > 0 || (cc == -1 && errno == EINTR));
65290744Sjmallett
65390744Sjmallett		(void) close(p[0]);
65490744Sjmallett		while ((pid = wait(&status)) != cpid && pid >= 0)
65590744Sjmallett			continue;
65690744Sjmallett		pbstr(getstring());
65790744Sjmallett	}
65890744Sjmallett}
659228063Sbapt
660228063Sbaptvoid
661228063Sbaptgetdivfile(const char *name)
662228063Sbapt{
663228063Sbapt	FILE *f;
664228063Sbapt	int c;
665228063Sbapt
666228063Sbapt	f = fopen(name, "r");
667228063Sbapt	if (!f)
668228063Sbapt		return;
669228063Sbapt
670228063Sbapt	while ((c = getc(f))!= EOF)
671228063Sbapt		putc(c, active);
672228063Sbapt	(void) fclose(f);
673228063Sbapt}
674