1 /* $NetBSD: main.c,v 1.23 1999/08/16 02:49:20 enami Exp $ */ 2 3 /*- 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Ozan Yigit at York University. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\ 42 The Regents of the University of California. All rights reserved.\n"); 43 #endif /* not lint */ 44 45 #ifndef lint 46 #if 0 47 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; 48 #else 49 __RCSID("$NetBSD: main.c,v 1.23 1999/08/16 02:49:20 enami Exp $"); 50 #endif 51 #endif /* not lint */ 52 53 /* 54 * main.c 55 * Facility: m4 macro processor 56 * by: oz 57 */ 58 59 #include <sys/types.h> 60 #include <ctype.h> 61 #include <err.h> 62 #include <errno.h> 63 #include <signal.h> 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <unistd.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 pbent buf[BUFSIZE]; /* push-back buffer */ 75 pbent *bufbase = buf; /* the base for current ilevel */ 76 pbent *bbase[MAXINP]; /* the base for each ilevel */ 77 pbent *bp = buf; /* first available character */ 78 pbent *endpbb = buf+BUFSIZE; /* end of push-back buffer */ 79 stae mstack[STACKMAX+1]; /* stack of m4 machine */ 80 char strspace[STRSPMAX+1]; /* string space for evaluation */ 81 char *ep = strspace; /* first free char in strspace */ 82 char *endest= strspace+STRSPMAX;/* end of string space */ 83 int sp; /* current m4 stack pointer */ 84 int fp; /* m4 call frame pointer */ 85 FILE *infile[MAXINP]; /* input file stack (0=stdin) */ 86 FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/ 87 FILE *active; /* active output file pointer */ 88 char *m4temp; /* filename for diversions */ 89 int ilevel = 0; /* input file stack pointer */ 90 int oindex = 0; /* diversion index.. */ 91 char *null = ""; /* as it says.. just a null.. */ 92 char *m4wraps = ""; /* m4wrap string default.. */ 93 char *progname; /* name of this program */ 94 int m4prefix = 0; /* prefix keywords with m4_ */ 95 char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */ 96 char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */ 97 char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */ 98 char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */ 99 100 struct keyblk keywrds[] = { /* m4 keywords to be installed */ 101 { "include", INCLTYPE }, 102 { "sinclude", SINCTYPE }, 103 { "define", DEFITYPE }, 104 { "defn", DEFNTYPE }, 105 { "divert", DIVRTYPE }, 106 { "expr", EXPRTYPE }, 107 { "eval", EXPRTYPE }, 108 { "substr", SUBSTYPE }, 109 { "ifelse", IFELTYPE }, 110 { "ifdef", IFDFTYPE }, 111 { "len", LENGTYPE }, 112 { "incr", INCRTYPE }, 113 { "decr", DECRTYPE }, 114 { "dnl", DNLNTYPE }, 115 { "changequote", CHNQTYPE }, 116 { "changecom", CHNCTYPE }, 117 { "index", INDXTYPE }, 118 #ifdef EXTENDED 119 { "paste", PASTTYPE }, 120 { "spaste", SPASTYPE }, 121 #endif 122 { "popdef", POPDTYPE }, 123 { "pushdef", PUSDTYPE }, 124 { "dumpdef", DUMPTYPE }, 125 { "shift", SHIFTYPE }, 126 { "translit", TRNLTYPE }, 127 { "undefine", UNDFTYPE }, 128 { "undivert", UNDVTYPE }, 129 { "divnum", DIVNTYPE }, 130 { "maketemp", MKTMTYPE }, 131 { "errprint", ERRPTYPE }, 132 { "m4wrap", M4WRTYPE }, 133 { "m4exit", EXITTYPE }, 134 { "syscmd", SYSCTYPE }, 135 { "sysval", SYSVTYPE }, 136 137 #ifdef unix 138 { "unix", MACRTYPE }, 139 #else 140 #ifdef vms 141 { "vms", MACRTYPE }, 142 #endif 143 #endif 144 }; 145 146 #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) 147 148 int do_look_ahead __P((int, char *)); 149 ndptr inspect __P((char *)); 150 void initkwds __P((void)); 151 void macro __P((void)); 152 int main __P((int, char **)); 153 154 int 155 main(argc,argv) 156 int argc; 157 char *argv[]; 158 { 159 int c; 160 int n; 161 int fd; 162 char *p; 163 FILE *ifp; 164 165 progname = basename(argv[0]); 166 167 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 168 signal(SIGINT, onintr); 169 170 /* 171 * We need to know if -P is there before checking -D and -U. 172 */ 173 while ((c = getopt(argc, argv, "tPD:U:")) != -1) 174 if (c == 'P') 175 m4prefix = 1; 176 optind = 1; 177 178 initkwds(); 179 180 while ((c = getopt(argc, argv, "tPD:U:")) != -1) 181 switch(c) { 182 183 case 'D': /* define something..*/ 184 for (p = optarg; *p; p++) 185 if (*p == '=') 186 break; 187 if (*p) 188 *p++ = EOS; 189 dodefine(optarg, p); 190 break; 191 case 'U': /* undefine... */ 192 remhash(optarg, TOP); 193 break; 194 case 'P': 195 break; 196 case '?': 197 default: 198 usage(); 199 } 200 201 argc -= optind; 202 argv += optind; 203 204 active = stdout; /* default active output */ 205 /* filename for diversions */ 206 m4temp = xstrdup(_PATH_DIVNAME); 207 fd = mkstemp(m4temp); 208 if (fd == -1) 209 err(1, "mkstemp failed"); 210 close(fd); 211 212 bbase[0] = bufbase; 213 if (!argc) { 214 sp = -1; /* stack pointer initialized */ 215 fp = 0; /* frame pointer initialized */ 216 infile[0] = stdin; /* default input (naturally) */ 217 macro(); 218 } else 219 for (; argc--; ++argv) { 220 p = *argv; 221 if (p[0] == '-' && p[1] == '\0') 222 ifp = stdin; 223 else if ((ifp = fopen(p, "r")) == NULL) 224 err(1, "%s", p); 225 sp = -1; 226 fp = 0; 227 infile[0] = ifp; 228 macro(); 229 if (ifp != stdin) 230 (void)fclose(ifp); 231 } 232 233 if (*m4wraps) { /* anything for rundown ?? */ 234 ilevel = 0; /* in case m4wrap includes.. */ 235 bufbase = bp = buf; /* use the entire buffer */ 236 putbackeof(); /* eof is a must !! */ 237 pbstr(m4wraps); /* user-defined wrapup act */ 238 macro(); /* last will and testament */ 239 } 240 241 if (active != stdout) 242 active = stdout; /* reset output just in case */ 243 for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */ 244 if (outfile[n] != NULL) 245 getdiv(n); 246 /* close bitbucket if used */ 247 if (outfile[0] != NULL) 248 (void) fclose(outfile[0]); 249 250 /* always remove bitbucket since mkstemp(3) creates it */ 251 m4temp[UNIQUE] = '0'; 252 #ifdef vms 253 (void) remove(m4temp); 254 #else 255 (void) unlink(m4temp); 256 #endif 257 258 return 0; 259 } 260 261 /* 262 * Look ahead (at most MAXCCHARS characters) for `token'. 263 * (on input `t == token[0]') 264 * Used for comment and quoting delimiters. 265 * Returns 1 if `token' present; copied to output. 266 * 0 if `token' not found; all characters pushed back 267 */ 268 int 269 do_look_ahead(t, token) 270 int t; 271 char *token; 272 { 273 int i; 274 275 if (t != token[0]) 276 errx(1, "internal error"); 277 278 for (i = 1; *++token; i++) { 279 t = gpbc(); 280 if (t == EOF || t != *token) { 281 if (t != EOF) 282 putback(t); 283 while (--i) 284 putback(*--token); 285 return 0; 286 } 287 } 288 return 1; 289 } 290 291 #define LOOK_AHEAD(t, token) ((t)==(token)[0] && do_look_ahead(t,token)) 292 293 /* 294 * macro - the work horse.. 295 */ 296 void 297 macro() 298 { 299 char token[MAXTOK], chars[2]; 300 char *s; 301 int t, l; 302 ndptr p; 303 int nlpar; 304 305 s = NULL; 306 cycle { 307 t = gpbc(); 308 if (t == '_' || isalpha(t)) { 309 putback(t); 310 if ((p = inspect(s = token)) == nil) { 311 if (sp < 0) 312 while (*s) 313 putc(*s++, active); 314 else 315 while (*s) 316 chrsave(*s++); 317 } 318 else { 319 /* 320 * real thing.. First build a call frame: 321 */ 322 pushf(fp); /* previous call frm */ 323 pushf(p->type); /* type of the call */ 324 pushf(0); /* parenthesis level */ 325 fp = sp; /* new frame pointer */ 326 /* 327 * now push the string arguments: 328 */ 329 pushs(p->defn); /* defn string */ 330 pushs(p->name); /* macro name */ 331 pushs(ep); /* start next..*/ 332 333 putback(l = gpbc()); 334 if (l != LPAREN) { /* add bracks */ 335 putback(RPAREN); 336 putback(LPAREN); 337 } 338 } 339 } 340 else if (t == EOF) { 341 if (sp > -1) 342 errx(1, "unexpected end of input"); 343 if (ilevel <= 0) 344 break; /* all done thanks.. */ 345 --ilevel; 346 (void) fclose(infile[ilevel+1]); 347 bufbase = bbase[ilevel]; 348 continue; 349 } 350 /* 351 * non-alpha token possibly seen.. 352 * [the order of else if .. stmts is important.] 353 */ 354 else if (LOOK_AHEAD(t,lquote)) { /* strip quotes */ 355 nlpar = 1; 356 do { 357 358 l = gpbc(); 359 if (LOOK_AHEAD(l,rquote)) { 360 nlpar--; 361 s = rquote; 362 } else if (LOOK_AHEAD(l,lquote)) { 363 nlpar++; 364 s = lquote; 365 } else if (l == EOF) 366 errx(1, "missing right quote"); 367 else { 368 chars[0] = l; 369 chars[1] = '\0'; 370 s = chars; 371 } 372 if (nlpar > 0) { 373 if (sp < 0) 374 while (*s) 375 putc(*s++, active); 376 else 377 while (*s) 378 chrsave(*s++); 379 } 380 } 381 while (nlpar != 0); 382 } 383 384 else if (sp < 0 && LOOK_AHEAD(t, scommt)) { 385 int i; 386 for (i = 0; i < MAXCCHARS && scommt[i]; i++) 387 putc(scommt[i], active); 388 389 for(;;) { 390 t = gpbc(); 391 if (LOOK_AHEAD(t, ecommt)) { 392 for (i = 0; i < MAXCCHARS && ecommt[i]; 393 i++) 394 putc(ecommt[i], 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 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((char **) mstack+fp+1, sp-fp); 429 else 430 eval((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 chrsave(t); /* stack the char */ 451 break; 452 } 453 } 454 } 455 456 /* 457 * build an input token.. 458 * consider only those starting with _ or A-Za-z. This is a 459 * combo with lookup to speed things up. 460 */ 461 ndptr 462 inspect(tp) 463 char *tp; 464 { 465 char c; 466 char *name = tp; 467 char *etp = tp+MAXTOK; 468 ndptr p; 469 unsigned long h = 0; 470 471 while ((isalnum((unsigned char)(c = gpbc())) || c == '_') && tp < etp) 472 h = (h << 5) + h + (*tp++ = c); 473 putback(c); 474 if (tp == etp) 475 errx(1, "token too long"); 476 477 *tp = EOS; 478 479 for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) 480 if (STREQ(name, p->name)) 481 break; 482 return p; 483 } 484 485 /* 486 * initkwds - initialise m4 keywords as fast as possible. 487 * This very similar to install, but without certain overheads, 488 * such as calling lookup. Malloc is not used for storing the 489 * keyword strings, since we simply use the static pointers 490 * within keywrds block. 491 */ 492 void 493 initkwds() 494 { 495 int i; 496 int h; 497 ndptr p; 498 char *k; 499 500 for (i = 0; i < MAXKEYS; i++) { 501 k = keywrds[i].knam; 502 if (m4prefix && asprintf(&k, "m4_%s", k) == -1) 503 err(1, "asprintf"); 504 h = hash(k); 505 p = (ndptr) xalloc(sizeof(struct ndblock)); 506 p->nxtptr = hashtab[h]; 507 hashtab[h] = p; 508 p->name = k; 509 p->defn = null; 510 p->type = keywrds[i].ktyp | STATIC; 511 } 512 } 513