1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * 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 sccsid[] = "@(#)main.c 5.9 (Berkeley) 11/20/91"; 39 #endif /* not lint */ 40 41 /* 42 * main.c 43 * Facility: m4 macro processor 44 * by: oz 45 */ 46 47 #include <sys/types.h> 48 #include <signal.h> 49 #include <errno.h> 50 #include <unistd.h> 51 #include <stdio.h> 52 #include <ctype.h> 53 #include <string.h> 54 #include "mdef.h" 55 #include "stdd.h" 56 #include "extern.h" 57 #include "pathnames.h" 58 59 ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ 60 char buf[BUFSIZE]; /* push-back buffer */ 61 char *bufbase = buf; /* the base for current ilevel */ 62 char *bbase[MAXINP]; /* the base for each ilevel */ 63 char *bp = buf; /* first available character */ 64 char *endpbb = buf+BUFSIZE; /* end of push-back buffer */ 65 stae mstack[STACKMAX+1]; /* stack of m4 machine */ 66 char strspace[STRSPMAX+1]; /* string space for evaluation */ 67 char *ep = strspace; /* first free char in strspace */ 68 char *endest= strspace+STRSPMAX;/* end of string space */ 69 int sp; /* current m4 stack pointer */ 70 int fp; /* m4 call frame pointer */ 71 FILE *infile[MAXINP]; /* input file stack (0=stdin) */ 72 FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/ 73 FILE *active; /* active output file pointer */ 74 char *m4temp; /* filename for diversions */ 75 int ilevel = 0; /* input file stack pointer */ 76 int oindex = 0; /* diversion index.. */ 77 char *null = ""; /* as it says.. just a null.. */ 78 char *m4wraps = ""; /* m4wrap string default.. */ 79 char *progname; /* name of this program */ 80 char lquote = LQUOTE; /* left quote character (`) */ 81 char rquote = RQUOTE; /* right quote character (') */ 82 char scommt = SCOMMT; /* start character for comment */ 83 char ecommt = ECOMMT; /* end character for comment */ 84 85 struct keyblk keywrds[] = { /* m4 keywords to be installed */ 86 "include", INCLTYPE, 87 "sinclude", SINCTYPE, 88 "define", DEFITYPE, 89 "defn", DEFNTYPE, 90 "divert", DIVRTYPE, 91 "expr", EXPRTYPE, 92 "eval", EXPRTYPE, 93 "substr", SUBSTYPE, 94 "ifelse", IFELTYPE, 95 "ifdef", IFDFTYPE, 96 "len", LENGTYPE, 97 "incr", INCRTYPE, 98 "decr", DECRTYPE, 99 "dnl", DNLNTYPE, 100 "changequote", CHNQTYPE, 101 "changecom", CHNCTYPE, 102 "index", INDXTYPE, 103 #ifdef EXTENDED 104 "paste", PASTTYPE, 105 "spaste", SPASTYPE, 106 #endif 107 "popdef", POPDTYPE, 108 "pushdef", PUSDTYPE, 109 "dumpdef", DUMPTYPE, 110 "shift", SHIFTYPE, 111 "translit", TRNLTYPE, 112 "undefine", UNDFTYPE, 113 "undivert", UNDVTYPE, 114 "divnum", DIVNTYPE, 115 "maketemp", MKTMTYPE, 116 "errprint", ERRPTYPE, 117 "m4wrap", M4WRTYPE, 118 "m4exit", EXITTYPE, 119 "syscmd", SYSCTYPE, 120 "sysval", SYSVTYPE, 121 122 #ifdef unix 123 "unix", MACRTYPE, 124 #else 125 #ifdef vms 126 "vms", MACRTYPE, 127 #endif 128 #endif 129 }; 130 131 #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) 132 133 extern int optind; 134 extern char *optarg; 135 136 void macro(); 137 void initkwds(); 138 extern int getopt(); 139 140 int 141 main(argc,argv) 142 int argc; 143 char *argv[]; 144 { 145 register int c; 146 register int n; 147 char *p; 148 register FILE *ifp; 149 150 progname = basename(argv[0]); 151 152 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 153 signal(SIGINT, onintr); 154 155 initkwds(); 156 157 while ((c = getopt(argc, argv, "tD:U:o:")) != EOF) 158 switch(c) { 159 160 case 'D': /* define something..*/ 161 for (p = optarg; *p; p++) 162 if (*p == '=') 163 break; 164 if (*p) 165 *p++ = EOS; 166 dodefine(optarg, p); 167 break; 168 case 'U': /* undefine... */ 169 remhash(optarg, TOP); 170 break; 171 case 'o': /* specific output */ 172 case '?': 173 usage(); 174 } 175 176 argc -= optind; 177 argv += optind; 178 179 active = stdout; /* default active output */ 180 /* filename for diversions */ 181 m4temp = mktemp(xstrdup(_PATH_DIVNAME)); 182 183 bbase[0] = bufbase; 184 if (!argc) { 185 sp = -1; /* stack pointer initialized */ 186 fp = 0; /* frame pointer initialized */ 187 infile[0] = stdin; /* default input (naturally) */ 188 macro(); 189 } 190 else 191 while (argc--) { 192 if ((ifp = fopen(*argv, "r")) == NULL) 193 oops("%s: %s", *argv, strerror(errno)); 194 else { 195 sp = -1; 196 fp = 0; 197 infile[0] = ifp; 198 macro(); 199 (void) fclose(ifp); 200 } 201 argv++; 202 } 203 204 205 if (*m4wraps) { /* anything for rundown ?? */ 206 ilevel = 0; /* in case m4wrap includes.. */ 207 bufbase = bp = buf; /* use the entire buffer */ 208 putback(EOF); /* eof is a must !! */ 209 pbstr(m4wraps); /* user-defined wrapup act */ 210 macro(); /* last will and testament */ 211 } 212 213 if (active != stdout) 214 active = stdout; /* reset output just in case */ 215 for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */ 216 if (outfile[n] != NULL) 217 getdiv(n); 218 /* remove bitbucket if used */ 219 if (outfile[0] != NULL) { 220 (void) fclose(outfile[0]); 221 m4temp[UNIQUE] = '0'; 222 #ifdef vms 223 (void) remove(m4temp); 224 #else 225 (void) unlink(m4temp); 226 #endif 227 } 228 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()) == '_' || 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 oops("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 oops("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 (isspace(l = gpbc())) 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 oops("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 (isspace(l = gpbc())) 355 ; 356 putback(l); 357 pushs(ep); 358 } 359 break; 360 default: 361 chrsave(t); /* stack the char */ 362 break; 363 } 364 } 365 } 366 367 /* 368 * build an input token.. 369 * consider only those starting with _ or A-Za-z. This is a 370 * combo with lookup to speed things up. 371 */ 372 ndptr 373 inspect(tp) 374 register char *tp; 375 { 376 register char c; 377 register char *name = tp; 378 register char *etp = tp+MAXTOK; 379 register ndptr p; 380 register unsigned long h = 0; 381 382 while ((isalnum(c = gpbc()) || c == '_') && tp < etp) 383 h = (h << 5) + h + (*tp++ = c); 384 putback(c); 385 if (tp == etp) 386 oops("token too long", ""); 387 388 *tp = EOS; 389 390 for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) 391 if (STREQ(name, p->name)) 392 break; 393 return p; 394 } 395 396 /* 397 * initkwds - initialise m4 keywords as fast as possible. 398 * This very similar to install, but without certain overheads, 399 * such as calling lookup. Malloc is not used for storing the 400 * keyword strings, since we simply use the static pointers 401 * within keywrds block. 402 */ 403 void 404 initkwds() { 405 register int i; 406 register int h; 407 register ndptr p; 408 409 for (i = 0; i < MAXKEYS; i++) { 410 h = hash(keywrds[i].knam); 411 p = (ndptr) xalloc(sizeof(struct ndblock)); 412 p->nxtptr = hashtab[h]; 413 hashtab[h] = p; 414 p->name = keywrds[i].knam; 415 p->defn = null; 416 p->type = keywrds[i].ktyp | STATIC; 417 } 418 } 419