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