1228063Sbapt/* $OpenBSD: gnum4.c,v 1.42 2011/11/06 12:25:43 espie 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$");
2995061Sjmallett
30100014Sjmallett/*
3190744Sjmallett * functions needed to support gnu-m4 extensions, including a fake freezing
3290744Sjmallett */
3390744Sjmallett
3490744Sjmallett#include <sys/param.h>
3590744Sjmallett#include <sys/types.h>
3690744Sjmallett#include <sys/wait.h>
3790744Sjmallett#include <ctype.h>
38228063Sbapt#include <err.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 <errno.h>
4690744Sjmallett#include <unistd.h>
4790744Sjmallett#include "mdef.h"
4890744Sjmallett#include "stdd.h"
4990744Sjmallett#include "extern.h"
5090744Sjmallett
5190744Sjmallett
5290744Sjmallettint mimic_gnu = 0;
5390744Sjmallett
5490744Sjmallett/*
5590744Sjmallett * Support for include path search
56218909Sbrucec * First search in the current directory.
5790744Sjmallett * If not found, and the path is not absolute, include path kicks in.
5890744Sjmallett * First, -I options, in the order found on the command line.
5990744Sjmallett * Then M4PATH env variable
6090744Sjmallett */
6190744Sjmallett
62241777Sedstatic struct path_entry {
6390744Sjmallett	char *name;
6490744Sjmallett	struct path_entry *next;
6590744Sjmallett} *first, *last;
6690744Sjmallett
6790744Sjmallettstatic struct path_entry *new_path_entry(const char *);
6890744Sjmallettstatic void ensure_m4path(void);
6990744Sjmallettstatic struct input_file *dopath(struct input_file *, const char *);
7090744Sjmallett
7190744Sjmallettstatic struct path_entry *
7295887Sjmallettnew_path_entry(const char *dirname)
7390744Sjmallett{
7490744Sjmallett	struct path_entry *n;
7590744Sjmallett
7690744Sjmallett	n = malloc(sizeof(struct path_entry));
7790744Sjmallett	if (!n)
7890744Sjmallett		errx(1, "out of memory");
7990744Sjmallett	n->name = strdup(dirname);
8090744Sjmallett	if (!n->name)
8190744Sjmallett		errx(1, "out of memory");
8290744Sjmallett	n->next = 0;
8390744Sjmallett	return n;
8490744Sjmallett}
85100014Sjmallett
86100014Sjmallettvoid
8795887Sjmallettaddtoincludepath(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
10299939Sjmallettensure_m4path(void)
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");
113100014Sjmallett	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");
119100014Sjmallett	for (sweep = envpath;
12090744Sjmallett	    (path = strsep(&sweep, ":")) != NULL;)
12190744Sjmallett	    addtoincludepath(path);
12290744Sjmallett	free(envpath);
12390744Sjmallett}
12490744Sjmallett
12590744Sjmallettstatic
12690744Sjmallettstruct input_file *
12795887Sjmallettdopath(struct input_file *i, const char *filename)
12890744Sjmallett{
12990744Sjmallett	char path[MAXPATHLEN];
13090744Sjmallett	struct path_entry *pe;
13190744Sjmallett	FILE *f;
13290744Sjmallett
13390744Sjmallett	for (pe = first; pe; pe = pe->next) {
13490744Sjmallett		snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
135228063Sbapt		if ((f = fopen(path, "r")) != 0) {
13690744Sjmallett			set_input(i, f, path);
13790744Sjmallett			return i;
13890744Sjmallett		}
13990744Sjmallett	}
14090744Sjmallett	return NULL;
14190744Sjmallett}
14290744Sjmallett
14390744Sjmallettstruct input_file *
14495887Sjmallettfopen_trypath(struct input_file *i, const char *filename)
14590744Sjmallett{
14690744Sjmallett	FILE *f;
14790744Sjmallett
14890744Sjmallett	f = fopen(filename, "r");
14990744Sjmallett	if (f != NULL) {
15090744Sjmallett		set_input(i, f, filename);
15190744Sjmallett		return i;
15290744Sjmallett	}
15390744Sjmallett	if (filename[0] == '/')
15490744Sjmallett		return NULL;
15590744Sjmallett
15690744Sjmallett	ensure_m4path();
15790744Sjmallett
15890744Sjmallett	return dopath(i, filename);
15990744Sjmallett}
16090744Sjmallett
161100014Sjmallettvoid
16295887Sjmallettdoindir(const char *argv[], int argc)
16390744Sjmallett{
164228063Sbapt	ndptr n;
165228063Sbapt	struct macro_definition *p = NULL;
16690744Sjmallett
167228063Sbapt	n = lookup(argv[2]);
168228063Sbapt	if (n == NULL || (p = macro_getdef(n)) == NULL)
169228063Sbapt		m4errx(1, "indir: undefined macro %s.", argv[2]);
17090744Sjmallett	argv[1] = p->defn;
171228063Sbapt
172228063Sbapt	eval(argv+1, argc-1, p->type, is_traced(n));
17390744Sjmallett}
17490744Sjmallett
175100014Sjmallettvoid
17695887Sjmallettdobuiltin(const char *argv[], int argc)
17790744Sjmallett{
178228063Sbapt	ndptr p;
179228063Sbapt
18090744Sjmallett	argv[1] = NULL;
181228063Sbapt	p = macro_getbuiltin(argv[2]);
182228063Sbapt	if (p != NULL)
183228063Sbapt		eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p));
18490744Sjmallett	else
185228063Sbapt		m4errx(1, "unknown builtin %s.", argv[2]);
186100014Sjmallett}
18790744Sjmallett
18890744Sjmallett
18990744Sjmallett/* We need some temporary buffer space, as pb pushes BACK and substitution
19090744Sjmallett * proceeds forward... */
19190744Sjmallettstatic char *buffer;
19290744Sjmallettstatic size_t bufsize = 0;
19390744Sjmallettstatic size_t current = 0;
19490744Sjmallett
19590744Sjmallettstatic void addchars(const char *, size_t);
19695887Sjmallettstatic void addchar(int);
19790744Sjmallettstatic char *twiddle(const char *);
19890744Sjmallettstatic char *getstring(void);
19990744Sjmallettstatic void exit_regerror(int, regex_t *);
20090744Sjmallettstatic void do_subst(const char *, regex_t *, const char *, regmatch_t *);
20190744Sjmallettstatic void do_regexpindex(const char *, regex_t *, regmatch_t *);
20290744Sjmallettstatic void do_regexp(const char *, regex_t *, const char *, regmatch_t *);
203228063Sbaptstatic void add_sub(int, const char *, regex_t *, regmatch_t *);
20490744Sjmallettstatic void add_replace(const char *, regex_t *, const char *, regmatch_t *);
20590744Sjmallett#define addconstantstring(s) addchars((s), sizeof(s)-1)
20690744Sjmallett
207100014Sjmallettstatic void
20895887Sjmallettaddchars(const char *c, size_t n)
20990744Sjmallett{
21090744Sjmallett	if (n == 0)
21190744Sjmallett		return;
21290744Sjmallett	while (current + n > bufsize) {
21390744Sjmallett		if (bufsize == 0)
21490744Sjmallett			bufsize = 1024;
21590744Sjmallett		else
21690744Sjmallett			bufsize *= 2;
217228063Sbapt		buffer = xrealloc(buffer, bufsize, NULL);
21890744Sjmallett	}
21990744Sjmallett	memcpy(buffer+current, c, n);
22090744Sjmallett	current += n;
22190744Sjmallett}
22290744Sjmallett
223100014Sjmallettstatic void
22495887Sjmallettaddchar(int c)
22590744Sjmallett{
22690744Sjmallett	if (current +1 > bufsize) {
22790744Sjmallett		if (bufsize == 0)
22890744Sjmallett			bufsize = 1024;
22990744Sjmallett		else
23090744Sjmallett			bufsize *= 2;
231228063Sbapt		buffer = xrealloc(buffer, bufsize, NULL);
23290744Sjmallett	}
23390744Sjmallett	buffer[current++] = c;
23490744Sjmallett}
23590744Sjmallett
23690744Sjmallettstatic char *
23799939Sjmallettgetstring(void)
23890744Sjmallett{
23990744Sjmallett	addchar('\0');
24090744Sjmallett	current = 0;
24190744Sjmallett	return buffer;
24290744Sjmallett}
24390744Sjmallett
24490744Sjmallett
245100014Sjmallettstatic void
24695887Sjmallettexit_regerror(int er, regex_t *re)
24790744Sjmallett{
248228063Sbapt	size_t	errlen;
249228063Sbapt	char	*errbuf;
25090744Sjmallett
25190744Sjmallett	errlen = regerror(er, re, NULL, 0);
252228063Sbapt	errbuf = xalloc(errlen,
253228063Sbapt	    "malloc in regerror: %lu", (unsigned long)errlen);
25490744Sjmallett	regerror(er, re, errbuf, errlen);
255228063Sbapt	m4errx(1, "regular expression error: %s.", errbuf);
25690744Sjmallett}
25790744Sjmallett
25890744Sjmallettstatic void
259228063Sbaptadd_sub(int n, const char *string, regex_t *re, regmatch_t *pm)
26090744Sjmallett{
261228063Sbapt	if (n > (int)re->re_nsub)
262228063Sbapt		warnx("No subexpression %d", n);
26390744Sjmallett	/* Subexpressions that did not match are
26490744Sjmallett	 * not an error.  */
26590744Sjmallett	else if (pm[n].rm_so != -1 &&
26690744Sjmallett	    pm[n].rm_eo != -1) {
26790744Sjmallett		addchars(string + pm[n].rm_so,
26890744Sjmallett			pm[n].rm_eo - pm[n].rm_so);
26990744Sjmallett	}
27090744Sjmallett}
27190744Sjmallett
27290744Sjmallett/* Add replacement string to the output buffer, recognizing special
27390744Sjmallett * constructs and replacing them with substrings of the original string.
27490744Sjmallett */
275100014Sjmallettstatic void
27695887Sjmallettadd_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
27790744Sjmallett{
27890744Sjmallett	const char *p;
27990744Sjmallett
28090744Sjmallett	for (p = replace; *p != '\0'; p++) {
28190744Sjmallett		if (*p == '&' && !mimic_gnu) {
28290744Sjmallett			add_sub(0, string, re, pm);
28390744Sjmallett			continue;
28490744Sjmallett		}
28590744Sjmallett		if (*p == '\\') {
28690744Sjmallett			if (p[1] == '\\') {
28790744Sjmallett				addchar(p[1]);
28890744Sjmallett				p++;
28990744Sjmallett				continue;
29090744Sjmallett			}
29190744Sjmallett			if (p[1] == '&') {
29290744Sjmallett				if (mimic_gnu)
29390744Sjmallett					add_sub(0, string, re, pm);
29490744Sjmallett				else
29590744Sjmallett					addchar(p[1]);
29690744Sjmallett				p++;
29790744Sjmallett				continue;
29890744Sjmallett			}
29990744Sjmallett			if (isdigit(p[1])) {
30090744Sjmallett				add_sub(*(++p) - '0', string, re, pm);
30190744Sjmallett				continue;
30290744Sjmallett			}
30390744Sjmallett		}
304228063Sbapt		addchar(*p);
30590744Sjmallett	}
30690744Sjmallett}
30790744Sjmallett
308100014Sjmallettstatic void
30995887Sjmallettdo_subst(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
31090744Sjmallett{
31190744Sjmallett	int error;
31290744Sjmallett	int flags = 0;
31390744Sjmallett	const char *last_match = NULL;
31490744Sjmallett
31590744Sjmallett	while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
31690744Sjmallett		if (pm[0].rm_eo != 0) {
31790744Sjmallett			if (string[pm[0].rm_eo-1] == '\n')
31890744Sjmallett				flags = 0;
31990744Sjmallett			else
32090744Sjmallett				flags = REG_NOTBOL;
32190744Sjmallett		}
32290744Sjmallett
323100014Sjmallett		/* NULL length matches are special... We use the `vi-mode'
32490744Sjmallett		 * rule: don't allow a NULL-match at the last match
325100014Sjmallett		 * position.
32690744Sjmallett		 */
327100014Sjmallett		if (pm[0].rm_so == pm[0].rm_eo &&
32890744Sjmallett		    string + pm[0].rm_so == last_match) {
32990744Sjmallett			if (*string == '\0')
33090744Sjmallett				return;
33190744Sjmallett			addchar(*string);
33290744Sjmallett			if (*string++ == '\n')
33390744Sjmallett				flags = 0;
33490744Sjmallett			else
33590744Sjmallett				flags = REG_NOTBOL;
33690744Sjmallett			continue;
33790744Sjmallett		}
33890744Sjmallett		last_match = string + pm[0].rm_so;
33990744Sjmallett		addchars(string, pm[0].rm_so);
34090744Sjmallett		add_replace(string, re, replace, pm);
34190744Sjmallett		string += pm[0].rm_eo;
34290744Sjmallett	}
34390744Sjmallett	if (error != REG_NOMATCH)
34490744Sjmallett		exit_regerror(error, re);
34590744Sjmallett	pbstr(string);
34690744Sjmallett}
34790744Sjmallett
348100014Sjmallettstatic void
34995887Sjmallettdo_regexp(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
35090744Sjmallett{
35190744Sjmallett	int error;
35290744Sjmallett
35390744Sjmallett	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
354100014Sjmallett	case 0:
35590744Sjmallett		add_replace(string, re, replace, pm);
35690744Sjmallett		pbstr(getstring());
35790744Sjmallett		break;
35890744Sjmallett	case REG_NOMATCH:
35990744Sjmallett		break;
36090744Sjmallett	default:
36190744Sjmallett		exit_regerror(error, re);
36290744Sjmallett	}
36390744Sjmallett}
36490744Sjmallett
365100014Sjmallettstatic void
36695887Sjmallettdo_regexpindex(const char *string, regex_t *re, regmatch_t *pm)
36790744Sjmallett{
36890744Sjmallett	int error;
36990744Sjmallett
37090744Sjmallett	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
37190744Sjmallett	case 0:
37290744Sjmallett		pbunsigned(pm[0].rm_so);
37390744Sjmallett		break;
37490744Sjmallett	case REG_NOMATCH:
37590744Sjmallett		pbnum(-1);
37690744Sjmallett		break;
37790744Sjmallett	default:
37890744Sjmallett		exit_regerror(error, re);
37990744Sjmallett	}
38090744Sjmallett}
38190744Sjmallett
38290744Sjmallett/* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
38390744Sjmallett * says. So we twiddle with the regexp before passing it to regcomp.
38490744Sjmallett */
38590744Sjmallettstatic char *
38695887Sjmalletttwiddle(const char *p)
38790744Sjmallett{
388228063Sbapt	/* + at start of regexp is a normal character for Gnu m4 */
389228063Sbapt	if (*p == '^') {
390228063Sbapt		addchar(*p);
391228063Sbapt		p++;
392228063Sbapt	}
393228063Sbapt	if (*p == '+') {
394228063Sbapt		addchar('\\');
395228063Sbapt	}
39690744Sjmallett	/* This could use strcspn for speed... */
39790744Sjmallett	while (*p != '\0') {
39890744Sjmallett		if (*p == '\\') {
39990744Sjmallett			switch(p[1]) {
40090744Sjmallett			case '(':
40190744Sjmallett			case ')':
40290744Sjmallett			case '|':
40390744Sjmallett				addchar(p[1]);
40490744Sjmallett				break;
40590744Sjmallett			case 'w':
40690744Sjmallett				addconstantstring("[_a-zA-Z0-9]");
40790744Sjmallett				break;
40890744Sjmallett			case 'W':
40990744Sjmallett				addconstantstring("[^_a-zA-Z0-9]");
41090744Sjmallett				break;
41190744Sjmallett			case '<':
41290744Sjmallett				addconstantstring("[[:<:]]");
41390744Sjmallett				break;
41490744Sjmallett			case '>':
41590744Sjmallett				addconstantstring("[[:>:]]");
41690744Sjmallett				break;
41790744Sjmallett			default:
41890744Sjmallett				addchars(p, 2);
41990744Sjmallett				break;
42090744Sjmallett			}
42190744Sjmallett			p+=2;
42290744Sjmallett			continue;
42390744Sjmallett		}
42490744Sjmallett		if (*p == '(' || *p == ')' || *p == '|')
42590744Sjmallett			addchar('\\');
42690744Sjmallett
42790744Sjmallett		addchar(*p);
42890744Sjmallett		p++;
42990744Sjmallett	}
43090744Sjmallett	return getstring();
43190744Sjmallett}
43290744Sjmallett
43390744Sjmallett/* patsubst(string, regexp, opt replacement) */
43490744Sjmallett/* argv[2]: string
43590744Sjmallett * argv[3]: regexp
43690744Sjmallett * argv[4]: opt rep
43790744Sjmallett */
43890744Sjmallettvoid
43995887Sjmallettdopatsubst(const char *argv[], int argc)
44090744Sjmallett{
44190744Sjmallett	if (argc <= 3) {
44290744Sjmallett		warnx("Too few arguments to patsubst");
44390744Sjmallett		return;
44490744Sjmallett	}
445228063Sbapt	/* special case: empty regexp */
446228063Sbapt	if (argv[3][0] == '\0') {
447228063Sbapt		const char *s;
448228063Sbapt		size_t len;
449228063Sbapt		if (argc > 4 && argv[4])
450228063Sbapt			len = strlen(argv[4]);
451228063Sbapt		else
452228063Sbapt			len = 0;
453228063Sbapt		for (s = argv[2]; *s != '\0'; s++) {
454228063Sbapt			addchars(argv[4], len);
455228063Sbapt			addchar(*s);
456228063Sbapt		}
457228063Sbapt	} else {
458228063Sbapt		int error;
459228063Sbapt		regex_t re;
460228063Sbapt		regmatch_t *pmatch;
461228063Sbapt		int mode = REG_EXTENDED;
462228063Sbapt		size_t l = strlen(argv[3]);
463100014Sjmallett
464228063Sbapt		if (!mimic_gnu ||
465228063Sbapt		    (argv[3][0] == '^') ||
466228063Sbapt		    (l > 0 && argv[3][l-1] == '$'))
467228063Sbapt			mode |= REG_NEWLINE;
468228063Sbapt
469228063Sbapt		error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
470228063Sbapt		    mode);
471228063Sbapt		if (error != 0)
472228063Sbapt			exit_regerror(error, &re);
473228063Sbapt
474228063Sbapt		pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
475228063Sbapt		do_subst(argv[2], &re,
476228063Sbapt		    argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch);
477228063Sbapt		free(pmatch);
478228063Sbapt		regfree(&re);
479228063Sbapt	}
48090744Sjmallett	pbstr(getstring());
48190744Sjmallett}
48290744Sjmallett
48390744Sjmallettvoid
48495887Sjmallettdoregexp(const char *argv[], int argc)
48590744Sjmallett{
48690744Sjmallett	int error;
48790744Sjmallett	regex_t re;
48890744Sjmallett	regmatch_t *pmatch;
48990744Sjmallett
49090744Sjmallett	if (argc <= 3) {
49190744Sjmallett		warnx("Too few arguments to regexp");
49290744Sjmallett		return;
49390744Sjmallett	}
494228063Sbapt	/* special gnu case */
495228063Sbapt	if (argv[3][0] == '\0' && mimic_gnu) {
496228063Sbapt		if (argc == 4 || argv[4] == NULL)
497228063Sbapt			return;
498228063Sbapt		else
499228063Sbapt			pbstr(argv[4]);
500228063Sbapt	}
501100014Sjmallett	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
502228063Sbapt	    REG_EXTENDED|REG_NEWLINE);
50390744Sjmallett	if (error != 0)
50490744Sjmallett		exit_regerror(error, &re);
505100014Sjmallett
506228063Sbapt	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
507228063Sbapt	if (argc == 4 || argv[4] == NULL)
50890744Sjmallett		do_regexpindex(argv[2], &re, pmatch);
50990744Sjmallett	else
51090744Sjmallett		do_regexp(argv[2], &re, argv[4], pmatch);
51190744Sjmallett	free(pmatch);
51290744Sjmallett	regfree(&re);
51390744Sjmallett}
51490744Sjmallett
51590744Sjmallettvoid
516228063Sbaptdoformat(const char *argv[], int argc)
517228063Sbapt{
518228063Sbapt	const char *format = argv[2];
519228063Sbapt	int pos = 3;
520228063Sbapt	int left_padded;
521228063Sbapt	long width;
522228063Sbapt	size_t l;
523228063Sbapt	const char *thisarg = NULL;
524228063Sbapt	char temp[2];
525228063Sbapt	long extra;
526228063Sbapt
527228063Sbapt	while (*format != 0) {
528228063Sbapt		if (*format != '%') {
529228063Sbapt			addchar(*format++);
530228063Sbapt			continue;
531228063Sbapt		}
532228063Sbapt
533228063Sbapt		format++;
534228063Sbapt		if (*format == '%') {
535228063Sbapt			addchar(*format++);
536228063Sbapt			continue;
537228063Sbapt		}
538228063Sbapt		if (*format == 0) {
539228063Sbapt			addchar('%');
540228063Sbapt			break;
541228063Sbapt		}
542228063Sbapt
543228063Sbapt		if (*format == '*') {
544228063Sbapt			format++;
545228063Sbapt			if (pos >= argc)
546228063Sbapt				m4errx(1,
547228063Sbapt				    "Format with too many format specifiers.");
548228063Sbapt			width = strtol(argv[pos++], NULL, 10);
549228063Sbapt		} else {
550228063Sbapt			width = strtol(format, __DECONST(char **,&format), 10);
551228063Sbapt		}
552228063Sbapt		if (width < 0) {
553228063Sbapt			left_padded = 1;
554228063Sbapt			width = -width;
555228063Sbapt		} else {
556228063Sbapt			left_padded = 0;
557228063Sbapt		}
558228063Sbapt		if (*format == '.') {
559228063Sbapt			format++;
560228063Sbapt			if (*format == '*') {
561228063Sbapt				format++;
562228063Sbapt				if (pos >= argc)
563228063Sbapt					m4errx(1,
564228063Sbapt					    "Format with too many format specifiers.");
565228063Sbapt				extra = strtol(argv[pos++], NULL, 10);
566228063Sbapt			} else {
567228063Sbapt				extra = strtol(format, __DECONST(char **, &format), 10);
568228063Sbapt			}
569228063Sbapt		} else {
570228063Sbapt			extra = LONG_MAX;
571228063Sbapt		}
572228063Sbapt		if (pos >= argc)
573228063Sbapt			m4errx(1, "Format with too many format specifiers.");
574228063Sbapt		switch(*format) {
575228063Sbapt		case 's':
576228063Sbapt			thisarg = argv[pos++];
577228063Sbapt			break;
578228063Sbapt		case 'c':
579228063Sbapt			temp[0] = strtoul(argv[pos++], NULL, 10);
580228063Sbapt			temp[1] = 0;
581228063Sbapt			thisarg = temp;
582228063Sbapt			break;
583228063Sbapt		default:
584228063Sbapt			m4errx(1, "Unsupported format specification: %s.",
585228063Sbapt			    argv[2]);
586228063Sbapt		}
587228063Sbapt		format++;
588228063Sbapt		l = strlen(thisarg);
589228063Sbapt		if ((long)l > extra)
590228063Sbapt			l = extra;
591228063Sbapt		if (!left_padded) {
592228063Sbapt			while ((long)l < width--)
593228063Sbapt				addchar(' ');
594228063Sbapt		}
595228063Sbapt		addchars(thisarg, l);
596228063Sbapt		if (left_padded) {
597228063Sbapt			while ((long)l < width--)
598228063Sbapt				addchar(' ');
599228063Sbapt		}
600228063Sbapt	}
601228063Sbapt	pbstr(getstring());
602228063Sbapt}
603228063Sbapt
604228063Sbaptvoid
60595887Sjmallettdoesyscmd(const char *cmd)
60690744Sjmallett{
60790744Sjmallett	int p[2];
60890744Sjmallett	pid_t pid, cpid;
609228063Sbapt	char *argv[4];
61090744Sjmallett	int cc;
61190744Sjmallett	int status;
61290744Sjmallett
61390744Sjmallett	/* Follow gnu m4 documentation: first flush buffers. */
61490744Sjmallett	fflush(NULL);
61590744Sjmallett
616228063Sbapt	argv[0] = __DECONST(char *, "sh");
617228063Sbapt	argv[1] = __DECONST(char *, "-c");
618228063Sbapt	argv[2] = __DECONST(char *, cmd);
619228063Sbapt	argv[3] = NULL;
620228063Sbapt
62190744Sjmallett	/* Just set up standard output, share stderr and stdin with m4 */
62290744Sjmallett	if (pipe(p) == -1)
62390744Sjmallett		err(1, "bad pipe");
62490744Sjmallett	switch(cpid = fork()) {
62590744Sjmallett	case -1:
62690744Sjmallett		err(1, "bad fork");
62790744Sjmallett		/* NOTREACHED */
62890744Sjmallett	case 0:
62990744Sjmallett		(void) close(p[0]);
63090744Sjmallett		(void) dup2(p[1], 1);
63190744Sjmallett		(void) close(p[1]);
632228063Sbapt		execv(_PATH_BSHELL, argv);
63390744Sjmallett		exit(1);
63490744Sjmallett	default:
63590744Sjmallett		/* Read result in two stages, since m4's buffer is
63690744Sjmallett		 * pushback-only. */
63790744Sjmallett		(void) close(p[1]);
63890744Sjmallett		do {
63990744Sjmallett			char result[BUFSIZE];
64090744Sjmallett			cc = read(p[0], result, sizeof result);
64190744Sjmallett			if (cc > 0)
64290744Sjmallett				addchars(result, cc);
64390744Sjmallett		} while (cc > 0 || (cc == -1 && errno == EINTR));
64490744Sjmallett
64590744Sjmallett		(void) close(p[0]);
64690744Sjmallett		while ((pid = wait(&status)) != cpid && pid >= 0)
64790744Sjmallett			continue;
64890744Sjmallett		pbstr(getstring());
64990744Sjmallett	}
65090744Sjmallett}
651228063Sbapt
652228063Sbaptvoid
653228063Sbaptgetdivfile(const char *name)
654228063Sbapt{
655228063Sbapt	FILE *f;
656228063Sbapt	int c;
657228063Sbapt
658228063Sbapt	f = fopen(name, "r");
659228063Sbapt	if (!f)
660228063Sbapt		return;
661228063Sbapt
662228063Sbapt	while ((c = getc(f))!= EOF)
663228063Sbapt		putc(c, active);
664228063Sbapt	(void) fclose(f);
665228063Sbapt}
666