1 /* $NetBSD: main.c,v 1.37 2008/07/21 14:19:24 lukem 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. 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 #if HAVE_NBTOOL_CONFIG_H 37 #include "nbtool_config.h" 38 #endif 39 40 #include <sys/cdefs.h> 41 #if defined(__COPYRIGHT) && !defined(lint) 42 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\ 43 The Regents of the University of California. All rights reserved."); 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.37 2008/07/21 14:19:24 lukem 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 l = 0; /* XXXGCC -Wuninitialized [sun2] */ 327 328 cycle { 329 t = gpbc(); 330 if (t == '_' || isalpha(t)) { 331 p = inspect(t, token); 332 if (p != nil) 333 putback(l = gpbc()); 334 if (p == nil || (l != LPAREN && 335 (p->type & NEEDARGS) != 0)) 336 outputstr(token); 337 else { 338 /* 339 * real thing.. First build a call frame: 340 */ 341 pushf(fp); /* previous call frm */ 342 pushf(p->type); /* type of the call */ 343 pushf(0); /* parenthesis level */ 344 fp = sp; /* new frame pointer */ 345 /* 346 * now push the string arguments: 347 */ 348 pushs1(p->defn); /* defn string */ 349 pushs1(p->name); /* macro name */ 350 pushs(ep); /* start next..*/ 351 352 if (l != LPAREN && PARLEV == 0) { 353 /* no bracks */ 354 chrsave(EOS); 355 356 if (sp == STACKMAX) 357 errx(1, "internal stack overflow"); 358 eval((const char **) mstack+fp+1, 2, 359 CALTYP); 360 361 ep = PREVEP; /* flush strspace */ 362 sp = PREVSP; /* previous sp.. */ 363 fp = PREVFP; /* rewind stack...*/ 364 } 365 } 366 } else if (t == EOF) { 367 if (sp > -1) { 368 warnx( "unexpected end of input, unclosed parenthesis:"); 369 dump_stack(paren, PARLEV); 370 exit(1); 371 } 372 if (ilevel <= 0) 373 break; /* all done thanks.. */ 374 release_input(infile+ilevel--); 375 bufbase = bbase[ilevel]; 376 continue; 377 } 378 /* 379 * non-alpha token possibly seen.. 380 * [the order of else if .. stmts is important.] 381 */ 382 else if (LOOK_AHEAD(t,lquote)) { /* strip quotes */ 383 nlpar = 0; 384 record(quotes, nlpar++); 385 /* 386 * Opening quote: scan forward until matching 387 * closing quote has been found. 388 */ 389 do { 390 391 l = gpbc(); 392 if (LOOK_AHEAD(l,rquote)) { 393 if (--nlpar > 0) 394 outputstr(rquote); 395 } else if (LOOK_AHEAD(l,lquote)) { 396 record(quotes, nlpar++); 397 outputstr(lquote); 398 } else if (l == EOF) { 399 if (nlpar == 1) 400 warnx("unclosed quote:"); 401 else 402 warnx("%d unclosed quotes:", nlpar); 403 dump_stack(quotes, nlpar); 404 exit(1); 405 } else { 406 if (nlpar > 0) { 407 if (sp < 0) 408 putc(l, active); 409 else 410 CHRSAVE(l); 411 } 412 } 413 } 414 while (nlpar != 0); 415 } 416 417 else if (sp < 0 && LOOK_AHEAD(t, scommt)) { 418 fputs(scommt, active); 419 420 for(;;) { 421 t = gpbc(); 422 if (LOOK_AHEAD(t, ecommt)) { 423 fputs(ecommt, active); 424 break; 425 } 426 if (t == EOF) 427 break; 428 putc(t, active); 429 } 430 } 431 432 else if (sp < 0) { /* not in a macro at all */ 433 putc(t, active); /* output directly.. */ 434 } 435 436 else switch(t) { 437 438 case LPAREN: 439 if (PARLEV > 0) 440 chrsave(t); 441 while (isspace(l = gpbc())) 442 ; /* skip blank, tab, nl.. */ 443 putback(l); 444 record(paren, PARLEV++); 445 break; 446 447 case RPAREN: 448 if (--PARLEV > 0) 449 chrsave(t); 450 else { /* end of argument list */ 451 chrsave(EOS); 452 453 if (sp == STACKMAX) 454 errx(1, "internal stack overflow"); 455 456 eval((const char **) mstack+fp+1, sp-fp, 457 CALTYP); 458 459 ep = PREVEP; /* flush strspace */ 460 sp = PREVSP; /* previous sp.. */ 461 fp = PREVFP; /* rewind stack...*/ 462 } 463 break; 464 465 case COMMA: 466 if (PARLEV == 1) { 467 chrsave(EOS); /* new argument */ 468 while (isspace(l = gpbc())) 469 ; 470 putback(l); 471 pushs(ep); 472 } else 473 chrsave(t); 474 break; 475 476 default: 477 if (LOOK_AHEAD(t, scommt)) { 478 char *p; 479 for (p = scommt; *p; p++) 480 chrsave(*p); 481 for(;;) { 482 t = gpbc(); 483 if (LOOK_AHEAD(t, ecommt)) { 484 for (p = ecommt; *p; p++) 485 chrsave(*p); 486 break; 487 } 488 if (t == EOF) 489 break; 490 CHRSAVE(t); 491 } 492 } else 493 CHRSAVE(t); /* stack the char */ 494 break; 495 } 496 } 497 } 498 499 /* 500 * output string directly, without pushing it for reparses. 501 */ 502 void 503 outputstr(s) 504 const char *s; 505 { 506 if (sp < 0) 507 while (*s) 508 putc(*s++, active); 509 else 510 while (*s) 511 CHRSAVE(*s++); 512 } 513 514 /* 515 * build an input token.. 516 * consider only those starting with _ or A-Za-z. This is a 517 * combo with lookup to speed things up. 518 */ 519 static ndptr 520 inspect(c, tp) 521 int c; 522 char *tp; 523 { 524 char *name = tp; 525 char *etp = tp+MAXTOK; 526 ndptr p; 527 unsigned int h; 528 529 h = *tp++ = c; 530 531 while ((isalnum(c = gpbc()) || c == '_') && tp < etp) 532 h = (h << 5) + h + (*tp++ = c); 533 if (c != EOF) 534 PUTBACK(c); 535 *tp = EOS; 536 /* token is too long, it won't match anything, but it can still 537 * be output. */ 538 if (tp == ep) { 539 outputstr(name); 540 while (isalnum(c = gpbc()) || c == '_') { 541 if (sp < 0) 542 putc(c, active); 543 else 544 CHRSAVE(c); 545 } 546 *name = EOS; 547 return nil; 548 } 549 550 for (p = hashtab[h % HASHSIZE]; p != nil; p = p->nxtptr) 551 if (h == p->hv && STREQ(name, p->name)) 552 break; 553 return p; 554 } 555 556 /* 557 * initkwds - initialise m4 keywords as fast as possible. 558 * This very similar to install, but without certain overheads, 559 * such as calling lookup. Malloc is not used for storing the 560 * keyword strings, since we simply use the static pointers 561 * within keywrds block. 562 */ 563 static void 564 initkwds() 565 { 566 size_t i; 567 unsigned int h; 568 ndptr p; 569 char *k; 570 571 for (i = 0; i < MAXKEYS; i++) { 572 k = (char *)keywrds[i].knam; 573 if (m4prefix) { 574 size_t klen = strlen(k); 575 char *newk = malloc(klen + 4); 576 577 if (snprintf(newk, klen+4, "m4_%s", k) == -1) 578 err(1, "snprintf"); 579 keywrds[i].knam = newk; 580 k = newk; 581 } 582 h = hash(k); 583 p = (ndptr) xalloc(sizeof(struct ndblock)); 584 p->nxtptr = hashtab[h % HASHSIZE]; 585 hashtab[h % HASHSIZE] = p; 586 p->name = xstrdup(keywrds[i].knam); 587 p->defn = null; 588 p->hv = h; 589 p->type = keywrds[i].ktyp & TYPEMASK; 590 if ((keywrds[i].ktyp & NOARGS) == 0) 591 p->type |= NEEDARGS; 592 } 593 } 594 595 /* Look up a builtin type, even if overridden by the user */ 596 int 597 builtin_type(key) 598 const char *key; 599 { 600 int i; 601 602 for (i = 0; i != MAXKEYS; i++) 603 if (STREQ(keywrds[i].knam, key)) 604 return keywrds[i].ktyp; 605 return -1; 606 } 607 608 const char * 609 builtin_realname(n) 610 int n; 611 { 612 int i; 613 614 for (i = 0; i != MAXKEYS; i++) 615 if (((keywrds[i].ktyp ^ n) & TYPEMASK) == 0) 616 return keywrds[i].knam; 617 return NULL; 618 } 619 620 static void 621 record(t, lev) 622 struct position *t; 623 int lev; 624 { 625 if (lev < MAXRECORD) { 626 t[lev].name = CURRENT_NAME; 627 t[lev].line = CURRENT_LINE; 628 } 629 } 630 631 static void 632 dump_stack(t, lev) 633 struct position *t; 634 int lev; 635 { 636 int i; 637 638 for (i = 0; i < lev; i++) { 639 if (i == MAXRECORD) { 640 fprintf(stderr, " ...\n"); 641 break; 642 } 643 fprintf(stderr, " %s at line %lu\n", 644 t[i].name, t[i].line); 645 } 646 } 647 648 649 static void 650 enlarge_stack() 651 { 652 STACKMAX *= 2; 653 mstack = realloc(mstack, sizeof(stae) * STACKMAX); 654 sstack = realloc(sstack, STACKMAX); 655 if (mstack == NULL || sstack == NULL) 656 errx(1, "Evaluation stack overflow (%lu)", 657 (unsigned long)STACKMAX); 658 } 659