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