1: #include <stdio.h>
2: #include <signal.h>
3:
4: #define ERROR NULL
5: #define READ "r"
6: #define WRITE "w"
7:
8: #define EOS 0
9: int lpar = '(';
10: #define LPAR lpar
11: #define RPAR ')'
12: #define COMMA ','
13: #define GRAVE '`'
14: #define ACUTE '\''
15: #define LBRAK '['
16: #define RBRAK ']'
17: #ifdef M4
18: char lquote LBRAK;
19: char rquote RBRAK;
20: #endif
21: #ifndef M4
22: char lquote = GRAVE;
23: char rquote = ACUTE;
24: #endif
25: #define '#'
26: #define ALPH 1
27: #define DIG 2
28:
29: #define HSHSIZ 199 /* prime */
30: #define STACKS 50
31: #define SAVS 4096
32: #define TOKS 128
33:
34: #define putbak(c) *ip++ = c;
35: #define getchr() (ip>cur_ip?*--ip: getc(infile[infptr]))
36: #define putchr(c) if (cp==NULL) {if (curfile)putc(c,curfile);} else *op++ = c
37: char type[] = {
38: 0, 0, 0, 0, 0, 0, 0, 0,
39: 0, 0, 0, 0, 0, 0, 0, 0,
40: 0, 0, 0, 0, 0, 0, 0, 0,
41: 0, 0, 0, 0, 0, 0, 0, 0,
42: 0, 0, 0, 0, 0, 0, 0, 0,
43: 0, 0, 0, 0, 0, 0, 0, 0,
44: DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG,
45: DIG, DIG, 0, 0, 0, 0, 0, 0,
46: 0, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH,
47: ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH,
48: ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH,
49: ALPH, ALPH, ALPH, 0, 0, 0, 0, ALPH,
50: 0, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH,
51: ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH,
52: ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH, ALPH,
53: ALPH, ALPH, ALPH, 0, 0, 0, 0, 0,
54: };
55:
56: char token[TOKS];
57: char eoa[] = "\0";
58: struct nlist {
59: char *name;
60: char *def;
61: struct nlist *next;
62: };
63:
64: struct nlist *hshtab[HSHSIZ];
65: char ibuf[SAVS+TOKS];
66: char obuf[SAVS+TOKS];
67: char *op = obuf;
68: char *ip = ibuf;
69: char *ip_stk[10] = {ibuf};
70: char *cur_ip = ibuf;
71: struct call {
72: char **argp;
73: int plev;
74: };
75: struct call *cp = NULL;
76:
77: char *makeloc;
78: char *ifdefloc;
79: char *lenloc;
80: char *undefloc;
81: char *shiftloc;
82: char *cqloc;
83: char *defloc;
84: char *evaloc;
85: char *incrloc;
86: char *substrloc;
87: char *indexloc;
88: char *transloc;
89: char *ifloc;
90: char *divloc;
91: char *divnumloc;
92: char *undivloc;
93: char *dnlloc;
94: char *inclloc;
95: char *sinclloc;
96: char *syscmdloc;
97: char *dumploc;
98: char *errploc;
99:
100: char *tempname;
101: struct nlist *lookup();
102: char *install();
103: char *malloc();
104: char *mktemp();
105: char *copy();
106: long ctol();
107: int hshval;
108: FILE *olist[11] = { stdout };
109: int okret;
110: int curout = 0;
111: FILE *curfile = { stdout };
112: FILE *infile[10] = { stdin };
113: int infptr = 0;
114:
115: main(argc, argv)
116: char **argv;
117: {
118: char *argstk[STACKS+10];
119: struct call callst[STACKS];
120: register char *tp, **ap;
121: int delexit(), catchsig();
122: register t;
123: int i;
124:
125: #ifdef gcos
126: #ifdef M4
127: install("GCOS", eoa);
128: #endif
129: #ifndef M4
130: install("gcos", eoa);
131: #endif
132: #endif
133: #ifdef unix
134: #ifdef M4
135: install("UNIX", eoa);
136: #endif
137: #ifndef M4
138: install("unix", eoa);
139: #endif
140: #endif
141:
142: #ifdef M4
143: makeloc = install("MAKETEMP", eoa);
144: ifdefloc = install("IFDEF", eoa);
145: lenloc = install("LEN", eoa);
146: undefloc = install("UNDEFINE", eoa);
147: shiftloc = install("SHIFT", eoa);
148: cqloc = install("CHANGEQUOTE", eoa);
149: defloc = install("DEFINE", eoa);
150: evaloc = install("EVAL", eoa);
151: inclloc = install("INCLUDE", eoa);
152: sinclloc = install("SINCLUDE", eoa);
153: syscmdloc = install("SYSCMD", eoa);
154: dumploc = install("DUMPDEF", eoa);
155: errploc = install("ERRPRINT", eoa);
156: incrloc = install("INCR", eoa);
157: substrloc = install("SUBSTR", eoa);
158: indexloc = install("INDEX", eoa);
159: transloc = install("TRANSLIT", eoa);
160: ifloc = install("IFELSE", eoa);
161: divloc = install("DIVERT", eoa);
162: divnumloc = install("DIVNUM", eoa);
163: undivloc = install("UNDIVERT", eoa);
164: dnlloc = install("DNL", eoa);
165: #endif
166:
167: #ifndef M4
168: makeloc = install("maketemp", eoa);
169: ifdefloc = install("ifdef", eoa);
170: lenloc = install("len", eoa);
171: undefloc = install("undefine", eoa);
172: shiftloc = install("shift", eoa);
173: cqloc = install("changequote", eoa);
174: defloc = install("define", eoa);
175: evaloc = install("eval", eoa);
176: inclloc = install("include", eoa);
177: sinclloc = install("sinclude", eoa);
178: syscmdloc = install("syscmd", eoa);
179: dumploc = install("dumpdef", eoa);
180: errploc = install("errprint", eoa);
181: incrloc = install("incr", eoa);
182: substrloc = install("substr", eoa);
183: indexloc = install("index", eoa);
184: transloc = install("translit", eoa);
185: ifloc = install("ifelse", eoa);
186: divloc = install("divert", eoa);
187: divnumloc = install("divnum", eoa);
188: undivloc = install("undivert", eoa);
189: dnlloc = install("dnl", eoa);
190: #endif
191: ap = argstk;
192: #ifndef gcos
193: if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
194: signal(SIGHUP, catchsig);
195: if (signal(SIGINT, SIG_IGN) != SIG_IGN)
196: signal(SIGINT, catchsig);
197: tempname = mktemp("/tmp/m4aXXXXX");
198: close(creat(tempname, 0));
199: #endif
200: #ifdef gcos
201: tempname = "m4.tempa";
202: #endif
203: if (argc>1)
204: putbak(0);
205: for (;;) {
206: tp = token;
207: *tp++ = t = getchr();
208: *tp = EOS;
209: if (t<=0) {
210: if (infptr > 0) {
211: fclose(infile[infptr]);
212: infptr--;
213: cur_ip = ip_stk[infptr];
214: continue;
215: }
216: if (argc<=1)
217: break;
218: argc--;
219: argv++;
220: if (infile[infptr]!=stdin)
221: fclose(infile[infptr]);
222: if (**argv=='-')
223: infile[infptr] = stdin;
224: else if ((infile[infptr]=fopen(argv[0], READ))==ERROR) {
225: fprintf(stderr, "m4: file not found: %s\n", argv[0]);
226: delexit();
227: }
228: continue;
229: }
230: if (type[t]==ALPH) {
231: while ((t=type[*tp++=getchr()])==ALPH||t==DIG);
232: putbak(*--tp);
233: *tp = EOS;
234: if (*ap = lookup(token)->def) {
235: if (++ap >= &argstk[STACKS]) {
236: fprintf(stderr, "m4: arg stack overflow\n");
237: delexit();
238: }
239: if (cp==NULL)
240: cp = callst;
241: else if (++cp > &callst[STACKS]) {
242: fprintf(stderr, "m4: call stack overflow\n");
243: delexit();
244: }
245: cp->argp = ap;
246: *ap++ = op;
247: puttok();
248: *op++ = '\0';
249: t = getchr();
250: putbak(t);
251: if (t!=LPAR) {
252: /* if (t!=' ' && t!='\t') */
253: putbak(')');
254: putbak('(');
255: }
256: else /* try to fix arg count */
257: *ap++ = op;
258: cp->plev = 0;
259: } else
260: puttok();
261: } else if (t==lquote) {
262: i = 1;
263: for (;;) {
264: t = getchr();
265: if (t==rquote) {
266: i--;
267: if (i==0)
268: break;
269: } else if (t==lquote)
270: i++;
271: else if (t<0) {
272: fprintf(stderr, "m4: EOF in string\n");
273: delexit();
274: }
275: putchr(t);
276: }
277: } else if (t==COMMENT) {
278: putbak(t);
279: while ((t = getchr())!='\n'&& t>=0)
280: if (cp==NULL)
281: putchr(t);
282: putbak(t);
283: } else if (cp==NULL) {
284: puttok();
285: } else if (t==LPAR) {
286: if (cp->plev)
287: *op++ = t;
288: cp->plev++;
289: while ( (t=getchr())==' ' || t=='\t' || t=='\n')
290: ; /* skip leading white space during arg collection */
291: putbak(t);
292: /*
293: } else if (t==' ' || t=='\t' || t=='\n') {
294: continue;
295: */
296: } else if (t==RPAR) {
297: cp->plev--;
298: if (cp->plev==0) {
299: *op++ = '\0';
300: expand(cp->argp, ap-cp->argp-1);
301: op = *cp->argp;
302: ap = cp->argp-1;
303: cp--;
304: if (cp < callst)
305: cp = NULL;
306: } else
307: *op++ = t;
308: } else if (t==COMMA && cp->plev<=1) {
309: *op++ = '\0';
310: *ap++ = op;
311: while ((t=getchr())==' ' || t=='\t' || t=='\n')
312: ; /* skip leading white space during arg collection */
313: putbak(t);
314: } else
315: *op++ = t;
316: }
317: if (cp!=NULL) {
318: fprintf(stderr, "m4: unexpected EOF\n");
319: delexit();
320: }
321: okret = 1;
322: delexit();
323: }
324:
325: catchsig()
326: {
327: okret = 0;
328: delexit();
329: }
330:
331: delexit()
332: {
333: register FILE *fp;
334: register i, c;
335:
336: if (!okret) {
337: signal(SIGHUP, SIG_IGN);
338: signal(SIGINT, SIG_IGN);
339: }
340: for (i=1; i<10; i++) {
341: if (olist[i]==NULL)
342: continue;
343: fclose(olist[i]);
344: tempname[7] = 'a'+i;
345: if (okret) {
346: fp = fopen(tempname, READ);
347: while ((c = getc(fp)) > 0)
348: putchar(c);
349: fclose(fp);
350: }
351: unlink(tempname);
352: }
353: tempname[7] = 'a';
354: unlink(tempname);
355: exit(1-okret);
356: }
357:
358: puttok()
359: {
360: register char *tp;
361:
362: tp = token;
363: if (cp) {
364: if (op >= &obuf[SAVS]) {
365: fprintf(stderr, "m4: argument overflow\n");
366: delexit();
367: }
368: while (*tp)
369: *op++ = *tp++;
370: } else if (curfile)
371: while (*tp)
372: putc(*tp++, curfile);
373: }
374:
375: pbstr(str)
376: register char *str;
377: {
378: register char *p;
379:
380: p = str;
381: while (*p++);
382: --p;
383: if (ip >= &ibuf[SAVS]) {
384: fprintf(stderr, "m4: pushback overflow\n");
385: delexit();
386: }
387: while (p > str)
388: putbak(*--p);
389: }
390:
391: expand(a1, c)
392: register char **a1;
393: {
394: register char *dp;
395: register n;
396:
397: dp = a1[-1];
398: if (dp==defloc)
399: dodef(a1, c);
400: else if (dp==evaloc)
401: doeval(a1, c);
402: else if (dp==inclloc)
403: doincl(a1, c, 1);
404: else if (dp==sinclloc)
405: doincl(a1, c, 0);
406: else if (dp==makeloc)
407: domake(a1, c);
408: else if (dp==syscmdloc)
409: dosyscmd(a1, c);
410: else if (dp==incrloc)
411: doincr(a1, c);
412: else if (dp==substrloc)
413: dosubstr(a1, c);
414: else if (dp==indexloc)
415: doindex(a1, c);
416: else if (dp==transloc)
417: dotransl(a1, c);
418: else if (dp==ifloc)
419: doif(a1, c);
420: else if (dp==divloc)
421: dodiv(a1, c);
422: else if (dp==divnumloc)
423: dodivnum(a1, c);
424: else if (dp==undivloc)
425: doundiv(a1, c);
426: else if (dp==dnlloc)
427: dodnl(a1, c);
428: else if (dp==dumploc)
429: dodump(a1, c);
430: else if (dp==errploc)
431: doerrp(a1, c);
432: else if (dp==lenloc)
433: dolen(a1, c);
434: else if (dp==ifdefloc)
435: doifdef(a1, c);
436: else if (dp==undefloc)
437: doundef(a1, c);
438: else if (dp==shiftloc)
439: doshift(a1, c);
440: else if (dp==cqloc)
441: docq(a1, c);
442: else {
443: while (*dp++);
444: for (dp--; dp>a1[-1]; ) {
445: if (--dp>a1[-1] && dp[-1]=='$') {
446: n = *dp-'0';
447: if (n>=0 && n<=9) {
448: if (n <= c)
449: pbstr(a1[n]);
450: dp--;
451: } else
452: putbak(*dp);
453: } else
454: putbak(*dp);
455: }
456: }
457: }
458:
459: struct nlist *lookup(str)
460: char *str;
461: {
462: register char *s1, *s2;
463: register struct nlist *np;
464: static struct nlist nodef;
465:
466: s1 = str;
467: for (hshval = 0; *s1; )
468: hshval += *s1++;
469: hshval %= HSHSIZ;
470: for (np = hshtab[hshval]; np!=NULL; np = np->next) {
471: s1 = str;
472: s2 = np->name;
473: while (*s1++ == *s2)
474: if (*s2++ == EOS)
475: return(np);
476: }
477: return(&nodef);
478: }
479:
480: char *install(nam, val)
481: char *nam, *val;
482: {
483: register struct nlist *np;
484:
485: if ((np = lookup(nam))->name == NULL) {
486: np = (struct nlist *)malloc(sizeof(*np));
487: if (np == NULL) {
488: fprintf(stderr, "m4: no space for alloc\n");
489: exit(1);
490: }
491: np->name = copy(nam);
492: np->def = copy(val);
493: np->next = hshtab[hshval];
494: hshtab[hshval] = np;
495: return(np->def);
496: }
497: free(np->def);
498: np->def = copy(val);
499: return(np->def);
500: }
501:
502: doundef(ap, c)
503: char **ap;
504: {
505: register struct nlist *np, *tnp;
506:
507: if (c < 1 || (np = lookup(ap[1]))->name == NULL)
508: return;
509: tnp = hshtab[hshval]; /* lookup sets hshval */
510: if (tnp == np) /* it's in first place */
511: hshtab[hshval] = np->next;
512: else {
513: for ( ; tnp->next != np; tnp = tnp->next)
514: ;
515: tnp->next = np->next;
516: }
517: free(np->name);
518: free(np->def);
519: free((char *)np);
520: }
521:
522: char *copy(s)
523: register char *s;
524: {
525: register char *p, *s1;
526:
527: p = s1 = malloc((unsigned)strlen(s)+1);
528: if (p == NULL) {
529: fprintf(stderr, "m4: no space for alloc\n");
530: exit(1);
531: }
532: while (*s1++ = *s++);
533: return(p);
534: }
535:
536: dodef(ap, c)
537: char **ap;
538: {
539: if (c >= 2) {
540: if (strcmp(ap[1], ap[2]) == 0) {
541: fprintf(stderr, "m4: %s defined as itself\n", ap[1]);
542: delexit();
543: }
544: install(ap[1], ap[2]);
545: }
546: else if (c == 1)
547: install(ap[1], "");
548: }
549:
550: doifdef(ap, c)
551: char **ap;
552: {
553: register struct nlist *np;
554:
555: if (c < 2)
556: return;
557: if (lookup(ap[1])->name != NULL)
558: pbstr(ap[2]);
559: else if (c >= 3)
560: pbstr(ap[3]);
561: }
562:
563: dolen(ap, c)
564: char **ap;
565: {
566: putnum((long) strlen(ap[1]));
567: }
568:
569: docq(ap, c)
570: char **ap;
571: {
572: if (c > 1) {
573: lquote = *ap[1];
574: rquote = *ap[2];
575: } else if (c == 1) {
576: lquote = rquote = *ap[1];
577: } else {
578: #ifndef M4
579: lquote = GRAVE;
580: rquote = ACUTE;
581: #endif
582: #ifdef M4
583: lquote = LBRAK;
584: rquote = RBRAK;
585: #endif
586: }
587: }
588:
589: doshift(ap, c)
590: char **ap;
591: {
592: fprintf(stderr, "m4: shift not yet implemented\n");
593: }
594:
595: dodump(ap, c)
596: char **ap;
597: {
598: int i;
599: register struct nlist *np;
600:
601: if (c > 0)
602: while (c--) {
603: if ((np = lookup(*++ap))->name != NULL)
604: fprintf(stderr, "`%s' `%s'\n", np->name, np->def);
605: }
606: else
607: for (i=0; i<HSHSIZ; i++)
608: for (np=hshtab[i]; np!=NULL; np=np->next)
609: fprintf(stderr, "`%s' `%s'\n", np->name, np->def);
610: }
611:
612: doerrp(ap, c)
613: char **ap;
614: {
615: if (c > 0) {
616: fprintf(stderr, ap[1], ap[2], ap[3], ap[4], ap[5], ap[6]);
617: fprintf(stderr, "\n");
618: }
619: }
620:
621:
622: long evalval; /* return value from yacc stuff */
623: char *pe; /* used by grammar */
624:
625: doeval(ap, c)
626: char **ap;
627: {
628:
629: if (c > 0) {
630: pe = ap[1];
631: if (yyparse() == 0)
632: putnum(evalval);
633: else
634: fprintf(stderr, "m4: invalid expression in eval: %s\n", ap[1]);
635: }
636: }
637:
638: doincl(ap, c, noisy)
639: char **ap;
640: {
641: if (c > 0 && strlen(ap[1]) > 0) {
642: infptr++;
643: ip_stk[infptr] = cur_ip = ip;
644: if ((infile[infptr] = fopen(ap[1], READ))==ERROR) {
645: if (noisy) {
646: fprintf(stderr, "m4: file not found: %s\n", ap[1]);
647: delexit();
648: }
649: else
650: infptr--;
651: }
652: }
653: }
654:
655: dosyscmd(ap, c)
656: char **ap;
657: {
658: if (c > 0)
659: system(ap[1]);
660: }
661:
662: domake(ap, c)
663: char **ap;
664: {
665: if (c > 0)
666: pbstr(mktemp(ap[1]));
667: }
668:
669: doincr(ap, c)
670: char **ap;
671: {
672: if (c >= 1)
673: putnum(ctol(ap[1])+1);
674: }
675:
676: putnum(num)
677: long num;
678: {
679: register sign;
680:
681: sign = (num < 0) ? '-' : '\0';
682: if (num < 0)
683: num = -num;
684: do {
685: putbak(num%10+'0');
686: num = num/10;
687: } while (num!=0);
688: if (sign == '-')
689: putbak('-');
690: }
691:
692: dosubstr(ap, c)
693: char **ap;
694: {
695: int nc;
696: register char *sp, *fc;
697:
698: if (c<2)
699: return;
700: if (c<3)
701: nc = TOKS;
702: else
703: nc = ctoi(ap[3]);
704: fc = ap[1] + max(0, min(ctoi(ap[2]), strlen(ap[1])));
705: sp = fc + min(nc, strlen(fc));
706: while (sp > fc)
707: putbak(*--sp);
708: }
709:
710: doindex(ap, c)
711: char **ap;
712: {
713: if (c >= 2)
714: putnum((long) strindex(ap[1], ap[2]));
715: }
716:
717: strindex(p1, p2)
718: char *p1, *p2;
719: {
720: register m;
721: register char *s, *t, *p;
722:
723: for (p=p1; *p; p++) {
724: s = p;
725: m = 1;
726: for (t=p2; *t; )
727: if (*t++ != *s++)
728: m = 0;
729: if (m == 1)
730: return(p-p1);
731: }
732: return(-1);
733: }
734:
735: dotransl(ap, c)
736: char **ap;
737: {
738: register char *s, *fr, *to;
739:
740: if (c <= 1) return;
741:
742: if (c == 2) {
743: register int i;
744: to = ap[1];
745: for (s = ap[1]; *s; s++) {
746: i = 0;
747: for (fr = ap[2]; *fr; fr++)
748: if (*s == *fr) {
749: i++;
750: break;
751: }
752: if (i == 0)
753: *to++ = *s;
754: }
755: *to = '\0';
756: }
757:
758: if (c >= 3) {
759: for (s = ap[1]; *s; s++)
760: for (fr = ap[2], to = ap[3]; *fr && *to; fr++, to++)
761: if (*s == *fr)
762: *s = *to;
763: }
764:
765: pbstr(ap[1]);
766: }
767:
768: doif(ap, c)
769: register char **ap;
770: {
771: if (c < 3)
772: return;
773: while (c >= 3) {
774: if (strcmp(ap[1], ap[2]) == 0) {
775: pbstr(ap[3]);
776: return;
777: }
778: c -= 3;
779: ap += 3;
780: }
781: if (c > 0)
782: pbstr(ap[1]);
783: }
784:
785: dodiv(ap, c)
786: register char **ap;
787: {
788: register int f;
789:
790: if (c<1)
791: f = 0;
792: else
793: f = ctoi(ap[1]);
794: if (f>=10 || f<0) {
795: curfile = NULL;
796: return;
797: }
798: tempname[7] = 'a' + f;
799: if (olist[f] || (olist[f]=fopen(tempname, WRITE))) {
800: curout = f;
801: curfile = olist[f];
802: }
803: }
804:
805: doundiv(ap, c)
806: char **ap;
807: {
808: register FILE *fp;
809: register int i, ch;
810: int j;
811:
812: if (c == 0) {
813: for (i=1; i<10; i++) {
814: if (i==curout || olist[i]==NULL)
815: continue;
816: fclose(olist[i]);
817: tempname[7] = 'a'+i;
818: fp = fopen(tempname, READ);
819: if (curfile != NULL)
820: while ((ch = getc(fp)) > 0)
821: putc(ch, curfile);
822: fclose(fp);
823: unlink(tempname);
824: olist[i] = NULL;
825: }
826:
827: }
828: else {
829: for (j = 1; j <= c; j++) {
830: i = ctoi(*++ap);
831: if (i<1 || i>9 || i==curout || olist[i]==NULL)
832: continue;
833: fclose(olist[i]);
834: tempname[7] = 'a'+i;
835: fp = fopen(tempname, READ);
836: if (curfile != NULL)
837: while ((ch = getc(fp)) > 0)
838: putc(ch, curfile);
839: fclose(fp);
840: unlink(tempname);
841: olist[i] = NULL;
842: }
843: }
844: }
845:
846: dodivnum(ap, c)
847: char **ap;
848: {
849: putnum((long) curout);
850: }
851:
852: dodnl(ap, c)
853: char **ap;
854: {
855: register t;
856:
857: while ((t=getchr())!='\n' && t>=0)
858: ;
859: }
860:
861: long ctol(str)
862: register char *str;
863: {
864: register sign;
865: long num;
866:
867: while (*str==' ' || *str=='\t' || *str=='\n')
868: str++;
869: num = 0;
870: if (*str == '-') {
871: sign = -1;
872: str++;
873: }
874: else
875: sign = 1;
876: while (*str>='0' && *str<='9')
877: num = num*10 + *str++ - '0';
878: return(sign * num);
879: }
880:
881: ctoi(s)
882: char *s;
883: {
884: return(ctol(s));
885: }
886:
887: min(a, b)
888: {
889: if (a>b)
890: return(b);
891: return(a);
892: }
893:
894: max(a, b)
895: {
896: if (a>b)
897: return(a);
898: return(b);
899: }