1 /* $OpenBSD: main.c,v 1.87 2017/06/15 13:48:42 bcallah Exp $ */ 2 /* $NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $ */ 3 4 /*- 5 * SPDX-License-Identifier: BSD-3-Clause 6 * 7 * Copyright (c) 1989, 1993 8 * The Regents of the University of California. All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Ozan Yigit at York University. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 /* 39 * main.c 40 * Facility: m4 macro processor 41 * by: oz 42 */ 43 #include <sys/cdefs.h> 44 __FBSDID("$FreeBSD$"); 45 46 #include <assert.h> 47 #include <signal.h> 48 #include <err.h> 49 #include <errno.h> 50 #include <unistd.h> 51 #include <stdio.h> 52 #include <ctype.h> 53 #include <string.h> 54 #include <stddef.h> 55 #include <stdint.h> 56 #include <stdlib.h> 57 #include <ohash.h> 58 #include "mdef.h" 59 #include "stdd.h" 60 #include "extern.h" 61 #include "pathnames.h" 62 63 stae *mstack; /* stack of m4 machine */ 64 char *sstack; /* shadow stack, for string space extension */ 65 static size_t STACKMAX; /* current maximum size of stack */ 66 int sp; /* current m4 stack pointer */ 67 int fp; /* m4 call frame pointer */ 68 struct input_file infile[MAXINP];/* input file stack (0=stdin) */ 69 FILE **outfile; /* diversion array(0=bitbucket)*/ 70 int maxout; 71 FILE *active; /* active output file pointer */ 72 int ilevel = 0; /* input file stack pointer */ 73 int oindex = 0; /* diversion index.. */ 74 const char *null = ""; /* as it says.. just a null.. */ 75 char **m4wraps = NULL; /* m4wraps array. */ 76 int maxwraps = 0; /* size of m4wraps array */ 77 int wrapindex = 0; /* current offset in m4wraps */ 78 char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */ 79 char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */ 80 char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */ 81 char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */ 82 int synch_lines = 0; /* line synchronisation for C preprocessor */ 83 int prefix_builtins = 0; /* -P option to prefix builtin keywords */ 84 int error_warns = 0; /* -E option to make warnings exit_code = 1 */ 85 int fatal_warns = 0; /* -E -E option to make warnings fatal */ 86 87 struct keyblk { 88 const char *knam; /* keyword name */ 89 int ktyp; /* keyword type */ 90 }; 91 92 static struct keyblk keywrds[] = { /* m4 keywords to be installed */ 93 { "include", INCLTYPE }, 94 { "sinclude", SINCTYPE }, 95 { "define", DEFITYPE }, 96 { "defn", DEFNTYPE }, 97 { "divert", DIVRTYPE | NOARGS }, 98 { "expr", EXPRTYPE }, 99 { "eval", EXPRTYPE }, 100 { "substr", SUBSTYPE }, 101 { "ifelse", IFELTYPE }, 102 { "ifdef", IFDFTYPE }, 103 { "len", LENGTYPE }, 104 { "incr", INCRTYPE }, 105 { "decr", DECRTYPE }, 106 { "dnl", DNLNTYPE | NOARGS }, 107 { "changequote", CHNQTYPE | NOARGS }, 108 { "changecom", CHNCTYPE | NOARGS }, 109 { "index", INDXTYPE }, 110 #ifdef EXTENDED 111 { "paste", PASTTYPE }, 112 { "spaste", SPASTYPE }, 113 /* Newer extensions, needed to handle gnu-m4 scripts */ 114 { "indir", INDIRTYPE}, 115 { "builtin", BUILTINTYPE}, 116 { "patsubst", PATSTYPE}, 117 { "regexp", REGEXPTYPE}, 118 { "esyscmd", ESYSCMDTYPE}, 119 { "__file__", FILENAMETYPE | NOARGS}, 120 { "__line__", LINETYPE | NOARGS}, 121 #endif 122 { "popdef", POPDTYPE }, 123 { "pushdef", PUSDTYPE }, 124 { "dumpdef", DUMPTYPE | NOARGS }, 125 { "shift", SHIFTYPE | NOARGS }, 126 { "translit", TRNLTYPE }, 127 { "undefine", UNDFTYPE }, 128 { "undivert", UNDVTYPE | NOARGS }, 129 { "divnum", DIVNTYPE | NOARGS }, 130 { "maketemp", MKTMTYPE }, 131 { "mkstemp", MKTMTYPE }, 132 { "errprint", ERRPTYPE | NOARGS }, 133 { "m4wrap", M4WRTYPE | NOARGS }, 134 { "m4exit", EXITTYPE | NOARGS }, 135 { "syscmd", SYSCTYPE }, 136 { "sysval", SYSVTYPE | NOARGS }, 137 { "traceon", TRACEONTYPE | NOARGS }, 138 { "traceoff", TRACEOFFTYPE | NOARGS }, 139 140 { "unix", SELFTYPE | NOARGS }, 141 }; 142 143 #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) 144 145 extern int optind; 146 extern char *optarg; 147 148 #define MAXRECORD 50 149 static struct position { 150 char *name; 151 unsigned long line; 152 } quotes[MAXRECORD], paren[MAXRECORD]; 153 154 static void record(struct position *, int); 155 static void dump_stack(struct position *, int); 156 157 static void macro(void); 158 static void initkwds(void); 159 static ndptr inspect(int, char *); 160 static int do_look_ahead(int, const char *); 161 static void reallyoutputstr(const char *); 162 static void reallyputchar(int); 163 164 static void enlarge_stack(void); 165 166 int main(int, char *[]); 167 168 int exit_code = 0; 169 170 int 171 main(int argc, char *argv[]) 172 { 173 int c; 174 int n; 175 char *p; 176 177 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 178 signal(SIGINT, onintr); 179 180 init_macros(); 181 initspaces(); 182 STACKMAX = INITSTACKMAX; 183 184 mstack = xreallocarray(NULL, STACKMAX, sizeof(stae), NULL); 185 sstack = xalloc(STACKMAX, NULL); 186 187 maxout = 0; 188 outfile = NULL; 189 resizedivs(MAXOUT); 190 191 while ((c = getopt(argc, argv, "gst:d:D:EU:o:I:P")) != -1) 192 switch(c) { 193 194 case 'D': /* define something..*/ 195 for (p = optarg; *p; p++) 196 if (*p == '=') 197 break; 198 if (*p) 199 *p++ = EOS; 200 dodefine(optarg, p); 201 break; 202 case 'E': /* like GNU m4 1.4.9+ */ 203 if (error_warns == 0) 204 error_warns = 1; 205 else 206 fatal_warns = 1; 207 break; 208 case 'I': 209 addtoincludepath(optarg); 210 break; 211 case 'P': 212 prefix_builtins = 1; 213 break; 214 case 'U': /* undefine... */ 215 macro_popdef(optarg); 216 break; 217 case 'g': 218 mimic_gnu = 1; 219 break; 220 case 'd': 221 set_trace_flags(optarg); 222 break; 223 case 's': 224 synch_lines = 1; 225 break; 226 case 't': 227 mark_traced(optarg, 1); 228 break; 229 case 'o': 230 trace_file(optarg); 231 break; 232 case '?': 233 usage(); 234 } 235 236 argc -= optind; 237 argv += optind; 238 239 initkwds(); 240 if (mimic_gnu) 241 setup_builtin("format", FORMATTYPE); 242 243 active = stdout; /* default active output */ 244 bbase[0] = bufbase; 245 if (!argc) { 246 sp = -1; /* stack pointer initialized */ 247 fp = 0; /* frame pointer initialized */ 248 set_input(infile+0, stdin, "stdin"); 249 /* default input (naturally) */ 250 macro(); 251 } else 252 for (; argc--; ++argv) { 253 p = *argv; 254 if (p[0] == '-' && p[1] == EOS) 255 set_input(infile, stdin, "stdin"); 256 else if (fopen_trypath(infile, p) == NULL) 257 err(1, "%s", p); 258 sp = -1; 259 fp = 0; 260 macro(); 261 release_input(infile); 262 } 263 264 if (wrapindex) { 265 int i; 266 267 ilevel = 0; /* in case m4wrap includes.. */ 268 bufbase = bp = buf; /* use the entire buffer */ 269 if (mimic_gnu) { 270 while (wrapindex != 0) { 271 for (i = 0; i < wrapindex; i++) 272 pbstr(m4wraps[i]); 273 wrapindex =0; 274 macro(); 275 } 276 } else { 277 for (i = 0; i < wrapindex; i++) { 278 pbstr(m4wraps[i]); 279 macro(); 280 } 281 } 282 } 283 284 if (active != stdout) 285 active = stdout; /* reset output just in case */ 286 for (n = 1; n < maxout; n++) /* default wrap-up: undivert */ 287 if (outfile[n] != NULL) 288 getdiv(n); 289 /* remove bitbucket if used */ 290 if (outfile[0] != NULL) { 291 (void) fclose(outfile[0]); 292 } 293 294 return exit_code; 295 } 296 297 /* 298 * Look ahead for `token'. 299 * (on input `t == token[0]') 300 * Used for comment and quoting delimiters. 301 * Returns 1 if `token' present; copied to output. 302 * 0 if `token' not found; all characters pushed back 303 */ 304 static int 305 do_look_ahead(int t, const char *token) 306 { 307 int i; 308 309 assert((unsigned char)t == (unsigned char)token[0]); 310 311 for (i = 1; *++token; i++) { 312 t = gpbc(); 313 if (t == EOF || (unsigned char)t != (unsigned char)*token) { 314 pushback(t); 315 while (--i) 316 pushback(*--token); 317 return 0; 318 } 319 } 320 return 1; 321 } 322 323 #define LOOK_AHEAD(t, token) (t != EOF && \ 324 (unsigned char)(t)==(unsigned char)(token)[0] && \ 325 do_look_ahead(t,token)) 326 327 /* 328 * macro - the work horse.. 329 */ 330 static void 331 macro(void) 332 { 333 char token[MAXTOK+1]; 334 int t, l; 335 ndptr p; 336 int nlpar; 337 338 cycle { 339 t = gpbc(); 340 341 if (LOOK_AHEAD(t,lquote)) { /* strip quotes */ 342 nlpar = 0; 343 record(quotes, nlpar++); 344 /* 345 * Opening quote: scan forward until matching 346 * closing quote has been found. 347 */ 348 do { 349 350 l = gpbc(); 351 if (LOOK_AHEAD(l,rquote)) { 352 if (--nlpar > 0) 353 outputstr(rquote); 354 } else if (LOOK_AHEAD(l,lquote)) { 355 record(quotes, nlpar++); 356 outputstr(lquote); 357 } else if (l == EOF) { 358 if (nlpar == 1) 359 warnx("unclosed quote:"); 360 else 361 warnx("%d unclosed quotes:", nlpar); 362 dump_stack(quotes, nlpar); 363 exit(1); 364 } else { 365 if (nlpar > 0) { 366 if (sp < 0) 367 reallyputchar(l); 368 else 369 CHRSAVE(l); 370 } 371 } 372 } 373 while (nlpar != 0); 374 } else if (sp < 0 && LOOK_AHEAD(t, scommt)) { 375 reallyoutputstr(scommt); 376 377 for(;;) { 378 t = gpbc(); 379 if (LOOK_AHEAD(t, ecommt)) { 380 reallyoutputstr(ecommt); 381 break; 382 } 383 if (t == EOF) 384 break; 385 reallyputchar(t); 386 } 387 } else if (t == '_' || isalpha(t)) { 388 p = inspect(t, token); 389 if (p != NULL) 390 pushback(l = gpbc()); 391 if (p == NULL || (l != LPAREN && 392 (macro_getdef(p)->type & NEEDARGS) != 0)) 393 outputstr(token); 394 else { 395 /* 396 * real thing.. First build a call frame: 397 */ 398 pushf(fp); /* previous call frm */ 399 pushf(macro_getdef(p)->type); /* type of the call */ 400 pushf(is_traced(p)); 401 pushf(0); /* parenthesis level */ 402 fp = sp; /* new frame pointer */ 403 /* 404 * now push the string arguments: 405 */ 406 pushdef(p); /* defn string */ 407 pushs1((char *)macro_name(p)); /* macro name */ 408 pushs(ep); /* start next..*/ 409 410 if (l != LPAREN && PARLEV == 0) { 411 /* no bracks */ 412 chrsave(EOS); 413 414 if (sp == (int)STACKMAX) 415 errx(1, "internal stack overflow"); 416 eval((const char **) mstack+fp+1, 2, 417 CALTYP, TRACESTATUS); 418 419 ep = PREVEP; /* flush strspace */ 420 sp = PREVSP; /* previous sp.. */ 421 fp = PREVFP; /* rewind stack...*/ 422 } 423 } 424 } else if (t == EOF) { 425 if (!mimic_gnu /* you can puke right there */ 426 && sp > -1 && ilevel <= 0) { 427 warnx( "unexpected end of input, unclosed parenthesis:"); 428 dump_stack(paren, PARLEV); 429 exit(1); 430 } 431 if (ilevel <= 0) 432 break; /* all done thanks.. */ 433 release_input(infile+ilevel--); 434 emit_synchline(); 435 bufbase = bbase[ilevel]; 436 continue; 437 } else if (sp < 0) { /* not in a macro at all */ 438 reallyputchar(t); /* output directly.. */ 439 } 440 441 else switch(t) { 442 443 case LPAREN: 444 if (PARLEV > 0) 445 chrsave(t); 446 while (isspace(l = gpbc())) /* skip blank, tab, nl.. */ 447 if (PARLEV > 0) 448 chrsave(l); 449 pushback(l); 450 record(paren, PARLEV++); 451 break; 452 453 case RPAREN: 454 if (--PARLEV > 0) 455 chrsave(t); 456 else { /* end of argument list */ 457 chrsave(EOS); 458 459 if (sp == (int)STACKMAX) 460 errx(1, "internal stack overflow"); 461 462 eval((const char **) mstack+fp+1, sp-fp, 463 CALTYP, TRACESTATUS); 464 465 ep = PREVEP; /* flush strspace */ 466 sp = PREVSP; /* previous sp.. */ 467 fp = PREVFP; /* rewind stack...*/ 468 } 469 break; 470 471 case COMMA: 472 if (PARLEV == 1) { 473 chrsave(EOS); /* new argument */ 474 while (isspace(l = gpbc())) 475 ; 476 pushback(l); 477 pushs(ep); 478 } else 479 chrsave(t); 480 break; 481 482 default: 483 if (LOOK_AHEAD(t, scommt)) { 484 char *p; 485 for (p = scommt; *p; p++) 486 chrsave(*p); 487 for(;;) { 488 t = gpbc(); 489 if (LOOK_AHEAD(t, ecommt)) { 490 for (p = ecommt; *p; p++) 491 chrsave(*p); 492 break; 493 } 494 if (t == EOF) 495 break; 496 CHRSAVE(t); 497 } 498 } else 499 CHRSAVE(t); /* stack the char */ 500 break; 501 } 502 } 503 } 504 505 /* 506 * output string directly, without pushing it for reparses. 507 */ 508 void 509 outputstr(const char *s) 510 { 511 if (sp < 0) 512 reallyoutputstr(s); 513 else 514 while (*s) 515 CHRSAVE(*s++); 516 } 517 518 void 519 reallyoutputstr(const char *s) 520 { 521 if (synch_lines) { 522 while (*s) { 523 fputc(*s, active); 524 if (*s++ == '\n') { 525 infile[ilevel].synch_lineno++; 526 if (infile[ilevel].synch_lineno != 527 infile[ilevel].lineno) 528 do_emit_synchline(); 529 } 530 } 531 } else 532 fputs(s, active); 533 } 534 535 void 536 reallyputchar(int c) 537 { 538 putc(c, active); 539 if (synch_lines && c == '\n') { 540 infile[ilevel].synch_lineno++; 541 if (infile[ilevel].synch_lineno != infile[ilevel].lineno) 542 do_emit_synchline(); 543 } 544 } 545 546 /* 547 * build an input token.. 548 * consider only those starting with _ or A-Za-z. 549 */ 550 static ndptr 551 inspect(int c, char *tp) 552 { 553 char *name = tp; 554 char *etp = tp+MAXTOK; 555 ndptr p; 556 557 *tp++ = c; 558 559 while ((isalnum(c = gpbc()) || c == '_') && tp < etp) 560 *tp++ = c; 561 if (c != EOF) 562 PUSHBACK(c); 563 *tp = EOS; 564 /* token is too long, it won't match anything, but it can still 565 * be output. */ 566 if (tp == ep) { 567 outputstr(name); 568 while (isalnum(c = gpbc()) || c == '_') { 569 if (sp < 0) 570 reallyputchar(c); 571 else 572 CHRSAVE(c); 573 } 574 *name = EOS; 575 return NULL; 576 } 577 578 p = ohash_find(¯os, ohash_qlookupi(¯os, name, (const char **)&tp)); 579 if (p == NULL) 580 return NULL; 581 if (macro_getdef(p) == NULL) 582 return NULL; 583 return p; 584 } 585 586 /* 587 * initkwds - initialise m4 keywords as fast as possible. 588 * This very similar to install, but without certain overheads, 589 * such as calling lookup. Malloc is not used for storing the 590 * keyword strings, since we simply use the static pointers 591 * within keywrds block. 592 */ 593 static void 594 initkwds(void) 595 { 596 unsigned int type; 597 int i; 598 599 for (i = 0; i < (int)MAXKEYS; i++) { 600 type = keywrds[i].ktyp & TYPEMASK; 601 if ((keywrds[i].ktyp & NOARGS) == 0) 602 type |= NEEDARGS; 603 setup_builtin(keywrds[i].knam, type); 604 } 605 } 606 607 static void 608 record(struct position *t, int lev) 609 { 610 if (lev < MAXRECORD) { 611 t[lev].name = CURRENT_NAME; 612 t[lev].line = CURRENT_LINE; 613 } 614 } 615 616 static void 617 dump_stack(struct position *t, int lev) 618 { 619 int i; 620 621 for (i = 0; i < lev; i++) { 622 if (i == MAXRECORD) { 623 fprintf(stderr, " ...\n"); 624 break; 625 } 626 fprintf(stderr, " %s at line %lu\n", 627 t[i].name, t[i].line); 628 } 629 } 630 631 632 static void 633 enlarge_stack(void) 634 { 635 STACKMAX += STACKMAX/2; 636 mstack = xreallocarray(mstack, STACKMAX, sizeof(stae), 637 "Evaluation stack overflow (%lu)", 638 (unsigned long)STACKMAX); 639 sstack = xrealloc(sstack, STACKMAX, 640 "Evaluation stack overflow (%lu)", 641 (unsigned long)STACKMAX); 642 } 643