# define SBSIZE 2000
char sbf[SBSIZE];
/* C command */

char *tmp0;
char *tmp1;
char *tmp2;
char *tmp3;
char *tmp4;
char *tmp5;
char ts[1000];
char *tsp = ts;
char *av[50];
char *clist[50];
char *llist[50];
int instring;
int pflag;
int sflag;
int cflag;
int eflag;
int oflag;
int proflag;
int depth;
int *ibuf;
int *ibuf1;
int *ibuf2;
int *obuf;
char *lp;
char *line;
int lineno;
int exfail;
struct symtab {
	char name[8];
	char *value;
} *symtab;
# define symsiz 400
struct symtab *defloc;
struct symtab *incloc;
struct symtab *eifloc;
struct symtab *ifdloc;
struct symtab *ifnloc;
struct symtab *unxloc;
int	trulvl;
int	flslvl;
char *stringbuf;
char *pass0 = "/lib/c0";
char *pass1 = "/lib/c1";
char *pass2 = "/lib/c2";
char *pref = "/lib/crt0.o";

main(argc, argv)
char *argv[]; {
	char *t;
	int nc, nl, i, j, c, f20, nxo;
	int dexit();

	i = nc = nl = f20 = nxo = 0;
	while(++i < argc) {
		if(*argv[i] == '-')
			switch (argv[i][1]) {
				default:
					goto passa;
				case 'S':
					sflag++;
					cflag++;
					break;
				case 'O':
					oflag++;
					break;
				case 'p':
					proflag++;
					pref = "/lib/mcrt0.o";
					break;
				case 'P':
					pflag++;
				case 'c':
					cflag++;
					break;

				case 'f':
					pref = "/lib/fcrt0.o";
					pass0 = "/lib/fc0";
					break;

				case '2':
					if(argv[i][2] == '\0')
						pref = "/lib/crt2.o";
					else {
						pref = "/lib/crt20.o";
						f20 = 1;
					}
					break;
				case 't':
					if (argv[i][2]=='0')
						pass0 = "/usr/c/c0";
					if (argv[i][2]=='1')
						pass1 = "/usr/c/c1";
					if (argv[i][2]=='2')
						pass2 = "/usr/c/c2";
					break;
			}
		else {
		passa:
			t = argv[i];
			if(getsuf(t)=='c') {
				clist[nc++] = t;
				t = setsuf(t, 'o');
			}
			if (nodup(llist, t)) {
				llist[nl++] = t;
				if (getsuf(t)=='o')
					nxo++;
			}
		}
	}
	if(nc==0)
		goto nocom;
	if (pflag==0) {
		tmp0 = copy("/tmp/ctm0a");
		while((c=open(tmp0, 0))>=0) {
			close(c);
			tmp0[9]++;
		}
		while((creat(tmp0, 0400))<0)
			tmp0[9]++;
	}
	if ((signal(2, 1) & 01) == 0)
		signal(2, &dexit);
	(tmp1 = copy(tmp0))[8] = '1';
	(tmp2 = copy(tmp0))[8] = '2';
	(tmp3 = copy(tmp0))[8] = '3';
	if (oflag)
		(tmp5 = copy(tmp0))[8] = '5';
	if (pflag==0)
		(tmp4 = copy(tmp0))[8] = '4';
	for (i=0; i<nc; i++) {
		if (nc>1)
			printf("%s:\n", clist[i]);
		av[0] = "c0";
		if (pflag)
			tmp4 = setsuf(clist[i], 'i');
		av[1] = expand(clist[i]);
		if (pflag || exfail)
			continue;
		if (av[1] == 0) {
			cflag++;
			eflag++;
			continue;
		}
		av[2] = tmp1;
		av[3] = tmp2;
		if (proflag) {
			av[4] = "-P";
			av[5] = 0;
		} else
			av[4] = 0;
		if (callsys(pass0, av)) {
			cflag++;
			eflag++;
			continue;
		}
		av[0] = "c1";
		av[1] = tmp1;
		av[2] = tmp2;
		if (sflag)
			tmp3 = setsuf(clist[i], 's');
		av[3] = tmp3;
		if (oflag)
			av[3] = tmp5;
		av[4] = 0;
		if(callsys(pass1, av)) {
			cflag++;
			eflag++;
			continue;
		}
		if (oflag) {
			av[0] = "c2";
			av[1] = tmp5;
			av[2] = tmp3;
			av[3] = 0;
			callsys(pass2, av);
			unlink(tmp5);
		}
		if (sflag)
			continue;
		av[0] = "as";
		av[1] = "-";
		av[2] = tmp3;
		av[3] = 0;
		cunlink(tmp1);
		cunlink(tmp2);
		cunlink(tmp4);
		callsys("/bin/as", av);
		t = setsuf(clist[i], 'o');
		cunlink(t);
		if(link("a.out", t) || cunlink("a.out")) {
			printf("move failed: %s\n", t);
			eflag++;
			cflag++;
		}
	}
nocom:
	if (cflag==0 && nl!=0) {
		i = 0;
		av[0] = "ld";
		av[1] = "-X";
		av[2] = pref;
		j = 3;
		while(i<nl)
			av[j++] = llist[i++];
		if(f20)
			av[j++] = "-l2";
		else {
			av[j++] = "-lc";
			av[j++] = "-l";
		}
		av[j++] = 0;
		callsys("/bin/ld", av);
		if (nc==1 && nxo==1)
			cunlink(setsuf(clist[0], 'o'));
	}
	dexit();
}

