BSD 4 release
[unix-history] / usr / src / cmd / m4 / m4.c
CommitLineData
aaa7ced1
BJ
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
9int 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
18char lquote LBRAK;
19char rquote RBRAK;
20#endif
21#ifndef M4
22char lquote = GRAVE;
23char rquote = ACUTE;
24#endif
25#define COMMENT '#'
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
37char 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
56char token[TOKS];
57char eoa[] = "\0";
58struct nlist {
59 char *name;
60 char *def;
61 struct nlist *next;
62};
63
64struct nlist *hshtab[HSHSIZ];
65char ibuf[SAVS+TOKS];
66char obuf[SAVS+TOKS];
67char *op = obuf;
68char *ip = ibuf;
69char *ip_stk[10] = {ibuf};
70char *cur_ip = ibuf;
71struct call {
72 char **argp;
73 int plev;
74};
75struct call *cp = NULL;
76
77char *makeloc;
78char *ifdefloc;
79char *lenloc;
80char *undefloc;
81char *shiftloc;
82char *cqloc;
83char *defloc;
84char *evaloc;
85char *incrloc;
86char *substrloc;
87char *indexloc;
88char *transloc;
89char *ifloc;
90char *divloc;
91char *divnumloc;
92char *undivloc;
93char *dnlloc;
94char *inclloc;
95char *sinclloc;
96char *syscmdloc;
97char *dumploc;
98char *errploc;
99
100char *tempname;
101struct nlist *lookup();
102char *install();
103char *malloc();
104char *mktemp();
105char *copy();
106long ctol();
107int hshval;
108FILE *olist[11] = { stdout };
109int okret;
110int curout = 0;
111FILE *curfile = { stdout };
112FILE *infile[10] = { stdin };
113int infptr = 0;
114
115main(argc, argv)
116char **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
325catchsig()
326{
327 okret = 0;
328 delexit();
329}
330
331delexit()
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
358puttok()
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
375pbstr(str)
376register 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
391expand(a1, c)
392register 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
459struct nlist *lookup(str)
460char *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
480char *install(nam, val)
481char *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
502doundef(ap, c)
503char **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
522char *copy(s)
523register 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
536dodef(ap, c)
537char **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
550doifdef(ap, c)
551char **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
563dolen(ap, c)
564char **ap;
565{
566 putnum((long) strlen(ap[1]));
567}
568
569docq(ap, c)
570char **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
589doshift(ap, c)
590char **ap;
591{
592 fprintf(stderr, "m4: shift not yet implemented\n");
593}
594
595dodump(ap, c)
596char **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
612doerrp(ap, c)
613char **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
622long evalval; /* return value from yacc stuff */
623char *pe; /* used by grammar */
624
625doeval(ap, c)
626char **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
638doincl(ap, c, noisy)
639char **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
655dosyscmd(ap, c)
656char **ap;
657{
658 if (c > 0)
659 system(ap[1]);
660}
661
662domake(ap, c)
663char **ap;
664{
665 if (c > 0)
666 pbstr(mktemp(ap[1]));
667}
668
669doincr(ap, c)
670char **ap;
671{
672 if (c >= 1)
673 putnum(ctol(ap[1])+1);
674}
675
676putnum(num)
677long 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
692dosubstr(ap, c)
693char **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
710doindex(ap, c)
711char **ap;
712{
713 if (c >= 2)
714 putnum((long) strindex(ap[1], ap[2]));
715}
716
717strindex(p1, p2)
718char *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
735dotransl(ap, c)
736char **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
768doif(ap, c)
769register 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
785dodiv(ap, c)
786register 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
805doundiv(ap, c)
806char **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
846dodivnum(ap, c)
847char **ap;
848{
849 putnum((long) curout);
850}
851
852dodnl(ap, c)
853char **ap;
854{
855 register t;
856
857 while ((t=getchr())!='\n' && t>=0)
858 ;
859}
860
861long ctol(str)
862register 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
881ctoi(s)
882char *s;
883{
884 return(ctol(s));
885}
886
887min(a, b)
888{
889 if (a>b)
890 return(b);
891 return(a);
892}
893
894max(a, b)
895{
896 if (a>b)
897 return(a);
898 return(b);
899}