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