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 <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 #include "mdef.h" 66 #include "stdd.h" 67 #include "extern.h" 68 #include "pathnames.h" 69 70 ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ 71 unsigned char buf[BUFSIZE]; /* push-back buffer */ 72 unsigned char *bufbase = buf; /* the base for current ilevel */ 73 unsigned char *bbase[MAXINP]; /* the base for each ilevel */ 74 unsigned char *bp = buf; /* first available character */ 75 unsigned char *endpbb = buf+BUFSIZE; /* end of push-back buffer */ 76 stae mstack[STACKMAX+1]; /* stack of m4 machine */ 77 char strspace[STRSPMAX+1]; /* string space for evaluation */ 78 char *ep = strspace; /* first free char in strspace */ 79 char *endest= strspace+STRSPMAX;/* end of string space */ 80 int sp; /* current m4 stack pointer */ 81 int fp; /* m4 call frame pointer */ 82 FILE *infile[MAXINP]; /* input file stack (0=stdin) */ 83 FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/ 84 FILE *active; /* active output file pointer */ 85 char *m4temp; /* filename for diversions */ 86 char *m4dir; /* directory for diversions */ 87 int ilevel = 0; /* input file stack pointer */ 88 int oindex = 0; /* diversion index.. */ 89 char *null = ""; /* as it says.. just a null.. */ 90 char *m4wraps = ""; /* m4wrap string default.. */ 91 char lquote = LQUOTE; /* left quote character (`) */ 92 char rquote = RQUOTE; /* right quote character (') */ 93 char scommt = SCOMMT; /* start character for comment */ 94 char ecommt = ECOMMT; /* end character for comment */ 95 96 struct keyblk keywrds[] = { /* m4 keywords to be installed */ 97 "include", INCLTYPE, 98 "sinclude", SINCTYPE, 99 "define", DEFITYPE, 100 "defn", DEFNTYPE, 101 "divert", DIVRTYPE, 102 "expr", EXPRTYPE, 103 "eval", EXPRTYPE, 104 "substr", SUBSTYPE, 105 "ifelse", IFELTYPE, 106 "ifdef", IFDFTYPE, 107 "len", LENGTYPE, 108 "incr", INCRTYPE, 109 "decr", DECRTYPE, 110 "dnl", DNLNTYPE, 111 "changequote", CHNQTYPE, 112 "changecom", CHNCTYPE, 113 "index", INDXTYPE, 114 #ifdef EXTENDED 115 "paste", PASTTYPE, 116 "spaste", SPASTYPE, 117 #endif 118 "popdef", POPDTYPE, 119 "pushdef", PUSDTYPE, 120 "dumpdef", DUMPTYPE, 121 "shift", SHIFTYPE, 122 "translit", TRNLTYPE, 123 "undefine", UNDFTYPE, 124 "undivert", UNDVTYPE, 125 "divnum", DIVNTYPE, 126 "maketemp", MKTMTYPE, 127 "errprint", ERRPTYPE, 128 "m4wrap", M4WRTYPE, 129 "m4exit", EXITTYPE, 130 "syscmd", SYSCTYPE, 131 "sysval", SYSVTYPE, 132 133 #ifdef unix 134 "unix", MACRTYPE, 135 #else 136 #ifdef vms 137 "vms", MACRTYPE, 138 #endif 139 #endif 140 }; 141 142 #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) 143 144 void macro(); 145 void initkwds(); 146 147 int 148 main(argc,argv) 149 int argc; 150 char *argv[]; 151 { 152 register int c; 153 register int n; 154 char *p; 155 register FILE *ifp; 156 157 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 158 signal(SIGINT, onintr); 159 160 initkwds(); 161 162 while ((c = getopt(argc, argv, "tD:U:o:")) != -1) 163 switch(c) { 164 165 case 'D': /* define something..*/ 166 for (p = optarg; *p; p++) 167 if (*p == '=') 168 break; 169 if (*p) 170 *p++ = EOS; 171 dodefine(optarg, p); 172 break; 173 case 'U': /* undefine... */ 174 remhash(optarg, TOP); 175 break; 176 case 'o': /* specific output */ 177 case '?': 178 usage(); 179 } 180 181 argc -= optind; 182 argv += optind; 183 184 active = stdout; /* default active output */ 185 if ((p = strdup(_PATH_DIVDIRNAME)) == NULL) 186 err(1, "strdup"); 187 188 /* filename for diversions */ 189 m4dir = mkdtemp(p); 190 err_set_exit(cleanup); 191 (void) asprintf(&m4temp, "%s/%s", m4dir, _PATH_DIVNAME); 192 193 bbase[0] = bufbase; 194 if (!argc) { 195 sp = -1; /* stack pointer initialized */ 196 fp = 0; /* frame pointer initialized */ 197 infile[0] = stdin; /* default input (naturally) */ 198 macro(); 199 } else 200 for (; argc--; ++argv) { 201 p = *argv; 202 if (p[0] == '-' && p[1] == '\0') 203 ifp = stdin; 204 else if ((ifp = fopen(p, "r")) == NULL) 205 err(1, "%s", p); 206 sp = -1; 207 fp = 0; 208 infile[0] = ifp; 209 macro(); 210 if (ifp != stdin) 211 (void)fclose(ifp); 212 } 213 214 if (*m4wraps) { /* anything for rundown ?? */ 215 ilevel = 0; /* in case m4wrap includes.. */ 216 bufbase = bp = buf; /* use the entire buffer */ 217 putback(EOF); /* eof is a must !! */ 218 pbstr(m4wraps); /* user-defined wrapup act */ 219 macro(); /* last will and testament */ 220 } 221 222 if (active != stdout) 223 active = stdout; /* reset output just in case */ 224 for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */ 225 if (outfile[n] != NULL) 226 getdiv(n); 227 /* remove bitbucket if used */ 228 cleanup(0); 229 return 0; 230 } 231 232 ndptr inspect(); 233 234 /* 235 * macro - the work horse.. 236 */ 237 void 238 macro() { 239 char token[MAXTOK]; 240 register char *s; 241 register int t, l; 242 register ndptr p; 243 register int nlpar; 244 245 cycle { 246 if ((t = gpbc()) == '_' || (t != EOF && isalpha(t))) { 247 putback(t); 248 if ((p = inspect(s = token)) == nil) { 249 if (sp < 0) 250 while (*s) 251 putc(*s++, active); 252 else 253 while (*s) 254 chrsave(*s++); 255 } 256 else { 257 /* 258 * real thing.. First build a call frame: 259 */ 260 pushf(fp); /* previous call frm */ 261 pushf(p->type); /* type of the call */ 262 pushf(0); /* parenthesis level */ 263 fp = sp; /* new frame pointer */ 264 /* 265 * now push the string arguments: 266 */ 267 pushs(p->defn); /* defn string */ 268 pushs(p->name); /* macro name */ 269 pushs(ep); /* start next..*/ 270 271 putback(l = gpbc()); 272 if (l != LPAREN) { /* add bracks */ 273 putback(RPAREN); 274 putback(LPAREN); 275 } 276 } 277 } 278 else if (t == EOF) { 279 if (sp > -1) 280 errx(1, "unexpected end of input"); 281 if (ilevel <= 0) 282 break; /* all done thanks.. */ 283 --ilevel; 284 (void) fclose(infile[ilevel+1]); 285 bufbase = bbase[ilevel]; 286 continue; 287 } 288 /* 289 * non-alpha single-char token seen.. 290 * [the order of else if .. stmts is important.] 291 */ 292 else if (t == lquote) { /* strip quotes */ 293 nlpar = 1; 294 do { 295 if ((l = gpbc()) == rquote) 296 nlpar--; 297 else if (l == lquote) 298 nlpar++; 299 else if (l == EOF) 300 errx(1, "missing right quote"); 301 if (nlpar > 0) { 302 if (sp < 0) 303 putc(l, active); 304 else 305 chrsave(l); 306 } 307 } 308 while (nlpar != 0); 309 } 310 311 else if (sp < 0) { /* not in a macro at all */ 312 if (t == scommt) { /* comment handling here */ 313 putc(t, active); 314 while ((t = gpbc()) != ecommt) 315 putc(t, active); 316 } 317 putc(t, active); /* output directly.. */ 318 } 319 320 else switch(t) { 321 322 case LPAREN: 323 if (PARLEV > 0) 324 chrsave(t); 325 while ((l = gpbc()) != EOF && isspace(l)) 326 ; /* skip blank, tab, nl.. */ 327 putback(l); 328 PARLEV++; 329 break; 330 331 case RPAREN: 332 if (--PARLEV > 0) 333 chrsave(t); 334 else { /* end of argument list */ 335 chrsave(EOS); 336 337 if (sp == STACKMAX) 338 errx(1, "internal stack overflow"); 339 340 if (CALTYP == MACRTYPE) 341 expand((char **) mstack+fp+1, sp-fp); 342 else 343 eval((char **) mstack+fp+1, sp-fp, CALTYP); 344 345 ep = PREVEP; /* flush strspace */ 346 sp = PREVSP; /* previous sp.. */ 347 fp = PREVFP; /* rewind stack...*/ 348 } 349 break; 350 351 case COMMA: 352 if (PARLEV == 1) { 353 chrsave(EOS); /* new argument */ 354 while ((l = gpbc()) != EOF && isspace(l)) 355 ; 356 putback(l); 357 pushs(ep); 358 } else 359 chrsave(t); 360 break; 361 362 default: 363 chrsave(t); /* stack the char */ 364 break; 365 } 366 } 367 } 368 369 /* 370 * build an input token.. 371 * consider only those starting with _ or A-Za-z. This is a 372 * combo with lookup to speed things up. 373 */ 374 ndptr 375 inspect(tp) 376 register char *tp; 377 { 378 register int c; 379 register char *name = tp; 380 register char *etp = tp+MAXTOK; 381 register ndptr p; 382 register unsigned long h = 0; 383 384 while ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp) 385 h = (h << 5) + h + (*tp++ = c); 386 putback(c); 387 if (tp == etp) 388 errx(1, "token too long"); 389 390 *tp = EOS; 391 392 for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) 393 if (STREQ(name, p->name)) 394 break; 395 return p; 396 } 397 398 /* 399 * initkwds - initialise m4 keywords as fast as possible. 400 * This very similar to install, but without certain overheads, 401 * such as calling lookup. Malloc is not used for storing the 402 * keyword strings, since we simply use the static pointers 403 * within keywrds block. 404 */ 405 void 406 initkwds() { 407 register int i; 408 register int h; 409 register ndptr p; 410 411 for (i = 0; i < MAXKEYS; i++) { 412 h = hash(keywrds[i].knam); 413 if ((p = malloc(sizeof(struct ndblock))) == NULL) 414 err(1, "malloc"); 415 p->nxtptr = hashtab[h]; 416 hashtab[h] = p; 417 p->name = keywrds[i].knam; 418 p->defn = null; 419 p->type = keywrds[i].ktyp | STATIC; 420 } 421 } 422