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