#include #include #include int lpar = '('; #define LPAR lpar #define RPAR ')' #define COMMA ',' #define GRAVE '`' #define ACUTE '\'' #define LBRAK '[' #define RBRAK ']' char lquote = GRAVE; char rquote = ACUTE; #define COMMENT '#' #define ALPH 1 #define DIG 2 #define HSHSIZ 2003 /* prime */ #define STACKS 500 #define SAVS 40960 #define TOKS 1280 #define putbak(c) *ip++ = c; #define getchr() (ip>cur_ip?*--ip: Bgetc(infile[infptr])) #define putchr(c) if (cp==nil) {if (curfile)Bputc(curfile, c);} else *op++ = c char type[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, 0, 0, 0, 0, 0, 0, 0, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, 0, 0, 0, 0, ALPH, 0, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, 0, 0, 0, 0, 0, }; char token[TOKS]; char eoa[] = "\0"; struct nlist { char *name; char *def; struct nlist *next; }; long evalval; /* return value from yacc stuff */ char *pe; /* used by grammar */ struct nlist *hshtab[HSHSIZ]; char ibuf[SAVS+TOKS]; char obuf[SAVS+TOKS]; char *op = obuf; char *ip = ibuf; char *ip_stk[10] = {ibuf}; char *cur_ip = ibuf; struct call { char **argp; int plev; }; struct call *cp = nil; char *makeloc; char *ifdefloc; char *lenloc; char *undefloc; char *shiftloc; char *cqloc; char *defloc; char *evaloc; char *incrloc; char *substrloc; char *indexloc; char *transloc; char *ifloc; char *divloc; char *divnumloc; char *undivloc; char *dnlloc; char *inclloc; char *sinclloc; char *syscmdloc; char *dumploc; char *errploc; char *tmp_nam; int hshval; Biobuf *olist[11]; int okret; int curout = 0; Biobuf *curfile; Biobuf *infile[10]; int infptr = 0; extern int yyparse(void); void notifyf(void *, char *); void delexit(void); void puttok(void); void pbstr(char *); void expand(char **, int); struct nlist *lookup(char *); char *install(char *, char *); void doundef(char **, int); void dodef(char **, int); void doifdef(char **, int); void dolen(char **, int); void docq(char **, int); void doshift(char **, int); void dodump(char **, int); void doerrp(char **, int); void doeval(char **, int); void doincl(void **, int, int); void dosyscmd(char **, int); void domake(char **, int); void doincr(char **, int); void putnum(long); void dosubstr(char **, int); void doindex(char **, int); int strindex(char *, char *); void dotransl(char **, int ); void doif(char **, int); void dodiv(char **, int); void doundiv(char **, int); void dodivnum(char **, int); void dodnl(char **, int); long ctol(char *); int ctoi(char *); int min(int, int); int max(int, int); void main(int argc, char *argv[]) { Biobuf bin, bout; char *argstk[STACKS+10]; struct call callst[STACKS]; char *tp, **ap; int t, i; Binit(&bin, 0, OREAD); Binit(&bout, 1, OWRITE); curfile = &bout; *olist = &bout; *infile = &bin; install("plan9", eoa); makeloc = install("maketemp", eoa); ifdefloc = install("ifdef", eoa); lenloc = install("len", eoa); undefloc = install("undefine", eoa); shiftloc = install("shift", eoa); cqloc = install("changequote", eoa); defloc = install("define", eoa); evaloc = install("eval", eoa); inclloc = install("include", eoa); sinclloc = install("sinclude", eoa); syscmdloc = install("syscmd", eoa); dumploc = install("dumpdef", eoa); errploc = install("errprint", eoa); incrloc = install("incr", eoa); substrloc = install("substr", eoa); indexloc = install("index", eoa); transloc = install("translit", eoa); ifloc = install("ifelse", eoa); divloc = install("divert", eoa); divnumloc = install("divnum", eoa); undivloc = install("undivert", eoa); dnlloc = install("dnl", eoa); ap = argstk; notify(notifyf); tmp_nam = mktemp("/tmp/m4aXXXXX"); close(create(tmp_nam, 0, 0644)); if (argc>1) putbak(0); for (;;) { tp = token; *tp++ = t = getchr(); *tp = 0; if (t<=0) { if (infptr > 0) { Bterm(infile[infptr]); infptr--; cur_ip = ip_stk[infptr]; continue; } if (argc<=1) break; argc--; argv++; if (infile[infptr] != &bin) Bterm(infile[infptr]); if (**argv=='-') infile[infptr] = &bin; else if ((infile[infptr] = Bopen(argv[0], OREAD)) == nil) { fprint(2, "m4: file not found: %s\n", argv[0]); delexit(); } continue; } if (type[t]==ALPH) { while ((t=type[*tp++=getchr()])==ALPH||t==DIG); putbak(*--tp); *tp = 0; if (*ap = lookup(token)->def) { if (++ap >= &argstk[STACKS]) { fprint(2, "m4: arg stack overflow\n"); delexit(); } if (cp==nil) cp = callst; else if (++cp > &callst[STACKS]) { fprint(2, "m4: call stack overflow\n"); delexit(); } cp->argp = ap; *ap++ = op; puttok(); *op++ = '\0'; t = getchr(); putbak(t); if (t!=LPAR) { /* if (t!=' ' && t!='\t') */ putbak(')'); putbak('('); } else /* try to fix arg count */ *ap++ = op; cp->plev = 0; } else puttok(); } else if (t==lquote) { i = 1; for (;;) { t = getchr(); if (t==rquote) { i--; if (i==0) break; } else if (t==lquote) i++; else if (t<0) { fprint(2, "m4: EOF in string\n"); delexit(); } putchr(t); } } else if (t==COMMENT) { putbak(t); while ((t = getchr())!='\n'&& t>=0) if (cp==nil) putchr(t); putbak(t); } else if (cp==nil) { puttok(); } else if (t==LPAR) { if (cp->plev) *op++ = t; cp->plev++; while ( (t=getchr())==' ' || t=='\t' || t=='\n') ; /* skip leading white space during arg collection */ putbak(t); /* } else if (t==' ' || t=='\t' || t=='\n') { continue; */ } else if (t==RPAR) { cp->plev--; if (cp->plev==0) { *op++ = '\0'; expand(cp->argp, ap-cp->argp-1); op = *cp->argp; ap = cp->argp-1; cp--; if (cp < callst) cp = nil; } else *op++ = t; } else if (t==COMMA && cp->plev<=1) { *op++ = '\0'; *ap++ = op; while ((t=getchr())==' ' || t=='\t' || t=='\n') ; /* skip leading white space during arg collection */ putbak(t); } else *op++ = t; } if (cp!=nil) { fprint(2, "m4: unexpected EOF\n"); delexit(); } okret = 1; delexit(); } void notifyf(void *a, char *s) { USED(a, s); delexit(); } void delexit(void) { Biobuf *bp; int i, c; for (i=1; i<10; i++) { if (olist[i]==nil) continue; Bterm(olist[i]); tmp_nam[7] = 'a'+i; if (okret) { bp = Bopen(tmp_nam, OREAD); while ((c = Bgetc(bp)) > 0) Bputc(curfile, c); Bterm(bp); } remove(tmp_nam); } tmp_nam[7] = 'a'; remove(tmp_nam); exits((1-okret)? "error": 0); } void puttok(void) { char *tp; tp = token; if (cp) { if (op >= &obuf[SAVS]) { fprint(2, "m4: argument overflow\n"); delexit(); } while (*tp) *op++ = *tp++; } else if (curfile) while (*tp) Bputc(curfile, *tp++); } void pbstr(char *str) { char *p; p = str; while (*p++); --p; if (ip >= &ibuf[SAVS]) { fprint(2, "m4: pushback overflow\n"); delexit(); } while (p > str) putbak(*--p); } void expand(char **a1, int c) { char *dp; int n; dp = a1[-1]; if (dp==defloc) dodef(a1, c); else if (dp==evaloc) doeval(a1, c); else if (dp==inclloc) doincl(a1, c, 1); else if (dp==sinclloc) doincl(a1, c, 0); else if (dp==makeloc) domake(a1, c); else if (dp==syscmdloc) dosyscmd(a1, c); else if (dp==incrloc) doincr(a1, c); else if (dp==substrloc) dosubstr(a1, c); else if (dp==indexloc) doindex(a1, c); else if (dp==transloc) dotransl(a1, c); else if (dp==ifloc) doif(a1, c); else if (dp==divloc) dodiv(a1, c); else if (dp==divnumloc) dodivnum(a1, c); else if (dp==undivloc) doundiv(a1, c); else if (dp==dnlloc) dodnl(a1, c); else if (dp==dumploc) dodump(a1, c); else if (dp==errploc) doerrp(a1, c); else if (dp==lenloc) dolen(a1, c); else if (dp==ifdefloc) doifdef(a1, c); else if (dp==undefloc) doundef(a1, c); else if (dp==shiftloc) doshift(a1, c); else if (dp==cqloc) docq(a1, c); else { while (*dp++); for (dp--; dp>a1[-1]; ) { if (--dp>a1[-1] && dp[-1]=='$') { n = *dp-'0'; if (n>=0 && n<=9) { if (n <= c) pbstr(a1[n]); dp--; } else putbak(*dp); } else putbak(*dp); } } } struct nlist * lookup(char *str) { char *s1, *s2; struct nlist *np; static struct nlist nodef; s1 = str; for (hshval = 0; *s1; ) hshval += *s1++; hshval %= HSHSIZ; for (np = hshtab[hshval]; np!=nil; np = np->next) { s1 = str; s2 = np->name; while (*s1++ == *s2) if (*s2++ == 0) return(np); } return(&nodef); } char * install(char *nam, char *val) { struct nlist *np; if ((np = lookup(nam))->name == nil) { np = (struct nlist *)malloc(sizeof(*np)); if (np == nil) { fprint(2, "m4: no space for alloc\n"); exits("no memory"); } np->name = strdup(nam); np->def = strdup(val); np->next = hshtab[hshval]; hshtab[hshval] = np; return(np->def); } free(np->def); np->def = strdup(val); return(np->def); } void doundef(char **ap, int c) { struct nlist *np, *tnp; if (c < 1 || (np = lookup(ap[1]))->name == nil) return; tnp = hshtab[hshval]; /* lookup sets hshval */ if (tnp == np) /* it's in first place */ hshtab[hshval] = np->next; else { for ( ; tnp->next != np; tnp = tnp->next) ; tnp->next = np->next; } free(np->name); free(np->def); free((char *)np); } void dodef(char **ap, int c) { if (c >= 2) { if (strcmp(ap[1], ap[2]) == 0) { fprint(2, "m4: %s defined as itself\n", ap[1]); delexit(); } install(ap[1], ap[2]); } else if (c == 1) install(ap[1], ""); } void doifdef(char **ap, int c) { if (c < 2) return; if (lookup(ap[1])->name != nil) pbstr(ap[2]); else if (c >= 3) pbstr(ap[3]); } void dolen(char **ap, int c) { USED(c); putnum((long) strlen(ap[1])); } void docq(char **ap, int c) { if (c > 1) { lquote = *ap[1]; rquote = *ap[2]; } else if (c == 1) { lquote = rquote = *ap[1]; } else { lquote = GRAVE; rquote = ACUTE; } } void doshift(char **ap, int c) { USED(ap, c); fprint(2, "m4: shift not yet implemented\n"); } void dodump(char **ap, int c) { int i; struct nlist *np; if (c > 0) while (c--) { if ((np = lookup(*++ap))->name != nil) fprint(2, "`%s' `%s'\n", np->name, np->def); } else for (i=0; inext) fprint(2, "`%s' `%s'\n", np->name, np->def); } void doerrp(char **ap, int c) { if (c > 0) { fprint(2, ap[1], ap[2], ap[3], ap[4], ap[5], ap[6]); fprint(2, "\n"); } } void doeval(char **ap, int c) { if (c > 0) { pe = ap[1]; if (yyparse() == 0) putnum(evalval); else fprint(2, "m4: invalid expression in eval: %s\n", ap[1]); } } void doincl(void **ap, int c, int noisy) { if (c > 0 && strlen(ap[1]) > 0) { infptr++; ip_stk[infptr] = cur_ip = ip; if ((infile[infptr] = Bopen(ap[1], OREAD)) ==nil) { if (noisy) { fprint(2, "m4: file not found: %s\n", ap[1]); delexit(); } else infptr--; } } } void dosyscmd(char **ap, int c) { if (c <= 0) return; if(fork() == 0) execl("/bin/rc", "rc", "-c", ap[1], 0); waitpid(); } void domake(char **ap, int c) { if (c > 0) pbstr(mktemp(ap[1])); } void doincr(char **ap, int c) { if (c >= 1) putnum(ctol(ap[1])+1); } void putnum(long num) { int sign; sign = (num < 0) ? '-' : '\0'; if (num < 0) num = -num; do { putbak(num % 10 + '0'); num = num / 10; } while (num != 0); if (sign == '-') putbak('-'); } void dosubstr(char **ap, int c) { int nc; char *sp, *fc; if (c < 2) return; if (c < 3) nc = TOKS; else nc = ctoi(ap[3]); fc = ap[1] + max(0, min(ctoi(ap[2]), strlen(ap[1]))); sp = fc + min(nc, strlen(fc)); while (sp > fc) putbak(*--sp); } void doindex(char **ap, int c) { if (c >= 2) putnum((long) strindex(ap[1], ap[2])); } int strindex(char *p1, char *p2) { int m; char *s, *t, *p; for (p = p1; *p; p++) { s = p; m = 1; for (t = p2; *t; ) if (*t++ != *s++) m = 0; if (m == 1) return(p-p1); } return(-1); } void dotransl(char **ap, int c) { char *s, *fr, *to; if (c <= 1) return; if (c == 2) { int i; to = ap[1]; for (s = ap[1]; *s; s++) { i = 0; for (fr = ap[2]; *fr; fr++) if (*s == *fr) { i++; break; } if (i == 0) *to++ = *s; } *to = '\0'; } if (c >= 3) { for (s = ap[1]; *s; s++) for (fr = ap[2], to = ap[3]; *fr && *to; fr++, to++) if (*s == *fr) *s = *to; } pbstr(ap[1]); } void doif(char **ap, int c) { if (c < 3) return; while (c >= 3) { if (strcmp(ap[1], ap[2]) == 0) { pbstr(ap[3]); return; } c -= 3; ap += 3; } if (c > 0) pbstr(ap[1]); } void dodiv(char **ap, int c) { int f; if (c<1) f = 0; else f = ctoi(ap[1]); if (f>=10 || f<0) { curfile = nil; return; } tmp_nam[7] = 'a' + f; if (olist[f] || (olist[f]=Bopen(tmp_nam, OWRITE))) { curout = f; curfile = olist[f]; } } void doundiv(char **ap, int c) { Biobuf *bp; int i, ch; int j; if (c == 0) { for (i=1; i<10; i++) { if (i==curout || olist[i]==nil) continue; Bterm(olist[i]); tmp_nam[7] = 'a'+i; bp = Bopen(tmp_nam, OREAD); if (curfile != nil) while ((ch = Bgetc(bp)) > 0) Bputc(curfile, ch); Bterm(bp); remove(tmp_nam); olist[i] = nil; } } else { for (j = 1; j <= c; j++) { i = ctoi(*++ap); if (i<1 || i>9 || i==curout || olist[i]==nil) continue; Bterm(olist[i]); tmp_nam[7] = 'a'+i; bp = Bopen(tmp_nam, OREAD); if (curfile != nil) while ((ch = Bgetc(bp)) > 0) Bputc(curfile, ch); Bterm(bp); remove(tmp_nam); olist[i] = nil; } } } void dodivnum(char **ap, int c) { USED(ap, c); putnum((long) curout); } void dodnl(char **ap, int c) { int t; USED(ap, c); while ((t=getchr())!='\n' && t>=0) continue; } long ctol(char *str) { int sign; long num; while (*str==' ' || *str=='\t' || *str=='\n') str++; num = 0; if (*str == '-') { sign = -1; str++; } else sign = 1; while (*str>='0' && *str<='9') num = num*10 + *str++ - '0'; return(sign * num); } int ctoi(char *s) { return(ctol(s)); } int min(int a, int b) { if (a>b) return(b); return(a); } int max(int a, int b) { if (a>b) return(a); return(b); }