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