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