dexit()
{
	if (!pflag) {
		cunlink(tmp1);
		cunlink(tmp2);
		if (sflag==0)
			cunlink(tmp3);
		cunlink(tmp4);
		cunlink(tmp5);
		cunlink(tmp0);
	}
	exit(eflag);
}

expand(file)
char *file;
{
	int ib1[259], ib2[259], ob[259];
	struct symtab stab[symsiz];
	char ln[196];
	register int c;
	register char *rlp;

	exfail = 0;
	ibuf = ibuf1 = ib1;
	ibuf2 = ib2;
	if (fopen(file, ibuf1)<0)
		return(file);
	if (getc(ibuf1) != '#') {
		close(ibuf1[0]);
		return(file);
	}
	ibuf1[1]++;
	ibuf1[2]--;
	obuf = ob;
	symtab = stab;
	for (c=0; c<200; c++) {
		stab[c].name[0] = '\0';
		stab[c].value = 0;
	}
	insym(&defloc, "define");
	insym(&incloc, "include");
	insym(&eifloc, "endif");
	insym(&ifdloc, "ifdef");
	insym(&ifnloc, "ifndef");
	insym(&unxloc, "unix");
	stringbuf = sbf;
	trulvl = 0;
	flslvl = 0;
	line  = ln;
	lineno = 0;
	if (fcreat(tmp4, obuf) < 0) {
		printf("Can't creat %s\n", tmp4);
		dexit();
	}
	while(getline()) {
		if (ibuf==ibuf2 && pflag==0)
			putc(001, obuf);	/*SOH: insert */
		if (ln[0] != '#' && flslvl==0)
			for (rlp = line; c = *rlp++;)
				putc(c, obuf);
		putc('\n', obuf);
	}
	for(rlp=line; c = *rlp++;)
			putc(c,obuf);
	fflush(obuf);
	close(obuf[0]);
	close(ibuf1[0]);
	return(tmp4);
}

getline()
{
	register int c, sc, state;
	struct symtab *np;
	char *namep, *filname;

	if (ibuf==ibuf1)
		lineno++;
	lp = line;
	*lp = '\0';
	state = 0;
	if ((c=getch()) == '#')
		state = 1;
	while (c!='\n' && c!='\0') {
		if ('a'<=c && c<='z' || 'A'<=c && c<='Z' || c=='_') {
			namep = lp;
			sch(c);
			while ('a'<=(c=getch()) && c<='z'
			      ||'A'<=c && c<='Z'
			      ||'0'<=c && c<='9' 
			      ||c=='_')
				sch(c);
			sch('\0');
			lp--;
			if (state>3) {
				if (flslvl==0 &&(state+!lookup(namep,-1)->name[0])==5)
					trulvl++;
				else
					flslvl++;
		out:
				while (c!='\n' && c!= '\0')
					c = getch();
				return(c);
			}
			if (state!=2 || flslvl==0)
				{
				ungetc(c);
				np = lookup(namep, state);
				c = getch();
				}
			if (state==1) {
				if (np==defloc)
					state = 2;
				else if (np==incloc)
					state = 3;
				else if (np==ifnloc)
					state = 4;
				else if (np==ifdloc)
					state = 5;
				else if (np==eifloc) {
					if (flslvl)
						--flslvl;
					else if (trulvl)
						--trulvl;
					else error("If-less endif");
					goto out;
				}
				else {
					error("Undefined control");
					while (c!='\n' && c!='\0')
						c = getch();
					return(c);
				}
			} else if (state==2) {
				if (flslvl)
					goto out;
				np->value = stringbuf;
				savch(c);
				while ((c=getch())!='\n' && c!='\0')
					savch(c);
				savch('\0');
				return(1);
			}
			continue;
		} else if ((sc=c)=='\'' || sc=='"') {
			sch(sc);
			filname = lp;
			instring++;
			while ((c=getch())!=sc && c!='\n' && c!='\0') {
				sch(c);
				if (c=='\\')
					sch(getch());
			}
			instring = 0;
			if (flslvl)
				goto out;
			if (state==3) {
				if (flslvl)
					goto out;
				*lp = '\0';
				while ((c=getch())!='\n' && c!='\0');
				if (ibuf==ibuf2)
					error("Nested 'include'");
				if (fopen(filname, ibuf2)<0)
					error("Missing file %s", filname);
				else
					ibuf = ibuf2;
				return(c);
			}
		}
		sch(c);
		c = getch();
	}
	sch('\0');
	if (state>1)
		error("Control syntax");
	return(c);
}

