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