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