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