gnum4.c revision 241777
1228063Sbapt/* $OpenBSD: gnum4.c,v 1.42 2011/11/06 12:25:43 espie 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: head/usr.bin/m4/gnum4.c 241777 2012-10-20 10:33:15Z ed $"); 2995061Sjmallett 30100014Sjmallett/* 3190744Sjmallett * functions needed to support gnu-m4 extensions, including a fake freezing 3290744Sjmallett */ 3390744Sjmallett 3490744Sjmallett#include <sys/param.h> 3590744Sjmallett#include <sys/types.h> 3690744Sjmallett#include <sys/wait.h> 3790744Sjmallett#include <ctype.h> 38228063Sbapt#include <err.h> 3990744Sjmallett#include <paths.h> 4090744Sjmallett#include <regex.h> 4190744Sjmallett#include <stddef.h> 4290744Sjmallett#include <stdlib.h> 4390744Sjmallett#include <stdio.h> 4490744Sjmallett#include <string.h> 4590744Sjmallett#include <errno.h> 4690744Sjmallett#include <unistd.h> 4790744Sjmallett#include "mdef.h" 4890744Sjmallett#include "stdd.h" 4990744Sjmallett#include "extern.h" 5090744Sjmallett 5190744Sjmallett 5290744Sjmallettint mimic_gnu = 0; 5390744Sjmallett 5490744Sjmallett/* 5590744Sjmallett * Support for include path search 56218909Sbrucec * First search in the current directory. 5790744Sjmallett * If not found, and the path is not absolute, include path kicks in. 5890744Sjmallett * First, -I options, in the order found on the command line. 5990744Sjmallett * Then M4PATH env variable 6090744Sjmallett */ 6190744Sjmallett 62241777Sedstatic struct path_entry { 6390744Sjmallett char *name; 6490744Sjmallett struct path_entry *next; 6590744Sjmallett} *first, *last; 6690744Sjmallett 6790744Sjmallettstatic struct path_entry *new_path_entry(const char *); 6890744Sjmallettstatic void ensure_m4path(void); 6990744Sjmallettstatic struct input_file *dopath(struct input_file *, const char *); 7090744Sjmallett 7190744Sjmallettstatic struct path_entry * 7295887Sjmallettnew_path_entry(const char *dirname) 7390744Sjmallett{ 7490744Sjmallett struct path_entry *n; 7590744Sjmallett 7690744Sjmallett n = malloc(sizeof(struct path_entry)); 7790744Sjmallett if (!n) 7890744Sjmallett errx(1, "out of memory"); 7990744Sjmallett n->name = strdup(dirname); 8090744Sjmallett if (!n->name) 8190744Sjmallett errx(1, "out of memory"); 8290744Sjmallett n->next = 0; 8390744Sjmallett return n; 8490744Sjmallett} 85100014Sjmallett 86100014Sjmallettvoid 8795887Sjmallettaddtoincludepath(const char *dirname) 8890744Sjmallett{ 8990744Sjmallett struct path_entry *n; 9090744Sjmallett 9190744Sjmallett n = new_path_entry(dirname); 9290744Sjmallett 9390744Sjmallett if (last) { 9490744Sjmallett last->next = n; 9590744Sjmallett last = n; 9690744Sjmallett } 9790744Sjmallett else 9890744Sjmallett last = first = n; 9990744Sjmallett} 10090744Sjmallett 10190744Sjmallettstatic void 10299939Sjmallettensure_m4path(void) 10390744Sjmallett{ 10490744Sjmallett static int envpathdone = 0; 10590744Sjmallett char *envpath; 10690744Sjmallett char *sweep; 10790744Sjmallett char *path; 10890744Sjmallett 10990744Sjmallett if (envpathdone) 11090744Sjmallett return; 11190744Sjmallett envpathdone = TRUE; 11290744Sjmallett envpath = getenv("M4PATH"); 113100014Sjmallett if (!envpath) 11490744Sjmallett return; 11590744Sjmallett /* for portability: getenv result is read-only */ 11690744Sjmallett envpath = strdup(envpath); 11790744Sjmallett if (!envpath) 11890744Sjmallett errx(1, "out of memory"); 119100014Sjmallett for (sweep = envpath; 12090744Sjmallett (path = strsep(&sweep, ":")) != NULL;) 12190744Sjmallett addtoincludepath(path); 12290744Sjmallett free(envpath); 12390744Sjmallett} 12490744Sjmallett 12590744Sjmallettstatic 12690744Sjmallettstruct input_file * 12795887Sjmallettdopath(struct input_file *i, const char *filename) 12890744Sjmallett{ 12990744Sjmallett char path[MAXPATHLEN]; 13090744Sjmallett struct path_entry *pe; 13190744Sjmallett FILE *f; 13290744Sjmallett 13390744Sjmallett for (pe = first; pe; pe = pe->next) { 13490744Sjmallett snprintf(path, sizeof(path), "%s/%s", pe->name, filename); 135228063Sbapt if ((f = fopen(path, "r")) != 0) { 13690744Sjmallett set_input(i, f, path); 13790744Sjmallett return i; 13890744Sjmallett } 13990744Sjmallett } 14090744Sjmallett return NULL; 14190744Sjmallett} 14290744Sjmallett 14390744Sjmallettstruct input_file * 14495887Sjmallettfopen_trypath(struct input_file *i, const char *filename) 14590744Sjmallett{ 14690744Sjmallett FILE *f; 14790744Sjmallett 14890744Sjmallett f = fopen(filename, "r"); 14990744Sjmallett if (f != NULL) { 15090744Sjmallett set_input(i, f, filename); 15190744Sjmallett return i; 15290744Sjmallett } 15390744Sjmallett if (filename[0] == '/') 15490744Sjmallett return NULL; 15590744Sjmallett 15690744Sjmallett ensure_m4path(); 15790744Sjmallett 15890744Sjmallett return dopath(i, filename); 15990744Sjmallett} 16090744Sjmallett 161100014Sjmallettvoid 16295887Sjmallettdoindir(const char *argv[], int argc) 16390744Sjmallett{ 164228063Sbapt ndptr n; 165228063Sbapt struct macro_definition *p = NULL; 16690744Sjmallett 167228063Sbapt n = lookup(argv[2]); 168228063Sbapt if (n == NULL || (p = macro_getdef(n)) == NULL) 169228063Sbapt m4errx(1, "indir: undefined macro %s.", argv[2]); 17090744Sjmallett argv[1] = p->defn; 171228063Sbapt 172228063Sbapt eval(argv+1, argc-1, p->type, is_traced(n)); 17390744Sjmallett} 17490744Sjmallett 175100014Sjmallettvoid 17695887Sjmallettdobuiltin(const char *argv[], int argc) 17790744Sjmallett{ 178228063Sbapt ndptr p; 179228063Sbapt 18090744Sjmallett argv[1] = NULL; 181228063Sbapt p = macro_getbuiltin(argv[2]); 182228063Sbapt if (p != NULL) 183228063Sbapt eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p)); 18490744Sjmallett else 185228063Sbapt m4errx(1, "unknown builtin %s.", argv[2]); 186100014Sjmallett} 18790744Sjmallett 18890744Sjmallett 18990744Sjmallett/* We need some temporary buffer space, as pb pushes BACK and substitution 19090744Sjmallett * proceeds forward... */ 19190744Sjmallettstatic char *buffer; 19290744Sjmallettstatic size_t bufsize = 0; 19390744Sjmallettstatic size_t current = 0; 19490744Sjmallett 19590744Sjmallettstatic void addchars(const char *, size_t); 19695887Sjmallettstatic void addchar(int); 19790744Sjmallettstatic char *twiddle(const char *); 19890744Sjmallettstatic char *getstring(void); 19990744Sjmallettstatic void exit_regerror(int, regex_t *); 20090744Sjmallettstatic void do_subst(const char *, regex_t *, const char *, regmatch_t *); 20190744Sjmallettstatic void do_regexpindex(const char *, regex_t *, regmatch_t *); 20290744Sjmallettstatic void do_regexp(const char *, regex_t *, const char *, regmatch_t *); 203228063Sbaptstatic void add_sub(int, const char *, regex_t *, regmatch_t *); 20490744Sjmallettstatic void add_replace(const char *, regex_t *, const char *, regmatch_t *); 20590744Sjmallett#define addconstantstring(s) addchars((s), sizeof(s)-1) 20690744Sjmallett 207100014Sjmallettstatic void 20895887Sjmallettaddchars(const char *c, size_t n) 20990744Sjmallett{ 21090744Sjmallett if (n == 0) 21190744Sjmallett return; 21290744Sjmallett while (current + n > bufsize) { 21390744Sjmallett if (bufsize == 0) 21490744Sjmallett bufsize = 1024; 21590744Sjmallett else 21690744Sjmallett bufsize *= 2; 217228063Sbapt buffer = xrealloc(buffer, bufsize, NULL); 21890744Sjmallett } 21990744Sjmallett memcpy(buffer+current, c, n); 22090744Sjmallett current += n; 22190744Sjmallett} 22290744Sjmallett 223100014Sjmallettstatic void 22495887Sjmallettaddchar(int c) 22590744Sjmallett{ 22690744Sjmallett if (current +1 > bufsize) { 22790744Sjmallett if (bufsize == 0) 22890744Sjmallett bufsize = 1024; 22990744Sjmallett else 23090744Sjmallett bufsize *= 2; 231228063Sbapt buffer = xrealloc(buffer, bufsize, NULL); 23290744Sjmallett } 23390744Sjmallett buffer[current++] = c; 23490744Sjmallett} 23590744Sjmallett 23690744Sjmallettstatic char * 23799939Sjmallettgetstring(void) 23890744Sjmallett{ 23990744Sjmallett addchar('\0'); 24090744Sjmallett current = 0; 24190744Sjmallett return buffer; 24290744Sjmallett} 24390744Sjmallett 24490744Sjmallett 245100014Sjmallettstatic void 24695887Sjmallettexit_regerror(int er, regex_t *re) 24790744Sjmallett{ 248228063Sbapt size_t errlen; 249228063Sbapt char *errbuf; 25090744Sjmallett 25190744Sjmallett errlen = regerror(er, re, NULL, 0); 252228063Sbapt errbuf = xalloc(errlen, 253228063Sbapt "malloc in regerror: %lu", (unsigned long)errlen); 25490744Sjmallett regerror(er, re, errbuf, errlen); 255228063Sbapt m4errx(1, "regular expression error: %s.", errbuf); 25690744Sjmallett} 25790744Sjmallett 25890744Sjmallettstatic void 259228063Sbaptadd_sub(int n, const char *string, regex_t *re, regmatch_t *pm) 26090744Sjmallett{ 261228063Sbapt if (n > (int)re->re_nsub) 262228063Sbapt warnx("No subexpression %d", n); 26390744Sjmallett /* Subexpressions that did not match are 26490744Sjmallett * not an error. */ 26590744Sjmallett else if (pm[n].rm_so != -1 && 26690744Sjmallett pm[n].rm_eo != -1) { 26790744Sjmallett addchars(string + pm[n].rm_so, 26890744Sjmallett pm[n].rm_eo - pm[n].rm_so); 26990744Sjmallett } 27090744Sjmallett} 27190744Sjmallett 27290744Sjmallett/* Add replacement string to the output buffer, recognizing special 27390744Sjmallett * constructs and replacing them with substrings of the original string. 27490744Sjmallett */ 275100014Sjmallettstatic void 27695887Sjmallettadd_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm) 27790744Sjmallett{ 27890744Sjmallett const char *p; 27990744Sjmallett 28090744Sjmallett for (p = replace; *p != '\0'; p++) { 28190744Sjmallett if (*p == '&' && !mimic_gnu) { 28290744Sjmallett add_sub(0, string, re, pm); 28390744Sjmallett continue; 28490744Sjmallett } 28590744Sjmallett if (*p == '\\') { 28690744Sjmallett if (p[1] == '\\') { 28790744Sjmallett addchar(p[1]); 28890744Sjmallett p++; 28990744Sjmallett continue; 29090744Sjmallett } 29190744Sjmallett if (p[1] == '&') { 29290744Sjmallett if (mimic_gnu) 29390744Sjmallett add_sub(0, string, re, pm); 29490744Sjmallett else 29590744Sjmallett addchar(p[1]); 29690744Sjmallett p++; 29790744Sjmallett continue; 29890744Sjmallett } 29990744Sjmallett if (isdigit(p[1])) { 30090744Sjmallett add_sub(*(++p) - '0', string, re, pm); 30190744Sjmallett continue; 30290744Sjmallett } 30390744Sjmallett } 304228063Sbapt addchar(*p); 30590744Sjmallett } 30690744Sjmallett} 30790744Sjmallett 308100014Sjmallettstatic void 30995887Sjmallettdo_subst(const char *string, regex_t *re, const char *replace, regmatch_t *pm) 31090744Sjmallett{ 31190744Sjmallett int error; 31290744Sjmallett int flags = 0; 31390744Sjmallett const char *last_match = NULL; 31490744Sjmallett 31590744Sjmallett while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) { 31690744Sjmallett if (pm[0].rm_eo != 0) { 31790744Sjmallett if (string[pm[0].rm_eo-1] == '\n') 31890744Sjmallett flags = 0; 31990744Sjmallett else 32090744Sjmallett flags = REG_NOTBOL; 32190744Sjmallett } 32290744Sjmallett 323100014Sjmallett /* NULL length matches are special... We use the `vi-mode' 32490744Sjmallett * rule: don't allow a NULL-match at the last match 325100014Sjmallett * position. 32690744Sjmallett */ 327100014Sjmallett if (pm[0].rm_so == pm[0].rm_eo && 32890744Sjmallett string + pm[0].rm_so == last_match) { 32990744Sjmallett if (*string == '\0') 33090744Sjmallett return; 33190744Sjmallett addchar(*string); 33290744Sjmallett if (*string++ == '\n') 33390744Sjmallett flags = 0; 33490744Sjmallett else 33590744Sjmallett flags = REG_NOTBOL; 33690744Sjmallett continue; 33790744Sjmallett } 33890744Sjmallett last_match = string + pm[0].rm_so; 33990744Sjmallett addchars(string, pm[0].rm_so); 34090744Sjmallett add_replace(string, re, replace, pm); 34190744Sjmallett string += pm[0].rm_eo; 34290744Sjmallett } 34390744Sjmallett if (error != REG_NOMATCH) 34490744Sjmallett exit_regerror(error, re); 34590744Sjmallett pbstr(string); 34690744Sjmallett} 34790744Sjmallett 348100014Sjmallettstatic void 34995887Sjmallettdo_regexp(const char *string, regex_t *re, const char *replace, regmatch_t *pm) 35090744Sjmallett{ 35190744Sjmallett int error; 35290744Sjmallett 35390744Sjmallett switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) { 354100014Sjmallett case 0: 35590744Sjmallett add_replace(string, re, replace, pm); 35690744Sjmallett pbstr(getstring()); 35790744Sjmallett break; 35890744Sjmallett case REG_NOMATCH: 35990744Sjmallett break; 36090744Sjmallett default: 36190744Sjmallett exit_regerror(error, re); 36290744Sjmallett } 36390744Sjmallett} 36490744Sjmallett 365100014Sjmallettstatic void 36695887Sjmallettdo_regexpindex(const char *string, regex_t *re, regmatch_t *pm) 36790744Sjmallett{ 36890744Sjmallett int error; 36990744Sjmallett 37090744Sjmallett switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) { 37190744Sjmallett case 0: 37290744Sjmallett pbunsigned(pm[0].rm_so); 37390744Sjmallett break; 37490744Sjmallett case REG_NOMATCH: 37590744Sjmallett pbnum(-1); 37690744Sjmallett break; 37790744Sjmallett default: 37890744Sjmallett exit_regerror(error, re); 37990744Sjmallett } 38090744Sjmallett} 38190744Sjmallett 38290744Sjmallett/* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2 38390744Sjmallett * says. So we twiddle with the regexp before passing it to regcomp. 38490744Sjmallett */ 38590744Sjmallettstatic char * 38695887Sjmalletttwiddle(const char *p) 38790744Sjmallett{ 388228063Sbapt /* + at start of regexp is a normal character for Gnu m4 */ 389228063Sbapt if (*p == '^') { 390228063Sbapt addchar(*p); 391228063Sbapt p++; 392228063Sbapt } 393228063Sbapt if (*p == '+') { 394228063Sbapt addchar('\\'); 395228063Sbapt } 39690744Sjmallett /* This could use strcspn for speed... */ 39790744Sjmallett while (*p != '\0') { 39890744Sjmallett if (*p == '\\') { 39990744Sjmallett switch(p[1]) { 40090744Sjmallett case '(': 40190744Sjmallett case ')': 40290744Sjmallett case '|': 40390744Sjmallett addchar(p[1]); 40490744Sjmallett break; 40590744Sjmallett case 'w': 40690744Sjmallett addconstantstring("[_a-zA-Z0-9]"); 40790744Sjmallett break; 40890744Sjmallett case 'W': 40990744Sjmallett addconstantstring("[^_a-zA-Z0-9]"); 41090744Sjmallett break; 41190744Sjmallett case '<': 41290744Sjmallett addconstantstring("[[:<:]]"); 41390744Sjmallett break; 41490744Sjmallett case '>': 41590744Sjmallett addconstantstring("[[:>:]]"); 41690744Sjmallett break; 41790744Sjmallett default: 41890744Sjmallett addchars(p, 2); 41990744Sjmallett break; 42090744Sjmallett } 42190744Sjmallett p+=2; 42290744Sjmallett continue; 42390744Sjmallett } 42490744Sjmallett if (*p == '(' || *p == ')' || *p == '|') 42590744Sjmallett addchar('\\'); 42690744Sjmallett 42790744Sjmallett addchar(*p); 42890744Sjmallett p++; 42990744Sjmallett } 43090744Sjmallett return getstring(); 43190744Sjmallett} 43290744Sjmallett 43390744Sjmallett/* patsubst(string, regexp, opt replacement) */ 43490744Sjmallett/* argv[2]: string 43590744Sjmallett * argv[3]: regexp 43690744Sjmallett * argv[4]: opt rep 43790744Sjmallett */ 43890744Sjmallettvoid 43995887Sjmallettdopatsubst(const char *argv[], int argc) 44090744Sjmallett{ 44190744Sjmallett if (argc <= 3) { 44290744Sjmallett warnx("Too few arguments to patsubst"); 44390744Sjmallett return; 44490744Sjmallett } 445228063Sbapt /* special case: empty regexp */ 446228063Sbapt if (argv[3][0] == '\0') { 447228063Sbapt const char *s; 448228063Sbapt size_t len; 449228063Sbapt if (argc > 4 && argv[4]) 450228063Sbapt len = strlen(argv[4]); 451228063Sbapt else 452228063Sbapt len = 0; 453228063Sbapt for (s = argv[2]; *s != '\0'; s++) { 454228063Sbapt addchars(argv[4], len); 455228063Sbapt addchar(*s); 456228063Sbapt } 457228063Sbapt } else { 458228063Sbapt int error; 459228063Sbapt regex_t re; 460228063Sbapt regmatch_t *pmatch; 461228063Sbapt int mode = REG_EXTENDED; 462228063Sbapt size_t l = strlen(argv[3]); 463100014Sjmallett 464228063Sbapt if (!mimic_gnu || 465228063Sbapt (argv[3][0] == '^') || 466228063Sbapt (l > 0 && argv[3][l-1] == '$')) 467228063Sbapt mode |= REG_NEWLINE; 468228063Sbapt 469228063Sbapt error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3], 470228063Sbapt mode); 471228063Sbapt if (error != 0) 472228063Sbapt exit_regerror(error, &re); 473228063Sbapt 474228063Sbapt pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL); 475228063Sbapt do_subst(argv[2], &re, 476228063Sbapt argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch); 477228063Sbapt free(pmatch); 478228063Sbapt regfree(&re); 479228063Sbapt } 48090744Sjmallett pbstr(getstring()); 48190744Sjmallett} 48290744Sjmallett 48390744Sjmallettvoid 48495887Sjmallettdoregexp(const char *argv[], int argc) 48590744Sjmallett{ 48690744Sjmallett int error; 48790744Sjmallett regex_t re; 48890744Sjmallett regmatch_t *pmatch; 48990744Sjmallett 49090744Sjmallett if (argc <= 3) { 49190744Sjmallett warnx("Too few arguments to regexp"); 49290744Sjmallett return; 49390744Sjmallett } 494228063Sbapt /* special gnu case */ 495228063Sbapt if (argv[3][0] == '\0' && mimic_gnu) { 496228063Sbapt if (argc == 4 || argv[4] == NULL) 497228063Sbapt return; 498228063Sbapt else 499228063Sbapt pbstr(argv[4]); 500228063Sbapt } 501100014Sjmallett error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3], 502228063Sbapt REG_EXTENDED|REG_NEWLINE); 50390744Sjmallett if (error != 0) 50490744Sjmallett exit_regerror(error, &re); 505100014Sjmallett 506228063Sbapt pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL); 507228063Sbapt if (argc == 4 || argv[4] == NULL) 50890744Sjmallett do_regexpindex(argv[2], &re, pmatch); 50990744Sjmallett else 51090744Sjmallett do_regexp(argv[2], &re, argv[4], pmatch); 51190744Sjmallett free(pmatch); 51290744Sjmallett regfree(&re); 51390744Sjmallett} 51490744Sjmallett 51590744Sjmallettvoid 516228063Sbaptdoformat(const char *argv[], int argc) 517228063Sbapt{ 518228063Sbapt const char *format = argv[2]; 519228063Sbapt int pos = 3; 520228063Sbapt int left_padded; 521228063Sbapt long width; 522228063Sbapt size_t l; 523228063Sbapt const char *thisarg = NULL; 524228063Sbapt char temp[2]; 525228063Sbapt long extra; 526228063Sbapt 527228063Sbapt while (*format != 0) { 528228063Sbapt if (*format != '%') { 529228063Sbapt addchar(*format++); 530228063Sbapt continue; 531228063Sbapt } 532228063Sbapt 533228063Sbapt format++; 534228063Sbapt if (*format == '%') { 535228063Sbapt addchar(*format++); 536228063Sbapt continue; 537228063Sbapt } 538228063Sbapt if (*format == 0) { 539228063Sbapt addchar('%'); 540228063Sbapt break; 541228063Sbapt } 542228063Sbapt 543228063Sbapt if (*format == '*') { 544228063Sbapt format++; 545228063Sbapt if (pos >= argc) 546228063Sbapt m4errx(1, 547228063Sbapt "Format with too many format specifiers."); 548228063Sbapt width = strtol(argv[pos++], NULL, 10); 549228063Sbapt } else { 550228063Sbapt width = strtol(format, __DECONST(char **,&format), 10); 551228063Sbapt } 552228063Sbapt if (width < 0) { 553228063Sbapt left_padded = 1; 554228063Sbapt width = -width; 555228063Sbapt } else { 556228063Sbapt left_padded = 0; 557228063Sbapt } 558228063Sbapt if (*format == '.') { 559228063Sbapt format++; 560228063Sbapt if (*format == '*') { 561228063Sbapt format++; 562228063Sbapt if (pos >= argc) 563228063Sbapt m4errx(1, 564228063Sbapt "Format with too many format specifiers."); 565228063Sbapt extra = strtol(argv[pos++], NULL, 10); 566228063Sbapt } else { 567228063Sbapt extra = strtol(format, __DECONST(char **, &format), 10); 568228063Sbapt } 569228063Sbapt } else { 570228063Sbapt extra = LONG_MAX; 571228063Sbapt } 572228063Sbapt if (pos >= argc) 573228063Sbapt m4errx(1, "Format with too many format specifiers."); 574228063Sbapt switch(*format) { 575228063Sbapt case 's': 576228063Sbapt thisarg = argv[pos++]; 577228063Sbapt break; 578228063Sbapt case 'c': 579228063Sbapt temp[0] = strtoul(argv[pos++], NULL, 10); 580228063Sbapt temp[1] = 0; 581228063Sbapt thisarg = temp; 582228063Sbapt break; 583228063Sbapt default: 584228063Sbapt m4errx(1, "Unsupported format specification: %s.", 585228063Sbapt argv[2]); 586228063Sbapt } 587228063Sbapt format++; 588228063Sbapt l = strlen(thisarg); 589228063Sbapt if ((long)l > extra) 590228063Sbapt l = extra; 591228063Sbapt if (!left_padded) { 592228063Sbapt while ((long)l < width--) 593228063Sbapt addchar(' '); 594228063Sbapt } 595228063Sbapt addchars(thisarg, l); 596228063Sbapt if (left_padded) { 597228063Sbapt while ((long)l < width--) 598228063Sbapt addchar(' '); 599228063Sbapt } 600228063Sbapt } 601228063Sbapt pbstr(getstring()); 602228063Sbapt} 603228063Sbapt 604228063Sbaptvoid 60595887Sjmallettdoesyscmd(const char *cmd) 60690744Sjmallett{ 60790744Sjmallett int p[2]; 60890744Sjmallett pid_t pid, cpid; 609228063Sbapt char *argv[4]; 61090744Sjmallett int cc; 61190744Sjmallett int status; 61290744Sjmallett 61390744Sjmallett /* Follow gnu m4 documentation: first flush buffers. */ 61490744Sjmallett fflush(NULL); 61590744Sjmallett 616228063Sbapt argv[0] = __DECONST(char *, "sh"); 617228063Sbapt argv[1] = __DECONST(char *, "-c"); 618228063Sbapt argv[2] = __DECONST(char *, cmd); 619228063Sbapt argv[3] = NULL; 620228063Sbapt 62190744Sjmallett /* Just set up standard output, share stderr and stdin with m4 */ 62290744Sjmallett if (pipe(p) == -1) 62390744Sjmallett err(1, "bad pipe"); 62490744Sjmallett switch(cpid = fork()) { 62590744Sjmallett case -1: 62690744Sjmallett err(1, "bad fork"); 62790744Sjmallett /* NOTREACHED */ 62890744Sjmallett case 0: 62990744Sjmallett (void) close(p[0]); 63090744Sjmallett (void) dup2(p[1], 1); 63190744Sjmallett (void) close(p[1]); 632228063Sbapt execv(_PATH_BSHELL, argv); 63390744Sjmallett exit(1); 63490744Sjmallett default: 63590744Sjmallett /* Read result in two stages, since m4's buffer is 63690744Sjmallett * pushback-only. */ 63790744Sjmallett (void) close(p[1]); 63890744Sjmallett do { 63990744Sjmallett char result[BUFSIZE]; 64090744Sjmallett cc = read(p[0], result, sizeof result); 64190744Sjmallett if (cc > 0) 64290744Sjmallett addchars(result, cc); 64390744Sjmallett } while (cc > 0 || (cc == -1 && errno == EINTR)); 64490744Sjmallett 64590744Sjmallett (void) close(p[0]); 64690744Sjmallett while ((pid = wait(&status)) != cpid && pid >= 0) 64790744Sjmallett continue; 64890744Sjmallett pbstr(getstring()); 64990744Sjmallett } 65090744Sjmallett} 651228063Sbapt 652228063Sbaptvoid 653228063Sbaptgetdivfile(const char *name) 654228063Sbapt{ 655228063Sbapt FILE *f; 656228063Sbapt int c; 657228063Sbapt 658228063Sbapt f = fopen(name, "r"); 659228063Sbapt if (!f) 660228063Sbapt return; 661228063Sbapt 662228063Sbapt while ((c = getc(f))!= EOF) 663228063Sbapt putc(c, active); 664228063Sbapt (void) fclose(f); 665228063Sbapt} 666