Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/cmd/m4/m4.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <bio.h>

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; i<HSHSIZ; i++)
			for (np=hshtab[i]; np!=nil; np=np->next)
				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);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.