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