insym(sp, namep)
struct symtab **sp;
char *namep;
{
	register struct symtab *np;

	*sp = np = lookup(namep, 1);
	np->value = np->name;
}

error(s, x)
{
	printf("%d: ", lineno);
	printf(s, x);
	putchar('\n');
	exfail++;
	cflag++;
	eflag++;
}

sch(c)
{
	register char *rlp;

	rlp = lp;
	if (rlp==line+194)
		error("Line overflow");
	*rlp++ = c;
	if (rlp>line+195)
		rlp = line+195;
	lp = rlp;
}

savch(c)
{
	*stringbuf++ = c;
	if (stringbuf-sbf < SBSIZE)
		return;
	error("Too much defining");
	dexit();
}

getch()
{
	register int c;

loop:
	if ((c=getc1())=='/' && !instring) {
		if ((c=getc1())!='*')
			{
			ungetc(c);
			return('/');
			}
		for(;;) {
			c = getc1();
		cloop:
			switch (c) {

			case '\0':
				return('\0');

			case '*':
				if ((c=getc1())=='/')
					goto loop;
				goto cloop;

			case '\n':
				if (ibuf==ibuf1) {
					putc('\n', obuf);
					lineno++;
				}
				continue;
			}
		}
	}
	return(c);
}
char pushbuff[300];
char *pushp = pushbuff;
ungetc(c)
	{
	*++pushp = c;
	}

getc1()
{
	register c;

	if (*pushp !=0)
		return(*pushp--);
	depth=0;
	if ((c = getc(ibuf)) < 0 && ibuf==ibuf2) {
		close(ibuf2[0]);
		ibuf = ibuf1;
		putc('\n', obuf);
		lineno++;
		c = getc1();
	}
	if (c<0)
		return(0);
	return(c);
}

lookup(namep, enterf)
char *namep;
{
	register char *np, *snp;
	register struct symtab *sp;
	int i, c, around;
	np = namep;
	around = i = 0;
	while (c = *np++)
		i += c;
	i %= symsiz;
	sp = &symtab[i];
	while (sp->name[0]) {
		snp = sp;
		np = namep;
		while (*snp++ == *np)
			if (*np++ == '\0' || np==namep+8) {
				if (!enterf)
					subst(namep, sp);
				return(sp);
			}
		if (++sp >= &symtab[symsiz])
			if (around++)
				{
				error("too many defines");
				dexit();
				}
			else
			sp = symtab;
	}
	if (enterf>0) {
		snp = namep;
		for (np = &sp->name[0]; np < &sp->name[8];)
			if (*np++ = *snp)
				snp++;
	}
	return(sp);
}
char revbuff[200], *bp;
backsch(c)
	{
	if (bp-revbuff > 200)
		error("Excessive define looping", bp--);
	*bp++ = c;
	}

subst(np, sp)
char *np;
struct symtab *sp;
{
	register char *vp;

	lp = np;
	bp = revbuff;
	if (depth++>100)
		{
		error("define recursion loop\n");
		return;
		}
	if ((vp = sp->value) == 0)
		return;
	/* arrange that define unix unix still
	has no effect, avoiding rescanning */
	if (streq(sp->name,sp->value))
		{
		while (*vp)
			sch(*vp++);
		return;
		}
	backsch(' ');
	if (*vp == '(')
		expdef(vp);
	else
	while (*vp)
		backsch(*vp++);
	backsch(' ');
	while (bp>revbuff)
		ungetc(*--bp);
}

getsuf(as)
char as[];
{
	register int c;
	register char *s;
	register int t;

	s = as;
	c = 0;
	while(t = *s++)
		if (t=='/')
			c = 0;
		else
			c++;
	s -= 3;
	if (c<=14 && c>2 && *s++=='.')
		return(*s);
	return(0);
}

