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