main.c revision 95095
1/* $OpenBSD: main.c,v 1.52 2002/02/16 21:27:48 millert 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#include <sys/cdefs.h> 41__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\ 42 The Regents of the University of California. All rights reserved.\n"); 43__SCCSID("@(#)main.c 8.1 (Berkeley) 6/6/93"); 44__RCSID_SOURCE("$OpenBSD: main.c,v 1.52 2002/02/16 21:27:48 millert Exp $"); 45__FBSDID("$FreeBSD: head/usr.bin/m4/main.c 95095 2002-04-20 01:49:10Z jmallett $"); 46 47/* 48 * main.c 49 * Facility: m4 macro processor 50 * by: oz 51 */ 52 53#include <sys/types.h> 54#include <assert.h> 55#include <signal.h> 56#include <errno.h> 57#include <unistd.h> 58#include <stdio.h> 59#include <ctype.h> 60#include <string.h> 61#include <stddef.h> 62#include <stdlib.h> 63#include <err.h> 64#include "mdef.h" 65#include "stdd.h" 66#include "extern.h" 67#include "pathnames.h" 68 69ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ 70stae *mstack; /* stack of m4 machine */ 71char *sstack; /* shadow stack, for string space extension */ 72static size_t STACKMAX; /* current maximum size of stack */ 73int sp; /* current m4 stack pointer */ 74int fp; /* m4 call frame pointer */ 75struct input_file infile[MAXINP];/* input file stack (0=stdin) */ 76char *inname[MAXINP]; /* names of these input files */ 77int inlineno[MAXINP]; /* current number in each input file */ 78FILE **outfile; /* diversion array(0=bitbucket)*/ 79int maxout; 80FILE *active; /* active output file pointer */ 81int ilevel = 0; /* input file stack pointer */ 82int oindex = 0; /* diversion index.. */ 83const char *null = ""; /* as it says.. just a null.. */ 84const char *m4wraps = ""; /* m4wrap string default.. */ 85char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */ 86char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */ 87char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */ 88char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */ 89int synccpp; /* Line synchronisation for C preprocessor */ 90 91struct keyblk keywrds[] = { /* m4 keywords to be installed */ 92 { "include", INCLTYPE }, 93 { "sinclude", SINCTYPE }, 94 { "define", DEFITYPE }, 95 { "defn", DEFNTYPE }, 96 { "divert", DIVRTYPE | NOARGS }, 97 { "expr", EXPRTYPE }, 98 { "eval", EXPRTYPE }, 99 { "substr", SUBSTYPE }, 100 { "ifelse", IFELTYPE }, 101 { "ifdef", IFDFTYPE }, 102 { "len", LENGTYPE }, 103 { "incr", INCRTYPE }, 104 { "decr", DECRTYPE }, 105 { "dnl", DNLNTYPE | NOARGS }, 106 { "changequote", CHNQTYPE | NOARGS }, 107 { "changecom", CHNCTYPE | NOARGS }, 108 { "index", INDXTYPE }, 109#ifdef EXTENDED 110 { "paste", PASTTYPE }, 111 { "spaste", SPASTYPE }, 112 /* Newer extensions, needed to handle gnu-m4 scripts */ 113 { "indir", INDIRTYPE}, 114 { "builtin", BUILTINTYPE}, 115 { "patsubst", PATSTYPE}, 116 { "regexp", REGEXPTYPE}, 117 { "esyscmd", ESYSCMDTYPE}, 118 { "__file__", FILENAMETYPE | NOARGS}, 119 { "__line__", LINETYPE | NOARGS}, 120#endif 121 { "popdef", POPDTYPE }, 122 { "pushdef", PUSDTYPE }, 123 { "dumpdef", DUMPTYPE | NOARGS }, 124 { "shift", SHIFTYPE | NOARGS }, 125 { "translit", TRNLTYPE }, 126 { "undefine", UNDFTYPE }, 127 { "undivert", UNDVTYPE | NOARGS }, 128 { "divnum", DIVNTYPE | NOARGS }, 129 { "maketemp", MKTMTYPE }, 130 { "errprint", ERRPTYPE | NOARGS }, 131 { "m4wrap", M4WRTYPE | NOARGS }, 132 { "m4exit", EXITTYPE | NOARGS }, 133 { "syscmd", SYSCTYPE }, 134 { "sysval", SYSVTYPE | NOARGS }, 135 { "traceon", TRACEONTYPE | NOARGS }, 136 { "traceoff", TRACEOFFTYPE | NOARGS }, 137 138#if defined(unix) || defined(__unix__) 139 { "unix", SELFTYPE | NOARGS }, 140#else 141#ifdef vms 142 { "vms", SELFTYPE | NOARGS }, 143#endif 144#endif 145}; 146 147#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) 148 149extern int optind; 150extern char *optarg; 151 152#define MAXRECORD 50 153static struct position { 154 char *name; 155 unsigned long line; 156} quotes[MAXRECORD], paren[MAXRECORD]; 157 158static void record(struct position *, int); 159static void dump_stack(struct position *, int); 160 161static void macro(void); 162static void initkwds(void); 163static ndptr inspect(int, char *); 164static int do_look_ahead(int, const char *); 165 166static void enlarge_stack(void); 167 168int main(int, char *[]); 169 170int 171main(argc,argv) 172 int argc; 173 char *argv[]; 174{ 175 int c; 176 int n; 177 char *p; 178 179 traceout = stderr; 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, "gst: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 's': 219 synccpp = 1; 220 break; 221 case 't': 222 mark_traced(optarg, 1); 223 break; 224 case 'o': 225 trace_file(optarg); 226 break; 227 case '?': 228 usage(); 229 } 230 231 argc -= optind; 232 argv += optind; 233 234 active = stdout; /* default active output */ 235 bbase[0] = bufbase; 236 if (!argc) { 237 sp = -1; /* stack pointer initialized */ 238 fp = 0; /* frame pointer initialized */ 239 set_input(infile+0, stdin, "stdin"); 240 /* default input (naturally) */ 241 if ((inname[0] = strdup("-")) == NULL) 242 err(1, NULL); 243 inlineno[0] = 1; 244 emitline(); 245 macro(); 246 } else 247 for (; argc--; ++argv) { 248 p = *argv; 249 if (p[0] == '-' && p[1] == EOS) 250 set_input(infile, stdin, "stdin"); 251 else if (fopen_trypath(infile, p) == NULL) 252 err(1, "%s", p); 253 sp = -1; 254 fp = 0; 255 if ((inname[0] = strdup(p)) == NULL) 256 err(1, NULL); 257 inlineno[0] = 1; 258 emitline(); 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 */ 290static int 291do_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 */ 318static void 319macro() 320{ 321 char token[MAXTOK+1]; 322 int t, l; 323 ndptr p; 324 int nlpar; 325 326 cycle { 327 t = gpbc(); 328 if (t == '_' || isalpha(t)) { 329 p = inspect(t, token); 330 if (p != nil) 331 putback(l = gpbc()); 332 if (p == nil || (l != LPAREN && 333 (p->type & NEEDARGS) != 0)) 334 outputstr(token); 335 else { 336 /* 337 * real thing.. First build a call frame: 338 */ 339 pushf(fp); /* previous call frm */ 340 pushf(p->type); /* type of the call */ 341 pushf(0); /* parenthesis level */ 342 fp = sp; /* new frame pointer */ 343 /* 344 * now push the string arguments: 345 */ 346 pushs1(p->defn); /* defn string */ 347 pushs1(p->name); /* macro name */ 348 pushs(ep); /* start next..*/ 349 350 if (l != LPAREN && PARLEV == 0) { 351 /* no bracks */ 352 chrsave(EOS); 353 354 if (sp == STACKMAX) 355 errx(1, "internal stack overflow"); 356 eval((const char **) mstack+fp+1, 2, 357 CALTYP); 358 359 ep = PREVEP; /* flush strspace */ 360 sp = PREVSP; /* previous sp.. */ 361 fp = PREVFP; /* rewind stack...*/ 362 } 363 } 364 } else if (t == EOF) { 365 if (sp > -1) { 366 warnx( "unexpected end of input, unclosed parenthesis:"); 367 dump_stack(paren, PARLEV); 368 exit(1); 369 } 370 if (ilevel <= 0) 371 break; /* all done thanks.. */ 372 release_input(infile+ilevel--); 373 free(inname[ilevel+1]); 374 bufbase = bbase[ilevel]; 375 emitline(); 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 *pc; 479 for (pc = scommt; *pc; pc++) 480 chrsave(*pc); 481 for(;;) { 482 t = gpbc(); 483 if (LOOK_AHEAD(t, ecommt)) { 484 for (pc = ecommt; *pc; pc++) 485 chrsave(*pc); 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 */ 502void 503outputstr(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 */ 519static ndptr 520inspect(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 */ 563static void 564initkwds() 565{ 566 size_t i; 567 unsigned int h; 568 ndptr p; 569 570 for (i = 0; i < MAXKEYS; i++) { 571 h = hash(keywrds[i].knam); 572 p = (ndptr) xalloc(sizeof(struct ndblock)); 573 p->nxtptr = hashtab[h % HASHSIZE]; 574 hashtab[h % HASHSIZE] = p; 575 p->name = xstrdup(keywrds[i].knam); 576 p->defn = xstrdup(null); 577 p->hv = h; 578 p->type = keywrds[i].ktyp & TYPEMASK; 579 if ((keywrds[i].ktyp & NOARGS) == 0) 580 p->type |= NEEDARGS; 581 } 582} 583 584/* Look up a builtin type, even if overridden by the user */ 585int 586builtin_type(key) 587 const char *key; 588{ 589 int i; 590 591 for (i = 0; i != MAXKEYS; i++) 592 if (STREQ(keywrds[i].knam, key)) 593 return keywrds[i].ktyp; 594 return -1; 595} 596 597const char * 598builtin_realname(n) 599 int n; 600{ 601 int i; 602 603 for (i = 0; i != MAXKEYS; i++) 604 if (((keywrds[i].ktyp ^ n) & TYPEMASK) == 0) 605 return keywrds[i].knam; 606 return NULL; 607} 608 609static void 610record(t, lev) 611 struct position *t; 612 int lev; 613{ 614 if (lev < MAXRECORD) { 615 t[lev].name = CURRENT_NAME; 616 t[lev].line = CURRENT_LINE; 617 } 618} 619 620static void 621dump_stack(t, lev) 622 struct position *t; 623 int lev; 624{ 625 int i; 626 627 for (i = 0; i < lev; i++) { 628 if (i == MAXRECORD) { 629 fprintf(stderr, " ...\n"); 630 break; 631 } 632 fprintf(stderr, " %s at line %lu\n", 633 t[i].name, t[i].line); 634 } 635} 636 637 638static void 639enlarge_stack() 640{ 641 STACKMAX *= 2; 642 mstack = realloc(mstack, sizeof(stae) * STACKMAX); 643 sstack = realloc(sstack, STACKMAX); 644 if (mstack == NULL || sstack == NULL) 645 errx(1, "Evaluation stack overflow (%lu)", 646 (unsigned long)STACKMAX); 647} 648 649/* Emit preprocessor #line directive if -s option used. */ 650void 651emitline(void) 652{ 653 654 if (synccpp) 655 fprintf(active, "#line %d \"%s\"\n", inlineno[ilevel], 656 inname[ilevel]); 657} 658