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