setsuf(as, ch)
char as[];
{
	register char *s, *s1;

	s = s1 = copy(as);
	while(*s)
		if (*s++ == '/')
			s1 = s;
	s[-1] = ch;
	return(s1);
}

callsys(f, v)
char f[], *v[]; {
	int t, status;

	if ((t=fork())==0) {
		execv(f, v);
		printf("Can't find %s\n", f);
		exit(100);
	} else
		if (t == -1) {
			printf("Try again\n");
			return(1);
		}
	while(t!=wait(&status));
/*
	printf("status = %o\n", status);
*/
	if ((t=(status&0377)) != 0 && t!=14) {
		if (t!=2)		/* interrupt */
			printf("Fatal error in %s\n", f);
		dexit();
	}
	return((status>>8) & 0377);
}

copy(as)
char as[];
{
	register char *otsp, *s;

	otsp = tsp;
	s = as;
	while(*tsp++ = *s++);
	return(otsp);
}

nodup(l, os)
char **l, *os;
{
	register char *t, *s;
	register int c;

	s = os;
	if (getsuf(s) != 'o')
		return(1);
	while(t = *l++) {
		while(c = *s++)
			if (c != *t++)
				break;
		if (*t=='\0' && c=='\0')
			return(0);
		s = os;
	}
	return(1);
}

cunlink(f)
char *f;
{
	if (f==0)
		return(0);
	return(unlink(f));
}
expdef(proto)
  char *proto;
{
char buffer[100], *parg[20], *pval[20], name[20], *cspace, *wp;
char protcop[100], *pr;
int narg, k, i, c;
pr = protcop;
while (*pr++ = *proto++);
proto= protcop;
for (narg=0; (parg[narg] = token(&proto)) != 0; narg++)
	;
/* now scan input */
cspace = buffer;
while ((c=getch()) == ' ');
if (c != '(')
	{
	error("defined function requires arguments");
	return;
	}
ungetc(c);
for(k=0; pval[k] = coptok(&cspace); k++);
if (k!=narg)
 {
  error("define argument mismatch");
  return;
 }
while (c= *proto++)
   {
   if (!letter(c))
      backsch(c);
   else
      {
      wp = name;
      *wp++ = c;
      while (letnum(*proto))
        *wp++ = *proto++;
      *wp = 0;
      for (k=0; k<narg; k++)
      if(streq(name,parg[k]))
        break;
      wp = k <narg ? pval[k] : name;
      while (*wp) backsch(*wp++);
      }
   }
}
token(cpp) char **cpp;
{
char *val;
int stc;
stc = **cpp;
*(*cpp)++ = '\0';
if (stc==')') return(0);
while (**cpp == ' ') (*cpp)++;
for (val = *cpp; (stc= **cpp) != ',' && stc!= ')'; (*cpp)++)
  {
  if (!letnum(stc) || (val == *cpp && !letter(stc)))
    {
    error("define prototype argument error");
    break;
    }
  }
return(val);
}
coptok (cpp) char **cpp; {
char *val;
int stc, stop,paren;
paren = 0;
val = *cpp;
if (getch() == ')')
  return(0);
while (((stc = getch()) != ',' && stc != ')') || paren > 0)
  {
  if (stc == '"' || stc == '\'')
    {
    stop = stc;
    if (stop == '\'')
      *(*cpp)++ = '\'';
    while ( (stc = getch()) != stop)
      {
      if (stc == '\n')
        {
        error ("non-terminated string");
        break;
        }
      if (stc == '\\')
        if ((stc= getch()) != stop && stc != '\\')
          *(*cpp)++ = '\\';
      *(*cpp)++ = stc;
      }
    if (stop == '\'') 
      *(*cpp)++ = '\'';
    }
  else if (stc == '\\')
      {
      stc = getch();
      if (stc != '"' && stc != '\\')
        *(*cpp)++ = '\\';
      *(*cpp)++ = stc;
      }
  else
    {
    *(*cpp)++ = stc;
    if (stc == '(')
        paren++;
    if (stc == ')')
        paren--;
    }
  }
*(*cpp)++ = 0;
ungetc(stc);
return(val);
}
letter(c)
{
if ((c >= 'a' && c <= 'z') ||
    (c >= 'A' && c <= 'Z') ||
    (c == '_'))
    return (1);
else
    return(0);
}
letnum(c)
{
if (letter(c) || (c >= '0' && c <= '9'))
  return(1);
else
  return(0);
}
streq(s,t) char *s, *t;
{
int c;
while ( (c= *s++) == *t++)
   if (c==0) return(1);
return(0);
}
