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