gnum4.c revision 95164
190744Sjmallett/* $OpenBSD: gnum4.c,v 1.16 2002/02/16 21:27:48 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 */
2790744Sjmallett
2895061Sjmallett#include <sys/cdefs.h>
2995061Sjmallett__FBSDID("$FreeBSD: head/usr.bin/m4/gnum4.c 95164 2002-04-20 21:37:26Z jmallett $");
3095061Sjmallett
3190744Sjmallett/*
3290744Sjmallett * functions needed to support gnu-m4 extensions, including a fake freezing
3390744Sjmallett */
3490744Sjmallett
3590744Sjmallett#include <sys/param.h>
3690744Sjmallett#include <sys/types.h>
3790744Sjmallett#include <sys/wait.h>
3890744Sjmallett#include <ctype.h>
3990744Sjmallett#include <paths.h>
4090744Sjmallett#include <regex.h>
4190744Sjmallett#include <stddef.h>
4290744Sjmallett#include <stdlib.h>
4390744Sjmallett#include <stdio.h>
4490744Sjmallett#include <string.h>
4590744Sjmallett#include <err.h>
4690744Sjmallett#include <errno.h>
4790744Sjmallett#include <unistd.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
5790744Sjmallett * First search in the 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
6390744Sjmallettstruct 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 *
7390744Sjmallettnew_path_entry(dirname)
7490744Sjmallett	const char *dirname;
7590744Sjmallett{
7690744Sjmallett	struct path_entry *n;
7790744Sjmallett
7890744Sjmallett	n = malloc(sizeof(struct path_entry));
7990744Sjmallett	if (!n)
8090744Sjmallett		errx(1, "out of memory");
8190744Sjmallett	n->name = strdup(dirname);
8290744Sjmallett	if (!n->name)
8390744Sjmallett		errx(1, "out of memory");
8490744Sjmallett	n->next = 0;
8590744Sjmallett	return n;
8690744Sjmallett}
8790744Sjmallett
8890744Sjmallettvoid
8990744Sjmallettaddtoincludepath(dirname)
9090744Sjmallett	const char *dirname;
9190744Sjmallett{
9290744Sjmallett	struct path_entry *n;
9390744Sjmallett
9490744Sjmallett	n = new_path_entry(dirname);
9590744Sjmallett
9690744Sjmallett	if (last) {
9790744Sjmallett		last->next = n;
9890744Sjmallett		last = n;
9990744Sjmallett	}
10090744Sjmallett	else
10190744Sjmallett		last = first = n;
10290744Sjmallett}
10390744Sjmallett
10490744Sjmallettstatic void
10590744Sjmallettensure_m4path()
10690744Sjmallett{
10790744Sjmallett	static int envpathdone = 0;
10890744Sjmallett	char *envpath;
10990744Sjmallett	char *sweep;
11090744Sjmallett	char *path;
11190744Sjmallett
11290744Sjmallett	if (envpathdone)
11390744Sjmallett		return;
11490744Sjmallett	envpathdone = TRUE;
11590744Sjmallett	envpath = getenv("M4PATH");
11690744Sjmallett	if (!envpath)
11790744Sjmallett		return;
11890744Sjmallett	/* for portability: getenv result is read-only */
11990744Sjmallett	envpath = strdup(envpath);
12090744Sjmallett	if (!envpath)
12190744Sjmallett		errx(1, "out of memory");
12290744Sjmallett	for (sweep = envpath;
12390744Sjmallett	    (path = strsep(&sweep, ":")) != NULL;)
12490744Sjmallett	    addtoincludepath(path);
12590744Sjmallett	free(envpath);
12690744Sjmallett}
12790744Sjmallett
12890744Sjmallettstatic
12990744Sjmallettstruct input_file *
13090744Sjmallettdopath(i, filename)
13190744Sjmallett	struct input_file *i;
13290744Sjmallett	const char *filename;
13390744Sjmallett{
13490744Sjmallett	char path[MAXPATHLEN];
13590744Sjmallett	struct path_entry *pe;
13690744Sjmallett	FILE *f;
13790744Sjmallett
13890744Sjmallett	for (pe = first; pe; pe = pe->next) {
13990744Sjmallett		snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
14090744Sjmallett		if ((f = fopen(path, "r")) != 0) {
14190744Sjmallett			set_input(i, f, path);
14290744Sjmallett			return i;
14390744Sjmallett		}
14490744Sjmallett	}
14590744Sjmallett	return NULL;
14690744Sjmallett}
14790744Sjmallett
14890744Sjmallettstruct input_file *
14990744Sjmallettfopen_trypath(i, filename)
15090744Sjmallett	struct input_file *i;
15190744Sjmallett	const char *filename;
15290744Sjmallett{
15390744Sjmallett	FILE *f;
15490744Sjmallett
15590744Sjmallett	f = fopen(filename, "r");
15690744Sjmallett	if (f != NULL) {
15790744Sjmallett		set_input(i, f, filename);
15890744Sjmallett		return i;
15990744Sjmallett	}
16090744Sjmallett	if (filename[0] == '/')
16190744Sjmallett		return NULL;
16290744Sjmallett
16390744Sjmallett	ensure_m4path();
16490744Sjmallett
16590744Sjmallett	return dopath(i, filename);
16690744Sjmallett}
16790744Sjmallett
16890744Sjmallettvoid
16990744Sjmallettdoindir(argv, argc)
17090744Sjmallett	const char *argv[];
17190744Sjmallett	int argc;
17290744Sjmallett{
17390744Sjmallett	ndptr p;
17490744Sjmallett
17590744Sjmallett	p = lookup(argv[2]);
17690744Sjmallett	if (p == NULL)
17790744Sjmallett		errx(1, "undefined macro %s", argv[2]);
17890744Sjmallett	argv[1] = p->defn;
17990744Sjmallett	eval(argv+1, argc-1, p->type);
18090744Sjmallett}
18190744Sjmallett
18290744Sjmallettvoid
18390744Sjmallettdobuiltin(argv, argc)
18490744Sjmallett	const char *argv[];
18590744Sjmallett	int argc;
18690744Sjmallett{
18790744Sjmallett	int n;
18890744Sjmallett	argv[1] = NULL;
18990744Sjmallett	n = builtin_type(argv[2]);
19090744Sjmallett	if (n != -1)
19190744Sjmallett		eval(argv+1, argc-1, n);
19290744Sjmallett	else
19390744Sjmallett		errx(1, "unknown builtin %s", argv[2]);
19490744Sjmallett}
19590744Sjmallett
19690744Sjmallett
19790744Sjmallett/* We need some temporary buffer space, as pb pushes BACK and substitution
19890744Sjmallett * proceeds forward... */
19990744Sjmallettstatic char *buffer;
20090744Sjmallettstatic size_t bufsize = 0;
20190744Sjmallettstatic size_t current = 0;
20290744Sjmallett
20390744Sjmallettstatic void addchars(const char *, size_t);
20490744Sjmallettstatic void addchar(char);
20590744Sjmallettstatic char *twiddle(const char *);
20690744Sjmallettstatic char *getstring(void);
20790744Sjmallettstatic void exit_regerror(int, regex_t *);
20890744Sjmallettstatic void do_subst(const char *, regex_t *, const char *, regmatch_t *);
20990744Sjmallettstatic void do_regexpindex(const char *, regex_t *, regmatch_t *);
21090744Sjmallettstatic void do_regexp(const char *, regex_t *, const char *, regmatch_t *);
21195095Sjmallettstatic void add_sub(size_t, const char *, regex_t *, regmatch_t *);
21290744Sjmallettstatic void add_replace(const char *, regex_t *, const char *, regmatch_t *);
21390744Sjmallett#define addconstantstring(s) addchars((s), sizeof(s)-1)
21490744Sjmallett
21590744Sjmallettstatic void
21690744Sjmallettaddchars(c, n)
21790744Sjmallett	const char *c;
21890744Sjmallett	size_t n;
21990744Sjmallett{
22090744Sjmallett	if (n == 0)
22190744Sjmallett		return;
22290744Sjmallett	while (current + n > bufsize) {
22390744Sjmallett		if (bufsize == 0)
22490744Sjmallett			bufsize = 1024;
22590744Sjmallett		else
22690744Sjmallett			bufsize *= 2;
22790744Sjmallett		buffer = realloc(buffer, bufsize);
22890744Sjmallett		if (buffer == NULL)
22990744Sjmallett			errx(1, "out of memory");
23090744Sjmallett	}
23190744Sjmallett	memcpy(buffer+current, c, n);
23290744Sjmallett	current += n;
23390744Sjmallett}
23490744Sjmallett
23590744Sjmallettstatic void
23690744Sjmallettaddchar(c)
23790744Sjmallett	char c;
23890744Sjmallett{
23990744Sjmallett	if (current +1 > bufsize) {
24090744Sjmallett		if (bufsize == 0)
24190744Sjmallett			bufsize = 1024;
24290744Sjmallett		else
24390744Sjmallett			bufsize *= 2;
24490744Sjmallett		buffer = realloc(buffer, bufsize);
24590744Sjmallett		if (buffer == NULL)
24690744Sjmallett			errx(1, "out of memory");
24790744Sjmallett	}
24890744Sjmallett	buffer[current++] = c;
24990744Sjmallett}
25090744Sjmallett
25190744Sjmallettstatic char *
25290744Sjmallettgetstring()
25390744Sjmallett{
25490744Sjmallett	addchar('\0');
25590744Sjmallett	current = 0;
25690744Sjmallett	return buffer;
25790744Sjmallett}
25890744Sjmallett
25990744Sjmallett
26090744Sjmallettstatic void
26190744Sjmallettexit_regerror(er, re)
26290744Sjmallett	int er;
26390744Sjmallett	regex_t *re;
26490744Sjmallett{
26590744Sjmallett	size_t 	errlen;
26690744Sjmallett	char 	*errbuf;
26790744Sjmallett
26890744Sjmallett	errlen = regerror(er, re, NULL, 0);
26990744Sjmallett	errbuf = xalloc(errlen);
27090744Sjmallett	regerror(er, re, errbuf, errlen);
27190744Sjmallett	errx(1, "regular expression error: %s", errbuf);
27290744Sjmallett}
27390744Sjmallett
27490744Sjmallettstatic void
27590744Sjmallettadd_sub(n, string, re, pm)
27695095Sjmallett	size_t n;
27790744Sjmallett	const char *string;
27890744Sjmallett	regex_t *re;
27990744Sjmallett	regmatch_t *pm;
28090744Sjmallett{
28190744Sjmallett	if (n > re->re_nsub)
28295164Sjmallett		warnx("No subexpression %zu", n);
28390744Sjmallett	/* Subexpressions that did not match are
28490744Sjmallett	 * not an error.  */
28590744Sjmallett	else if (pm[n].rm_so != -1 &&
28690744Sjmallett	    pm[n].rm_eo != -1) {
28790744Sjmallett		addchars(string + pm[n].rm_so,
28890744Sjmallett			pm[n].rm_eo - pm[n].rm_so);
28990744Sjmallett	}
29090744Sjmallett}
29190744Sjmallett
29290744Sjmallett/* Add replacement string to the output buffer, recognizing special
29390744Sjmallett * constructs and replacing them with substrings of the original string.
29490744Sjmallett */
29590744Sjmallettstatic void
29690744Sjmallettadd_replace(string, re, replace, pm)
29790744Sjmallett	const char *string;
29890744Sjmallett	regex_t *re;
29990744Sjmallett	const char *replace;
30090744Sjmallett	regmatch_t *pm;
30190744Sjmallett{
30290744Sjmallett	const char *p;
30390744Sjmallett
30490744Sjmallett	for (p = replace; *p != '\0'; p++) {
30590744Sjmallett		if (*p == '&' && !mimic_gnu) {
30690744Sjmallett			add_sub(0, string, re, pm);
30790744Sjmallett			continue;
30890744Sjmallett		}
30990744Sjmallett		if (*p == '\\') {
31090744Sjmallett			if (p[1] == '\\') {
31190744Sjmallett				addchar(p[1]);
31290744Sjmallett				p++;
31390744Sjmallett				continue;
31490744Sjmallett			}
31590744Sjmallett			if (p[1] == '&') {
31690744Sjmallett				if (mimic_gnu)
31790744Sjmallett					add_sub(0, string, re, pm);
31890744Sjmallett				else
31990744Sjmallett					addchar(p[1]);
32090744Sjmallett				p++;
32190744Sjmallett				continue;
32290744Sjmallett			}
32390744Sjmallett			if (isdigit(p[1])) {
32490744Sjmallett				add_sub(*(++p) - '0', string, re, pm);
32590744Sjmallett				continue;
32690744Sjmallett			}
32790744Sjmallett		}
32890744Sjmallett	    	addchar(*p);
32990744Sjmallett	}
33090744Sjmallett}
33190744Sjmallett
33290744Sjmallettstatic void
33390744Sjmallettdo_subst(string, re, replace, pm)
33490744Sjmallett	const char *string;
33590744Sjmallett	regex_t *re;
33690744Sjmallett	const char *replace;
33790744Sjmallett	regmatch_t *pm;
33890744Sjmallett{
33990744Sjmallett	int error;
34090744Sjmallett	int flags = 0;
34190744Sjmallett	const char *last_match = NULL;
34290744Sjmallett
34390744Sjmallett	while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
34490744Sjmallett		if (pm[0].rm_eo != 0) {
34590744Sjmallett			if (string[pm[0].rm_eo-1] == '\n')
34690744Sjmallett				flags = 0;
34790744Sjmallett			else
34890744Sjmallett				flags = REG_NOTBOL;
34990744Sjmallett		}
35090744Sjmallett
35190744Sjmallett		/* NULL length matches are special... We use the `vi-mode'
35290744Sjmallett		 * rule: don't allow a NULL-match at the last match
35390744Sjmallett		 * position.
35490744Sjmallett		 */
35590744Sjmallett		if (pm[0].rm_so == pm[0].rm_eo &&
35690744Sjmallett		    string + pm[0].rm_so == last_match) {
35790744Sjmallett			if (*string == '\0')
35890744Sjmallett				return;
35990744Sjmallett			addchar(*string);
36090744Sjmallett			if (*string++ == '\n')
36190744Sjmallett				flags = 0;
36290744Sjmallett			else
36390744Sjmallett				flags = REG_NOTBOL;
36490744Sjmallett			continue;
36590744Sjmallett		}
36690744Sjmallett		last_match = string + pm[0].rm_so;
36790744Sjmallett		addchars(string, pm[0].rm_so);
36890744Sjmallett		add_replace(string, re, replace, pm);
36990744Sjmallett		string += pm[0].rm_eo;
37090744Sjmallett	}
37190744Sjmallett	if (error != REG_NOMATCH)
37290744Sjmallett		exit_regerror(error, re);
37390744Sjmallett	pbstr(string);
37490744Sjmallett}
37590744Sjmallett
37690744Sjmallettstatic void
37790744Sjmallettdo_regexp(string, re, replace, pm)
37890744Sjmallett	const char *string;
37990744Sjmallett	regex_t *re;
38090744Sjmallett	const char *replace;
38190744Sjmallett	regmatch_t *pm;
38290744Sjmallett{
38390744Sjmallett	int error;
38490744Sjmallett
38590744Sjmallett	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
38690744Sjmallett	case 0:
38790744Sjmallett		add_replace(string, re, replace, pm);
38890744Sjmallett		pbstr(getstring());
38990744Sjmallett		break;
39090744Sjmallett	case REG_NOMATCH:
39190744Sjmallett		break;
39290744Sjmallett	default:
39390744Sjmallett		exit_regerror(error, re);
39490744Sjmallett	}
39590744Sjmallett}
39690744Sjmallett
39790744Sjmallettstatic void
39890744Sjmallettdo_regexpindex(string, re, pm)
39990744Sjmallett	const char *string;
40090744Sjmallett	regex_t *re;
40190744Sjmallett	regmatch_t *pm;
40290744Sjmallett{
40390744Sjmallett	int error;
40490744Sjmallett
40590744Sjmallett	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
40690744Sjmallett	case 0:
40790744Sjmallett		pbunsigned(pm[0].rm_so);
40890744Sjmallett		break;
40990744Sjmallett	case REG_NOMATCH:
41090744Sjmallett		pbnum(-1);
41190744Sjmallett		break;
41290744Sjmallett	default:
41390744Sjmallett		exit_regerror(error, re);
41490744Sjmallett	}
41590744Sjmallett}
41690744Sjmallett
41790744Sjmallett/* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
41890744Sjmallett * says. So we twiddle with the regexp before passing it to regcomp.
41990744Sjmallett */
42090744Sjmallettstatic char *
42190744Sjmalletttwiddle(p)
42290744Sjmallett	const char *p;
42390744Sjmallett{
42490744Sjmallett	/* This could use strcspn for speed... */
42590744Sjmallett	while (*p != '\0') {
42690744Sjmallett		if (*p == '\\') {
42790744Sjmallett			switch(p[1]) {
42890744Sjmallett			case '(':
42990744Sjmallett			case ')':
43090744Sjmallett			case '|':
43190744Sjmallett				addchar(p[1]);
43290744Sjmallett				break;
43390744Sjmallett			case 'w':
43490744Sjmallett				addconstantstring("[_a-zA-Z0-9]");
43590744Sjmallett				break;
43690744Sjmallett			case 'W':
43790744Sjmallett				addconstantstring("[^_a-zA-Z0-9]");
43890744Sjmallett				break;
43990744Sjmallett			case '<':
44090744Sjmallett				addconstantstring("[[:<:]]");
44190744Sjmallett				break;
44290744Sjmallett			case '>':
44390744Sjmallett				addconstantstring("[[:>:]]");
44490744Sjmallett				break;
44590744Sjmallett			default:
44690744Sjmallett				addchars(p, 2);
44790744Sjmallett				break;
44890744Sjmallett			}
44990744Sjmallett			p+=2;
45090744Sjmallett			continue;
45190744Sjmallett		}
45290744Sjmallett		if (*p == '(' || *p == ')' || *p == '|')
45390744Sjmallett			addchar('\\');
45490744Sjmallett
45590744Sjmallett		addchar(*p);
45690744Sjmallett		p++;
45790744Sjmallett	}
45890744Sjmallett	return getstring();
45990744Sjmallett}
46090744Sjmallett
46190744Sjmallett/* patsubst(string, regexp, opt replacement) */
46290744Sjmallett/* argv[2]: string
46390744Sjmallett * argv[3]: regexp
46490744Sjmallett * argv[4]: opt rep
46590744Sjmallett */
46690744Sjmallettvoid
46790744Sjmallettdopatsubst(argv, argc)
46890744Sjmallett	const char *argv[];
46990744Sjmallett	int argc;
47090744Sjmallett{
47190744Sjmallett	int error;
47290744Sjmallett	regex_t re;
47390744Sjmallett	regmatch_t *pmatch;
47490744Sjmallett
47590744Sjmallett	if (argc <= 3) {
47690744Sjmallett		warnx("Too few arguments to patsubst");
47790744Sjmallett		return;
47890744Sjmallett	}
47990744Sjmallett	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
48090744Sjmallett	    REG_NEWLINE | REG_EXTENDED);
48190744Sjmallett	if (error != 0)
48290744Sjmallett		exit_regerror(error, &re);
48390744Sjmallett
48490744Sjmallett	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1));
48590744Sjmallett	do_subst(argv[2], &re,
48690744Sjmallett	    argc != 4 && argv[4] != NULL ? argv[4] : "", pmatch);
48790744Sjmallett	pbstr(getstring());
48890744Sjmallett	free(pmatch);
48990744Sjmallett	regfree(&re);
49090744Sjmallett}
49190744Sjmallett
49290744Sjmallettvoid
49390744Sjmallettdoregexp(argv, argc)
49490744Sjmallett	const char *argv[];
49590744Sjmallett	int argc;
49690744Sjmallett{
49790744Sjmallett	int error;
49890744Sjmallett	regex_t re;
49990744Sjmallett	regmatch_t *pmatch;
50090744Sjmallett
50190744Sjmallett	if (argc <= 3) {
50290744Sjmallett		warnx("Too few arguments to regexp");
50390744Sjmallett		return;
50490744Sjmallett	}
50590744Sjmallett	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
50690744Sjmallett	    REG_EXTENDED);
50790744Sjmallett	if (error != 0)
50890744Sjmallett		exit_regerror(error, &re);
50990744Sjmallett
51090744Sjmallett	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1));
51190744Sjmallett	if (argv[4] == NULL || argc == 4)
51290744Sjmallett		do_regexpindex(argv[2], &re, pmatch);
51390744Sjmallett	else
51490744Sjmallett		do_regexp(argv[2], &re, argv[4], pmatch);
51590744Sjmallett	free(pmatch);
51690744Sjmallett	regfree(&re);
51790744Sjmallett}
51890744Sjmallett
51990744Sjmallettvoid
52090744Sjmallettdoesyscmd(cmd)
52190744Sjmallett	const char *cmd;
52290744Sjmallett{
52390744Sjmallett	int p[2];
52490744Sjmallett	pid_t pid, cpid;
52590744Sjmallett	int cc;
52690744Sjmallett	int status;
52790744Sjmallett
52890744Sjmallett	/* Follow gnu m4 documentation: first flush buffers. */
52990744Sjmallett	fflush(NULL);
53090744Sjmallett
53190744Sjmallett	/* Just set up standard output, share stderr and stdin with m4 */
53290744Sjmallett	if (pipe(p) == -1)
53390744Sjmallett		err(1, "bad pipe");
53490744Sjmallett	switch(cpid = fork()) {
53590744Sjmallett	case -1:
53690744Sjmallett		err(1, "bad fork");
53790744Sjmallett		/* NOTREACHED */
53890744Sjmallett	case 0:
53990744Sjmallett		(void) close(p[0]);
54090744Sjmallett		(void) dup2(p[1], 1);
54190744Sjmallett		(void) close(p[1]);
54295095Sjmallett		execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
54390744Sjmallett		exit(1);
54490744Sjmallett	default:
54590744Sjmallett		/* Read result in two stages, since m4's buffer is
54690744Sjmallett		 * pushback-only. */
54790744Sjmallett		(void) close(p[1]);
54890744Sjmallett		do {
54990744Sjmallett			char result[BUFSIZE];
55090744Sjmallett			cc = read(p[0], result, sizeof result);
55190744Sjmallett			if (cc > 0)
55290744Sjmallett				addchars(result, cc);
55390744Sjmallett		} while (cc > 0 || (cc == -1 && errno == EINTR));
55490744Sjmallett
55590744Sjmallett		(void) close(p[0]);
55690744Sjmallett		while ((pid = wait(&status)) != cpid && pid >= 0)
55790744Sjmallett			continue;
55890744Sjmallett		pbstr(getstring());
55990744Sjmallett	}
56090744Sjmallett}
561