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 "$FreeBSD$"; 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 void macro(); 143 void initkwds(); 144 145 int 146 main(argc,argv) 147 int argc; 148 char *argv[]; 149 { 150 register int c; 151 register int n; 152 char *p; 153 register FILE *ifp; 154 155 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 156 signal(SIGINT, onintr); 157 158 initkwds(); 159 160 while ((c = getopt(argc, argv, "tD:U:o:")) != -1) 161 switch(c) { 162 163 case 'D': /* define something..*/ 164 for (p = optarg; *p; p++) 165 if (*p == '=') 166 break; 167 if (*p) 168 *p++ = EOS; 169 dodefine(optarg, p); 170 break; 171 case 'U': /* undefine... */ 172 remhash(optarg, TOP); 173 break; 174 case 'o': /* specific output */ 175 case '?': 176 usage(); 177 } 178 179 argc -= optind; 180 argv += optind; 181 182 active = stdout; /* default active output */ 183 /* filename for diversions */ 184 m4temp = mktemp(xstrdup(_PATH_DIVNAME)); 185 186 bbase[0] = bufbase; 187 if (!argc) { 188 sp = -1; /* stack pointer initialized */ 189 fp = 0; /* frame pointer initialized */ 190 infile[0] = stdin; /* default input (naturally) */ 191 macro(); 192 } else 193 for (; argc--; ++argv) { 194 p = *argv; 195 if (p[0] == '-' && p[1] == '\0') 196 ifp = stdin; 197 else if ((ifp = fopen(p, "r")) == NULL) 198 err(1, "%s", p); 199 sp = -1; 200 fp = 0; 201 infile[0] = ifp; 202 macro(); 203 if (ifp != stdin) 204 (void)fclose(ifp); 205 } 206 207 if (*m4wraps) { /* anything for rundown ?? */ 208 ilevel = 0; /* in case m4wrap includes.. */ 209 bufbase = bp = buf; /* use the entire buffer */ 210 putback(EOF); /* eof is a must !! */ 211 pbstr(m4wraps); /* user-defined wrapup act */ 212 macro(); /* last will and testament */ 213 } 214 215 if (active != stdout) 216 active = stdout; /* reset output just in case */ 217 for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */ 218 if (outfile[n] != NULL) 219 getdiv(n); 220 /* remove bitbucket if used */ 221 if (outfile[0] != NULL) { 222 (void) fclose(outfile[0]); 223 m4temp[UNIQUE] = '0'; 224 #ifdef vms 225 (void) remove(m4temp); 226 #else 227 (void) unlink(m4temp); 228 #endif 229 } 230 231 return 0; 232 } 233 234 ndptr inspect(); 235 236 /* 237 * macro - the work horse.. 238 */ 239 void 240 macro() { 241 char token[MAXTOK]; 242 register char *s; 243 register int t, l; 244 register ndptr p; 245 register int nlpar; 246 247 cycle { 248 if ((t = gpbc()) == '_' || (t != EOF && isalpha(t))) { 249 putback(t); 250 if ((p = inspect(s = token)) == nil) { 251 if (sp < 0) 252 while (*s) 253 putc(*s++, active); 254 else 255 while (*s) 256 chrsave(*s++); 257 } 258 else { 259 /* 260 * real thing.. First build a call frame: 261 */ 262 pushf(fp); /* previous call frm */ 263 pushf(p->type); /* type of the call */ 264 pushf(0); /* parenthesis level */ 265 fp = sp; /* new frame pointer */ 266 /* 267 * now push the string arguments: 268 */ 269 pushs(p->defn); /* defn string */ 270 pushs(p->name); /* macro name */ 271 pushs(ep); /* start next..*/ 272 273 putback(l = gpbc()); 274 if (l != LPAREN) { /* add bracks */ 275 putback(RPAREN); 276 putback(LPAREN); 277 } 278 } 279 } 280 else if (t == EOF) { 281 if (sp > -1) 282 errx(1, "unexpected end of input"); 283 if (ilevel <= 0) 284 break; /* all done thanks.. */ 285 --ilevel; 286 (void) fclose(infile[ilevel+1]); 287 bufbase = bbase[ilevel]; 288 continue; 289 } 290 /* 291 * non-alpha single-char token seen.. 292 * [the order of else if .. stmts is important.] 293 */ 294 else if (t == lquote) { /* strip quotes */ 295 nlpar = 1; 296 do { 297 if ((l = gpbc()) == rquote) 298 nlpar--; 299 else if (l == lquote) 300 nlpar++; 301 else if (l == EOF) 302 errx(1, "missing right quote"); 303 if (nlpar > 0) { 304 if (sp < 0) 305 putc(l, active); 306 else 307 chrsave(l); 308 } 309 } 310 while (nlpar != 0); 311 } 312 313 else if (sp < 0) { /* not in a macro at all */ 314 if (t == scommt) { /* comment handling here */ 315 putc(t, active); 316 while ((t = gpbc()) != ecommt) 317 putc(t, active); 318 } 319 putc(t, active); /* output directly.. */ 320 } 321 322 else switch(t) { 323 324 case LPAREN: 325 if (PARLEV > 0) 326 chrsave(t); 327 while ((l = gpbc()) != EOF && isspace(l)) 328 ; /* skip blank, tab, nl.. */ 329 putback(l); 330 PARLEV++; 331 break; 332 333 case RPAREN: 334 if (--PARLEV > 0) 335 chrsave(t); 336 else { /* end of argument list */ 337 chrsave(EOS); 338 339 if (sp == STACKMAX) 340 errx(1, "internal stack overflow"); 341 342 if (CALTYP == MACRTYPE) 343 expand((char **) mstack+fp+1, sp-fp); 344 else 345 eval((char **) mstack+fp+1, sp-fp, CALTYP); 346 347 ep = PREVEP; /* flush strspace */ 348 sp = PREVSP; /* previous sp.. */ 349 fp = PREVFP; /* rewind stack...*/ 350 } 351 break; 352 353 case COMMA: 354 if (PARLEV == 1) { 355 chrsave(EOS); /* new argument */ 356 while ((l = gpbc()) != EOF && isspace(l)) 357 ; 358 putback(l); 359 pushs(ep); 360 } else 361 chrsave(t); 362 break; 363 364 default: 365 chrsave(t); /* stack the char */ 366 break; 367 } 368 } 369 } 370 371 /* 372 * build an input token.. 373 * consider only those starting with _ or A-Za-z. This is a 374 * combo with lookup to speed things up. 375 */ 376 ndptr 377 inspect(tp) 378 register char *tp; 379 { 380 register int c; 381 register char *name = tp; 382 register char *etp = tp+MAXTOK; 383 register ndptr p; 384 register unsigned long h = 0; 385 386 while ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp) 387 h = (h << 5) + h + (*tp++ = c); 388 putback(c); 389 if (tp == etp) 390 errx(1, "token too long"); 391 392 *tp = EOS; 393 394 for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) 395 if (STREQ(name, p->name)) 396 break; 397 return p; 398 } 399 400 /* 401 * initkwds - initialise m4 keywords as fast as possible. 402 * This very similar to install, but without certain overheads, 403 * such as calling lookup. Malloc is not used for storing the 404 * keyword strings, since we simply use the static pointers 405 * within keywrds block. 406 */ 407 void 408 initkwds() { 409 register int i; 410 register int h; 411 register ndptr p; 412 413 for (i = 0; i < MAXKEYS; i++) { 414 h = hash(keywrds[i].knam); 415 p = (ndptr) xalloc(sizeof(struct ndblock)); 416 p->nxtptr = hashtab[h]; 417 hashtab[h] = p; 418 p->name = keywrds[i].knam; 419 p->defn = null; 420 p->type = keywrds[i].ktyp | STATIC; 421 } 422 } 423