xref: /netbsd-src/usr.bin/m4/gnum4.c (revision c700643e72fb010125fd26c8ad4f03e2e291c849)
1*c700643eSchristos /* $NetBSD: gnum4.c,v 1.13 2023/05/24 22:14:31 christos Exp $ */
2f3efdb75Schristos /* $OpenBSD: gnum4.c,v 1.39 2008/08/21 21:01:04 espie Exp $ */
34b087712Stv 
44b087712Stv /*
54b087712Stv  * Copyright (c) 1999 Marc Espie
64b087712Stv  *
74b087712Stv  * Redistribution and use in source and binary forms, with or without
84b087712Stv  * modification, are permitted provided that the following conditions
94b087712Stv  * are met:
104b087712Stv  * 1. Redistributions of source code must retain the above copyright
114b087712Stv  *    notice, this list of conditions and the following disclaimer.
124b087712Stv  * 2. Redistributions in binary form must reproduce the above copyright
134b087712Stv  *    notice, this list of conditions and the following disclaimer in the
144b087712Stv  *    documentation and/or other materials provided with the distribution.
154b087712Stv  *
164b087712Stv  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
174b087712Stv  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
184b087712Stv  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
194b087712Stv  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
204b087712Stv  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
214b087712Stv  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
224b087712Stv  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
234b087712Stv  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
244b087712Stv  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
254b087712Stv  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
264b087712Stv  * SUCH DAMAGE.
274b087712Stv  */
284b087712Stv 
294b087712Stv /*
304b087712Stv  * functions needed to support gnu-m4 extensions, including a fake freezing
314b087712Stv  */
32f3efdb75Schristos #if HAVE_NBTOOL_CONFIG_H
33f3efdb75Schristos #include "nbtool_config.h"
34f3efdb75Schristos #endif
35f3efdb75Schristos #include <sys/cdefs.h>
36*c700643eSchristos __RCSID("$NetBSD: gnum4.c,v 1.13 2023/05/24 22:14:31 christos Exp $");
374b087712Stv 
384b087712Stv #include <sys/param.h>
394b087712Stv #include <sys/types.h>
404b087712Stv #include <sys/wait.h>
414b087712Stv #include <ctype.h>
42f3efdb75Schristos #include <err.h>
434b087712Stv #include <paths.h>
444b087712Stv #include <regex.h>
454b087712Stv #include <stddef.h>
464b087712Stv #include <stdlib.h>
474b087712Stv #include <stdio.h>
484b087712Stv #include <string.h>
49f3efdb75Schristos #include <errno.h>
50f3efdb75Schristos #include <unistd.h>
514b087712Stv #include "mdef.h"
524b087712Stv #include "stdd.h"
534b087712Stv #include "extern.h"
544b087712Stv 
554b087712Stv 
564b087712Stv int mimic_gnu = 0;
57c6ea3d2aSchristos #ifndef SIZE_T_MAX
58c6ea3d2aSchristos #define SIZE_T_MAX (size_t)~0ull
59c6ea3d2aSchristos #endif
604b087712Stv 
614b087712Stv /*
624b087712Stv  * Support for include path search
639bc855a9Ssimonb  * First search in the current directory.
644b087712Stv  * If not found, and the path is not absolute, include path kicks in.
654b087712Stv  * First, -I options, in the order found on the command line.
664b087712Stv  * Then M4PATH env variable
674b087712Stv  */
684b087712Stv 
694b087712Stv struct path_entry {
704b087712Stv 	char *name;
714b087712Stv 	struct path_entry *next;
724b087712Stv } *first, *last;
734b087712Stv 
74f3efdb75Schristos static struct path_entry *new_path_entry(const char *);
75f3efdb75Schristos static void ensure_m4path(void);
76f3efdb75Schristos static struct input_file *dopath(struct input_file *, const char *);
774b087712Stv 
784b087712Stv static struct path_entry *
new_path_entry(const char * dirname)79f3efdb75Schristos new_path_entry(const char *dirname)
804b087712Stv {
814b087712Stv 	struct path_entry *n;
824b087712Stv 
834b087712Stv 	n = malloc(sizeof(struct path_entry));
844b087712Stv 	if (!n)
854b087712Stv 		errx(1, "out of memory");
864b087712Stv 	n->name = strdup(dirname);
874b087712Stv 	if (!n->name)
884b087712Stv 		errx(1, "out of memory");
894b087712Stv 	n->next = 0;
904b087712Stv 	return n;
914b087712Stv }
924b087712Stv 
934b087712Stv void
addtoincludepath(const char * dirname)94f3efdb75Schristos addtoincludepath(const char *dirname)
954b087712Stv {
964b087712Stv 	struct path_entry *n;
974b087712Stv 
984b087712Stv 	n = new_path_entry(dirname);
994b087712Stv 
1004b087712Stv 	if (last) {
1014b087712Stv 		last->next = n;
1024b087712Stv 		last = n;
1034b087712Stv 	}
1044b087712Stv 	else
1054b087712Stv 		last = first = n;
1064b087712Stv }
1074b087712Stv 
1084b087712Stv static void
ensure_m4path(void)109d34c2845Smatt ensure_m4path(void)
1104b087712Stv {
1114b087712Stv 	static int envpathdone = 0;
1124b087712Stv 	char *envpath;
1134b087712Stv 	char *sweep;
1144b087712Stv 	char *path;
1154b087712Stv 
1164b087712Stv 	if (envpathdone)
1174b087712Stv 		return;
1184b087712Stv 	envpathdone = TRUE;
1194b087712Stv 	envpath = getenv("M4PATH");
1204b087712Stv 	if (!envpath)
1214b087712Stv 		return;
1224b087712Stv 	/* for portability: getenv result is read-only */
1234b087712Stv 	envpath = strdup(envpath);
1244b087712Stv 	if (!envpath)
1254b087712Stv 		errx(1, "out of memory");
1264b087712Stv 	for (sweep = envpath;
1274b087712Stv 	    (path = strsep(&sweep, ":")) != NULL;)
1284b087712Stv 	    addtoincludepath(path);
1294b087712Stv 	free(envpath);
1304b087712Stv }
1314b087712Stv 
1324b087712Stv static
1334b087712Stv struct input_file *
dopath(struct input_file * i,const char * filename)134f3efdb75Schristos dopath(struct input_file *i, const char *filename)
1354b087712Stv {
1364b087712Stv 	char path[MAXPATHLEN];
1374b087712Stv 	struct path_entry *pe;
1384b087712Stv 	FILE *f;
1394b087712Stv 
1404b087712Stv 	for (pe = first; pe; pe = pe->next) {
1414b087712Stv 		snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
1424b087712Stv 		if ((f = fopen(path, "r")) != 0) {
1434b087712Stv 			set_input(i, f, path);
1444b087712Stv 			return i;
1454b087712Stv 		}
1464b087712Stv 	}
1474b087712Stv 	return NULL;
1484b087712Stv }
1494b087712Stv 
1504b087712Stv struct input_file *
fopen_trypath(struct input_file * i,const char * filename)151f3efdb75Schristos fopen_trypath(struct input_file *i, const char *filename)
1524b087712Stv {
1534b087712Stv 	FILE *f;
1544b087712Stv 
1554b087712Stv 	f = fopen(filename, "r");
1564b087712Stv 	if (f != NULL) {
1574b087712Stv 		set_input(i, f, filename);
1584b087712Stv 		return i;
1594b087712Stv 	}
1604b087712Stv 	if (filename[0] == '/')
1614b087712Stv 		return NULL;
1624b087712Stv 
1634b087712Stv 	ensure_m4path();
1644b087712Stv 
1654b087712Stv 	return dopath(i, filename);
1664b087712Stv }
1674b087712Stv 
1684b087712Stv void
doindir(const char * argv[],int argc)169f3efdb75Schristos doindir(const char *argv[], int argc)
1704b087712Stv {
171f3efdb75Schristos 	ndptr n;
172f3efdb75Schristos 	struct macro_definition *p;
1734b087712Stv 
174f3efdb75Schristos 	n = lookup(argv[2]);
175f3efdb75Schristos 	if (n == NULL || (p = macro_getdef(n)) == NULL)
176f3efdb75Schristos 		m4errx(1, "indir: undefined macro %s.", argv[2]);
1774b087712Stv 	argv[1] = p->defn;
178f3efdb75Schristos 
179f3efdb75Schristos 	eval(argv+1, argc-1, p->type, is_traced(n));
1804b087712Stv }
1814b087712Stv 
1824b087712Stv void
dobuiltin(const char * argv[],int argc)183f3efdb75Schristos dobuiltin(const char *argv[], int argc)
1844b087712Stv {
185f3efdb75Schristos 	ndptr p;
186f3efdb75Schristos 
1874b087712Stv 	argv[1] = NULL;
188f3efdb75Schristos 	p = macro_getbuiltin(argv[2]);
189f3efdb75Schristos 	if (p != NULL)
190f3efdb75Schristos 		eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p));
1914b087712Stv 	else
192f3efdb75Schristos 		m4errx(1, "unknown builtin %s.", argv[2]);
1934b087712Stv }
1944b087712Stv 
1954b087712Stv 
1964b087712Stv /* We need some temporary buffer space, as pb pushes BACK and substitution
1974b087712Stv  * proceeds forward... */
1984b087712Stv static char *buffer;
1994b087712Stv static size_t bufsize = 0;
2004b087712Stv static size_t current = 0;
2014b087712Stv 
202f3efdb75Schristos static void addchars(const char *, size_t);
203f3efdb75Schristos static void addchar(int);
204f3efdb75Schristos static char *twiddle(const char *);
205f3efdb75Schristos static char *getstring(void);
2068a4a35b9Schristos static void exit_regerror(int, const char *, regex_t *) __dead;
2078a4a35b9Schristos static void do_subst(const char *, const char *, regex_t *, const char *,
2088a4a35b9Schristos     regmatch_t *);
2098a4a35b9Schristos static void do_regexpindex(const char *, const char *, regex_t *, regmatch_t *);
2108a4a35b9Schristos static void do_regexp(const char *, const char *, regex_t *, const char *, regmatch_t *);
211f3efdb75Schristos static void add_sub(size_t, const char *, regex_t *, regmatch_t *);
212f3efdb75Schristos static void add_replace(const char *, regex_t *, const char *, regmatch_t *);
2134b087712Stv #define addconstantstring(s) addchars((s), sizeof(s)-1)
2144b087712Stv 
2154b087712Stv static void
addchars(const char * c,size_t n)216f3efdb75Schristos addchars(const char *c, size_t n)
2174b087712Stv {
2184b087712Stv 	if (n == 0)
2194b087712Stv 		return;
2204b087712Stv 	while (current + n > bufsize) {
2214b087712Stv 		if (bufsize == 0)
2224b087712Stv 			bufsize = 1024;
2234b087712Stv 		else
2244b087712Stv 			bufsize *= 2;
225f3efdb75Schristos 		buffer = xrealloc(buffer, bufsize, NULL);
2264b087712Stv 	}
2274b087712Stv 	memcpy(buffer+current, c, n);
2284b087712Stv 	current += n;
2294b087712Stv }
2304b087712Stv 
2314b087712Stv static void
addchar(int c)232f3efdb75Schristos addchar(int c)
2334b087712Stv {
2344b087712Stv 	if (current +1 > bufsize) {
2354b087712Stv 		if (bufsize == 0)
2364b087712Stv 			bufsize = 1024;
2374b087712Stv 		else
2384b087712Stv 			bufsize *= 2;
239f3efdb75Schristos 		buffer = xrealloc(buffer, bufsize, NULL);
2404b087712Stv 	}
2414b087712Stv 	buffer[current++] = c;
2424b087712Stv }
2434b087712Stv 
2444b087712Stv static char *
getstring(void)245d34c2845Smatt getstring(void)
2464b087712Stv {
2474b087712Stv 	addchar('\0');
2484b087712Stv 	current = 0;
2494b087712Stv 	return buffer;
2504b087712Stv }
2514b087712Stv 
2524b087712Stv 
2534b087712Stv static void
exit_regerror(int er,const char * pat,regex_t * re)2548a4a35b9Schristos exit_regerror(int er, const char *pat, regex_t *re)
2554b087712Stv {
2564b087712Stv 	size_t 	errlen;
2574b087712Stv 	char 	*errbuf;
2584b087712Stv 
2594b087712Stv 	errlen = regerror(er, re, NULL, 0);
260f3efdb75Schristos 	errbuf = xalloc(errlen,
261f3efdb75Schristos 	    "malloc in regerror: %lu", (unsigned long)errlen);
2624b087712Stv 	regerror(er, re, errbuf, errlen);
2638a4a35b9Schristos 	m4errx(1, "regular expression error: %s for: `%s'", errbuf, pat);
2644b087712Stv }
2654b087712Stv 
2664b087712Stv static void
add_sub(size_t n,const char * string,regex_t * re,regmatch_t * pm)267f3efdb75Schristos add_sub(size_t n, const char *string, regex_t *re, regmatch_t *pm)
2684b087712Stv {
2698a4a35b9Schristos 	if (n > re->re_nsub) {
2708a4a35b9Schristos 		if (!quiet)
271f3efdb75Schristos 			warnx("No subexpression %zu", n);
2728a4a35b9Schristos 		if (fatal_warnings)
2738a4a35b9Schristos 			exit(EXIT_FAILURE);
2748a4a35b9Schristos 	}
2754b087712Stv 	/* Subexpressions that did not match are
2764b087712Stv 	 * not an error.  */
2774b087712Stv 	else if (pm[n].rm_so != -1 &&
2784b087712Stv 	    pm[n].rm_eo != -1) {
2794b087712Stv 		addchars(string + pm[n].rm_so,
2804b087712Stv 			pm[n].rm_eo - pm[n].rm_so);
2814b087712Stv 	}
2824b087712Stv }
2834b087712Stv 
2844b087712Stv /* Add replacement string to the output buffer, recognizing special
2854b087712Stv  * constructs and replacing them with substrings of the original string.
2864b087712Stv  */
2874b087712Stv static void
add_replace(const char * string,regex_t * re,const char * replace,regmatch_t * pm)288f3efdb75Schristos add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
2894b087712Stv {
2904b087712Stv 	const char *p;
2914b087712Stv 
2924b087712Stv 	for (p = replace; *p != '\0'; p++) {
2934b087712Stv 		if (*p == '&' && !mimic_gnu) {
2944b087712Stv 			add_sub(0, string, re, pm);
2954b087712Stv 			continue;
2964b087712Stv 		}
2974b087712Stv 		if (*p == '\\') {
2984b087712Stv 			if (p[1] == '\\') {
2994b087712Stv 				addchar(p[1]);
3004b087712Stv 				p++;
3014b087712Stv 				continue;
3024b087712Stv 			}
3034b087712Stv 			if (p[1] == '&') {
3044b087712Stv 				if (mimic_gnu)
3054b087712Stv 					add_sub(0, string, re, pm);
3064b087712Stv 				else
3074b087712Stv 					addchar(p[1]);
3084b087712Stv 				p++;
3094b087712Stv 				continue;
3104b087712Stv 			}
311108eb2abSdsl 			if (isdigit((unsigned char)p[1])) {
3124b087712Stv 				add_sub(*(++p) - '0', string, re, pm);
3134b087712Stv 				continue;
3144b087712Stv 			}
3154b087712Stv 		}
3164b087712Stv 	    	addchar(*p);
3174b087712Stv 	}
3184b087712Stv }
3194b087712Stv 
3204b087712Stv static void
do_subst(const char * pat,const char * string,regex_t * re,const char * replace,regmatch_t * pm)3218a4a35b9Schristos do_subst(const char *pat, const char *string, regex_t *re, const char *replace,
3228a4a35b9Schristos     regmatch_t *pm)
3234b087712Stv {
3244b087712Stv 	int error;
3254b087712Stv 	int flags = 0;
3264b087712Stv 	const char *last_match = NULL;
3274b087712Stv 
3284b087712Stv 	while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
3294b087712Stv 		if (pm[0].rm_eo != 0) {
3304b087712Stv 			if (string[pm[0].rm_eo-1] == '\n')
3314b087712Stv 				flags = 0;
3324b087712Stv 			else
3334b087712Stv 				flags = REG_NOTBOL;
3344b087712Stv 		}
3354b087712Stv 
3364b087712Stv 		/* NULL length matches are special... We use the `vi-mode'
3374b087712Stv 		 * rule: don't allow a NULL-match at the last match
3384b087712Stv 		 * position.
3394b087712Stv 		 */
3404b087712Stv 		if (pm[0].rm_so == pm[0].rm_eo &&
3414b087712Stv 		    string + pm[0].rm_so == last_match) {
3424b087712Stv 			if (*string == '\0')
3434b087712Stv 				return;
3444b087712Stv 			addchar(*string);
3454b087712Stv 			if (*string++ == '\n')
3464b087712Stv 				flags = 0;
3474b087712Stv 			else
3484b087712Stv 				flags = REG_NOTBOL;
3494b087712Stv 			continue;
3504b087712Stv 		}
3514b087712Stv 		last_match = string + pm[0].rm_so;
3524b087712Stv 		addchars(string, pm[0].rm_so);
3534b087712Stv 		add_replace(string, re, replace, pm);
3544b087712Stv 		string += pm[0].rm_eo;
3558a4a35b9Schristos 		buffer[current] = '\0';
3564b087712Stv 	}
3578a4a35b9Schristos 	while (*string)
3588a4a35b9Schristos 		addchar(*string++);
3594b087712Stv 	if (error != REG_NOMATCH)
3608a4a35b9Schristos 		exit_regerror(error, pat, re);
3614b087712Stv 	pbstr(string);
3624b087712Stv }
3634b087712Stv 
3644b087712Stv static void
do_regexp(const char * pat,const char * string,regex_t * re,const char * replace,regmatch_t * pm)3658a4a35b9Schristos do_regexp(const char *pat, const char *string, regex_t *re, const char *replace,
3668a4a35b9Schristos     regmatch_t *pm)
3674b087712Stv {
3684b087712Stv 	int error;
3694b087712Stv 
3704b087712Stv 	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
3714b087712Stv 	case 0:
3724b087712Stv 		add_replace(string, re, replace, pm);
3734b087712Stv 		pbstr(getstring());
3744b087712Stv 		break;
3754b087712Stv 	case REG_NOMATCH:
3764b087712Stv 		break;
3774b087712Stv 	default:
3788a4a35b9Schristos 		exit_regerror(error, pat, re);
3794b087712Stv 	}
3804b087712Stv }
3814b087712Stv 
3824b087712Stv static void
do_regexpindex(const char * pat,const char * string,regex_t * re,regmatch_t * pm)3838a4a35b9Schristos do_regexpindex(const char *pat, const char *string, regex_t *re, regmatch_t *pm)
3844b087712Stv {
3854b087712Stv 	int error;
3864b087712Stv 
3874b087712Stv 	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
3884b087712Stv 	case 0:
3894b087712Stv 		pbunsigned(pm[0].rm_so);
3904b087712Stv 		break;
3914b087712Stv 	case REG_NOMATCH:
3924b087712Stv 		pbnum(-1);
3934b087712Stv 		break;
3944b087712Stv 	default:
3958a4a35b9Schristos 		exit_regerror(error, pat, re);
3964b087712Stv 	}
3974b087712Stv }
3984b087712Stv 
3994b087712Stv /* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
4004b087712Stv  * says. So we twiddle with the regexp before passing it to regcomp.
4014b087712Stv  */
4024b087712Stv static char *
twiddle(const char * p)403f3efdb75Schristos twiddle(const char *p)
4044b087712Stv {
405f3efdb75Schristos 	/* + at start of regexp is a normal character for Gnu m4 */
406f3efdb75Schristos 	if (*p == '^') {
407f3efdb75Schristos 		addchar(*p);
408f3efdb75Schristos 		p++;
409f3efdb75Schristos 	}
410f3efdb75Schristos 	if (*p == '+') {
411f3efdb75Schristos 		addchar('\\');
412f3efdb75Schristos 	}
4134b087712Stv 	/* This could use strcspn for speed... */
4144b087712Stv 	while (*p != '\0') {
4154b087712Stv 		if (*p == '\\') {
4164b087712Stv 			switch(p[1]) {
4174b087712Stv 			case '(':
4184b087712Stv 			case ')':
4194b087712Stv 			case '|':
4204b087712Stv 				addchar(p[1]);
4214b087712Stv 				break;
4224b087712Stv 			case 'w':
4234b087712Stv 				addconstantstring("[_a-zA-Z0-9]");
4244b087712Stv 				break;
4254b087712Stv 			case 'W':
4264b087712Stv 				addconstantstring("[^_a-zA-Z0-9]");
4274b087712Stv 				break;
4284b087712Stv 			case '<':
4294b087712Stv 				addconstantstring("[[:<:]]");
4304b087712Stv 				break;
4314b087712Stv 			case '>':
4324b087712Stv 				addconstantstring("[[:>:]]");
4334b087712Stv 				break;
4344b087712Stv 			default:
4354b087712Stv 				addchars(p, 2);
4364b087712Stv 				break;
4374b087712Stv 			}
4384b087712Stv 			p+=2;
4394b087712Stv 			continue;
4404b087712Stv 		}
441*c700643eSchristos 		if (strchr("()|{}", *p) != NULL)
4424b087712Stv 			addchar('\\');
4434b087712Stv 
4444b087712Stv 		addchar(*p);
4454b087712Stv 		p++;
4464b087712Stv 	}
4474b087712Stv 	return getstring();
4484b087712Stv }
4494b087712Stv 
4508a4a35b9Schristos static int
checkempty(const char * argv[],int argc)4518a4a35b9Schristos checkempty(const char *argv[], int argc)
4524b087712Stv {
453f3efdb75Schristos 	const char *s;
454f3efdb75Schristos 	size_t len;
4558a4a35b9Schristos 
4568a4a35b9Schristos 	if (argc != 3 && argv[3][0] != '\0')
4578a4a35b9Schristos 		return 0;
4588a4a35b9Schristos 
4598a4a35b9Schristos 	if (argc == 3) {
4608a4a35b9Schristos 		if (!quiet)
4618a4a35b9Schristos 			warnx("Too few arguments to patsubst");
4628a4a35b9Schristos 		if (fatal_warnings)
4638a4a35b9Schristos 			exit(EXIT_FAILURE);
4648a4a35b9Schristos 	}
4658a4a35b9Schristos 
466f3efdb75Schristos 	if (argv[4] && argc > 4)
467f3efdb75Schristos 		len = strlen(argv[4]);
468f3efdb75Schristos 	else
469f3efdb75Schristos 		len = 0;
470f3efdb75Schristos 	for (s = argv[2]; *s != '\0'; s++) {
471f3efdb75Schristos 		addchars(argv[4], len);
472f3efdb75Schristos 		addchar(*s);
473f3efdb75Schristos 	}
4748a4a35b9Schristos 	return 1;
4758a4a35b9Schristos }
4768a4a35b9Schristos 
4778a4a35b9Schristos /* patsubst(string, regexp, opt replacement) */
4788a4a35b9Schristos /* argv[2]: string
4798a4a35b9Schristos  * argv[3]: regexp
4808a4a35b9Schristos  * argv[4]: opt rep
4818a4a35b9Schristos  */
4828a4a35b9Schristos void
dopatsubst(const char * argv[],int argc)4838a4a35b9Schristos dopatsubst(const char *argv[], int argc)
4848a4a35b9Schristos {
4858a4a35b9Schristos 	if (argc < 3) {
4868a4a35b9Schristos 		if (!quiet)
4878a4a35b9Schristos 			warnx("Too few arguments to patsubst");
4888a4a35b9Schristos 		if (fatal_warnings)
4898a4a35b9Schristos 			exit(EXIT_FAILURE);
4908a4a35b9Schristos 		return;
4918a4a35b9Schristos 	}
4928a4a35b9Schristos 	/* special case: empty regexp */
4938a4a35b9Schristos 	if (!checkempty(argv, argc)) {
4948a4a35b9Schristos 
4958a4a35b9Schristos 		const char *pat;
496f3efdb75Schristos 		int error;
497f3efdb75Schristos 		regex_t re;
498f3efdb75Schristos 		regmatch_t *pmatch;
499f3efdb75Schristos 		int mode = REG_EXTENDED;
500f3efdb75Schristos 		size_t l = strlen(argv[3]);
501f3efdb75Schristos 
502f3efdb75Schristos 		if (!mimic_gnu ||
503f3efdb75Schristos 		    (argv[3][0] == '^') ||
504f3efdb75Schristos 		    (l > 0 && argv[3][l-1] == '$'))
505f3efdb75Schristos 			mode |= REG_NEWLINE;
506f3efdb75Schristos 
5078a4a35b9Schristos 		pat = mimic_gnu ? twiddle(argv[3]) : argv[3];
5088a4a35b9Schristos 		error = regcomp(&re, pat, mode);
5094b087712Stv 		if (error != 0)
5108a4a35b9Schristos 			exit_regerror(error, pat, &re);
5114b087712Stv 
512f3efdb75Schristos 		pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
5138a4a35b9Schristos 		do_subst(pat, argv[2], &re,
514f3efdb75Schristos 		    argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch);
5154b087712Stv 		free(pmatch);
5164b087712Stv 		regfree(&re);
5174b087712Stv 	}
518f3efdb75Schristos 	pbstr(getstring());
519f3efdb75Schristos }
5204b087712Stv 
5214b087712Stv void
doregexp(const char * argv[],int argc)522f3efdb75Schristos doregexp(const char *argv[], int argc)
5234b087712Stv {
5244b087712Stv 	int error;
5254b087712Stv 	regex_t re;
5264b087712Stv 	regmatch_t *pmatch;
5278a4a35b9Schristos 	const char *pat;
5284b087712Stv 
5298a4a35b9Schristos 	if (argc < 3) {
5308a4a35b9Schristos 		if (!quiet)
5314b087712Stv 			warnx("Too few arguments to regexp");
5328a4a35b9Schristos 		if (fatal_warnings)
5338a4a35b9Schristos 			exit(EXIT_FAILURE);
5344b087712Stv 		return;
5354b087712Stv 	}
5368a4a35b9Schristos 	if (checkempty(argv, argc)) {
5378a4a35b9Schristos 		return;
5388a4a35b9Schristos 	}
5398a4a35b9Schristos 
5408a4a35b9Schristos 	pat = mimic_gnu ? twiddle(argv[3]) : argv[3];
5418a4a35b9Schristos 	error = regcomp(&re, pat, REG_EXTENDED);
5424b087712Stv 	if (error != 0)
5438a4a35b9Schristos 		exit_regerror(error, pat, &re);
5444b087712Stv 
545f3efdb75Schristos 	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
5464b087712Stv 	if (argv[4] == NULL || argc == 4)
5478a4a35b9Schristos 		do_regexpindex(pat, argv[2], &re, pmatch);
5484b087712Stv 	else
5498a4a35b9Schristos 		do_regexp(pat, argv[2], &re, argv[4], pmatch);
5504b087712Stv 	free(pmatch);
5514b087712Stv 	regfree(&re);
5524b087712Stv }
5534b087712Stv 
5544b087712Stv void
doformat(const char * argv[],int argc)555f3efdb75Schristos doformat(const char *argv[], int argc)
556f3efdb75Schristos {
557f3efdb75Schristos 	const char *format = argv[2];
558f3efdb75Schristos 	int pos = 3;
559f3efdb75Schristos 	int left_padded;
560f3efdb75Schristos 	long width;
561f3efdb75Schristos 	size_t l;
562f3efdb75Schristos 	const char *thisarg;
563f3efdb75Schristos 	char temp[2];
564f3efdb75Schristos 	size_t extra;
565f3efdb75Schristos 
566f3efdb75Schristos 	while (*format != 0) {
567f3efdb75Schristos 		if (*format != '%') {
568f3efdb75Schristos 			addchar(*format++);
569f3efdb75Schristos 			continue;
570f3efdb75Schristos 		}
571f3efdb75Schristos 
572f3efdb75Schristos 		format++;
573f3efdb75Schristos 		if (*format == '%') {
574f3efdb75Schristos 			addchar(*format++);
575f3efdb75Schristos 			continue;
576f3efdb75Schristos 		}
577f3efdb75Schristos 		if (*format == 0) {
578f3efdb75Schristos 			addchar('%');
579f3efdb75Schristos 			break;
580f3efdb75Schristos 		}
581f3efdb75Schristos 
582f3efdb75Schristos 		if (*format == '*') {
583f3efdb75Schristos 			format++;
584f3efdb75Schristos 			if (pos >= argc)
585f3efdb75Schristos 				m4errx(1,
586f3efdb75Schristos 				    "Format with too many format specifiers.");
587f3efdb75Schristos 			width = strtol(argv[pos++], NULL, 10);
588f3efdb75Schristos 		} else {
589f3efdb75Schristos 			char *eformat;
590f3efdb75Schristos 			width = strtol(format, &eformat, 10);
591f3efdb75Schristos 			format = eformat;
592f3efdb75Schristos 		}
593f3efdb75Schristos 		if (width < 0) {
594f3efdb75Schristos 			left_padded = 1;
595f3efdb75Schristos 			width = -width;
596f3efdb75Schristos 		} else {
597f3efdb75Schristos 			left_padded = 0;
598f3efdb75Schristos 		}
599f3efdb75Schristos 		if (*format == '.') {
600f3efdb75Schristos 			format++;
601f3efdb75Schristos 			if (*format == '*') {
602f3efdb75Schristos 				format++;
603f3efdb75Schristos 				if (pos >= argc)
604f3efdb75Schristos 					m4errx(1,
605f3efdb75Schristos 					    "Format with too many format specifiers.");
606f3efdb75Schristos 				extra = strtol(argv[pos++], NULL, 10);
607f3efdb75Schristos 			} else {
608f3efdb75Schristos 				char *eformat;
609f3efdb75Schristos 				extra = strtol(format, &eformat, 10);
610f3efdb75Schristos 				format = eformat;
611f3efdb75Schristos 			}
612f3efdb75Schristos 		} else {
613f3efdb75Schristos 			extra = SIZE_T_MAX;
614f3efdb75Schristos 		}
615f3efdb75Schristos 		if (pos >= argc)
616f3efdb75Schristos 			m4errx(1, "Format with too many format specifiers.");
617f3efdb75Schristos 		switch(*format) {
618f3efdb75Schristos 		case 's':
619f3efdb75Schristos 			thisarg = argv[pos++];
620f3efdb75Schristos 			break;
621f3efdb75Schristos 		case 'c':
622f3efdb75Schristos 			temp[0] = strtoul(argv[pos++], NULL, 10);
623f3efdb75Schristos 			temp[1] = 0;
624f3efdb75Schristos 			thisarg = temp;
625f3efdb75Schristos 			break;
626f3efdb75Schristos 		default:
627f3efdb75Schristos 			m4errx(1, "Unsupported format specification: %s.",
628f3efdb75Schristos 			    argv[2]);
629f3efdb75Schristos 		}
630f3efdb75Schristos 		format++;
631f3efdb75Schristos 		l = strlen(thisarg);
632f3efdb75Schristos 		if (l > extra)
633f3efdb75Schristos 			l = extra;
634f3efdb75Schristos 		if (!left_padded) {
635f3efdb75Schristos 			while (l < (size_t)width--)
636f3efdb75Schristos 				addchar(' ');
637f3efdb75Schristos 		}
638f3efdb75Schristos 		addchars(thisarg, l);
639f3efdb75Schristos 		if (left_padded) {
640f3efdb75Schristos 			while (l < (size_t)width--)
641f3efdb75Schristos 				addchar(' ');
642f3efdb75Schristos 		}
643f3efdb75Schristos 	}
644f3efdb75Schristos 	pbstr(getstring());
645f3efdb75Schristos }
646f3efdb75Schristos 
647f3efdb75Schristos void
doesyscmd(const char * cmd)648f3efdb75Schristos doesyscmd(const char *cmd)
6494b087712Stv {
6504b087712Stv 	int p[2];
6514b087712Stv 	pid_t pid, cpid;
652f3efdb75Schristos 	const char *argv[4];
6534b087712Stv 	int cc;
6544b087712Stv 	int status;
6554b087712Stv 
6564b087712Stv 	/* Follow gnu m4 documentation: first flush buffers. */
6574b087712Stv 	fflush(NULL);
6584b087712Stv 
6594b087712Stv 	argv[0] = "sh";
6604b087712Stv 	argv[1] = "-c";
661f3efdb75Schristos 	argv[2] = cmd;
6624b087712Stv 	argv[3] = NULL;
6634b087712Stv 
6644b087712Stv 	/* Just set up standard output, share stderr and stdin with m4 */
6654b087712Stv 	if (pipe(p) == -1)
6664b087712Stv 		err(1, "bad pipe");
6674b087712Stv 	switch(cpid = fork()) {
6684b087712Stv 	case -1:
6694b087712Stv 		err(1, "bad fork");
6704b087712Stv 		/* NOTREACHED */
6714b087712Stv 	case 0:
6724b087712Stv 		(void) close(p[0]);
6734b087712Stv 		(void) dup2(p[1], 1);
6744b087712Stv 		(void) close(p[1]);
675f3efdb75Schristos 		execv(_PATH_BSHELL, __UNCONST(argv));
6764b087712Stv 		exit(1);
6774b087712Stv 	default:
6784b087712Stv 		/* Read result in two stages, since m4's buffer is
6794b087712Stv 		 * pushback-only. */
6804b087712Stv 		(void) close(p[1]);
6814b087712Stv 		do {
6824b087712Stv 			char result[BUFSIZE];
6834b087712Stv 			cc = read(p[0], result, sizeof result);
6844b087712Stv 			if (cc > 0)
6854b087712Stv 				addchars(result, cc);
6864b087712Stv 		} while (cc > 0 || (cc == -1 && errno == EINTR));
6874b087712Stv 
6884b087712Stv 		(void) close(p[0]);
6894b087712Stv 		while ((pid = wait(&status)) != cpid && pid >= 0)
6904b087712Stv 			continue;
6914b087712Stv 		pbstr(getstring());
6924b087712Stv 	}
6934b087712Stv }
694f3efdb75Schristos 
695f3efdb75Schristos void
getdivfile(const char * name)696f3efdb75Schristos getdivfile(const char *name)
697f3efdb75Schristos {
698f3efdb75Schristos 	FILE *f;
699f3efdb75Schristos 	int c;
700f3efdb75Schristos 
701f3efdb75Schristos 	f = fopen(name, "r");
702f3efdb75Schristos 	if (!f)
703f3efdb75Schristos 		return;
704f3efdb75Schristos 
705f3efdb75Schristos 	while ((c = getc(f))!= EOF)
706f3efdb75Schristos 		putc(c, active);
707f3efdb75Schristos 	(void) fclose(f);
708f3efdb75Schristos }
7098a4a35b9Schristos 
7108a4a35b9Schristos #ifdef REAL_FREEZE
7118a4a35b9Schristos void
freeze_state(const char * fname)7128a4a35b9Schristos freeze_state(const char *fname)
7138a4a35b9Schristos {
7148a4a35b9Schristos 	FILE *f;
7158a4a35b9Schristos 
7168a4a35b9Schristos 	if ((f = fopen(fname, "wb")) == NULL)
7178a4a35b9Schristos 		m4errx(EXIT_FAILURE, "Can't open output freeze file `%s' (%s)",
7188a4a35b9Schristos 		    fname, strerror(errno));
7198a4a35b9Schristos 	fprintf(f, "# This is a frozen state file generated by %s\nV1\n",
7208a4a35b9Schristos 	    getprogname());
7218a4a35b9Schristos 	fprintf(f, "Q%zu,%zu\n%s%s\n", strlen(lquote), strlen(rquote),
7228a4a35b9Schristos 	    lquote, rquote);
7238a4a35b9Schristos 	fprintf(f, "C%zu,%zu\n%s%s\n", strlen(scommt), strlen(ecommt),
7248a4a35b9Schristos 	    scommt, ecommt);
7258a4a35b9Schristos 	dump_state(f);
7268a4a35b9Schristos 	/* XXX: diversions? */
7278a4a35b9Schristos 	fprintf(f, "D-1,0\n");
7288a4a35b9Schristos 	fprintf(f, "# End of frozen state file\n");
7298a4a35b9Schristos 	fclose(f);
7308a4a35b9Schristos }
7318a4a35b9Schristos 
7328a4a35b9Schristos void
thaw_state(const char * fname)7338a4a35b9Schristos thaw_state(const char *fname)
7348a4a35b9Schristos {
7358a4a35b9Schristos 	char *name = NULL;
7368a4a35b9Schristos 	size_t nl, namelen = 0;
7378a4a35b9Schristos 	char *defn = NULL;
7388a4a35b9Schristos 	size_t dl, defnlen = 0;
7398a4a35b9Schristos 	size_t lineno = 0;
7408a4a35b9Schristos 	char line[1024], *ptr, type;
7418a4a35b9Schristos 	FILE *f;
7428a4a35b9Schristos 
7438a4a35b9Schristos 	if ((f = fopen(fname, "rb")) == NULL)
7448a4a35b9Schristos 		m4errx(EXIT_FAILURE, "Can't open frozen file `%s' (%s)",
7458a4a35b9Schristos 		    fname, strerror(errno));
7468a4a35b9Schristos 
7478a4a35b9Schristos #define GET() if (fgets(line, (int)sizeof(line), f) == NULL) goto out
7488a4a35b9Schristos #define GETSTR(s, l) if (fread(s, 1, l, f) != l) goto out; else s[l] = '\0'
7498a4a35b9Schristos 
7508a4a35b9Schristos 	GET();	/* comment */
7518a4a35b9Schristos 	GET();	/* version */
7528a4a35b9Schristos 	if ((ptr = strrchr(line, '\n')) != NULL)
7538a4a35b9Schristos 		*ptr = '\0';
7548a4a35b9Schristos 	if (strcmp(line, "V1") != 0)
7558a4a35b9Schristos 		m4errx(EXIT_FAILURE, "Bad frozen version `%s'", line);
7568a4a35b9Schristos 
7578a4a35b9Schristos 	for (;;) {
7588a4a35b9Schristos 		GET();
7598a4a35b9Schristos 		lineno++;
7608a4a35b9Schristos 		switch (*line) {
7618a4a35b9Schristos 		case '\n':
7628a4a35b9Schristos 			continue;
7638a4a35b9Schristos 		case '#':
7648a4a35b9Schristos 			free(name);
7658a4a35b9Schristos 			free(defn);
7668a4a35b9Schristos 			fclose(f);
7678a4a35b9Schristos 			return;
7688a4a35b9Schristos 		default:
7698a4a35b9Schristos 			if (sscanf(line, "%c%zu,%zu\n", &type, &nl, &dl) != 3)
7708a4a35b9Schristos 				m4errx(EXIT_FAILURE, "%s, %zu: Bad line `%s'",
7718a4a35b9Schristos 				    fname, lineno, line);
7728a4a35b9Schristos 			break;
7738a4a35b9Schristos 		}
7748a4a35b9Schristos 
7758a4a35b9Schristos 		switch (type) {
7768a4a35b9Schristos 		case 'Q':
7778a4a35b9Schristos 			if (nl >= sizeof(lquote) || dl >= sizeof(rquote))
7788a4a35b9Schristos 				m4errx(EXIT_FAILURE, "%s, %zu: Quote too long",
7798a4a35b9Schristos 				    fname, lineno);
7808a4a35b9Schristos 			GETSTR(lquote, nl);
7818a4a35b9Schristos 			GETSTR(rquote, dl);
7828a4a35b9Schristos 			break;
7838a4a35b9Schristos 
7848a4a35b9Schristos 		case 'C':
7858a4a35b9Schristos 			if (nl >= sizeof(scommt) || dl >= sizeof(ecommt))
7868a4a35b9Schristos 				m4errx(EXIT_FAILURE, "%s, %zu: Comment too long",
7878a4a35b9Schristos 				    fname, lineno);
7888a4a35b9Schristos 			GETSTR(scommt, nl);
7898a4a35b9Schristos 			GETSTR(ecommt, dl);
7908a4a35b9Schristos 			break;
7918a4a35b9Schristos 
7928a4a35b9Schristos 		case 'T':
7938a4a35b9Schristos 		case 'F':
7948a4a35b9Schristos 			if (nl >= namelen)
7958a4a35b9Schristos 				name = xrealloc(name, namelen = nl + 1,
7968a4a35b9Schristos 					"name grow");
7978a4a35b9Schristos 			if (dl >= defnlen)
7988a4a35b9Schristos 				defn = xrealloc(defn, defnlen = dl + 1,
7998a4a35b9Schristos 					"defn grow");
8008a4a35b9Schristos 			GETSTR(name, nl);
8018a4a35b9Schristos 			GETSTR(defn, dl);
8028a4a35b9Schristos 			macro_pushdef(name, defn);
8038a4a35b9Schristos 			break;
8048a4a35b9Schristos 
8058a4a35b9Schristos 		case 'D':
8068a4a35b9Schristos 			/* XXX: Not implemented */
8078a4a35b9Schristos 			break;
8088a4a35b9Schristos 
8098a4a35b9Schristos 		default:
8108a4a35b9Schristos 			m4errx(EXIT_FAILURE, "%s, %zu: Unknown type %c",
8118a4a35b9Schristos 			    fname, lineno,type);
8128a4a35b9Schristos 		}
8138a4a35b9Schristos 	}
8148a4a35b9Schristos out:
81559db2a97Srillig 	m4errx(EXIT_FAILURE, "Unexpected end of file in `%s'", fname);
8168a4a35b9Schristos }
8178a4a35b9Schristos #endif
818