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