1 /* $OpenBSD: main.c,v 1.90 2026/02/25 05:37:25 op Exp $ */ 2 /* $NetBSD: main.c,v 1.52 2026/06/10 22:25:02 christos Exp $ */ 3 4 /*- 5 * Copyright (c) 1989, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Ozan Yigit at York University. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 /* 37 * main.c 38 * Facility: m4 macro processor 39 * by: oz 40 */ 41 #if HAVE_NBTOOL_CONFIG_H 42 #include "nbtool_config.h" 43 #endif 44 #include <sys/cdefs.h> 45 __RCSID("$NetBSD: main.c,v 1.52 2026/06/10 22:25:02 christos Exp $"); 46 #include <assert.h> 47 #include <signal.h> 48 #include <getopt.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <unistd.h> 52 #include <stdio.h> 53 #include <ctype.h> 54 #include <string.h> 55 #include <stddef.h> 56 #include <stdint.h> 57 #include <stdlib.h> 58 #include <ohash.h> 59 #include "mdef.h" 60 #include "stdd.h" 61 #include "extern.h" 62 #include "pathnames.h" 63 64 ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ 65 stae *mstack; /* stack of m4 machine */ 66 char *sstack; /* shadow stack, for string space extension */ 67 static size_t STACKMAX; /* current maximum size of stack */ 68 int sp; /* current m4 stack pointer */ 69 int fp; /* m4 call frame pointer */ 70 struct input_file infile[MAXINP];/* input file stack (0=stdin) */ 71 FILE **outfile; /* diversion array(0=bitbucket)*/ 72 int maxout; 73 FILE *active; /* active output file pointer */ 74 int ilevel = 0; /* input file stack pointer */ 75 int oindex = 0; /* diversion index.. */ 76 const char *null = ""; /* as it says.. just a null.. */ 77 char **m4wraps = NULL; /* m4wraps array. */ 78 int maxwraps = 0; /* size of m4wraps array */ 79 int wrapindex = 0; /* current offset in m4wraps */ 80 char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */ 81 char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */ 82 char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */ 83 char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */ 84 int synch_lines = 0; /* line synchronisation for C preprocessor */ 85 int prefix_builtins = 0; /* -P option to prefix builtin keywords */ 86 int error_warns = 0; /* -E option to make warnings exit_code = 1 */ 87 int fatal_warns = 0; /* -E -E option to exit on warnings */ 88 int quiet = 0; /* -Q option to silence warnings */ 89 int nesting_limit = -1; /* -L for nesting limit */ 90 static const char *freeze = NULL; /* -F to freeze state */ 91 static const char *reload = NULL; /* -R to reload state */ 92 #ifndef REAL_FREEZE 93 FILE *freezef = NULL; 94 int thawing = 0; 95 #endif 96 97 struct keyblk { 98 const char *knam; /* keyword name */ 99 int ktyp; /* keyword type */ 100 }; 101 102 static struct keyblk keywrds[] = { /* m4 keywords to be installed */ 103 { "include", INCLUDETYPE }, 104 { "sinclude", SINCLUDETYPE }, 105 { "define", DEFINETYPE }, 106 { "defn", DEFNTYPE }, 107 { "divert", DIVERTTYPE | NOARGS }, 108 { "eval", EVALTYPE }, 109 { "expr", EVALTYPE }, 110 { "substr", SUBSTRTYPE }, 111 { "ifelse", IFELSETYPE }, 112 { "ifdef", IFDEFTYPE }, 113 { "len", LENTYPE }, 114 { "incr", INCRTYPE }, 115 { "decr", DECRTYPE }, 116 { "dnl", DNLTYPE | NOARGS }, 117 { "changequote", CHANGEQUOTETYPE | NOARGS }, 118 { "changecom", CHANGECOMTYPE | NOARGS }, 119 { "index", INDEXTYPE }, 120 #ifdef EXTENDED 121 { "paste", PASTETYPE }, 122 { "spaste", SPASTETYPE }, 123 /* Newer extensions, needed to handle gnu-m4 scripts */ 124 { "indir", INDIRTYPE}, 125 { "builtin", BUILTINTYPE}, 126 { "patsubst", PATSUBSTTYPE}, 127 { "regexp", REGEXPTYPE}, 128 { "esyscmd", ESYSCMDTYPE}, 129 { "__file__", FILENAMETYPE | NOARGS}, 130 { "__line__", LINETYPE | NOARGS}, 131 #endif 132 { "popdef", POPDEFTYPE }, 133 { "pushdef", PUSHDEFTYPE }, 134 { "dumpdef", DUMPDEFTYPE | NOARGS }, 135 { "shift", SHIFTTYPE | NOARGS }, 136 { "translit", TRANSLITTYPE }, 137 { "undefine", UNDEFINETYPE }, 138 { "undivert", UNDIVERTTYPE | NOARGS }, 139 { "divnum", DIVNUMTYPE | NOARGS }, 140 { "maketemp", MKSTEMPTYPE }, 141 { "mkstemp", MKSTEMPTYPE }, 142 { "errprint", ERRPRINTTYPE | NOARGS }, 143 { "m4wrap", M4WRAPTYPE | NOARGS }, 144 { "m4exit", M4EXITTYPE | NOARGS }, 145 { "syscmd", SYSCMDTYPE }, 146 { "sysval", SYSVALTYPE | NOARGS }, 147 { "traceon", TRACEONTYPE | NOARGS }, 148 { "traceoff", TRACEOFFTYPE | NOARGS }, 149 150 /* Macro that expands to itself, signature of the current OS */ 151 { "unix", SELFTYPE | NOARGS }, 152 }; 153 154 #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) 155 156 #define MAXRECORD 50 157 static struct position { 158 char *name; 159 unsigned long line; 160 } quotes[MAXRECORD], paren[MAXRECORD]; 161 162 static void record(struct position *, int); 163 static void dump_stack(struct position *, int); 164 165 static void macro(void); 166 static void initkwds(void); 167 static ndptr inspect(int, char *); 168 static int do_look_ahead(int, const char *); 169 static void reallyoutputstr(const char *); 170 static void reallyputchar(int); 171 172 static void enlarge_stack(void); 173 static void help(void); 174 175 int exit_code = 0; 176 #define OPT_HELP 1 177 178 static struct option longopts[] = { 179 { "debug", optional_argument, 0, 'd' }, 180 { "define", required_argument, 0, 'D' }, 181 { "error-output", required_argument, 0, 'o' }, /* sic */ 182 { "fatal-warnings", no_argument, 0, 'E' }, 183 { "freeze-state", required_argument, 0, 'F' }, 184 { "gnu", no_argument, 0, 'g' }, 185 { "help", no_argument, 0, OPT_HELP }, 186 { "include", required_argument, 0, 'I' }, 187 { "interactive", no_argument, 0, 'i' }, 188 { "nesting-limit", required_argument, 0, 'L' }, 189 { "prefix-builtins", no_argument, 0, 'P' }, 190 { "quiet", no_argument, 0, 'Q' }, 191 { "reload-state", required_argument, 0, 'R' }, 192 { "silent", no_argument, 0, 'Q' }, 193 { "synclines", no_argument, 0, 's' }, 194 { "trace", required_argument, 0, 't' }, 195 { "traditional", no_argument, 0, 'G' }, 196 { "undefine", required_argument, 0, 'U' }, 197 { "version", no_argument, 0, 'v' }, 198 #ifdef notyet 199 { "arglength", required_argument, 0, 'l' }, 200 { "debugfile", optional_argument, 0, OPT_DEBUGFILE }, 201 { "hashsize", required_argument, 0, 'H' }, 202 { "warn-macro-sequence",optional_argument, 0, OPT_WARN_SEQUENCE }, 203 #endif 204 { 0, 0, 0, 0 }, 205 }; 206 207 int 208 main(int argc, char *argv[]) 209 { 210 int c; 211 int n; 212 int error; 213 char *p; 214 215 setprogname(argv[0]); 216 217 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 218 signal(SIGINT, onintr); 219 220 init_macros(); 221 initspaces(); 222 STACKMAX = INITSTACKMAX; 223 224 mstack = xreallocarray(NULL, STACKMAX, sizeof(stae), NULL); 225 sstack = xalloc(STACKMAX, NULL); 226 227 maxout = 0; 228 outfile = NULL; 229 resizedivs(MAXOUT); 230 231 while ((c = getopt_long(argc, argv, "D:d:EF:GgI:iL:o:PR:Qst:U:v", 232 longopts, NULL)) != -1) 233 switch(c) { 234 case 'D': /* define something..*/ 235 for (p = optarg; *p; p++) 236 if (*p == '=') 237 break; 238 if (*p) 239 *p++ = EOS; 240 dodefine(optarg, p); 241 break; 242 case 'd': 243 set_trace_flags(optarg); 244 break; 245 case 'E': /* like GNU m4 1.4.9+ */ 246 if (error_warns == 0) 247 error_warns = 1; 248 else 249 fatal_warns = 1; 250 break; 251 case 'F': 252 freeze = optarg; 253 #ifndef REAL_FREEZE 254 if ((freezef = fopen(freeze, "w")) == NULL) 255 err(EXIT_FAILURE, "Can't open `%s'", freeze); 256 #endif 257 break; 258 case 'I': 259 addtoincludepath(optarg); 260 break; 261 case 'i': 262 setvbuf(stdout, NULL, _IONBF, 0); 263 signal(SIGINT, SIG_IGN); 264 break; 265 case 'G': 266 mimic_gnu = 0; 267 break; 268 case 'g': 269 mimic_gnu = 1; 270 break; 271 case 'L': 272 nesting_limit = atoi(optarg); 273 break; 274 case 'o': 275 error = trace_file(optarg); 276 if (error) 277 warn("%s", optarg); 278 break; 279 case 'P': 280 prefix_builtins = 1; 281 break; 282 case 'Q': 283 quiet++; 284 break; 285 case 'R': 286 reload = optarg; 287 break; 288 case 's': 289 synch_lines = 1; 290 break; 291 case 't': 292 mark_traced(optarg, 1); 293 break; 294 case 'U': /* undefine... */ 295 macro_popdef(optarg); 296 break; 297 case 'v': 298 fprintf(stderr, "%s version %d\n", getprogname(), 299 VERSION); 300 return EXIT_SUCCESS; 301 case OPT_HELP: 302 help(); 303 return EXIT_SUCCESS; 304 case '?': 305 default: 306 usage(stderr); 307 return EXIT_FAILURE; 308 } 309 310 #ifdef REDIRECT 311 /* 312 * This is meant only for debugging; it makes all output 313 * go to a known file, even if the command line options 314 * send it elsewhere. It should not be turned of in production code. 315 */ 316 if (freopen("/tmp/m4", "w+", stderr) == NULL) 317 err(EXIT_FAILURE, "Can't redirect errors to `%s'", 318 "/tmp/m4"); 319 #endif 320 argc -= optind; 321 argv += optind; 322 323 initkwds(); 324 if (mimic_gnu) 325 setup_builtin("format", FORMATTYPE); 326 327 active = stdout; /* default active output */ 328 bbase[0] = bufbase; 329 330 if (reload) { 331 #ifdef REAL_FREEZE 332 thaw_state(reload); 333 #else 334 if (fopen_trypath(infile, reload) == NULL) 335 err(1, "Can't open `%s'", reload); 336 sp = -1; 337 fp = 0; 338 thawing = 1; 339 macro(); 340 thawing = 0; 341 release_input(infile); 342 #endif 343 } 344 345 if (!argc) { 346 sp = -1; /* stack pointer initialized */ 347 fp = 0; /* frame pointer initialized */ 348 set_input(infile+0, stdin, "stdin"); 349 /* default input (naturally) */ 350 macro(); 351 } else 352 for (; argc--; ++argv) { 353 p = *argv; 354 if (p[0] == '-' && p[1] == EOS) 355 set_input(infile, stdin, "stdin"); 356 else if (fopen_trypath(infile, p) == NULL) 357 err(1, "%s", p); 358 sp = -1; 359 fp = 0; 360 macro(); 361 release_input(infile); 362 } 363 364 if (wrapindex) { 365 int i; 366 367 ilevel = 0; /* in case m4wrap includes.. */ 368 bufbase = bp = buf; /* use the entire buffer */ 369 if (mimic_gnu) { 370 while (wrapindex != 0) { 371 for (i = 0; i < wrapindex; i++) 372 pbstr(m4wraps[i]); 373 wrapindex =0; 374 macro(); 375 } 376 } else { 377 for (i = 0; i < wrapindex; i++) { 378 pbstr(m4wraps[i]); 379 macro(); 380 } 381 } 382 } 383 384 if (active != stdout) 385 active = stdout; /* reset output just in case */ 386 for (n = 1; n < maxout; n++) /* default wrap-up: undivert */ 387 if (outfile[n] != NULL) 388 getdiv(n); 389 /* remove bitbucket if used */ 390 if (outfile[0] != NULL) { 391 (void) fclose(outfile[0]); 392 } 393 394 #ifdef REAL_FREEZE 395 if (freeze) 396 freeze_state(freeze); 397 #else 398 if (freezef) 399 fclose(freezef); 400 #endif 401 402 return 0; 403 } 404 405 /* 406 * Look ahead for `token'. 407 * (on input `t == token[0]') 408 * Used for comment and quoting delimiters. 409 * Returns 1 if `token' present; copied to output. 410 * 0 if `token' not found; all characters pushed back 411 */ 412 static int 413 do_look_ahead(int t, const char *token) 414 { 415 int i; 416 417 assert((unsigned char)t == (unsigned char)token[0]); 418 419 for (i = 1; *++token; i++) { 420 t = gpbc(); 421 if (t == EOF || (unsigned char)t != (unsigned char)*token) { 422 pushback(t); 423 while (--i) 424 pushback(*--token); 425 return 0; 426 } 427 } 428 return 1; 429 } 430 431 #define LOOK_AHEAD(t, token) (t != EOF && \ 432 (unsigned char)(t)==(unsigned char)(token)[0] && \ 433 do_look_ahead(t,token)) 434 435 /* 436 * macro - the work horse.. 437 */ 438 static void 439 macro(void) 440 { 441 char token[MAXTOK+1]; 442 int t, l; 443 ndptr p; 444 int nlpar; 445 446 cycle { 447 t = gpbc(); 448 449 if (LOOK_AHEAD(t,lquote)) { /* strip quotes */ 450 nlpar = 0; 451 record(quotes, nlpar++); 452 /* 453 * Opening quote: scan forward until matching 454 * closing quote has been found. 455 */ 456 do { 457 458 l = gpbc(); 459 if (LOOK_AHEAD(l,rquote)) { 460 if (--nlpar > 0) 461 outputstr(rquote); 462 } else if (LOOK_AHEAD(l,lquote)) { 463 record(quotes, nlpar++); 464 outputstr(lquote); 465 } else if (l == EOF) { 466 if (!quiet) { 467 if (nlpar == 1) 468 warnx("unclosed quote:"); 469 else 470 warnx( 471 "%d unclosed quotes:", 472 nlpar); 473 dump_stack(quotes, nlpar); 474 } 475 exit(EXIT_FAILURE); 476 } else { 477 if (nlpar > 0) { 478 if (sp < 0) 479 reallyputchar(l); 480 else 481 CHRSAVE(l); 482 } 483 } 484 } 485 while (nlpar != 0); 486 } else if (sp < 0 && LOOK_AHEAD(t, scommt)) { 487 reallyoutputstr(scommt); 488 489 for(;;) { 490 t = gpbc(); 491 if (LOOK_AHEAD(t, ecommt)) { 492 reallyoutputstr(ecommt); 493 break; 494 } 495 if (t == EOF) 496 break; 497 reallyputchar(t); 498 } 499 } else if (t == '_' || isalpha(t)) { 500 p = inspect(t, token); 501 if (p != NULL) 502 pushback(l = gpbc()); 503 if (p == NULL || (l != LPAREN && 504 (macro_getdef(p)->type & NEEDARGS) != 0)) 505 outputstr(token); 506 else { 507 /* 508 * real thing.. First build a call frame: 509 */ 510 pushf(fp); /* previous call frm */ 511 pushf(macro_getdef(p)->type); /* type of the call */ 512 pushf(is_traced(p)); 513 pushf(0); /* parenthesis level */ 514 fp = sp; /* new frame pointer */ 515 /* 516 * now push the string arguments: 517 * XXX: Copy the macro definition. This leaks, but too 518 * lazy to fix properly. 519 * The problem is that if we evaluate a pushdef'ed 520 * macro and then popdef it while it the definition 521 * is still on the stack we are going to reference 522 * free memory. 523 */ 524 pushs1(xstrdup(macro_getdef(p)->defn)); /* defn string */ 525 pushs1((char *)macro_name(p)); /* macro name */ 526 pushs(ep); /* start next..*/ 527 528 if (l != LPAREN && PARLEV == 0) { 529 /* no bracks */ 530 chrsave(EOS); 531 532 if ((size_t)sp == STACKMAX) 533 errx(1, "internal stack overflow"); 534 eval((const char **)(void *)mstack+fp+1, 2, 535 CALTYP, TRACESTATUS); 536 537 ep = PREVEP; /* flush strspace */ 538 sp = PREVSP; /* previous sp.. */ 539 fp = PREVFP; /* rewind stack...*/ 540 } 541 } 542 } else if (t == EOF) { 543 if (sp > -1 && ilevel <= 0) { 544 if (!quiet) { 545 warnx("unexpected end of input, " 546 "unclosed parenthesis:"); 547 dump_stack(paren, PARLEV); 548 } 549 exit(EXIT_FAILURE); 550 } 551 if (ilevel <= 0) 552 break; /* all done thanks.. */ 553 release_input(infile+ilevel--); 554 emit_synchline(); 555 bufbase = bbase[ilevel]; 556 continue; 557 } else if (sp < 0) { /* not in a macro at all */ 558 reallyputchar(t); /* output directly.. */ 559 } 560 561 else switch(t) { 562 563 case LPAREN: 564 if (PARLEV > 0) 565 chrsave(t); 566 while (isspace(l = gpbc())) /* skip blank, tab, nl.. */ 567 if (PARLEV > 0) 568 chrsave(l); 569 pushback(l); 570 record(paren, PARLEV++); 571 break; 572 573 case RPAREN: 574 if (--PARLEV > 0) 575 chrsave(t); 576 else { /* end of argument list */ 577 chrsave(EOS); 578 579 if ((size_t)sp == STACKMAX) 580 errx(1, "internal stack overflow"); 581 582 eval((const char **)(void *)mstack+fp+1, sp-fp, 583 CALTYP, TRACESTATUS); 584 585 ep = PREVEP; /* flush strspace */ 586 sp = PREVSP; /* previous sp.. */ 587 fp = PREVFP; /* rewind stack...*/ 588 } 589 break; 590 591 case COMMA: 592 if (PARLEV == 1) { 593 chrsave(EOS); /* new argument */ 594 while (isspace(l = gpbc())) 595 ; 596 pushback(l); 597 pushs(ep); 598 } else 599 chrsave(t); 600 break; 601 602 default: 603 if (LOOK_AHEAD(t, scommt)) { 604 char *q; 605 for (q = scommt; *q; q++) 606 chrsave(*q); 607 for(;;) { 608 t = gpbc(); 609 if (LOOK_AHEAD(t, ecommt)) { 610 for (q = ecommt; *q; q++) 611 chrsave(*q); 612 break; 613 } 614 if (t == EOF) 615 break; 616 CHRSAVE(t); 617 } 618 } else 619 CHRSAVE(t); /* stack the char */ 620 break; 621 } 622 } 623 } 624 625 /* 626 * output string directly, without pushing it for reparses. 627 */ 628 void 629 outputstr(const char *s) 630 { 631 if (sp < 0) 632 reallyoutputstr(s); 633 else 634 while (*s) 635 CHRSAVE(*s++); 636 } 637 638 void 639 reallyoutputstr(const char *s) 640 { 641 if (synch_lines) { 642 while (*s) { 643 fputc(*s, active); 644 if (*s++ == '\n') { 645 infile[ilevel].synch_lineno++; 646 if (infile[ilevel].synch_lineno != 647 infile[ilevel].lineno) 648 do_emit_synchline(); 649 } 650 } 651 } else 652 fputs(s, active); 653 } 654 655 void 656 reallyputchar(int c) 657 { 658 putc(c, active); 659 if (synch_lines && c == '\n') { 660 infile[ilevel].synch_lineno++; 661 if (infile[ilevel].synch_lineno != infile[ilevel].lineno) 662 do_emit_synchline(); 663 } 664 } 665 666 /* 667 * build an input token.. 668 * consider only those starting with _ or A-Za-z. 669 */ 670 static ndptr 671 inspect(int c, char *tp) 672 { 673 char *name = tp; 674 char *etp = tp+MAXTOK; 675 ndptr p; 676 677 *tp++ = c; 678 679 while ((isalnum(c = gpbc()) || c == '_') && tp < etp) 680 *tp++ = c; 681 if (c != EOF) 682 PUSHBACK(c); 683 *tp = EOS; 684 /* token is too long, it won't match anything, but it can still 685 * be output. */ 686 if (tp == ep) { 687 outputstr(name); 688 while (isalnum(c = gpbc()) || c == '_') { 689 if (sp < 0) 690 reallyputchar(c); 691 else 692 CHRSAVE(c); 693 } 694 *name = EOS; 695 return NULL; 696 } 697 698 p = ohash_find(¯os, ohash_qlookupi(¯os, name, (void *)&tp)); 699 if (p == NULL) 700 return NULL; 701 if (macro_getdef(p) == NULL) 702 return NULL; 703 return p; 704 } 705 706 /* 707 * initkwds - initialise m4 keywords as fast as possible. 708 * This very similar to install, but without certain overheads, 709 * such as calling lookup. Malloc is not used for storing the 710 * keyword strings, since we simply use the static pointers 711 * within keywrds block. 712 */ 713 static void 714 initkwds(void) 715 { 716 unsigned int type; 717 size_t i; 718 719 for (i = 0; i < MAXKEYS; i++) { 720 type = keywrds[i].ktyp & TYPEMASK; 721 if ((keywrds[i].ktyp & NOARGS) == 0) 722 type |= NEEDARGS; 723 setup_builtin(keywrds[i].knam, type); 724 } 725 } 726 727 static void 728 record(struct position *t, int lev) 729 { 730 if (lev < MAXRECORD) { 731 t[lev].name = CURRENT_NAME; 732 t[lev].line = CURRENT_LINE; 733 } 734 } 735 736 static void 737 dump_stack(struct position *t, int lev) 738 { 739 int i; 740 741 for (i = 0; i < lev; i++) { 742 if (i == MAXRECORD) { 743 fprintf(stderr, " ...\n"); 744 break; 745 } 746 fprintf(stderr, " %s at line %lu\n", 747 t[i].name, t[i].line); 748 } 749 } 750 751 752 static void 753 enlarge_stack(void) 754 { 755 STACKMAX += STACKMAX/2; 756 mstack = xreallocarray(mstack, STACKMAX, sizeof(stae), 757 "Evaluation stack overflow (%lu)", 758 (unsigned long)STACKMAX); 759 sstack = xrealloc(sstack, STACKMAX, 760 "Evaluation stack overflow (%lu)", 761 (unsigned long)STACKMAX); 762 } 763 764 static const struct { 765 const char *n; 766 const char *d; 767 } nd [] = { 768 { "-d, --debug[=flags]", "set debug flags" }, 769 { "-D, --define=name[=value]", "define macro" }, 770 { "-e, --error-output=file", "send error output to file" }, 771 { "-E, --fatal-warnings", "exit on warnings" }, 772 { "-F, --freeze-state=file", "save state to file" }, 773 { "-g, --gnu", "enable gnu extensions" }, 774 { " --help", "print this message and exit" }, 775 { "-I, --include=file", "include file" }, 776 { "-i, --interactive", "unbuffer output, ignore tty signals" }, 777 { "-L, --nesting-limit=num", "macro expansion nesting limit (unimpl)" }, 778 { "-P, --prefix-builtins", "prefix builtins with m4_" }, 779 { "-Q, --quiet", "don't print warnings" }, 780 { "-R, --reload-state=file", "restore state from file" }, 781 { "-Q, --silent", "don't print warnings" }, 782 { "-s, --synclines", "output line directives for cpp(1)" }, 783 { "-t, --trace=macro", "trace the named macro" }, 784 { "-G, --traditional", "disable gnu extensions" }, 785 { "-U, --undefine=name", "undefine the named macro" }, 786 { "-v, --version", "print the version number and exit" }, 787 }; 788 789 static void 790 help(void) 791 { 792 size_t i; 793 fprintf(stdout, "%s version %d\n\n", getprogname(), VERSION); 794 usage(stdout); 795 796 fprintf(stdout, "\nThe long options are:\n"); 797 for (i = 0; i < __arraycount(nd); i++) 798 fprintf(stdout, "\t%-25.25s\t%s\n", nd[i].n, nd[i].d); 799 } 800