+#include <stdio.h>
+#include <signal.h>
+
+#define ERROR NULL
+#define READ "r"
+#define WRITE "w"
+
+#define EOS 0
+int lpar = '(';
+#define LPAR lpar
+#define RPAR ')'
+#define COMMA ','
+#define GRAVE '`'
+#define ACUTE '\''
+#define LBRAK '['
+#define RBRAK ']'
+#ifdef M4
+char lquote LBRAK;
+char rquote RBRAK;
+#endif
+#ifndef M4
+char lquote = GRAVE;
+char rquote = ACUTE;
+#endif
+#define COMMENT '#'
+#define ALPH 1
+#define DIG 2
+
+#define HSHSIZ 199 /* prime */
+#define STACKS 50
+#define SAVS 4096
+#define TOKS 128
+
+#define putbak(c) *ip++ = c;
+#define getchr() (ip>cur_ip?*--ip: getc(infile[infptr]))
+#define putchr(c) if (cp==NULL) {if (curfile)putc(c,curfile);} 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;
+};
+
+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 = NULL;
+
+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 *tempname;
+struct nlist *lookup();
+char *install();
+char *malloc();
+char *mktemp();
+char *copy();
+long ctol();
+int hshval;
+FILE *olist[11] = { stdout };
+int okret;
+int curout = 0;
+FILE *curfile = { stdout };
+FILE *infile[10] = { stdin };
+int infptr = 0;
+
+main(argc, argv)
+char **argv;
+{
+ char *argstk[STACKS+10];
+ struct call callst[STACKS];
+ register char *tp, **ap;
+ int delexit(), catchsig();
+ register t;
+ int i;
+
+#ifdef gcos
+#ifdef M4
+ install("GCOS", eoa);
+#endif
+#ifndef M4
+ install("gcos", eoa);
+#endif
+#endif
+#ifdef unix
+#ifdef M4
+ install("UNIX", eoa);
+#endif
+#ifndef M4
+ install("unix", eoa);
+#endif
+#endif
+
+#ifdef M4
+ 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);
+#endif
+
+#ifndef M4
+ 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);
+#endif
+ ap = argstk;
+#ifndef gcos
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ signal(SIGHUP, catchsig);
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, catchsig);
+ tempname = mktemp("/tmp/m4aXXXXX");
+ close(creat(tempname, 0));
+#endif
+#ifdef gcos
+ tempname = "m4.tempa";
+#endif
+ if (argc>1)
+ putbak(0);
+ for (;;) {
+ tp = token;
+ *tp++ = t = getchr();
+ *tp = EOS;
+ if (t<=0) {
+ if (infptr > 0) {
+ fclose(infile[infptr]);
+ infptr--;
+ cur_ip = ip_stk[infptr];
+ continue;
+ }
+ if (argc<=1)
+ break;
+ argc--;
+ argv++;
+ if (infile[infptr]!=stdin)
+ fclose(infile[infptr]);
+ if (**argv=='-')
+ infile[infptr] = stdin;
+ else if ((infile[infptr]=fopen(argv[0], READ))==ERROR) {
+ fprintf(stderr, "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 = EOS;
+ if (*ap = lookup(token)->def) {
+ if (++ap >= &argstk[STACKS]) {
+ fprintf(stderr, "m4: arg stack overflow\n");
+ delexit();
+ }
+ if (cp==NULL)
+ cp = callst;
+ else if (++cp > &callst[STACKS]) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "m4: EOF in string\n");
+ delexit();
+ }
+ putchr(t);
+ }
+ } else if (t==COMMENT) {
+ putbak(t);
+ while ((t = getchr())!='\n'&& t>=0)
+ if (cp==NULL)
+ putchr(t);
+ putbak(t);
+ } else if (cp==NULL) {
+ 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 = NULL;
+ } 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!=NULL) {
+ fprintf(stderr, "m4: unexpected EOF\n");
+ delexit();
+ }
+ okret = 1;
+ delexit();
+}
+
+catchsig()
+{
+ okret = 0;
+ delexit();
+}
+
+delexit()
+{
+ register FILE *fp;
+ register i, c;
+
+ if (!okret) {
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ }
+ for (i=1; i<10; i++) {
+ if (olist[i]==NULL)
+ continue;
+ fclose(olist[i]);
+ tempname[7] = 'a'+i;
+ if (okret) {
+ fp = fopen(tempname, READ);
+ while ((c = getc(fp)) > 0)
+ putchar(c);
+ fclose(fp);
+ }
+ unlink(tempname);
+ }
+ tempname[7] = 'a';
+ unlink(tempname);
+ exit(1-okret);
+}
+
+puttok()
+{
+ register char *tp;
+
+ tp = token;
+ if (cp) {
+ if (op >= &obuf[SAVS]) {
+ fprintf(stderr, "m4: argument overflow\n");
+ delexit();
+ }
+ while (*tp)
+ *op++ = *tp++;
+ } else if (curfile)
+ while (*tp)
+ putc(*tp++, curfile);
+}
+
+pbstr(str)
+register char *str;
+{
+ register char *p;
+
+ p = str;
+ while (*p++);
+ --p;
+ if (ip >= &ibuf[SAVS]) {
+ fprintf(stderr, "m4: pushback overflow\n");
+ delexit();
+ }
+ while (p > str)
+ putbak(*--p);
+}
+
+expand(a1, c)
+register char **a1;
+{
+ register char *dp;
+ register 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(str)
+char *str;
+{
+ register char *s1, *s2;
+ register struct nlist *np;
+ static struct nlist nodef;
+
+ s1 = str;
+ for (hshval = 0; *s1; )
+ hshval += *s1++;
+ hshval %= HSHSIZ;
+ for (np = hshtab[hshval]; np!=NULL; np = np->next) {
+ s1 = str;
+ s2 = np->name;
+ while (*s1++ == *s2)
+ if (*s2++ == EOS)
+ return(np);
+ }
+ return(&nodef);
+}
+
+char *install(nam, val)
+char *nam, *val;
+{
+ register struct nlist *np;
+
+ if ((np = lookup(nam))->name == NULL) {
+ np = (struct nlist *)malloc(sizeof(*np));
+ if (np == NULL) {
+ fprintf(stderr, "m4: no space for alloc\n");
+ exit(1);
+ }
+ np->name = copy(nam);
+ np->def = copy(val);
+ np->next = hshtab[hshval];
+ hshtab[hshval] = np;
+ return(np->def);
+ }
+ free(np->def);
+ np->def = copy(val);
+ return(np->def);
+}
+
+doundef(ap, c)
+char **ap;
+{
+ register struct nlist *np, *tnp;
+
+ if (c < 1 || (np = lookup(ap[1]))->name == NULL)
+ 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);
+}
+
+char *copy(s)
+register char *s;
+{
+ register char *p, *s1;
+
+ p = s1 = malloc((unsigned)strlen(s)+1);
+ if (p == NULL) {
+ fprintf(stderr, "m4: no space for alloc\n");
+ exit(1);
+ }
+ while (*s1++ = *s++);
+ return(p);
+}
+
+dodef(ap, c)
+char **ap;
+{
+ if (c >= 2) {
+ if (strcmp(ap[1], ap[2]) == 0) {
+ fprintf(stderr, "m4: %s defined as itself\n", ap[1]);
+ delexit();
+ }
+ install(ap[1], ap[2]);
+ }
+ else if (c == 1)
+ install(ap[1], "");
+}
+
+doifdef(ap, c)
+char **ap;
+{
+ register struct nlist *np;
+
+ if (c < 2)
+ return;
+ if (lookup(ap[1])->name != NULL)
+ pbstr(ap[2]);
+ else if (c >= 3)
+ pbstr(ap[3]);
+}
+
+dolen(ap, c)
+char **ap;
+{
+ putnum((long) strlen(ap[1]));
+}
+
+docq(ap, c)
+char **ap;
+{
+ if (c > 1) {
+ lquote = *ap[1];
+ rquote = *ap[2];
+ } else if (c == 1) {
+ lquote = rquote = *ap[1];
+ } else {
+#ifndef M4
+ lquote = GRAVE;
+ rquote = ACUTE;
+#endif
+#ifdef M4
+ lquote = LBRAK;
+ rquote = RBRAK;
+#endif
+ }
+}
+
+doshift(ap, c)
+char **ap;
+{
+ fprintf(stderr, "m4: shift not yet implemented\n");
+}
+
+dodump(ap, c)
+char **ap;
+{
+ int i;
+ register struct nlist *np;
+
+ if (c > 0)
+ while (c--) {
+ if ((np = lookup(*++ap))->name != NULL)
+ fprintf(stderr, "`%s' `%s'\n", np->name, np->def);
+ }
+ else
+ for (i=0; i<HSHSIZ; i++)
+ for (np=hshtab[i]; np!=NULL; np=np->next)
+ fprintf(stderr, "`%s' `%s'\n", np->name, np->def);
+}
+
+doerrp(ap, c)
+char **ap;
+{
+ if (c > 0) {
+ fprintf(stderr, ap[1], ap[2], ap[3], ap[4], ap[5], ap[6]);
+ fprintf(stderr, "\n");
+ }
+}
+
+
+long evalval; /* return value from yacc stuff */
+char *pe; /* used by grammar */
+
+doeval(ap, c)
+char **ap;
+{
+
+ if (c > 0) {
+ pe = ap[1];
+ if (yyparse() == 0)
+ putnum(evalval);
+ else
+ fprintf(stderr, "m4: invalid expression in eval: %s\n", ap[1]);
+ }
+}
+
+doincl(ap, c, noisy)
+char **ap;
+{
+ if (c > 0 && strlen(ap[1]) > 0) {
+ infptr++;
+ ip_stk[infptr] = cur_ip = ip;
+ if ((infile[infptr] = fopen(ap[1], READ))==ERROR) {
+ if (noisy) {
+ fprintf(stderr, "m4: file not found: %s\n", ap[1]);
+ delexit();
+ }
+ else
+ infptr--;
+ }
+ }
+}
+
+dosyscmd(ap, c)
+char **ap;
+{
+ if (c > 0)
+ system(ap[1]);
+}
+
+domake(ap, c)
+char **ap;
+{
+ if (c > 0)
+ pbstr(mktemp(ap[1]));
+}
+
+doincr(ap, c)
+char **ap;
+{
+ if (c >= 1)
+ putnum(ctol(ap[1])+1);
+}
+
+putnum(num)
+long num;
+{
+ register sign;
+
+ sign = (num < 0) ? '-' : '\0';
+ if (num < 0)
+ num = -num;
+ do {
+ putbak(num%10+'0');
+ num = num/10;
+ } while (num!=0);
+ if (sign == '-')
+ putbak('-');
+}
+
+dosubstr(ap, c)
+char **ap;
+{
+ int nc;
+ register 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);
+}
+
+doindex(ap, c)
+char **ap;
+{
+ if (c >= 2)
+ putnum((long) strindex(ap[1], ap[2]));
+}
+
+strindex(p1, p2)
+char *p1, *p2;
+{
+ register m;
+ register 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);
+}
+
+dotransl(ap, c)
+char **ap;
+{
+ register char *s, *fr, *to;
+
+ if (c <= 1) return;
+
+ if (c == 2) {
+ register 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]);
+}
+
+doif(ap, c)
+register char **ap;
+{
+ 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]);
+}
+
+dodiv(ap, c)
+register char **ap;
+{
+ register int f;
+
+ if (c<1)
+ f = 0;
+ else
+ f = ctoi(ap[1]);
+ if (f>=10 || f<0) {
+ curfile = NULL;
+ return;
+ }
+ tempname[7] = 'a' + f;
+ if (olist[f] || (olist[f]=fopen(tempname, WRITE))) {
+ curout = f;
+ curfile = olist[f];
+ }
+}
+
+doundiv(ap, c)
+char **ap;
+{
+ register FILE *fp;
+ register int i, ch;
+ int j;
+
+ if (c == 0) {
+ for (i=1; i<10; i++) {
+ if (i==curout || olist[i]==NULL)
+ continue;
+ fclose(olist[i]);
+ tempname[7] = 'a'+i;
+ fp = fopen(tempname, READ);
+ if (curfile != NULL)
+ while ((ch = getc(fp)) > 0)
+ putc(ch, curfile);
+ fclose(fp);
+ unlink(tempname);
+ olist[i] = NULL;
+ }
+
+ }
+ else {
+ for (j = 1; j <= c; j++) {
+ i = ctoi(*++ap);
+ if (i<1 || i>9 || i==curout || olist[i]==NULL)
+ continue;
+ fclose(olist[i]);
+ tempname[7] = 'a'+i;
+ fp = fopen(tempname, READ);
+ if (curfile != NULL)
+ while ((ch = getc(fp)) > 0)
+ putc(ch, curfile);
+ fclose(fp);
+ unlink(tempname);
+ olist[i] = NULL;
+ }
+ }
+}
+
+dodivnum(ap, c)
+char **ap;
+{
+ putnum((long) curout);
+}
+
+dodnl(ap, c)
+char **ap;
+{
+ register t;
+
+ while ((t=getchr())!='\n' && t>=0)
+ ;
+}
+
+long ctol(str)
+register char *str;
+{
+ register 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);
+}
+
+ctoi(s)
+char *s;
+{
+ return(ctol(s));
+}
+
+min(a, b)
+{
+ if (a>b)
+ return(b);
+ return(a);
+}
+
+max(a, b)
+{
+ if (a>b)
+ return(a);
+ return(b);
+}