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