1283089Sbapt/* $OpenBSD: gnum4.c,v 1.50 2015/04/29 00:13:26 millert Exp $ */ 290744Sjmallett 390744Sjmallett/* 490744Sjmallett * Copyright (c) 1999 Marc Espie 590744Sjmallett * 690744Sjmallett * Redistribution and use in source and binary forms, with or without 790744Sjmallett * modification, are permitted provided that the following conditions 890744Sjmallett * are met: 990744Sjmallett * 1. Redistributions of source code must retain the above copyright 1090744Sjmallett * notice, this list of conditions and the following disclaimer. 1190744Sjmallett * 2. Redistributions in binary form must reproduce the above copyright 1290744Sjmallett * notice, this list of conditions and the following disclaimer in the 1390744Sjmallett * documentation and/or other materials provided with the distribution. 1490744Sjmallett * 1590744Sjmallett * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1690744Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1790744Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1890744Sjmallett * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1990744Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2090744Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2190744Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2290744Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2390744Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2490744Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2590744Sjmallett * SUCH DAMAGE. 2690744Sjmallett */ 2795061Sjmallett#include <sys/cdefs.h> 2895061Sjmallett__FBSDID("$FreeBSD: releng/11.0/usr.bin/m4/gnum4.c 298190 2016-04-18 07:09:34Z araujo $"); 2995061Sjmallett 30100014Sjmallett/* 3190744Sjmallett * functions needed to support gnu-m4 extensions, including a fake freezing 3290744Sjmallett */ 3390744Sjmallett 3490744Sjmallett#include <sys/types.h> 3590744Sjmallett#include <sys/wait.h> 3690744Sjmallett#include <ctype.h> 37228063Sbapt#include <err.h> 3890744Sjmallett#include <paths.h> 3990744Sjmallett#include <regex.h> 4090744Sjmallett#include <stddef.h> 4190744Sjmallett#include <stdlib.h> 42283089Sbapt#include <stdint.h> 4390744Sjmallett#include <stdio.h> 4490744Sjmallett#include <string.h> 4590744Sjmallett#include <errno.h> 4690744Sjmallett#include <unistd.h> 47283089Sbapt#include <limits.h> 4890744Sjmallett#include "mdef.h" 4990744Sjmallett#include "stdd.h" 5090744Sjmallett#include "extern.h" 5190744Sjmallett 5290744Sjmallett 5390744Sjmallettint mimic_gnu = 0; 5490744Sjmallett 5590744Sjmallett/* 5690744Sjmallett * Support for include path search 57218909Sbrucec * First search in the current directory. 5890744Sjmallett * If not found, and the path is not absolute, include path kicks in. 5990744Sjmallett * First, -I options, in the order found on the command line. 6090744Sjmallett * Then M4PATH env variable 6190744Sjmallett */ 6290744Sjmallett 63241777Sedstatic struct path_entry { 6490744Sjmallett char *name; 6590744Sjmallett struct path_entry *next; 6690744Sjmallett} *first, *last; 6790744Sjmallett 6890744Sjmallettstatic struct path_entry *new_path_entry(const char *); 6990744Sjmallettstatic void ensure_m4path(void); 7090744Sjmallettstatic struct input_file *dopath(struct input_file *, const char *); 7190744Sjmallett 7290744Sjmallettstatic struct path_entry * 7395887Sjmallettnew_path_entry(const char *dirname) 7490744Sjmallett{ 7590744Sjmallett struct path_entry *n; 7690744Sjmallett 7790744Sjmallett n = malloc(sizeof(struct path_entry)); 7890744Sjmallett if (!n) 7990744Sjmallett errx(1, "out of memory"); 80283089Sbapt n->name = xstrdup(dirname); 8190744Sjmallett n->next = 0; 8290744Sjmallett return n; 8390744Sjmallett} 84100014Sjmallett 85100014Sjmallettvoid 8695887Sjmallettaddtoincludepath(const char *dirname) 8790744Sjmallett{ 8890744Sjmallett struct path_entry *n; 8990744Sjmallett 9090744Sjmallett n = new_path_entry(dirname); 9190744Sjmallett 9290744Sjmallett if (last) { 9390744Sjmallett last->next = n; 9490744Sjmallett last = n; 9590744Sjmallett } 9690744Sjmallett else 9790744Sjmallett last = first = n; 9890744Sjmallett} 9990744Sjmallett 10090744Sjmallettstatic void 10199939Sjmallettensure_m4path(void) 10290744Sjmallett{ 10390744Sjmallett static int envpathdone = 0; 10490744Sjmallett char *envpath; 10590744Sjmallett char *sweep; 10690744Sjmallett char *path; 10790744Sjmallett 10890744Sjmallett if (envpathdone) 10990744Sjmallett return; 11090744Sjmallett envpathdone = TRUE; 11190744Sjmallett envpath = getenv("M4PATH"); 112100014Sjmallett if (!envpath) 11390744Sjmallett return; 11490744Sjmallett /* for portability: getenv result is read-only */ 115283089Sbapt envpath = xstrdup(envpath); 116100014Sjmallett for (sweep = envpath; 11790744Sjmallett (path = strsep(&sweep, ":")) != NULL;) 11890744Sjmallett addtoincludepath(path); 11990744Sjmallett free(envpath); 12090744Sjmallett} 12190744Sjmallett 12290744Sjmallettstatic 12390744Sjmallettstruct input_file * 12495887Sjmallettdopath(struct input_file *i, const char *filename) 12590744Sjmallett{ 126283089Sbapt char path[PATH_MAX]; 12790744Sjmallett struct path_entry *pe; 12890744Sjmallett FILE *f; 12990744Sjmallett 13090744Sjmallett for (pe = first; pe; pe = pe->next) { 13190744Sjmallett snprintf(path, sizeof(path), "%s/%s", pe->name, filename); 132298190Saraujo if ((f = fopen(path, "r")) != NULL) { 13390744Sjmallett set_input(i, f, path); 13490744Sjmallett return i; 13590744Sjmallett } 13690744Sjmallett } 13790744Sjmallett return NULL; 13890744Sjmallett} 13990744Sjmallett 14090744Sjmallettstruct input_file * 14195887Sjmallettfopen_trypath(struct input_file *i, const char *filename) 14290744Sjmallett{ 14390744Sjmallett FILE *f; 14490744Sjmallett 14590744Sjmallett f = fopen(filename, "r"); 14690744Sjmallett if (f != NULL) { 14790744Sjmallett set_input(i, f, filename); 14890744Sjmallett return i; 14990744Sjmallett } 15090744Sjmallett if (filename[0] == '/') 15190744Sjmallett return NULL; 15290744Sjmallett 15390744Sjmallett ensure_m4path(); 15490744Sjmallett 15590744Sjmallett return dopath(i, filename); 15690744Sjmallett} 15790744Sjmallett 158100014Sjmallettvoid 15995887Sjmallettdoindir(const char *argv[], int argc) 16090744Sjmallett{ 161228063Sbapt ndptr n; 162228063Sbapt struct macro_definition *p = NULL; 16390744Sjmallett 164228063Sbapt n = lookup(argv[2]); 165228063Sbapt if (n == NULL || (p = macro_getdef(n)) == NULL) 166228063Sbapt m4errx(1, "indir: undefined macro %s.", argv[2]); 16790744Sjmallett argv[1] = p->defn; 168228063Sbapt 169228063Sbapt eval(argv+1, argc-1, p->type, is_traced(n)); 17090744Sjmallett} 17190744Sjmallett 172100014Sjmallettvoid 17395887Sjmallettdobuiltin(const char *argv[], int argc) 17490744Sjmallett{ 175228063Sbapt ndptr p; 176228063Sbapt 17790744Sjmallett argv[1] = NULL; 178228063Sbapt p = macro_getbuiltin(argv[2]); 179228063Sbapt if (p != NULL) 180228063Sbapt eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p)); 18190744Sjmallett else 182228063Sbapt m4errx(1, "unknown builtin %s.", argv[2]); 183100014Sjmallett} 18490744Sjmallett 18590744Sjmallett 18690744Sjmallett/* We need some temporary buffer space, as pb pushes BACK and substitution 18790744Sjmallett * proceeds forward... */ 18890744Sjmallettstatic char *buffer; 18990744Sjmallettstatic size_t bufsize = 0; 19090744Sjmallettstatic size_t current = 0; 19190744Sjmallett 19290744Sjmallettstatic void addchars(const char *, size_t); 19395887Sjmallettstatic void addchar(int); 19490744Sjmallettstatic char *twiddle(const char *); 19590744Sjmallettstatic char *getstring(void); 196269162Sbaptstatic void exit_regerror(int, regex_t *, const char *); 197269162Sbaptstatic void do_subst(const char *, regex_t *, const char *, const char *, 198269162Sbapt regmatch_t *); 199269162Sbaptstatic void do_regexpindex(const char *, regex_t *, const char *, regmatch_t *); 200269162Sbaptstatic void do_regexp(const char *, regex_t *, const char *, const char *, 201269162Sbapt regmatch_t *); 202228063Sbaptstatic void add_sub(int, const char *, regex_t *, regmatch_t *); 20390744Sjmallettstatic void add_replace(const char *, regex_t *, const char *, regmatch_t *); 20490744Sjmallett#define addconstantstring(s) addchars((s), sizeof(s)-1) 20590744Sjmallett 206100014Sjmallettstatic void 20795887Sjmallettaddchars(const char *c, size_t n) 20890744Sjmallett{ 20990744Sjmallett if (n == 0) 21090744Sjmallett return; 21190744Sjmallett while (current + n > bufsize) { 21290744Sjmallett if (bufsize == 0) 21390744Sjmallett bufsize = 1024; 214283089Sbapt else if (bufsize <= SIZE_MAX/2) { 21590744Sjmallett bufsize *= 2; 216283089Sbapt } else { 217283089Sbapt errx(1, "size overflow"); 218283089Sbapt } 219228063Sbapt buffer = xrealloc(buffer, bufsize, NULL); 22090744Sjmallett } 22190744Sjmallett memcpy(buffer+current, c, n); 22290744Sjmallett current += n; 22390744Sjmallett} 22490744Sjmallett 225100014Sjmallettstatic void 22695887Sjmallettaddchar(int c) 22790744Sjmallett{ 22890744Sjmallett if (current +1 > bufsize) { 22990744Sjmallett if (bufsize == 0) 23090744Sjmallett bufsize = 1024; 23190744Sjmallett else 23290744Sjmallett bufsize *= 2; 233228063Sbapt buffer = xrealloc(buffer, bufsize, NULL); 23490744Sjmallett } 23590744Sjmallett buffer[current++] = c; 23690744Sjmallett} 23790744Sjmallett 23890744Sjmallettstatic char * 23999939Sjmallettgetstring(void) 24090744Sjmallett{ 24190744Sjmallett addchar('\0'); 24290744Sjmallett current = 0; 24390744Sjmallett return buffer; 24490744Sjmallett} 24590744Sjmallett 24690744Sjmallett 247100014Sjmallettstatic void 248269162Sbaptexit_regerror(int er, regex_t *re, const char *source) 24990744Sjmallett{ 250228063Sbapt size_t errlen; 251228063Sbapt char *errbuf; 25290744Sjmallett 25390744Sjmallett errlen = regerror(er, re, NULL, 0); 254228063Sbapt errbuf = xalloc(errlen, 255228063Sbapt "malloc in regerror: %lu", (unsigned long)errlen); 25690744Sjmallett regerror(er, re, errbuf, errlen); 257269162Sbapt m4errx(1, "regular expression error in %s: %s.", source, errbuf); 25890744Sjmallett} 25990744Sjmallett 26090744Sjmallettstatic void 261228063Sbaptadd_sub(int n, const char *string, regex_t *re, regmatch_t *pm) 26290744Sjmallett{ 263228063Sbapt if (n > (int)re->re_nsub) 264228063Sbapt warnx("No subexpression %d", n); 26590744Sjmallett /* Subexpressions that did not match are 26690744Sjmallett * not an error. */ 26790744Sjmallett else if (pm[n].rm_so != -1 && 26890744Sjmallett pm[n].rm_eo != -1) { 26990744Sjmallett addchars(string + pm[n].rm_so, 27090744Sjmallett pm[n].rm_eo - pm[n].rm_so); 27190744Sjmallett } 27290744Sjmallett} 27390744Sjmallett 27490744Sjmallett/* Add replacement string to the output buffer, recognizing special 27590744Sjmallett * constructs and replacing them with substrings of the original string. 27690744Sjmallett */ 277100014Sjmallettstatic void 27895887Sjmallettadd_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm) 27990744Sjmallett{ 28090744Sjmallett const char *p; 28190744Sjmallett 28290744Sjmallett for (p = replace; *p != '\0'; p++) { 28390744Sjmallett if (*p == '&' && !mimic_gnu) { 28490744Sjmallett add_sub(0, string, re, pm); 28590744Sjmallett continue; 28690744Sjmallett } 28790744Sjmallett if (*p == '\\') { 28890744Sjmallett if (p[1] == '\\') { 28990744Sjmallett addchar(p[1]); 29090744Sjmallett p++; 29190744Sjmallett continue; 29290744Sjmallett } 29390744Sjmallett if (p[1] == '&') { 29490744Sjmallett if (mimic_gnu) 29590744Sjmallett add_sub(0, string, re, pm); 29690744Sjmallett else 29790744Sjmallett addchar(p[1]); 29890744Sjmallett p++; 29990744Sjmallett continue; 30090744Sjmallett } 301269162Sbapt if (isdigit((unsigned char)p[1])) { 30290744Sjmallett add_sub(*(++p) - '0', string, re, pm); 30390744Sjmallett continue; 30490744Sjmallett } 30590744Sjmallett } 306228063Sbapt addchar(*p); 30790744Sjmallett } 30890744Sjmallett} 30990744Sjmallett 310100014Sjmallettstatic void 311269162Sbaptdo_subst(const char *string, regex_t *re, const char *source, 312269162Sbapt const char *replace, regmatch_t *pm) 31390744Sjmallett{ 31490744Sjmallett int error; 31590744Sjmallett int flags = 0; 31690744Sjmallett const char *last_match = NULL; 31790744Sjmallett 31890744Sjmallett while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) { 31990744Sjmallett if (pm[0].rm_eo != 0) { 32090744Sjmallett if (string[pm[0].rm_eo-1] == '\n') 32190744Sjmallett flags = 0; 32290744Sjmallett else 32390744Sjmallett flags = REG_NOTBOL; 32490744Sjmallett } 32590744Sjmallett 326100014Sjmallett /* NULL length matches are special... We use the `vi-mode' 32790744Sjmallett * rule: don't allow a NULL-match at the last match 328100014Sjmallett * position. 32990744Sjmallett */ 330100014Sjmallett if (pm[0].rm_so == pm[0].rm_eo && 33190744Sjmallett string + pm[0].rm_so == last_match) { 33290744Sjmallett if (*string == '\0') 33390744Sjmallett return; 33490744Sjmallett addchar(*string); 33590744Sjmallett if (*string++ == '\n') 33690744Sjmallett flags = 0; 33790744Sjmallett else 33890744Sjmallett flags = REG_NOTBOL; 33990744Sjmallett continue; 34090744Sjmallett } 34190744Sjmallett last_match = string + pm[0].rm_so; 34290744Sjmallett addchars(string, pm[0].rm_so); 34390744Sjmallett add_replace(string, re, replace, pm); 34490744Sjmallett string += pm[0].rm_eo; 34590744Sjmallett } 34690744Sjmallett if (error != REG_NOMATCH) 347269162Sbapt exit_regerror(error, re, source); 34890744Sjmallett pbstr(string); 34990744Sjmallett} 35090744Sjmallett 351100014Sjmallettstatic void 352269162Sbaptdo_regexp(const char *string, regex_t *re, const char *source, 353269162Sbapt const char *replace, regmatch_t *pm) 35490744Sjmallett{ 35590744Sjmallett int error; 35690744Sjmallett 35790744Sjmallett switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) { 358100014Sjmallett case 0: 35990744Sjmallett add_replace(string, re, replace, pm); 36090744Sjmallett pbstr(getstring()); 36190744Sjmallett break; 36290744Sjmallett case REG_NOMATCH: 36390744Sjmallett break; 36490744Sjmallett default: 365269162Sbapt exit_regerror(error, re, source); 36690744Sjmallett } 36790744Sjmallett} 36890744Sjmallett 369100014Sjmallettstatic void 370269162Sbaptdo_regexpindex(const char *string, regex_t *re, const char *source, 371269162Sbapt regmatch_t *pm) 37290744Sjmallett{ 37390744Sjmallett int error; 37490744Sjmallett 37590744Sjmallett switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) { 37690744Sjmallett case 0: 37790744Sjmallett pbunsigned(pm[0].rm_so); 37890744Sjmallett break; 37990744Sjmallett case REG_NOMATCH: 38090744Sjmallett pbnum(-1); 38190744Sjmallett break; 38290744Sjmallett default: 383269162Sbapt exit_regerror(error, re, source); 38490744Sjmallett } 38590744Sjmallett} 38690744Sjmallett 38790744Sjmallett/* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2 38890744Sjmallett * says. So we twiddle with the regexp before passing it to regcomp. 38990744Sjmallett */ 39090744Sjmallettstatic char * 39195887Sjmalletttwiddle(const char *p) 39290744Sjmallett{ 393228063Sbapt /* + at start of regexp is a normal character for Gnu m4 */ 394228063Sbapt if (*p == '^') { 395228063Sbapt addchar(*p); 396228063Sbapt p++; 397228063Sbapt } 398228063Sbapt if (*p == '+') { 399228063Sbapt addchar('\\'); 400228063Sbapt } 40190744Sjmallett /* This could use strcspn for speed... */ 40290744Sjmallett while (*p != '\0') { 40390744Sjmallett if (*p == '\\') { 40490744Sjmallett switch(p[1]) { 40590744Sjmallett case '(': 40690744Sjmallett case ')': 40790744Sjmallett case '|': 40890744Sjmallett addchar(p[1]); 40990744Sjmallett break; 41090744Sjmallett case 'w': 41190744Sjmallett addconstantstring("[_a-zA-Z0-9]"); 41290744Sjmallett break; 41390744Sjmallett case 'W': 41490744Sjmallett addconstantstring("[^_a-zA-Z0-9]"); 41590744Sjmallett break; 41690744Sjmallett case '<': 41790744Sjmallett addconstantstring("[[:<:]]"); 41890744Sjmallett break; 41990744Sjmallett case '>': 42090744Sjmallett addconstantstring("[[:>:]]"); 42190744Sjmallett break; 42290744Sjmallett default: 42390744Sjmallett addchars(p, 2); 42490744Sjmallett break; 42590744Sjmallett } 42690744Sjmallett p+=2; 42790744Sjmallett continue; 42890744Sjmallett } 42990744Sjmallett if (*p == '(' || *p == ')' || *p == '|') 43090744Sjmallett addchar('\\'); 43190744Sjmallett 43290744Sjmallett addchar(*p); 43390744Sjmallett p++; 43490744Sjmallett } 43590744Sjmallett return getstring(); 43690744Sjmallett} 43790744Sjmallett 43890744Sjmallett/* patsubst(string, regexp, opt replacement) */ 43990744Sjmallett/* argv[2]: string 44090744Sjmallett * argv[3]: regexp 44190744Sjmallett * argv[4]: opt rep 44290744Sjmallett */ 44390744Sjmallettvoid 44495887Sjmallettdopatsubst(const char *argv[], int argc) 44590744Sjmallett{ 44690744Sjmallett if (argc <= 3) { 44790744Sjmallett warnx("Too few arguments to patsubst"); 44890744Sjmallett return; 44990744Sjmallett } 450228063Sbapt /* special case: empty regexp */ 451228063Sbapt if (argv[3][0] == '\0') { 452228063Sbapt const char *s; 453228063Sbapt size_t len; 454228063Sbapt if (argc > 4 && argv[4]) 455228063Sbapt len = strlen(argv[4]); 456228063Sbapt else 457228063Sbapt len = 0; 458228063Sbapt for (s = argv[2]; *s != '\0'; s++) { 459228063Sbapt addchars(argv[4], len); 460228063Sbapt addchar(*s); 461228063Sbapt } 462228063Sbapt } else { 463228063Sbapt int error; 464228063Sbapt regex_t re; 465228063Sbapt regmatch_t *pmatch; 466228063Sbapt int mode = REG_EXTENDED; 467269162Sbapt const char *source; 468228063Sbapt size_t l = strlen(argv[3]); 469100014Sjmallett 470228063Sbapt if (!mimic_gnu || 471228063Sbapt (argv[3][0] == '^') || 472228063Sbapt (l > 0 && argv[3][l-1] == '$')) 473228063Sbapt mode |= REG_NEWLINE; 474228063Sbapt 475269162Sbapt source = mimic_gnu ? twiddle(argv[3]) : argv[3]; 476269162Sbapt error = regcomp(&re, source, mode); 477228063Sbapt if (error != 0) 478269162Sbapt exit_regerror(error, &re, source); 479228063Sbapt 480269162Sbapt pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t), 481269162Sbapt NULL); 482269162Sbapt do_subst(argv[2], &re, source, 483228063Sbapt argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch); 484228063Sbapt free(pmatch); 485228063Sbapt regfree(&re); 486228063Sbapt } 48790744Sjmallett pbstr(getstring()); 48890744Sjmallett} 48990744Sjmallett 49090744Sjmallettvoid 49195887Sjmallettdoregexp(const char *argv[], int argc) 49290744Sjmallett{ 49390744Sjmallett int error; 49490744Sjmallett regex_t re; 49590744Sjmallett regmatch_t *pmatch; 496269162Sbapt const char *source; 49790744Sjmallett 49890744Sjmallett if (argc <= 3) { 49990744Sjmallett warnx("Too few arguments to regexp"); 50090744Sjmallett return; 50190744Sjmallett } 502228063Sbapt /* special gnu case */ 503228063Sbapt if (argv[3][0] == '\0' && mimic_gnu) { 504228063Sbapt if (argc == 4 || argv[4] == NULL) 505228063Sbapt return; 506228063Sbapt else 507228063Sbapt pbstr(argv[4]); 508228063Sbapt } 509269162Sbapt source = mimic_gnu ? twiddle(argv[3]) : argv[3]; 510269162Sbapt error = regcomp(&re, source, REG_EXTENDED|REG_NEWLINE); 51190744Sjmallett if (error != 0) 512269162Sbapt exit_regerror(error, &re, source); 513100014Sjmallett 514269162Sbapt pmatch = xreallocarray(NULL, re.re_nsub+1, sizeof(regmatch_t), NULL); 515228063Sbapt if (argc == 4 || argv[4] == NULL) 516269162Sbapt do_regexpindex(argv[2], &re, source, pmatch); 51790744Sjmallett else 518269162Sbapt do_regexp(argv[2], &re, source, argv[4], pmatch); 51990744Sjmallett free(pmatch); 52090744Sjmallett regfree(&re); 52190744Sjmallett} 52290744Sjmallett 52390744Sjmallettvoid 524228063Sbaptdoformat(const char *argv[], int argc) 525228063Sbapt{ 526228063Sbapt const char *format = argv[2]; 527228063Sbapt int pos = 3; 528228063Sbapt int left_padded; 529228063Sbapt long width; 530228063Sbapt size_t l; 531228063Sbapt const char *thisarg = NULL; 532228063Sbapt char temp[2]; 533228063Sbapt long extra; 534228063Sbapt 535228063Sbapt while (*format != 0) { 536228063Sbapt if (*format != '%') { 537228063Sbapt addchar(*format++); 538228063Sbapt continue; 539228063Sbapt } 540228063Sbapt 541228063Sbapt format++; 542228063Sbapt if (*format == '%') { 543228063Sbapt addchar(*format++); 544228063Sbapt continue; 545228063Sbapt } 546228063Sbapt if (*format == 0) { 547228063Sbapt addchar('%'); 548228063Sbapt break; 549228063Sbapt } 550228063Sbapt 551228063Sbapt if (*format == '*') { 552228063Sbapt format++; 553228063Sbapt if (pos >= argc) 554228063Sbapt m4errx(1, 555228063Sbapt "Format with too many format specifiers."); 556228063Sbapt width = strtol(argv[pos++], NULL, 10); 557228063Sbapt } else { 558228063Sbapt width = strtol(format, __DECONST(char **,&format), 10); 559228063Sbapt } 560228063Sbapt if (width < 0) { 561228063Sbapt left_padded = 1; 562228063Sbapt width = -width; 563228063Sbapt } else { 564228063Sbapt left_padded = 0; 565228063Sbapt } 566228063Sbapt if (*format == '.') { 567228063Sbapt format++; 568228063Sbapt if (*format == '*') { 569228063Sbapt format++; 570228063Sbapt if (pos >= argc) 571228063Sbapt m4errx(1, 572228063Sbapt "Format with too many format specifiers."); 573228063Sbapt extra = strtol(argv[pos++], NULL, 10); 574228063Sbapt } else { 575228063Sbapt extra = strtol(format, __DECONST(char **, &format), 10); 576228063Sbapt } 577228063Sbapt } else { 578228063Sbapt extra = LONG_MAX; 579228063Sbapt } 580228063Sbapt if (pos >= argc) 581228063Sbapt m4errx(1, "Format with too many format specifiers."); 582228063Sbapt switch(*format) { 583228063Sbapt case 's': 584228063Sbapt thisarg = argv[pos++]; 585228063Sbapt break; 586228063Sbapt case 'c': 587228063Sbapt temp[0] = strtoul(argv[pos++], NULL, 10); 588228063Sbapt temp[1] = 0; 589228063Sbapt thisarg = temp; 590228063Sbapt break; 591228063Sbapt default: 592228063Sbapt m4errx(1, "Unsupported format specification: %s.", 593228063Sbapt argv[2]); 594228063Sbapt } 595228063Sbapt format++; 596228063Sbapt l = strlen(thisarg); 597228063Sbapt if ((long)l > extra) 598228063Sbapt l = extra; 599228063Sbapt if (!left_padded) { 600228063Sbapt while ((long)l < width--) 601228063Sbapt addchar(' '); 602228063Sbapt } 603228063Sbapt addchars(thisarg, l); 604228063Sbapt if (left_padded) { 605228063Sbapt while ((long)l < width--) 606228063Sbapt addchar(' '); 607228063Sbapt } 608228063Sbapt } 609228063Sbapt pbstr(getstring()); 610228063Sbapt} 611228063Sbapt 612228063Sbaptvoid 61395887Sjmallettdoesyscmd(const char *cmd) 61490744Sjmallett{ 61590744Sjmallett int p[2]; 61690744Sjmallett pid_t pid, cpid; 617228063Sbapt char *argv[4]; 61890744Sjmallett int cc; 61990744Sjmallett int status; 62090744Sjmallett 62190744Sjmallett /* Follow gnu m4 documentation: first flush buffers. */ 62290744Sjmallett fflush(NULL); 62390744Sjmallett 624228063Sbapt argv[0] = __DECONST(char *, "sh"); 625228063Sbapt argv[1] = __DECONST(char *, "-c"); 626228063Sbapt argv[2] = __DECONST(char *, cmd); 627228063Sbapt argv[3] = NULL; 628228063Sbapt 62990744Sjmallett /* Just set up standard output, share stderr and stdin with m4 */ 63090744Sjmallett if (pipe(p) == -1) 63190744Sjmallett err(1, "bad pipe"); 63290744Sjmallett switch(cpid = fork()) { 63390744Sjmallett case -1: 63490744Sjmallett err(1, "bad fork"); 63590744Sjmallett /* NOTREACHED */ 63690744Sjmallett case 0: 63790744Sjmallett (void) close(p[0]); 63890744Sjmallett (void) dup2(p[1], 1); 63990744Sjmallett (void) close(p[1]); 640228063Sbapt execv(_PATH_BSHELL, argv); 64190744Sjmallett exit(1); 64290744Sjmallett default: 64390744Sjmallett /* Read result in two stages, since m4's buffer is 64490744Sjmallett * pushback-only. */ 64590744Sjmallett (void) close(p[1]); 64690744Sjmallett do { 64790744Sjmallett char result[BUFSIZE]; 64890744Sjmallett cc = read(p[0], result, sizeof result); 64990744Sjmallett if (cc > 0) 65090744Sjmallett addchars(result, cc); 65190744Sjmallett } while (cc > 0 || (cc == -1 && errno == EINTR)); 65290744Sjmallett 65390744Sjmallett (void) close(p[0]); 65490744Sjmallett while ((pid = wait(&status)) != cpid && pid >= 0) 65590744Sjmallett continue; 65690744Sjmallett pbstr(getstring()); 65790744Sjmallett } 65890744Sjmallett} 659228063Sbapt 660228063Sbaptvoid 661228063Sbaptgetdivfile(const char *name) 662228063Sbapt{ 663228063Sbapt FILE *f; 664228063Sbapt int c; 665228063Sbapt 666228063Sbapt f = fopen(name, "r"); 667228063Sbapt if (!f) 668228063Sbapt return; 669228063Sbapt 670228063Sbapt while ((c = getc(f))!= EOF) 671228063Sbapt putc(c, active); 672228063Sbapt (void) fclose(f); 673228063Sbapt} 674