xref: /minix3/usr.bin/m4/gnum4.c (revision 71c7dcb9ce66b73f93cf114720b764542608dc95)
1*71c7dcb9SLionel Sambuc /* $NetBSD: gnum4.c,v 1.9 2012/03/20 20:34:58 matt Exp $ */
22e8d1edaSArun Thomas /* $OpenBSD: gnum4.c,v 1.39 2008/08/21 21:01:04 espie Exp $ */
32e8d1edaSArun Thomas 
42e8d1edaSArun Thomas /*
52e8d1edaSArun Thomas  * Copyright (c) 1999 Marc Espie
62e8d1edaSArun Thomas  *
72e8d1edaSArun Thomas  * Redistribution and use in source and binary forms, with or without
82e8d1edaSArun Thomas  * modification, are permitted provided that the following conditions
92e8d1edaSArun Thomas  * are met:
102e8d1edaSArun Thomas  * 1. Redistributions of source code must retain the above copyright
112e8d1edaSArun Thomas  *    notice, this list of conditions and the following disclaimer.
122e8d1edaSArun Thomas  * 2. Redistributions in binary form must reproduce the above copyright
132e8d1edaSArun Thomas  *    notice, this list of conditions and the following disclaimer in the
142e8d1edaSArun Thomas  *    documentation and/or other materials provided with the distribution.
152e8d1edaSArun Thomas  *
162e8d1edaSArun Thomas  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
172e8d1edaSArun Thomas  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182e8d1edaSArun Thomas  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
192e8d1edaSArun Thomas  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
202e8d1edaSArun Thomas  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212e8d1edaSArun Thomas  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222e8d1edaSArun Thomas  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232e8d1edaSArun Thomas  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242e8d1edaSArun Thomas  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252e8d1edaSArun Thomas  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262e8d1edaSArun Thomas  * SUCH DAMAGE.
272e8d1edaSArun Thomas  */
282e8d1edaSArun Thomas 
292e8d1edaSArun Thomas /*
302e8d1edaSArun Thomas  * functions needed to support gnu-m4 extensions, including a fake freezing
312e8d1edaSArun Thomas  */
322e8d1edaSArun Thomas #if HAVE_NBTOOL_CONFIG_H
332e8d1edaSArun Thomas #include "nbtool_config.h"
342e8d1edaSArun Thomas #endif
352e8d1edaSArun Thomas #include <sys/cdefs.h>
36*71c7dcb9SLionel Sambuc __RCSID("$NetBSD: gnum4.c,v 1.9 2012/03/20 20:34:58 matt Exp $");
372e8d1edaSArun Thomas 
382e8d1edaSArun Thomas #include <sys/param.h>
392e8d1edaSArun Thomas #include <sys/types.h>
402e8d1edaSArun Thomas #include <sys/wait.h>
412e8d1edaSArun Thomas #include <ctype.h>
422e8d1edaSArun Thomas #include <err.h>
432e8d1edaSArun Thomas #include <paths.h>
442e8d1edaSArun Thomas #include <regex.h>
452e8d1edaSArun Thomas #include <stddef.h>
462e8d1edaSArun Thomas #include <stdlib.h>
472e8d1edaSArun Thomas #include <stdio.h>
482e8d1edaSArun Thomas #include <string.h>
492e8d1edaSArun Thomas #include <errno.h>
502e8d1edaSArun Thomas #include <unistd.h>
512e8d1edaSArun Thomas #include "mdef.h"
522e8d1edaSArun Thomas #include "stdd.h"
532e8d1edaSArun Thomas #include "extern.h"
542e8d1edaSArun Thomas 
552e8d1edaSArun Thomas 
562e8d1edaSArun Thomas int mimic_gnu = 0;
572e8d1edaSArun Thomas #ifndef SIZE_T_MAX
582e8d1edaSArun Thomas #define SIZE_T_MAX (size_t)~0ull
592e8d1edaSArun Thomas #endif
602e8d1edaSArun Thomas 
612e8d1edaSArun Thomas /*
622e8d1edaSArun Thomas  * Support for include path search
632e8d1edaSArun Thomas  * First search in the current directory.
642e8d1edaSArun Thomas  * If not found, and the path is not absolute, include path kicks in.
652e8d1edaSArun Thomas  * First, -I options, in the order found on the command line.
662e8d1edaSArun Thomas  * Then M4PATH env variable
672e8d1edaSArun Thomas  */
682e8d1edaSArun Thomas 
692e8d1edaSArun Thomas struct path_entry {
702e8d1edaSArun Thomas 	char *name;
712e8d1edaSArun Thomas 	struct path_entry *next;
722e8d1edaSArun Thomas } *first, *last;
732e8d1edaSArun Thomas 
742e8d1edaSArun Thomas static struct path_entry *new_path_entry(const char *);
752e8d1edaSArun Thomas static void ensure_m4path(void);
762e8d1edaSArun Thomas static struct input_file *dopath(struct input_file *, const char *);
772e8d1edaSArun Thomas 
782e8d1edaSArun Thomas static struct path_entry *
new_path_entry(const char * dirname)792e8d1edaSArun Thomas new_path_entry(const char *dirname)
802e8d1edaSArun Thomas {
812e8d1edaSArun Thomas 	struct path_entry *n;
822e8d1edaSArun Thomas 
832e8d1edaSArun Thomas 	n = malloc(sizeof(struct path_entry));
842e8d1edaSArun Thomas 	if (!n)
852e8d1edaSArun Thomas 		errx(1, "out of memory");
862e8d1edaSArun Thomas 	n->name = strdup(dirname);
872e8d1edaSArun Thomas 	if (!n->name)
882e8d1edaSArun Thomas 		errx(1, "out of memory");
892e8d1edaSArun Thomas 	n->next = 0;
902e8d1edaSArun Thomas 	return n;
912e8d1edaSArun Thomas }
922e8d1edaSArun Thomas 
932e8d1edaSArun Thomas void
addtoincludepath(const char * dirname)942e8d1edaSArun Thomas addtoincludepath(const char *dirname)
952e8d1edaSArun Thomas {
962e8d1edaSArun Thomas 	struct path_entry *n;
972e8d1edaSArun Thomas 
982e8d1edaSArun Thomas 	n = new_path_entry(dirname);
992e8d1edaSArun Thomas 
1002e8d1edaSArun Thomas 	if (last) {
1012e8d1edaSArun Thomas 		last->next = n;
1022e8d1edaSArun Thomas 		last = n;
1032e8d1edaSArun Thomas 	}
1042e8d1edaSArun Thomas 	else
1052e8d1edaSArun Thomas 		last = first = n;
1062e8d1edaSArun Thomas }
1072e8d1edaSArun Thomas 
1082e8d1edaSArun Thomas static void
ensure_m4path(void)109*71c7dcb9SLionel Sambuc ensure_m4path(void)
1102e8d1edaSArun Thomas {
1112e8d1edaSArun Thomas 	static int envpathdone = 0;
1122e8d1edaSArun Thomas 	char *envpath;
1132e8d1edaSArun Thomas 	char *sweep;
1142e8d1edaSArun Thomas 	char *path;
1152e8d1edaSArun Thomas 
1162e8d1edaSArun Thomas 	if (envpathdone)
1172e8d1edaSArun Thomas 		return;
1182e8d1edaSArun Thomas 	envpathdone = TRUE;
1192e8d1edaSArun Thomas 	envpath = getenv("M4PATH");
1202e8d1edaSArun Thomas 	if (!envpath)
1212e8d1edaSArun Thomas 		return;
1222e8d1edaSArun Thomas 	/* for portability: getenv result is read-only */
1232e8d1edaSArun Thomas 	envpath = strdup(envpath);
1242e8d1edaSArun Thomas 	if (!envpath)
1252e8d1edaSArun Thomas 		errx(1, "out of memory");
1262e8d1edaSArun Thomas 	for (sweep = envpath;
1272e8d1edaSArun Thomas 	    (path = strsep(&sweep, ":")) != NULL;)
1282e8d1edaSArun Thomas 	    addtoincludepath(path);
1292e8d1edaSArun Thomas 	free(envpath);
1302e8d1edaSArun Thomas }
1312e8d1edaSArun Thomas 
1322e8d1edaSArun Thomas static
1332e8d1edaSArun Thomas struct input_file *
dopath(struct input_file * i,const char * filename)1342e8d1edaSArun Thomas dopath(struct input_file *i, const char *filename)
1352e8d1edaSArun Thomas {
1362e8d1edaSArun Thomas 	char path[MAXPATHLEN];
1372e8d1edaSArun Thomas 	struct path_entry *pe;
1382e8d1edaSArun Thomas 	FILE *f;
1392e8d1edaSArun Thomas 
1402e8d1edaSArun Thomas 	for (pe = first; pe; pe = pe->next) {
1412e8d1edaSArun Thomas 		snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
1422e8d1edaSArun Thomas 		if ((f = fopen(path, "r")) != 0) {
1432e8d1edaSArun Thomas 			set_input(i, f, path);
1442e8d1edaSArun Thomas 			return i;
1452e8d1edaSArun Thomas 		}
1462e8d1edaSArun Thomas 	}
1472e8d1edaSArun Thomas 	return NULL;
1482e8d1edaSArun Thomas }
1492e8d1edaSArun Thomas 
1502e8d1edaSArun Thomas struct input_file *
fopen_trypath(struct input_file * i,const char * filename)1512e8d1edaSArun Thomas fopen_trypath(struct input_file *i, const char *filename)
1522e8d1edaSArun Thomas {
1532e8d1edaSArun Thomas 	FILE *f;
1542e8d1edaSArun Thomas 
1552e8d1edaSArun Thomas 	f = fopen(filename, "r");
1562e8d1edaSArun Thomas 	if (f != NULL) {
1572e8d1edaSArun Thomas 		set_input(i, f, filename);
1582e8d1edaSArun Thomas 		return i;
1592e8d1edaSArun Thomas 	}
1602e8d1edaSArun Thomas 	if (filename[0] == '/')
1612e8d1edaSArun Thomas 		return NULL;
1622e8d1edaSArun Thomas 
1632e8d1edaSArun Thomas 	ensure_m4path();
1642e8d1edaSArun Thomas 
1652e8d1edaSArun Thomas 	return dopath(i, filename);
1662e8d1edaSArun Thomas }
1672e8d1edaSArun Thomas 
1682e8d1edaSArun Thomas void
doindir(const char * argv[],int argc)1692e8d1edaSArun Thomas doindir(const char *argv[], int argc)
1702e8d1edaSArun Thomas {
1712e8d1edaSArun Thomas 	ndptr n;
1722e8d1edaSArun Thomas 	struct macro_definition *p;
1732e8d1edaSArun Thomas 
1742e8d1edaSArun Thomas 	n = lookup(argv[2]);
1752e8d1edaSArun Thomas 	if (n == NULL || (p = macro_getdef(n)) == NULL)
1762e8d1edaSArun Thomas 		m4errx(1, "indir: undefined macro %s.", argv[2]);
1772e8d1edaSArun Thomas 	argv[1] = p->defn;
1782e8d1edaSArun Thomas 
1792e8d1edaSArun Thomas 	eval(argv+1, argc-1, p->type, is_traced(n));
1802e8d1edaSArun Thomas }
1812e8d1edaSArun Thomas 
1822e8d1edaSArun Thomas void
dobuiltin(const char * argv[],int argc)1832e8d1edaSArun Thomas dobuiltin(const char *argv[], int argc)
1842e8d1edaSArun Thomas {
1852e8d1edaSArun Thomas 	ndptr p;
1862e8d1edaSArun Thomas 
1872e8d1edaSArun Thomas 	argv[1] = NULL;
1882e8d1edaSArun Thomas 	p = macro_getbuiltin(argv[2]);
1892e8d1edaSArun Thomas 	if (p != NULL)
1902e8d1edaSArun Thomas 		eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p));
1912e8d1edaSArun Thomas 	else
1922e8d1edaSArun Thomas 		m4errx(1, "unknown builtin %s.", argv[2]);
1932e8d1edaSArun Thomas }
1942e8d1edaSArun Thomas 
1952e8d1edaSArun Thomas 
1962e8d1edaSArun Thomas /* We need some temporary buffer space, as pb pushes BACK and substitution
1972e8d1edaSArun Thomas  * proceeds forward... */
1982e8d1edaSArun Thomas static char *buffer;
1992e8d1edaSArun Thomas static size_t bufsize = 0;
2002e8d1edaSArun Thomas static size_t current = 0;
2012e8d1edaSArun Thomas 
2022e8d1edaSArun Thomas static void addchars(const char *, size_t);
2032e8d1edaSArun Thomas static void addchar(int);
2042e8d1edaSArun Thomas static char *twiddle(const char *);
2052e8d1edaSArun Thomas static char *getstring(void);
206*71c7dcb9SLionel Sambuc static void exit_regerror(int, regex_t *) __dead;
2072e8d1edaSArun Thomas static void do_subst(const char *, regex_t *, const char *, regmatch_t *);
2082e8d1edaSArun Thomas static void do_regexpindex(const char *, regex_t *, regmatch_t *);
2092e8d1edaSArun Thomas static void do_regexp(const char *, regex_t *, const char *, regmatch_t *);
2102e8d1edaSArun Thomas static void add_sub(size_t, const char *, regex_t *, regmatch_t *);
2112e8d1edaSArun Thomas static void add_replace(const char *, regex_t *, const char *, regmatch_t *);
2122e8d1edaSArun Thomas #define addconstantstring(s) addchars((s), sizeof(s)-1)
2132e8d1edaSArun Thomas 
2142e8d1edaSArun Thomas static void
addchars(const char * c,size_t n)2152e8d1edaSArun Thomas addchars(const char *c, size_t n)
2162e8d1edaSArun Thomas {
2172e8d1edaSArun Thomas 	if (n == 0)
2182e8d1edaSArun Thomas 		return;
2192e8d1edaSArun Thomas 	while (current + n > bufsize) {
2202e8d1edaSArun Thomas 		if (bufsize == 0)
2212e8d1edaSArun Thomas 			bufsize = 1024;
2222e8d1edaSArun Thomas 		else
2232e8d1edaSArun Thomas 			bufsize *= 2;
2242e8d1edaSArun Thomas 		buffer = xrealloc(buffer, bufsize, NULL);
2252e8d1edaSArun Thomas 	}
2262e8d1edaSArun Thomas 	memcpy(buffer+current, c, n);
2272e8d1edaSArun Thomas 	current += n;
2282e8d1edaSArun Thomas }
2292e8d1edaSArun Thomas 
2302e8d1edaSArun Thomas static void
addchar(int c)2312e8d1edaSArun Thomas addchar(int c)
2322e8d1edaSArun Thomas {
2332e8d1edaSArun Thomas 	if (current +1 > bufsize) {
2342e8d1edaSArun Thomas 		if (bufsize == 0)
2352e8d1edaSArun Thomas 			bufsize = 1024;
2362e8d1edaSArun Thomas 		else
2372e8d1edaSArun Thomas 			bufsize *= 2;
2382e8d1edaSArun Thomas 		buffer = xrealloc(buffer, bufsize, NULL);
2392e8d1edaSArun Thomas 	}
2402e8d1edaSArun Thomas 	buffer[current++] = c;
2412e8d1edaSArun Thomas }
2422e8d1edaSArun Thomas 
2432e8d1edaSArun Thomas static char *
getstring(void)244*71c7dcb9SLionel Sambuc getstring(void)
2452e8d1edaSArun Thomas {
2462e8d1edaSArun Thomas 	addchar('\0');
2472e8d1edaSArun Thomas 	current = 0;
2482e8d1edaSArun Thomas 	return buffer;
2492e8d1edaSArun Thomas }
2502e8d1edaSArun Thomas 
2512e8d1edaSArun Thomas 
2522e8d1edaSArun Thomas static void
exit_regerror(int er,regex_t * re)2532e8d1edaSArun Thomas exit_regerror(int er, regex_t *re)
2542e8d1edaSArun Thomas {
2552e8d1edaSArun Thomas 	size_t 	errlen;
2562e8d1edaSArun Thomas 	char 	*errbuf;
2572e8d1edaSArun Thomas 
2582e8d1edaSArun Thomas 	errlen = regerror(er, re, NULL, 0);
2592e8d1edaSArun Thomas 	errbuf = xalloc(errlen,
2602e8d1edaSArun Thomas 	    "malloc in regerror: %lu", (unsigned long)errlen);
2612e8d1edaSArun Thomas 	regerror(er, re, errbuf, errlen);
2622e8d1edaSArun Thomas 	m4errx(1, "regular expression error: %s.", errbuf);
2632e8d1edaSArun Thomas }
2642e8d1edaSArun Thomas 
2652e8d1edaSArun Thomas static void
add_sub(size_t n,const char * string,regex_t * re,regmatch_t * pm)2662e8d1edaSArun Thomas add_sub(size_t n, const char *string, regex_t *re, regmatch_t *pm)
2672e8d1edaSArun Thomas {
2682e8d1edaSArun Thomas 	if (n > re->re_nsub)
2692e8d1edaSArun Thomas 		warnx("No subexpression %zu", n);
2702e8d1edaSArun Thomas 	/* Subexpressions that did not match are
2712e8d1edaSArun Thomas 	 * not an error.  */
2722e8d1edaSArun Thomas 	else if (pm[n].rm_so != -1 &&
2732e8d1edaSArun Thomas 	    pm[n].rm_eo != -1) {
2742e8d1edaSArun Thomas 		addchars(string + pm[n].rm_so,
2752e8d1edaSArun Thomas 			pm[n].rm_eo - pm[n].rm_so);
2762e8d1edaSArun Thomas 	}
2772e8d1edaSArun Thomas }
2782e8d1edaSArun Thomas 
2792e8d1edaSArun Thomas /* Add replacement string to the output buffer, recognizing special
2802e8d1edaSArun Thomas  * constructs and replacing them with substrings of the original string.
2812e8d1edaSArun Thomas  */
2822e8d1edaSArun Thomas static void
add_replace(const char * string,regex_t * re,const char * replace,regmatch_t * pm)2832e8d1edaSArun Thomas add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
2842e8d1edaSArun Thomas {
2852e8d1edaSArun Thomas 	const char *p;
2862e8d1edaSArun Thomas 
2872e8d1edaSArun Thomas 	for (p = replace; *p != '\0'; p++) {
2882e8d1edaSArun Thomas 		if (*p == '&' && !mimic_gnu) {
2892e8d1edaSArun Thomas 			add_sub(0, string, re, pm);
2902e8d1edaSArun Thomas 			continue;
2912e8d1edaSArun Thomas 		}
2922e8d1edaSArun Thomas 		if (*p == '\\') {
2932e8d1edaSArun Thomas 			if (p[1] == '\\') {
2942e8d1edaSArun Thomas 				addchar(p[1]);
2952e8d1edaSArun Thomas 				p++;
2962e8d1edaSArun Thomas 				continue;
2972e8d1edaSArun Thomas 			}
2982e8d1edaSArun Thomas 			if (p[1] == '&') {
2992e8d1edaSArun Thomas 				if (mimic_gnu)
3002e8d1edaSArun Thomas 					add_sub(0, string, re, pm);
3012e8d1edaSArun Thomas 				else
3022e8d1edaSArun Thomas 					addchar(p[1]);
3032e8d1edaSArun Thomas 				p++;
3042e8d1edaSArun Thomas 				continue;
3052e8d1edaSArun Thomas 			}
3062e8d1edaSArun Thomas 			if (isdigit((unsigned char)p[1])) {
3072e8d1edaSArun Thomas 				add_sub(*(++p) - '0', string, re, pm);
3082e8d1edaSArun Thomas 				continue;
3092e8d1edaSArun Thomas 			}
3102e8d1edaSArun Thomas 		}
3112e8d1edaSArun Thomas 	    	addchar(*p);
3122e8d1edaSArun Thomas 	}
3132e8d1edaSArun Thomas }
3142e8d1edaSArun Thomas 
3152e8d1edaSArun Thomas static void
do_subst(const char * string,regex_t * re,const char * replace,regmatch_t * pm)3162e8d1edaSArun Thomas do_subst(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
3172e8d1edaSArun Thomas {
3182e8d1edaSArun Thomas 	int error;
3192e8d1edaSArun Thomas 	int flags = 0;
3202e8d1edaSArun Thomas 	const char *last_match = NULL;
3212e8d1edaSArun Thomas 
3222e8d1edaSArun Thomas 	while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
3232e8d1edaSArun Thomas 		if (pm[0].rm_eo != 0) {
3242e8d1edaSArun Thomas 			if (string[pm[0].rm_eo-1] == '\n')
3252e8d1edaSArun Thomas 				flags = 0;
3262e8d1edaSArun Thomas 			else
3272e8d1edaSArun Thomas 				flags = REG_NOTBOL;
3282e8d1edaSArun Thomas 		}
3292e8d1edaSArun Thomas 
3302e8d1edaSArun Thomas 		/* NULL length matches are special... We use the `vi-mode'
3312e8d1edaSArun Thomas 		 * rule: don't allow a NULL-match at the last match
3322e8d1edaSArun Thomas 		 * position.
3332e8d1edaSArun Thomas 		 */
3342e8d1edaSArun Thomas 		if (pm[0].rm_so == pm[0].rm_eo &&
3352e8d1edaSArun Thomas 		    string + pm[0].rm_so == last_match) {
3362e8d1edaSArun Thomas 			if (*string == '\0')
3372e8d1edaSArun Thomas 				return;
3382e8d1edaSArun Thomas 			addchar(*string);
3392e8d1edaSArun Thomas 			if (*string++ == '\n')
3402e8d1edaSArun Thomas 				flags = 0;
3412e8d1edaSArun Thomas 			else
3422e8d1edaSArun Thomas 				flags = REG_NOTBOL;
3432e8d1edaSArun Thomas 			continue;
3442e8d1edaSArun Thomas 		}
3452e8d1edaSArun Thomas 		last_match = string + pm[0].rm_so;
3462e8d1edaSArun Thomas 		addchars(string, pm[0].rm_so);
3472e8d1edaSArun Thomas 		add_replace(string, re, replace, pm);
3482e8d1edaSArun Thomas 		string += pm[0].rm_eo;
3492e8d1edaSArun Thomas 	}
3502e8d1edaSArun Thomas 	if (error != REG_NOMATCH)
3512e8d1edaSArun Thomas 		exit_regerror(error, re);
3522e8d1edaSArun Thomas 	pbstr(string);
3532e8d1edaSArun Thomas }
3542e8d1edaSArun Thomas 
3552e8d1edaSArun Thomas static void
do_regexp(const char * string,regex_t * re,const char * replace,regmatch_t * pm)3562e8d1edaSArun Thomas do_regexp(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
3572e8d1edaSArun Thomas {
3582e8d1edaSArun Thomas 	int error;
3592e8d1edaSArun Thomas 
3602e8d1edaSArun Thomas 	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
3612e8d1edaSArun Thomas 	case 0:
3622e8d1edaSArun Thomas 		add_replace(string, re, replace, pm);
3632e8d1edaSArun Thomas 		pbstr(getstring());
3642e8d1edaSArun Thomas 		break;
3652e8d1edaSArun Thomas 	case REG_NOMATCH:
3662e8d1edaSArun Thomas 		break;
3672e8d1edaSArun Thomas 	default:
3682e8d1edaSArun Thomas 		exit_regerror(error, re);
3692e8d1edaSArun Thomas 	}
3702e8d1edaSArun Thomas }
3712e8d1edaSArun Thomas 
3722e8d1edaSArun Thomas static void
do_regexpindex(const char * string,regex_t * re,regmatch_t * pm)3732e8d1edaSArun Thomas do_regexpindex(const char *string, regex_t *re, regmatch_t *pm)
3742e8d1edaSArun Thomas {
3752e8d1edaSArun Thomas 	int error;
3762e8d1edaSArun Thomas 
3772e8d1edaSArun Thomas 	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
3782e8d1edaSArun Thomas 	case 0:
3792e8d1edaSArun Thomas 		pbunsigned(pm[0].rm_so);
3802e8d1edaSArun Thomas 		break;
3812e8d1edaSArun Thomas 	case REG_NOMATCH:
3822e8d1edaSArun Thomas 		pbnum(-1);
3832e8d1edaSArun Thomas 		break;
3842e8d1edaSArun Thomas 	default:
3852e8d1edaSArun Thomas 		exit_regerror(error, re);
3862e8d1edaSArun Thomas 	}
3872e8d1edaSArun Thomas }
3882e8d1edaSArun Thomas 
3892e8d1edaSArun Thomas /* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
3902e8d1edaSArun Thomas  * says. So we twiddle with the regexp before passing it to regcomp.
3912e8d1edaSArun Thomas  */
3922e8d1edaSArun Thomas static char *
twiddle(const char * p)3932e8d1edaSArun Thomas twiddle(const char *p)
3942e8d1edaSArun Thomas {
3952e8d1edaSArun Thomas 	/* + at start of regexp is a normal character for Gnu m4 */
3962e8d1edaSArun Thomas 	if (*p == '^') {
3972e8d1edaSArun Thomas 		addchar(*p);
3982e8d1edaSArun Thomas 		p++;
3992e8d1edaSArun Thomas 	}
4002e8d1edaSArun Thomas 	if (*p == '+') {
4012e8d1edaSArun Thomas 		addchar('\\');
4022e8d1edaSArun Thomas 	}
4032e8d1edaSArun Thomas 	/* This could use strcspn for speed... */
4042e8d1edaSArun Thomas 	while (*p != '\0') {
4052e8d1edaSArun Thomas 		if (*p == '\\') {
4062e8d1edaSArun Thomas 			switch(p[1]) {
4072e8d1edaSArun Thomas 			case '(':
4082e8d1edaSArun Thomas 			case ')':
4092e8d1edaSArun Thomas 			case '|':
4102e8d1edaSArun Thomas 				addchar(p[1]);
4112e8d1edaSArun Thomas 				break;
4122e8d1edaSArun Thomas 			case 'w':
4132e8d1edaSArun Thomas 				addconstantstring("[_a-zA-Z0-9]");
4142e8d1edaSArun Thomas 				break;
4152e8d1edaSArun Thomas 			case 'W':
4162e8d1edaSArun Thomas 				addconstantstring("[^_a-zA-Z0-9]");
4172e8d1edaSArun Thomas 				break;
4182e8d1edaSArun Thomas 			case '<':
4192e8d1edaSArun Thomas 				addconstantstring("[[:<:]]");
4202e8d1edaSArun Thomas 				break;
4212e8d1edaSArun Thomas 			case '>':
4222e8d1edaSArun Thomas 				addconstantstring("[[:>:]]");
4232e8d1edaSArun Thomas 				break;
4242e8d1edaSArun Thomas 			default:
4252e8d1edaSArun Thomas 				addchars(p, 2);
4262e8d1edaSArun Thomas 				break;
4272e8d1edaSArun Thomas 			}
4282e8d1edaSArun Thomas 			p+=2;
4292e8d1edaSArun Thomas 			continue;
4302e8d1edaSArun Thomas 		}
4312e8d1edaSArun Thomas 		if (*p == '(' || *p == ')' || *p == '|')
4322e8d1edaSArun Thomas 			addchar('\\');
4332e8d1edaSArun Thomas 
4342e8d1edaSArun Thomas 		addchar(*p);
4352e8d1edaSArun Thomas 		p++;
4362e8d1edaSArun Thomas 	}
4372e8d1edaSArun Thomas 	return getstring();
4382e8d1edaSArun Thomas }
4392e8d1edaSArun Thomas 
4402e8d1edaSArun Thomas /* patsubst(string, regexp, opt replacement) */
4412e8d1edaSArun Thomas /* argv[2]: string
4422e8d1edaSArun Thomas  * argv[3]: regexp
4432e8d1edaSArun Thomas  * argv[4]: opt rep
4442e8d1edaSArun Thomas  */
4452e8d1edaSArun Thomas void
dopatsubst(const char * argv[],int argc)4462e8d1edaSArun Thomas dopatsubst(const char *argv[], int argc)
4472e8d1edaSArun Thomas {
4482e8d1edaSArun Thomas 	if (argc <= 3) {
4492e8d1edaSArun Thomas 		warnx("Too few arguments to patsubst");
4502e8d1edaSArun Thomas 		return;
4512e8d1edaSArun Thomas 	}
4522e8d1edaSArun Thomas 	/* special case: empty regexp */
4532e8d1edaSArun Thomas 	if (argv[3][0] == '\0') {
4542e8d1edaSArun Thomas 		const char *s;
4552e8d1edaSArun Thomas 		size_t len;
4562e8d1edaSArun Thomas 		if (argv[4] && argc > 4)
4572e8d1edaSArun Thomas 			len = strlen(argv[4]);
4582e8d1edaSArun Thomas 		else
4592e8d1edaSArun Thomas 			len = 0;
4602e8d1edaSArun Thomas 		for (s = argv[2]; *s != '\0'; s++) {
4612e8d1edaSArun Thomas 			addchars(argv[4], len);
4622e8d1edaSArun Thomas 			addchar(*s);
4632e8d1edaSArun Thomas 		}
4642e8d1edaSArun Thomas 	} else {
4652e8d1edaSArun Thomas 		int error;
4662e8d1edaSArun Thomas 		regex_t re;
4672e8d1edaSArun Thomas 		regmatch_t *pmatch;
4682e8d1edaSArun Thomas 		int mode = REG_EXTENDED;
4692e8d1edaSArun Thomas 		size_t l = strlen(argv[3]);
4702e8d1edaSArun Thomas 
4712e8d1edaSArun Thomas 		if (!mimic_gnu ||
4722e8d1edaSArun Thomas 		    (argv[3][0] == '^') ||
4732e8d1edaSArun Thomas 		    (l > 0 && argv[3][l-1] == '$'))
4742e8d1edaSArun Thomas 			mode |= REG_NEWLINE;
4752e8d1edaSArun Thomas 
4762e8d1edaSArun Thomas 		error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
4772e8d1edaSArun Thomas 		    mode);
4782e8d1edaSArun Thomas 		if (error != 0)
4792e8d1edaSArun Thomas 			exit_regerror(error, &re);
4802e8d1edaSArun Thomas 
4812e8d1edaSArun Thomas 		pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
4822e8d1edaSArun Thomas 		do_subst(argv[2], &re,
4832e8d1edaSArun Thomas 		    argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch);
4842e8d1edaSArun Thomas 		free(pmatch);
4852e8d1edaSArun Thomas 		regfree(&re);
4862e8d1edaSArun Thomas 	}
4872e8d1edaSArun Thomas 	pbstr(getstring());
4882e8d1edaSArun Thomas }
4892e8d1edaSArun Thomas 
4902e8d1edaSArun Thomas void
doregexp(const char * argv[],int argc)4912e8d1edaSArun Thomas doregexp(const char *argv[], int argc)
4922e8d1edaSArun Thomas {
4932e8d1edaSArun Thomas 	int error;
4942e8d1edaSArun Thomas 	regex_t re;
4952e8d1edaSArun Thomas 	regmatch_t *pmatch;
4962e8d1edaSArun Thomas 
4972e8d1edaSArun Thomas 	if (argc <= 3) {
4982e8d1edaSArun Thomas 		warnx("Too few arguments to regexp");
4992e8d1edaSArun Thomas 		return;
5002e8d1edaSArun Thomas 	}
5012e8d1edaSArun Thomas 	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
5022e8d1edaSArun Thomas 	    REG_EXTENDED);
5032e8d1edaSArun Thomas 	if (error != 0)
5042e8d1edaSArun Thomas 		exit_regerror(error, &re);
5052e8d1edaSArun Thomas 
5062e8d1edaSArun Thomas 	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
5072e8d1edaSArun Thomas 	if (argv[4] == NULL || argc == 4)
5082e8d1edaSArun Thomas 		do_regexpindex(argv[2], &re, pmatch);
5092e8d1edaSArun Thomas 	else
5102e8d1edaSArun Thomas 		do_regexp(argv[2], &re, argv[4], pmatch);
5112e8d1edaSArun Thomas 	free(pmatch);
5122e8d1edaSArun Thomas 	regfree(&re);
5132e8d1edaSArun Thomas }
5142e8d1edaSArun Thomas 
5152e8d1edaSArun Thomas void
doformat(const char * argv[],int argc)5162e8d1edaSArun Thomas doformat(const char *argv[], int argc)
5172e8d1edaSArun Thomas {
5182e8d1edaSArun Thomas 	const char *format = argv[2];
5192e8d1edaSArun Thomas 	int pos = 3;
5202e8d1edaSArun Thomas 	int left_padded;
5212e8d1edaSArun Thomas 	long width;
5222e8d1edaSArun Thomas 	size_t l;
5232e8d1edaSArun Thomas 	const char *thisarg;
5242e8d1edaSArun Thomas 	char temp[2];
5252e8d1edaSArun Thomas 	size_t extra;
5262e8d1edaSArun Thomas 
5272e8d1edaSArun Thomas 	while (*format != 0) {
5282e8d1edaSArun Thomas 		if (*format != '%') {
5292e8d1edaSArun Thomas 			addchar(*format++);
5302e8d1edaSArun Thomas 			continue;
5312e8d1edaSArun Thomas 		}
5322e8d1edaSArun Thomas 
5332e8d1edaSArun Thomas 		format++;
5342e8d1edaSArun Thomas 		if (*format == '%') {
5352e8d1edaSArun Thomas 			addchar(*format++);
5362e8d1edaSArun Thomas 			continue;
5372e8d1edaSArun Thomas 		}
5382e8d1edaSArun Thomas 		if (*format == 0) {
5392e8d1edaSArun Thomas 			addchar('%');
5402e8d1edaSArun Thomas 			break;
5412e8d1edaSArun Thomas 		}
5422e8d1edaSArun Thomas 
5432e8d1edaSArun Thomas 		if (*format == '*') {
5442e8d1edaSArun Thomas 			format++;
5452e8d1edaSArun Thomas 			if (pos >= argc)
5462e8d1edaSArun Thomas 				m4errx(1,
5472e8d1edaSArun Thomas 				    "Format with too many format specifiers.");
5482e8d1edaSArun Thomas 			width = strtol(argv[pos++], NULL, 10);
5492e8d1edaSArun Thomas 		} else {
5502e8d1edaSArun Thomas 			char *eformat;
5512e8d1edaSArun Thomas 			width = strtol(format, &eformat, 10);
5522e8d1edaSArun Thomas 			format = eformat;
5532e8d1edaSArun Thomas 		}
5542e8d1edaSArun Thomas 		if (width < 0) {
5552e8d1edaSArun Thomas 			left_padded = 1;
5562e8d1edaSArun Thomas 			width = -width;
5572e8d1edaSArun Thomas 		} else {
5582e8d1edaSArun Thomas 			left_padded = 0;
5592e8d1edaSArun Thomas 		}
5602e8d1edaSArun Thomas 		if (*format == '.') {
5612e8d1edaSArun Thomas 			format++;
5622e8d1edaSArun Thomas 			if (*format == '*') {
5632e8d1edaSArun Thomas 				format++;
5642e8d1edaSArun Thomas 				if (pos >= argc)
5652e8d1edaSArun Thomas 					m4errx(1,
5662e8d1edaSArun Thomas 					    "Format with too many format specifiers.");
5672e8d1edaSArun Thomas 				extra = strtol(argv[pos++], NULL, 10);
5682e8d1edaSArun Thomas 			} else {
5692e8d1edaSArun Thomas 				char *eformat;
5702e8d1edaSArun Thomas 				extra = strtol(format, &eformat, 10);
5712e8d1edaSArun Thomas 				format = eformat;
5722e8d1edaSArun Thomas 			}
5732e8d1edaSArun Thomas 		} else {
5742e8d1edaSArun Thomas 			extra = SIZE_T_MAX;
5752e8d1edaSArun Thomas 		}
5762e8d1edaSArun Thomas 		if (pos >= argc)
5772e8d1edaSArun Thomas 			m4errx(1, "Format with too many format specifiers.");
5782e8d1edaSArun Thomas 		switch(*format) {
5792e8d1edaSArun Thomas 		case 's':
5802e8d1edaSArun Thomas 			thisarg = argv[pos++];
5812e8d1edaSArun Thomas 			break;
5822e8d1edaSArun Thomas 		case 'c':
5832e8d1edaSArun Thomas 			temp[0] = strtoul(argv[pos++], NULL, 10);
5842e8d1edaSArun Thomas 			temp[1] = 0;
5852e8d1edaSArun Thomas 			thisarg = temp;
5862e8d1edaSArun Thomas 			break;
5872e8d1edaSArun Thomas 		default:
5882e8d1edaSArun Thomas 			m4errx(1, "Unsupported format specification: %s.",
5892e8d1edaSArun Thomas 			    argv[2]);
5902e8d1edaSArun Thomas 		}
5912e8d1edaSArun Thomas 		format++;
5922e8d1edaSArun Thomas 		l = strlen(thisarg);
5932e8d1edaSArun Thomas 		if (l > extra)
5942e8d1edaSArun Thomas 			l = extra;
5952e8d1edaSArun Thomas 		if (!left_padded) {
5962e8d1edaSArun Thomas 			while (l < (size_t)width--)
5972e8d1edaSArun Thomas 				addchar(' ');
5982e8d1edaSArun Thomas 		}
5992e8d1edaSArun Thomas 		addchars(thisarg, l);
6002e8d1edaSArun Thomas 		if (left_padded) {
6012e8d1edaSArun Thomas 			while (l < (size_t)width--)
6022e8d1edaSArun Thomas 				addchar(' ');
6032e8d1edaSArun Thomas 		}
6042e8d1edaSArun Thomas 	}
6052e8d1edaSArun Thomas 	pbstr(getstring());
6062e8d1edaSArun Thomas }
6072e8d1edaSArun Thomas 
6082e8d1edaSArun Thomas void
doesyscmd(const char * cmd)6092e8d1edaSArun Thomas doesyscmd(const char *cmd)
6102e8d1edaSArun Thomas {
6112e8d1edaSArun Thomas 	int p[2];
6122e8d1edaSArun Thomas 	pid_t pid, cpid;
6132e8d1edaSArun Thomas 	const char *argv[4];
6142e8d1edaSArun Thomas 	int cc;
6152e8d1edaSArun Thomas 	int status;
6162e8d1edaSArun Thomas 
6172e8d1edaSArun Thomas 	/* Follow gnu m4 documentation: first flush buffers. */
6182e8d1edaSArun Thomas 	fflush(NULL);
6192e8d1edaSArun Thomas 
6202e8d1edaSArun Thomas 	argv[0] = "sh";
6212e8d1edaSArun Thomas 	argv[1] = "-c";
6222e8d1edaSArun Thomas 	argv[2] = cmd;
6232e8d1edaSArun Thomas 	argv[3] = NULL;
6242e8d1edaSArun Thomas 
6252e8d1edaSArun Thomas 	/* Just set up standard output, share stderr and stdin with m4 */
6262e8d1edaSArun Thomas 	if (pipe(p) == -1)
6272e8d1edaSArun Thomas 		err(1, "bad pipe");
6282e8d1edaSArun Thomas 	switch(cpid = fork()) {
6292e8d1edaSArun Thomas 	case -1:
6302e8d1edaSArun Thomas 		err(1, "bad fork");
6312e8d1edaSArun Thomas 		/* NOTREACHED */
6322e8d1edaSArun Thomas 	case 0:
6332e8d1edaSArun Thomas 		(void) close(p[0]);
6342e8d1edaSArun Thomas 		(void) dup2(p[1], 1);
6352e8d1edaSArun Thomas 		(void) close(p[1]);
6362e8d1edaSArun Thomas 		execv(_PATH_BSHELL, __UNCONST(argv));
6372e8d1edaSArun Thomas 		exit(1);
6382e8d1edaSArun Thomas 	default:
6392e8d1edaSArun Thomas 		/* Read result in two stages, since m4's buffer is
6402e8d1edaSArun Thomas 		 * pushback-only. */
6412e8d1edaSArun Thomas 		(void) close(p[1]);
6422e8d1edaSArun Thomas 		do {
6432e8d1edaSArun Thomas 			char result[BUFSIZE];
6442e8d1edaSArun Thomas 			cc = read(p[0], result, sizeof result);
6452e8d1edaSArun Thomas 			if (cc > 0)
6462e8d1edaSArun Thomas 				addchars(result, cc);
6472e8d1edaSArun Thomas 		} while (cc > 0 || (cc == -1 && errno == EINTR));
6482e8d1edaSArun Thomas 
6492e8d1edaSArun Thomas 		(void) close(p[0]);
6502e8d1edaSArun Thomas 		while ((pid = wait(&status)) != cpid && pid >= 0)
6512e8d1edaSArun Thomas 			continue;
6522e8d1edaSArun Thomas 		pbstr(getstring());
6532e8d1edaSArun Thomas 	}
6542e8d1edaSArun Thomas }
6552e8d1edaSArun Thomas 
6562e8d1edaSArun Thomas void
getdivfile(const char * name)6572e8d1edaSArun Thomas getdivfile(const char *name)
6582e8d1edaSArun Thomas {
6592e8d1edaSArun Thomas 	FILE *f;
6602e8d1edaSArun Thomas 	int c;
6612e8d1edaSArun Thomas 
6622e8d1edaSArun Thomas 	f = fopen(name, "r");
6632e8d1edaSArun Thomas 	if (!f)
6642e8d1edaSArun Thomas 		return;
6652e8d1edaSArun Thomas 
6662e8d1edaSArun Thomas 	while ((c = getc(f))!= EOF)
6672e8d1edaSArun Thomas 		putc(c, active);
6682e8d1edaSArun Thomas 	(void) fclose(f);
6692e8d1edaSArun Thomas }
670