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