1*88497f0cSBaptiste Daroussin /* $OpenBSD: gnum4.c,v 1.46 2014/07/10 14:12:31 espie Exp $ */ 2e3d86717SJuli Mallett 3e3d86717SJuli Mallett /* 4e3d86717SJuli Mallett * Copyright (c) 1999 Marc Espie 5e3d86717SJuli Mallett * 6e3d86717SJuli Mallett * Redistribution and use in source and binary forms, with or without 7e3d86717SJuli Mallett * modification, are permitted provided that the following conditions 8e3d86717SJuli Mallett * are met: 9e3d86717SJuli Mallett * 1. Redistributions of source code must retain the above copyright 10e3d86717SJuli Mallett * notice, this list of conditions and the following disclaimer. 11e3d86717SJuli Mallett * 2. Redistributions in binary form must reproduce the above copyright 12e3d86717SJuli Mallett * notice, this list of conditions and the following disclaimer in the 13e3d86717SJuli Mallett * documentation and/or other materials provided with the distribution. 14e3d86717SJuli Mallett * 15e3d86717SJuli Mallett * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 16e3d86717SJuli Mallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17e3d86717SJuli Mallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18e3d86717SJuli Mallett * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 19e3d86717SJuli Mallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20e3d86717SJuli Mallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21e3d86717SJuli Mallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22e3d86717SJuli Mallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23e3d86717SJuli Mallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24e3d86717SJuli Mallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25e3d86717SJuli Mallett * SUCH DAMAGE. 26e3d86717SJuli Mallett */ 2756ca2b35SJuli Mallett #include <sys/cdefs.h> 2856ca2b35SJuli Mallett __FBSDID("$FreeBSD$"); 2956ca2b35SJuli Mallett 30e3d86717SJuli Mallett /* 31e3d86717SJuli Mallett * functions needed to support gnu-m4 extensions, including a fake freezing 32e3d86717SJuli Mallett */ 33e3d86717SJuli Mallett 34e3d86717SJuli Mallett #include <sys/param.h> 35e3d86717SJuli Mallett #include <sys/types.h> 36e3d86717SJuli Mallett #include <sys/wait.h> 37e3d86717SJuli Mallett #include <ctype.h> 38a841e1ebSBaptiste Daroussin #include <err.h> 39e3d86717SJuli Mallett #include <paths.h> 40e3d86717SJuli Mallett #include <regex.h> 41e3d86717SJuli Mallett #include <stddef.h> 42e3d86717SJuli Mallett #include <stdlib.h> 43e3d86717SJuli Mallett #include <stdio.h> 44e3d86717SJuli Mallett #include <string.h> 45e3d86717SJuli Mallett #include <errno.h> 46e3d86717SJuli Mallett #include <unistd.h> 47e3d86717SJuli Mallett #include "mdef.h" 48e3d86717SJuli Mallett #include "stdd.h" 49e3d86717SJuli Mallett #include "extern.h" 50e3d86717SJuli Mallett 51e3d86717SJuli Mallett 52e3d86717SJuli Mallett int mimic_gnu = 0; 53e3d86717SJuli Mallett 54e3d86717SJuli Mallett /* 55e3d86717SJuli Mallett * Support for include path search 566bccea7cSRebecca Cran * First search in the current directory. 57e3d86717SJuli Mallett * If not found, and the path is not absolute, include path kicks in. 58e3d86717SJuli Mallett * First, -I options, in the order found on the command line. 59e3d86717SJuli Mallett * Then M4PATH env variable 60e3d86717SJuli Mallett */ 61e3d86717SJuli Mallett 62eccad222SEd Schouten static struct path_entry { 63e3d86717SJuli Mallett char *name; 64e3d86717SJuli Mallett struct path_entry *next; 65e3d86717SJuli Mallett } *first, *last; 66e3d86717SJuli Mallett 67e3d86717SJuli Mallett static struct path_entry *new_path_entry(const char *); 68e3d86717SJuli Mallett static void ensure_m4path(void); 69e3d86717SJuli Mallett static struct input_file *dopath(struct input_file *, const char *); 70e3d86717SJuli Mallett 71e3d86717SJuli Mallett static struct path_entry * 72bd2bfb58SJuli Mallett new_path_entry(const char *dirname) 73e3d86717SJuli Mallett { 74e3d86717SJuli Mallett struct path_entry *n; 75e3d86717SJuli Mallett 76e3d86717SJuli Mallett n = malloc(sizeof(struct path_entry)); 77e3d86717SJuli Mallett if (!n) 78e3d86717SJuli Mallett errx(1, "out of memory"); 79e3d86717SJuli Mallett n->name = strdup(dirname); 80e3d86717SJuli Mallett if (!n->name) 81e3d86717SJuli Mallett errx(1, "out of memory"); 82e3d86717SJuli Mallett n->next = 0; 83e3d86717SJuli Mallett return n; 84e3d86717SJuli Mallett } 85e3d86717SJuli Mallett 86e3d86717SJuli Mallett void 87bd2bfb58SJuli Mallett addtoincludepath(const char *dirname) 88e3d86717SJuli Mallett { 89e3d86717SJuli Mallett struct path_entry *n; 90e3d86717SJuli Mallett 91e3d86717SJuli Mallett n = new_path_entry(dirname); 92e3d86717SJuli Mallett 93e3d86717SJuli Mallett if (last) { 94e3d86717SJuli Mallett last->next = n; 95e3d86717SJuli Mallett last = n; 96e3d86717SJuli Mallett } 97e3d86717SJuli Mallett else 98e3d86717SJuli Mallett last = first = n; 99e3d86717SJuli Mallett } 100e3d86717SJuli Mallett 101e3d86717SJuli Mallett static void 102d1fea89cSJuli Mallett ensure_m4path(void) 103e3d86717SJuli Mallett { 104e3d86717SJuli Mallett static int envpathdone = 0; 105e3d86717SJuli Mallett char *envpath; 106e3d86717SJuli Mallett char *sweep; 107e3d86717SJuli Mallett char *path; 108e3d86717SJuli Mallett 109e3d86717SJuli Mallett if (envpathdone) 110e3d86717SJuli Mallett return; 111e3d86717SJuli Mallett envpathdone = TRUE; 112e3d86717SJuli Mallett envpath = getenv("M4PATH"); 113e3d86717SJuli Mallett if (!envpath) 114e3d86717SJuli Mallett return; 115e3d86717SJuli Mallett /* for portability: getenv result is read-only */ 116e3d86717SJuli Mallett envpath = strdup(envpath); 117e3d86717SJuli Mallett if (!envpath) 118e3d86717SJuli Mallett errx(1, "out of memory"); 119e3d86717SJuli Mallett for (sweep = envpath; 120e3d86717SJuli Mallett (path = strsep(&sweep, ":")) != NULL;) 121e3d86717SJuli Mallett addtoincludepath(path); 122e3d86717SJuli Mallett free(envpath); 123e3d86717SJuli Mallett } 124e3d86717SJuli Mallett 125e3d86717SJuli Mallett static 126e3d86717SJuli Mallett struct input_file * 127bd2bfb58SJuli Mallett dopath(struct input_file *i, const char *filename) 128e3d86717SJuli Mallett { 129e3d86717SJuli Mallett char path[MAXPATHLEN]; 130e3d86717SJuli Mallett struct path_entry *pe; 131e3d86717SJuli Mallett FILE *f; 132e3d86717SJuli Mallett 133e3d86717SJuli Mallett for (pe = first; pe; pe = pe->next) { 134e3d86717SJuli Mallett snprintf(path, sizeof(path), "%s/%s", pe->name, filename); 135a841e1ebSBaptiste Daroussin if ((f = fopen(path, "r")) != 0) { 136e3d86717SJuli Mallett set_input(i, f, path); 137e3d86717SJuli Mallett return i; 138e3d86717SJuli Mallett } 139e3d86717SJuli Mallett } 140e3d86717SJuli Mallett return NULL; 141e3d86717SJuli Mallett } 142e3d86717SJuli Mallett 143e3d86717SJuli Mallett struct input_file * 144bd2bfb58SJuli Mallett fopen_trypath(struct input_file *i, const char *filename) 145e3d86717SJuli Mallett { 146e3d86717SJuli Mallett FILE *f; 147e3d86717SJuli Mallett 148e3d86717SJuli Mallett f = fopen(filename, "r"); 149e3d86717SJuli Mallett if (f != NULL) { 150e3d86717SJuli Mallett set_input(i, f, filename); 151e3d86717SJuli Mallett return i; 152e3d86717SJuli Mallett } 153e3d86717SJuli Mallett if (filename[0] == '/') 154e3d86717SJuli Mallett return NULL; 155e3d86717SJuli Mallett 156e3d86717SJuli Mallett ensure_m4path(); 157e3d86717SJuli Mallett 158e3d86717SJuli Mallett return dopath(i, filename); 159e3d86717SJuli Mallett } 160e3d86717SJuli Mallett 161e3d86717SJuli Mallett void 162bd2bfb58SJuli Mallett doindir(const char *argv[], int argc) 163e3d86717SJuli Mallett { 164a841e1ebSBaptiste Daroussin ndptr n; 165a841e1ebSBaptiste Daroussin struct macro_definition *p = NULL; 166e3d86717SJuli Mallett 167a841e1ebSBaptiste Daroussin n = lookup(argv[2]); 168a841e1ebSBaptiste Daroussin if (n == NULL || (p = macro_getdef(n)) == NULL) 169a841e1ebSBaptiste Daroussin m4errx(1, "indir: undefined macro %s.", argv[2]); 170e3d86717SJuli Mallett argv[1] = p->defn; 171a841e1ebSBaptiste Daroussin 172a841e1ebSBaptiste Daroussin eval(argv+1, argc-1, p->type, is_traced(n)); 173e3d86717SJuli Mallett } 174e3d86717SJuli Mallett 175e3d86717SJuli Mallett void 176bd2bfb58SJuli Mallett dobuiltin(const char *argv[], int argc) 177e3d86717SJuli Mallett { 178a841e1ebSBaptiste Daroussin ndptr p; 179a841e1ebSBaptiste Daroussin 180e3d86717SJuli Mallett argv[1] = NULL; 181a841e1ebSBaptiste Daroussin p = macro_getbuiltin(argv[2]); 182a841e1ebSBaptiste Daroussin if (p != NULL) 183a841e1ebSBaptiste Daroussin eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p)); 184e3d86717SJuli Mallett else 185a841e1ebSBaptiste Daroussin m4errx(1, "unknown builtin %s.", argv[2]); 186e3d86717SJuli Mallett } 187e3d86717SJuli Mallett 188e3d86717SJuli Mallett 189e3d86717SJuli Mallett /* We need some temporary buffer space, as pb pushes BACK and substitution 190e3d86717SJuli Mallett * proceeds forward... */ 191e3d86717SJuli Mallett static char *buffer; 192e3d86717SJuli Mallett static size_t bufsize = 0; 193e3d86717SJuli Mallett static size_t current = 0; 194e3d86717SJuli Mallett 195e3d86717SJuli Mallett static void addchars(const char *, size_t); 196bd2bfb58SJuli Mallett static void addchar(int); 197e3d86717SJuli Mallett static char *twiddle(const char *); 198e3d86717SJuli Mallett static char *getstring(void); 199*88497f0cSBaptiste Daroussin static void exit_regerror(int, regex_t *, const char *); 200*88497f0cSBaptiste Daroussin static void do_subst(const char *, regex_t *, const char *, const char *, 201*88497f0cSBaptiste Daroussin regmatch_t *); 202*88497f0cSBaptiste Daroussin static void do_regexpindex(const char *, regex_t *, const char *, regmatch_t *); 203*88497f0cSBaptiste Daroussin static void do_regexp(const char *, regex_t *, const char *, const char *, 204*88497f0cSBaptiste Daroussin regmatch_t *); 205a841e1ebSBaptiste Daroussin static void add_sub(int, const char *, regex_t *, regmatch_t *); 206e3d86717SJuli Mallett static void add_replace(const char *, regex_t *, const char *, regmatch_t *); 207e3d86717SJuli Mallett #define addconstantstring(s) addchars((s), sizeof(s)-1) 208e3d86717SJuli Mallett 209e3d86717SJuli Mallett static void 210bd2bfb58SJuli Mallett addchars(const char *c, size_t n) 211e3d86717SJuli Mallett { 212e3d86717SJuli Mallett if (n == 0) 213e3d86717SJuli Mallett return; 214e3d86717SJuli Mallett while (current + n > bufsize) { 215e3d86717SJuli Mallett if (bufsize == 0) 216e3d86717SJuli Mallett bufsize = 1024; 217e3d86717SJuli Mallett else 218e3d86717SJuli Mallett bufsize *= 2; 219a841e1ebSBaptiste Daroussin buffer = xrealloc(buffer, bufsize, NULL); 220e3d86717SJuli Mallett } 221e3d86717SJuli Mallett memcpy(buffer+current, c, n); 222e3d86717SJuli Mallett current += n; 223e3d86717SJuli Mallett } 224e3d86717SJuli Mallett 225e3d86717SJuli Mallett static void 226bd2bfb58SJuli Mallett addchar(int c) 227e3d86717SJuli Mallett { 228e3d86717SJuli Mallett if (current +1 > bufsize) { 229e3d86717SJuli Mallett if (bufsize == 0) 230e3d86717SJuli Mallett bufsize = 1024; 231e3d86717SJuli Mallett else 232e3d86717SJuli Mallett bufsize *= 2; 233a841e1ebSBaptiste Daroussin buffer = xrealloc(buffer, bufsize, NULL); 234e3d86717SJuli Mallett } 235e3d86717SJuli Mallett buffer[current++] = c; 236e3d86717SJuli Mallett } 237e3d86717SJuli Mallett 238e3d86717SJuli Mallett static char * 239d1fea89cSJuli Mallett getstring(void) 240e3d86717SJuli Mallett { 241e3d86717SJuli Mallett addchar('\0'); 242e3d86717SJuli Mallett current = 0; 243e3d86717SJuli Mallett return buffer; 244e3d86717SJuli Mallett } 245e3d86717SJuli Mallett 246e3d86717SJuli Mallett 247e3d86717SJuli Mallett static void 248*88497f0cSBaptiste Daroussin exit_regerror(int er, regex_t *re, const char *source) 249e3d86717SJuli Mallett { 250e3d86717SJuli Mallett size_t errlen; 251e3d86717SJuli Mallett char *errbuf; 252e3d86717SJuli Mallett 253e3d86717SJuli Mallett errlen = regerror(er, re, NULL, 0); 254a841e1ebSBaptiste Daroussin errbuf = xalloc(errlen, 255a841e1ebSBaptiste Daroussin "malloc in regerror: %lu", (unsigned long)errlen); 256e3d86717SJuli Mallett regerror(er, re, errbuf, errlen); 257*88497f0cSBaptiste Daroussin m4errx(1, "regular expression error in %s: %s.", source, errbuf); 258e3d86717SJuli Mallett } 259e3d86717SJuli Mallett 260e3d86717SJuli Mallett static void 261a841e1ebSBaptiste Daroussin add_sub(int n, const char *string, regex_t *re, regmatch_t *pm) 262e3d86717SJuli Mallett { 263a841e1ebSBaptiste Daroussin if (n > (int)re->re_nsub) 264a841e1ebSBaptiste Daroussin warnx("No subexpression %d", n); 265e3d86717SJuli Mallett /* Subexpressions that did not match are 266e3d86717SJuli Mallett * not an error. */ 267e3d86717SJuli Mallett else if (pm[n].rm_so != -1 && 268e3d86717SJuli Mallett pm[n].rm_eo != -1) { 269e3d86717SJuli Mallett addchars(string + pm[n].rm_so, 270e3d86717SJuli Mallett pm[n].rm_eo - pm[n].rm_so); 271e3d86717SJuli Mallett } 272e3d86717SJuli Mallett } 273e3d86717SJuli Mallett 274e3d86717SJuli Mallett /* Add replacement string to the output buffer, recognizing special 275e3d86717SJuli Mallett * constructs and replacing them with substrings of the original string. 276e3d86717SJuli Mallett */ 277e3d86717SJuli Mallett static void 278bd2bfb58SJuli Mallett add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm) 279e3d86717SJuli Mallett { 280e3d86717SJuli Mallett const char *p; 281e3d86717SJuli Mallett 282e3d86717SJuli Mallett for (p = replace; *p != '\0'; p++) { 283e3d86717SJuli Mallett if (*p == '&' && !mimic_gnu) { 284e3d86717SJuli Mallett add_sub(0, string, re, pm); 285e3d86717SJuli Mallett continue; 286e3d86717SJuli Mallett } 287e3d86717SJuli Mallett if (*p == '\\') { 288e3d86717SJuli Mallett if (p[1] == '\\') { 289e3d86717SJuli Mallett addchar(p[1]); 290e3d86717SJuli Mallett p++; 291e3d86717SJuli Mallett continue; 292e3d86717SJuli Mallett } 293e3d86717SJuli Mallett if (p[1] == '&') { 294e3d86717SJuli Mallett if (mimic_gnu) 295e3d86717SJuli Mallett add_sub(0, string, re, pm); 296e3d86717SJuli Mallett else 297e3d86717SJuli Mallett addchar(p[1]); 298e3d86717SJuli Mallett p++; 299e3d86717SJuli Mallett continue; 300e3d86717SJuli Mallett } 301*88497f0cSBaptiste Daroussin if (isdigit((unsigned char)p[1])) { 302e3d86717SJuli Mallett add_sub(*(++p) - '0', string, re, pm); 303e3d86717SJuli Mallett continue; 304e3d86717SJuli Mallett } 305e3d86717SJuli Mallett } 306e3d86717SJuli Mallett addchar(*p); 307e3d86717SJuli Mallett } 308e3d86717SJuli Mallett } 309e3d86717SJuli Mallett 310e3d86717SJuli Mallett static void 311*88497f0cSBaptiste Daroussin do_subst(const char *string, regex_t *re, const char *source, 312*88497f0cSBaptiste Daroussin const char *replace, regmatch_t *pm) 313e3d86717SJuli Mallett { 314e3d86717SJuli Mallett int error; 315e3d86717SJuli Mallett int flags = 0; 316e3d86717SJuli Mallett const char *last_match = NULL; 317e3d86717SJuli Mallett 318e3d86717SJuli Mallett while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) { 319e3d86717SJuli Mallett if (pm[0].rm_eo != 0) { 320e3d86717SJuli Mallett if (string[pm[0].rm_eo-1] == '\n') 321e3d86717SJuli Mallett flags = 0; 322e3d86717SJuli Mallett else 323e3d86717SJuli Mallett flags = REG_NOTBOL; 324e3d86717SJuli Mallett } 325e3d86717SJuli Mallett 326e3d86717SJuli Mallett /* NULL length matches are special... We use the `vi-mode' 327e3d86717SJuli Mallett * rule: don't allow a NULL-match at the last match 328e3d86717SJuli Mallett * position. 329e3d86717SJuli Mallett */ 330e3d86717SJuli Mallett if (pm[0].rm_so == pm[0].rm_eo && 331e3d86717SJuli Mallett string + pm[0].rm_so == last_match) { 332e3d86717SJuli Mallett if (*string == '\0') 333e3d86717SJuli Mallett return; 334e3d86717SJuli Mallett addchar(*string); 335e3d86717SJuli Mallett if (*string++ == '\n') 336e3d86717SJuli Mallett flags = 0; 337e3d86717SJuli Mallett else 338e3d86717SJuli Mallett flags = REG_NOTBOL; 339e3d86717SJuli Mallett continue; 340e3d86717SJuli Mallett } 341e3d86717SJuli Mallett last_match = string + pm[0].rm_so; 342e3d86717SJuli Mallett addchars(string, pm[0].rm_so); 343e3d86717SJuli Mallett add_replace(string, re, replace, pm); 344e3d86717SJuli Mallett string += pm[0].rm_eo; 345e3d86717SJuli Mallett } 346e3d86717SJuli Mallett if (error != REG_NOMATCH) 347*88497f0cSBaptiste Daroussin exit_regerror(error, re, source); 348e3d86717SJuli Mallett pbstr(string); 349e3d86717SJuli Mallett } 350e3d86717SJuli Mallett 351e3d86717SJuli Mallett static void 352*88497f0cSBaptiste Daroussin do_regexp(const char *string, regex_t *re, const char *source, 353*88497f0cSBaptiste Daroussin const char *replace, regmatch_t *pm) 354e3d86717SJuli Mallett { 355e3d86717SJuli Mallett int error; 356e3d86717SJuli Mallett 357e3d86717SJuli Mallett switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) { 358e3d86717SJuli Mallett case 0: 359e3d86717SJuli Mallett add_replace(string, re, replace, pm); 360e3d86717SJuli Mallett pbstr(getstring()); 361e3d86717SJuli Mallett break; 362e3d86717SJuli Mallett case REG_NOMATCH: 363e3d86717SJuli Mallett break; 364e3d86717SJuli Mallett default: 365*88497f0cSBaptiste Daroussin exit_regerror(error, re, source); 366e3d86717SJuli Mallett } 367e3d86717SJuli Mallett } 368e3d86717SJuli Mallett 369e3d86717SJuli Mallett static void 370*88497f0cSBaptiste Daroussin do_regexpindex(const char *string, regex_t *re, const char *source, 371*88497f0cSBaptiste Daroussin regmatch_t *pm) 372e3d86717SJuli Mallett { 373e3d86717SJuli Mallett int error; 374e3d86717SJuli Mallett 375e3d86717SJuli Mallett switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) { 376e3d86717SJuli Mallett case 0: 377e3d86717SJuli Mallett pbunsigned(pm[0].rm_so); 378e3d86717SJuli Mallett break; 379e3d86717SJuli Mallett case REG_NOMATCH: 380e3d86717SJuli Mallett pbnum(-1); 381e3d86717SJuli Mallett break; 382e3d86717SJuli Mallett default: 383*88497f0cSBaptiste Daroussin exit_regerror(error, re, source); 384e3d86717SJuli Mallett } 385e3d86717SJuli Mallett } 386e3d86717SJuli Mallett 387e3d86717SJuli Mallett /* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2 388e3d86717SJuli Mallett * says. So we twiddle with the regexp before passing it to regcomp. 389e3d86717SJuli Mallett */ 390e3d86717SJuli Mallett static char * 391bd2bfb58SJuli Mallett twiddle(const char *p) 392e3d86717SJuli Mallett { 393a841e1ebSBaptiste Daroussin /* + at start of regexp is a normal character for Gnu m4 */ 394a841e1ebSBaptiste Daroussin if (*p == '^') { 395a841e1ebSBaptiste Daroussin addchar(*p); 396a841e1ebSBaptiste Daroussin p++; 397a841e1ebSBaptiste Daroussin } 398a841e1ebSBaptiste Daroussin if (*p == '+') { 399a841e1ebSBaptiste Daroussin addchar('\\'); 400a841e1ebSBaptiste Daroussin } 401e3d86717SJuli Mallett /* This could use strcspn for speed... */ 402e3d86717SJuli Mallett while (*p != '\0') { 403e3d86717SJuli Mallett if (*p == '\\') { 404e3d86717SJuli Mallett switch(p[1]) { 405e3d86717SJuli Mallett case '(': 406e3d86717SJuli Mallett case ')': 407e3d86717SJuli Mallett case '|': 408e3d86717SJuli Mallett addchar(p[1]); 409e3d86717SJuli Mallett break; 410e3d86717SJuli Mallett case 'w': 411e3d86717SJuli Mallett addconstantstring("[_a-zA-Z0-9]"); 412e3d86717SJuli Mallett break; 413e3d86717SJuli Mallett case 'W': 414e3d86717SJuli Mallett addconstantstring("[^_a-zA-Z0-9]"); 415e3d86717SJuli Mallett break; 416e3d86717SJuli Mallett case '<': 417e3d86717SJuli Mallett addconstantstring("[[:<:]]"); 418e3d86717SJuli Mallett break; 419e3d86717SJuli Mallett case '>': 420e3d86717SJuli Mallett addconstantstring("[[:>:]]"); 421e3d86717SJuli Mallett break; 422e3d86717SJuli Mallett default: 423e3d86717SJuli Mallett addchars(p, 2); 424e3d86717SJuli Mallett break; 425e3d86717SJuli Mallett } 426e3d86717SJuli Mallett p+=2; 427e3d86717SJuli Mallett continue; 428e3d86717SJuli Mallett } 429e3d86717SJuli Mallett if (*p == '(' || *p == ')' || *p == '|') 430e3d86717SJuli Mallett addchar('\\'); 431e3d86717SJuli Mallett 432e3d86717SJuli Mallett addchar(*p); 433e3d86717SJuli Mallett p++; 434e3d86717SJuli Mallett } 435e3d86717SJuli Mallett return getstring(); 436e3d86717SJuli Mallett } 437e3d86717SJuli Mallett 438e3d86717SJuli Mallett /* patsubst(string, regexp, opt replacement) */ 439e3d86717SJuli Mallett /* argv[2]: string 440e3d86717SJuli Mallett * argv[3]: regexp 441e3d86717SJuli Mallett * argv[4]: opt rep 442e3d86717SJuli Mallett */ 443e3d86717SJuli Mallett void 444bd2bfb58SJuli Mallett dopatsubst(const char *argv[], int argc) 445e3d86717SJuli Mallett { 446e3d86717SJuli Mallett if (argc <= 3) { 447e3d86717SJuli Mallett warnx("Too few arguments to patsubst"); 448e3d86717SJuli Mallett return; 449e3d86717SJuli Mallett } 450a841e1ebSBaptiste Daroussin /* special case: empty regexp */ 451a841e1ebSBaptiste Daroussin if (argv[3][0] == '\0') { 452a841e1ebSBaptiste Daroussin const char *s; 453a841e1ebSBaptiste Daroussin size_t len; 454a841e1ebSBaptiste Daroussin if (argc > 4 && argv[4]) 455a841e1ebSBaptiste Daroussin len = strlen(argv[4]); 456a841e1ebSBaptiste Daroussin else 457a841e1ebSBaptiste Daroussin len = 0; 458a841e1ebSBaptiste Daroussin for (s = argv[2]; *s != '\0'; s++) { 459a841e1ebSBaptiste Daroussin addchars(argv[4], len); 460a841e1ebSBaptiste Daroussin addchar(*s); 461a841e1ebSBaptiste Daroussin } 462a841e1ebSBaptiste Daroussin } else { 463a841e1ebSBaptiste Daroussin int error; 464a841e1ebSBaptiste Daroussin regex_t re; 465a841e1ebSBaptiste Daroussin regmatch_t *pmatch; 466a841e1ebSBaptiste Daroussin int mode = REG_EXTENDED; 467*88497f0cSBaptiste Daroussin const char *source; 468a841e1ebSBaptiste Daroussin size_t l = strlen(argv[3]); 469a841e1ebSBaptiste Daroussin 470a841e1ebSBaptiste Daroussin if (!mimic_gnu || 471a841e1ebSBaptiste Daroussin (argv[3][0] == '^') || 472a841e1ebSBaptiste Daroussin (l > 0 && argv[3][l-1] == '$')) 473a841e1ebSBaptiste Daroussin mode |= REG_NEWLINE; 474a841e1ebSBaptiste Daroussin 475*88497f0cSBaptiste Daroussin source = mimic_gnu ? twiddle(argv[3]) : argv[3]; 476*88497f0cSBaptiste Daroussin error = regcomp(&re, source, mode); 477e3d86717SJuli Mallett if (error != 0) 478*88497f0cSBaptiste Daroussin exit_regerror(error, &re, source); 479e3d86717SJuli Mallett 480*88497f0cSBaptiste Daroussin pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t), 481*88497f0cSBaptiste Daroussin NULL); 482*88497f0cSBaptiste Daroussin do_subst(argv[2], &re, source, 483a841e1ebSBaptiste Daroussin argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch); 484e3d86717SJuli Mallett free(pmatch); 485e3d86717SJuli Mallett regfree(&re); 486e3d86717SJuli Mallett } 487a841e1ebSBaptiste Daroussin pbstr(getstring()); 488a841e1ebSBaptiste Daroussin } 489e3d86717SJuli Mallett 490e3d86717SJuli Mallett void 491bd2bfb58SJuli Mallett doregexp(const char *argv[], int argc) 492e3d86717SJuli Mallett { 493e3d86717SJuli Mallett int error; 494e3d86717SJuli Mallett regex_t re; 495e3d86717SJuli Mallett regmatch_t *pmatch; 496*88497f0cSBaptiste Daroussin const char *source; 497e3d86717SJuli Mallett 498e3d86717SJuli Mallett if (argc <= 3) { 499e3d86717SJuli Mallett warnx("Too few arguments to regexp"); 500e3d86717SJuli Mallett return; 501e3d86717SJuli Mallett } 502a841e1ebSBaptiste Daroussin /* special gnu case */ 503a841e1ebSBaptiste Daroussin if (argv[3][0] == '\0' && mimic_gnu) { 504a841e1ebSBaptiste Daroussin if (argc == 4 || argv[4] == NULL) 505a841e1ebSBaptiste Daroussin return; 506a841e1ebSBaptiste Daroussin else 507a841e1ebSBaptiste Daroussin pbstr(argv[4]); 508a841e1ebSBaptiste Daroussin } 509*88497f0cSBaptiste Daroussin source = mimic_gnu ? twiddle(argv[3]) : argv[3]; 510*88497f0cSBaptiste Daroussin error = regcomp(&re, source, REG_EXTENDED|REG_NEWLINE); 511e3d86717SJuli Mallett if (error != 0) 512*88497f0cSBaptiste Daroussin exit_regerror(error, &re, source); 513e3d86717SJuli Mallett 514*88497f0cSBaptiste Daroussin pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t), NULL); 515a841e1ebSBaptiste Daroussin if (argc == 4 || argv[4] == NULL) 516*88497f0cSBaptiste Daroussin do_regexpindex(argv[2], &re, source, pmatch); 517e3d86717SJuli Mallett else 518*88497f0cSBaptiste Daroussin do_regexp(argv[2], &re, source, argv[4], pmatch); 519e3d86717SJuli Mallett free(pmatch); 520e3d86717SJuli Mallett regfree(&re); 521e3d86717SJuli Mallett } 522e3d86717SJuli Mallett 523e3d86717SJuli Mallett void 524a841e1ebSBaptiste Daroussin doformat(const char *argv[], int argc) 525a841e1ebSBaptiste Daroussin { 526a841e1ebSBaptiste Daroussin const char *format = argv[2]; 527a841e1ebSBaptiste Daroussin int pos = 3; 528a841e1ebSBaptiste Daroussin int left_padded; 529a841e1ebSBaptiste Daroussin long width; 530a841e1ebSBaptiste Daroussin size_t l; 531a841e1ebSBaptiste Daroussin const char *thisarg = NULL; 532a841e1ebSBaptiste Daroussin char temp[2]; 533a841e1ebSBaptiste Daroussin long extra; 534a841e1ebSBaptiste Daroussin 535a841e1ebSBaptiste Daroussin while (*format != 0) { 536a841e1ebSBaptiste Daroussin if (*format != '%') { 537a841e1ebSBaptiste Daroussin addchar(*format++); 538a841e1ebSBaptiste Daroussin continue; 539a841e1ebSBaptiste Daroussin } 540a841e1ebSBaptiste Daroussin 541a841e1ebSBaptiste Daroussin format++; 542a841e1ebSBaptiste Daroussin if (*format == '%') { 543a841e1ebSBaptiste Daroussin addchar(*format++); 544a841e1ebSBaptiste Daroussin continue; 545a841e1ebSBaptiste Daroussin } 546a841e1ebSBaptiste Daroussin if (*format == 0) { 547a841e1ebSBaptiste Daroussin addchar('%'); 548a841e1ebSBaptiste Daroussin break; 549a841e1ebSBaptiste Daroussin } 550a841e1ebSBaptiste Daroussin 551a841e1ebSBaptiste Daroussin if (*format == '*') { 552a841e1ebSBaptiste Daroussin format++; 553a841e1ebSBaptiste Daroussin if (pos >= argc) 554a841e1ebSBaptiste Daroussin m4errx(1, 555a841e1ebSBaptiste Daroussin "Format with too many format specifiers."); 556a841e1ebSBaptiste Daroussin width = strtol(argv[pos++], NULL, 10); 557a841e1ebSBaptiste Daroussin } else { 558a841e1ebSBaptiste Daroussin width = strtol(format, __DECONST(char **,&format), 10); 559a841e1ebSBaptiste Daroussin } 560a841e1ebSBaptiste Daroussin if (width < 0) { 561a841e1ebSBaptiste Daroussin left_padded = 1; 562a841e1ebSBaptiste Daroussin width = -width; 563a841e1ebSBaptiste Daroussin } else { 564a841e1ebSBaptiste Daroussin left_padded = 0; 565a841e1ebSBaptiste Daroussin } 566a841e1ebSBaptiste Daroussin if (*format == '.') { 567a841e1ebSBaptiste Daroussin format++; 568a841e1ebSBaptiste Daroussin if (*format == '*') { 569a841e1ebSBaptiste Daroussin format++; 570a841e1ebSBaptiste Daroussin if (pos >= argc) 571a841e1ebSBaptiste Daroussin m4errx(1, 572a841e1ebSBaptiste Daroussin "Format with too many format specifiers."); 573a841e1ebSBaptiste Daroussin extra = strtol(argv[pos++], NULL, 10); 574a841e1ebSBaptiste Daroussin } else { 575a841e1ebSBaptiste Daroussin extra = strtol(format, __DECONST(char **, &format), 10); 576a841e1ebSBaptiste Daroussin } 577a841e1ebSBaptiste Daroussin } else { 578a841e1ebSBaptiste Daroussin extra = LONG_MAX; 579a841e1ebSBaptiste Daroussin } 580a841e1ebSBaptiste Daroussin if (pos >= argc) 581a841e1ebSBaptiste Daroussin m4errx(1, "Format with too many format specifiers."); 582a841e1ebSBaptiste Daroussin switch(*format) { 583a841e1ebSBaptiste Daroussin case 's': 584a841e1ebSBaptiste Daroussin thisarg = argv[pos++]; 585a841e1ebSBaptiste Daroussin break; 586a841e1ebSBaptiste Daroussin case 'c': 587a841e1ebSBaptiste Daroussin temp[0] = strtoul(argv[pos++], NULL, 10); 588a841e1ebSBaptiste Daroussin temp[1] = 0; 589a841e1ebSBaptiste Daroussin thisarg = temp; 590a841e1ebSBaptiste Daroussin break; 591a841e1ebSBaptiste Daroussin default: 592a841e1ebSBaptiste Daroussin m4errx(1, "Unsupported format specification: %s.", 593a841e1ebSBaptiste Daroussin argv[2]); 594a841e1ebSBaptiste Daroussin } 595a841e1ebSBaptiste Daroussin format++; 596a841e1ebSBaptiste Daroussin l = strlen(thisarg); 597a841e1ebSBaptiste Daroussin if ((long)l > extra) 598a841e1ebSBaptiste Daroussin l = extra; 599a841e1ebSBaptiste Daroussin if (!left_padded) { 600a841e1ebSBaptiste Daroussin while ((long)l < width--) 601a841e1ebSBaptiste Daroussin addchar(' '); 602a841e1ebSBaptiste Daroussin } 603a841e1ebSBaptiste Daroussin addchars(thisarg, l); 604a841e1ebSBaptiste Daroussin if (left_padded) { 605a841e1ebSBaptiste Daroussin while ((long)l < width--) 606a841e1ebSBaptiste Daroussin addchar(' '); 607a841e1ebSBaptiste Daroussin } 608a841e1ebSBaptiste Daroussin } 609a841e1ebSBaptiste Daroussin pbstr(getstring()); 610a841e1ebSBaptiste Daroussin } 611a841e1ebSBaptiste Daroussin 612a841e1ebSBaptiste Daroussin void 613bd2bfb58SJuli Mallett doesyscmd(const char *cmd) 614e3d86717SJuli Mallett { 615e3d86717SJuli Mallett int p[2]; 616e3d86717SJuli Mallett pid_t pid, cpid; 617a841e1ebSBaptiste Daroussin char *argv[4]; 618e3d86717SJuli Mallett int cc; 619e3d86717SJuli Mallett int status; 620e3d86717SJuli Mallett 621e3d86717SJuli Mallett /* Follow gnu m4 documentation: first flush buffers. */ 622e3d86717SJuli Mallett fflush(NULL); 623e3d86717SJuli Mallett 624a841e1ebSBaptiste Daroussin argv[0] = __DECONST(char *, "sh"); 625a841e1ebSBaptiste Daroussin argv[1] = __DECONST(char *, "-c"); 626a841e1ebSBaptiste Daroussin argv[2] = __DECONST(char *, cmd); 627a841e1ebSBaptiste Daroussin argv[3] = NULL; 628a841e1ebSBaptiste Daroussin 629e3d86717SJuli Mallett /* Just set up standard output, share stderr and stdin with m4 */ 630e3d86717SJuli Mallett if (pipe(p) == -1) 631e3d86717SJuli Mallett err(1, "bad pipe"); 632e3d86717SJuli Mallett switch(cpid = fork()) { 633e3d86717SJuli Mallett case -1: 634e3d86717SJuli Mallett err(1, "bad fork"); 635e3d86717SJuli Mallett /* NOTREACHED */ 636e3d86717SJuli Mallett case 0: 637e3d86717SJuli Mallett (void) close(p[0]); 638e3d86717SJuli Mallett (void) dup2(p[1], 1); 639e3d86717SJuli Mallett (void) close(p[1]); 640a841e1ebSBaptiste Daroussin execv(_PATH_BSHELL, argv); 641e3d86717SJuli Mallett exit(1); 642e3d86717SJuli Mallett default: 643e3d86717SJuli Mallett /* Read result in two stages, since m4's buffer is 644e3d86717SJuli Mallett * pushback-only. */ 645e3d86717SJuli Mallett (void) close(p[1]); 646e3d86717SJuli Mallett do { 647e3d86717SJuli Mallett char result[BUFSIZE]; 648e3d86717SJuli Mallett cc = read(p[0], result, sizeof result); 649e3d86717SJuli Mallett if (cc > 0) 650e3d86717SJuli Mallett addchars(result, cc); 651e3d86717SJuli Mallett } while (cc > 0 || (cc == -1 && errno == EINTR)); 652e3d86717SJuli Mallett 653e3d86717SJuli Mallett (void) close(p[0]); 654e3d86717SJuli Mallett while ((pid = wait(&status)) != cpid && pid >= 0) 655e3d86717SJuli Mallett continue; 656e3d86717SJuli Mallett pbstr(getstring()); 657e3d86717SJuli Mallett } 658e3d86717SJuli Mallett } 659a841e1ebSBaptiste Daroussin 660a841e1ebSBaptiste Daroussin void 661a841e1ebSBaptiste Daroussin getdivfile(const char *name) 662a841e1ebSBaptiste Daroussin { 663a841e1ebSBaptiste Daroussin FILE *f; 664a841e1ebSBaptiste Daroussin int c; 665a841e1ebSBaptiste Daroussin 666a841e1ebSBaptiste Daroussin f = fopen(name, "r"); 667a841e1ebSBaptiste Daroussin if (!f) 668a841e1ebSBaptiste Daroussin return; 669a841e1ebSBaptiste Daroussin 670a841e1ebSBaptiste Daroussin while ((c = getc(f))!= EOF) 671a841e1ebSBaptiste Daroussin putc(c, active); 672a841e1ebSBaptiste Daroussin (void) fclose(f); 673a841e1ebSBaptiste Daroussin } 674