/*
 * cc - front end for C compiler
 */
#include <sys/param.h>
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/dir.h>

char *getenv();
char *malloc();

char *cpp = "/lib/cpp";
char *ccom = "/lib/ccom";
char *sccom = "/lib/sccom";
char *c2 = "/lib/c2";
char bsstodata[256] = "/local/lib/bsstodata";
char *as = "/bin/as";
char *ld = "/bin/ld";
char *crt0_tbl[] = { "/lib/crt0.o",
#define CRT0_NORMAL 0
		     "/lib/mcrt0.o",
#define CRT0_PROF 1
		     "/lib/gcrt0.o" };
#define CRT0_GPROF 2
int crt0 = CRT0_NORMAL;
#ifdef sun
struct {
  char *foption;
  char *crt1; } foptions[] = { { "-fsoft", "/lib/Fcrt1.o" },
			       { "-f68881", "/lib/Mcrt1.o" },
			       { "-fsky", "/lib/Scrt1.o" },
			       { 0, 0 } };
int fopt = -1;
#endif
char *tmpdir = "/tmp";
char *LIBSENV = "C_LIBS";

char tmp0[1024];
char *tmp1;
char *tmp2;
char *tmp3;
char *tmp4;
char *tmp5;
char *tmp6;
char *outfile;
char *savestr();
char *strspl();
char *setsuf();
int idexit();
char **av;
char **clist;
char **llist;
char **plist;
char **libslist;
int Gflag;
int Mflag;
int Rflag;
int Vflag;
int bflag;
int cflag;
int eflag;
int exflag;
int fflag;
int gflag;
int oflag;
int pflag;
int proflag;
int sflag;
int wflag;
int lfirst;
int n_libs;
char *dflag;
int exfail;
int bfail;
char *chpass;
char *npassname;
int debug;

int nc, nl, np, nxo, na;

#define cunlink(s) if (s) unlink(s)

main(ac,av)
int ac;
char **av;
{
 char *opts;

 if (getenv("RCCL"))
  { sprintf (bsstodata, "%s/lib/bsstodata", getenv("RCCL"));
  }

 opts = getenv("C_OPTS");
 if (! opts)
  { return(main_(ac,av));
  }
 else
  { char *cp;
    char *str;
    int idx;
    int len;
    int n;
    int new_ac;
    char **new_av;
    cp = opts;
    n = 0;
    while (*cp)
     { while (isspace(*cp))
	{ cp ++;
	}
       while ((*cp) && !isspace(*cp))
	{ cp ++;
	}
       n ++;
     }
    len = cp - opts;
    new_ac = ac + n;
    new_av = (char **) malloc((new_ac+1) * sizeof(char *));
    for (idx=0;idx<ac;idx++)
     { new_av[idx] = av[idx];
     }
    str = malloc(len+1);
    cp = opts;
    while (*cp)
     { while (isspace(*cp))
	{ cp ++;
	}
       new_av[idx++] = str;
       while ((*cp) && !isspace(*cp))
	{ *str++ = *cp++;
	}
       *str++ = '\0';
     }
    new_av[idx++] = 0;
    return(main_(new_ac,new_av));
  }
}

main_(argc,argv)
int argc;
char **argv;
{
 char *t;
 char *assource;
 int i;
 int j;
 int k;
 int c;
 char *libsenv;
 char *ccom_pgm;
 char *ccom_env;

 n_libs = 0;
 libsenv = getenv(LIBSENV);
 if (libsenv)
  { char *cp;
    char *str;
    int idx;
    int len;
    cp = libsenv;
    while (*cp)
     { while (isspace(*cp))
	{ cp ++;
	}
       while ((*cp) && !isspace(*cp))
	{ cp ++;
	}
       n_libs ++;
     }
    len = cp - libsenv;
    libslist = (char **) malloc(n_libs * sizeof(char *));
    str = malloc(len+1);
    idx = 0;
    cp = libsenv;
    while (*cp)
     { while (isspace(*cp))
	{ cp ++;
	}
       libslist[idx++] = str;
       while ((*cp) && !isspace(*cp))
	{ *str++ = *cp++;
	}
       *str++ = '\0';
     }
  }
 else
  { libslist = (char **) malloc(0);
  }
 /* ld currently adds up to 8 args; 10 is room to spare */
 av = (char **) calloc(argc+10+n_libs,sizeof(char *));
 clist = (char **) calloc(argc,sizeof(char *));
 llist = (char **) calloc(argc,sizeof(char *));
 plist = (char **) calloc(argc,sizeof(char *));
 lfirst = -1;
 for (i=1;i<argc;i++)
  { if (*argv[i] == '-')
     { switch (argv[i][1])
	{ case 'b':
#ifndef sun
	     bflag ++;
#endif
	     continue;
	     break;
	  case 'B':
	     if (npassname)
	      { error("-B overwrites earlier option",(char *)0);
	      }
	     npassname = argv[i]+2;
	     if (npassname[0] == '\0')
	      { npassname = "/usr/c/o";
	      }
	     continue;
	     break;
	  case 'c':
	     cflag++;
	     continue;
	     break;
	  case 'C':
	     plist[np++] = argv[i];
	     continue;
	     break;
	  case 'd':
	     if (argv[i][2] == '\0')
	      { debug ++;
		continue;
	      }
	     dflag = argv[i];
	     continue;
	     break;
	  case 'D':
	     plist[np++] = argv[i];
	     continue;
	     break;
	  case 'E':
	     exflag ++;
	     pflag ++;
	     plist[np++] = argv[i];
	     cflag ++;
	     continue;
	     break;
	  case 'f':
#ifdef vax /* really, 4.3bsd */
	     fflag ++;
#else
#ifdef sun
	     if (strcmp(argv[i],"-fsingle") == 0)
	      { fflag ++;
	      }
	     else
	      { for (j=0;foptions[j].foption;j++)
		 { if (strcmp(argv[i],foptions[j].foption) == 0)
		    { if (fopt >= 0)
		       { error("-f overwrites earlier option",(char *)0);
		       }
		      fopt = j;
		      break;
		    }
		 }
		if (! foptions[j].foption)
		 { error("unrecognized -f option %s",argv[i]+2);
		 }
	      }
#endif
#endif
	     continue;
	     break;
	  case 'g':
	     if (argv[i][2] == 'o')
	      { Gflag++;
		/* old format for -go */
	      }
	     else
	      { gflag++;
		/* new format for -g */
	      }
	     continue;
	     break;
	  case 'I':
	     plist[np++] = argv[i];
	     continue;
	     break;
	  case 'L':
	     llist[nl++] = argv[i];
	     continue;
	     break;
	  case 'M':
	     exflag ++;
	     pflag ++;
	     Mflag ++;
	     plist[np++] = argv[i];
	     continue;
	     break;
	  case 'o':
	     if (++i < argc)
	      { outfile = argv[i];
		switch (getsuf(outfile))
		 { case 'c':
		   case 'o':
		      error("-o would overwrite %s",outfile);
		      exit(8);
		 }
	      }
	     continue;
	     break;
	  case 'O':
	     oflag++;
	     continue;
	     break;
	  case 'p':
	     proflag++;
	     crt0 = CRT0_PROF;
	     if (argv[i][2] == 'g')
	      { crt0 = CRT0_GPROF;
	      }
	     continue;
	     break;
	  case 'P':
	     pflag++;
	     fprintf(stderr,
	"cc: warning: -P option obsolete; you should use -E instead\n");
	     plist[np++] = argv[i];
	     cflag++;
	     continue;
	     break;
	  case 'R':
	     Rflag++;
	     continue;
	     break;
	  case 'S':
	     sflag++;
	     cflag++;
	     continue;
	     break;
	  case 't':
	     if (argv[i][2] == 'd')
	      { tmpdir = argv[i] + 3;
	      }
	     else
	      { if (chpass)
		 { error("-t overwrites earlier option",(char *)0);
		 }
		chpass = argv[i]+2;
		if (chpass[0]==0)
		 { chpass = "012p";
		 }
	      }
	     continue;
	     break;
	  case 'U':
	     plist[np++] = argv[i];
	     continue;
	     break;
	  case 'V':
	     Vflag ++;
	     continue;
	     break;
	  case 'w':
	     wflag++;
	     continue;
	     break;
	}
     }
    t = argv[i];
    c = getsuf(t);
    if ((c == 'c') || (c == 's') || exflag)
     { clist[nc++] = t;
       t = setsuf(t,'o');
     }
    if (nodup(llist,t))
     { llist[k=nl++] = t;
       if (getsuf(t) == 'o')
	{ nxo++;
	}
     }
  }
 if (gflag || Gflag)
  { if (oflag)
     { fprintf(stderr, "cc: warning: -g disables -O\n");
     }
    oflag = 0;
  }
 if (npassname && chpass ==0)
  { chpass = "012p";
  }
 if (chpass && npassname==0)
  { npassname = "/usr/new";
  }
 if (chpass)
  { for (t=chpass; *t; t++)
     { switch (*t)
	{ case '0':
	     if (fflag)
	      {
#ifdef vax /* really, 4.3bsd */
		sccom = strspl(npassname,"sccom");
#else
#ifdef sun
		ccom = strspl(npassname,"ccom");
#endif
#endif
	      }
	     else
	      { ccom = strspl(npassname,"ccom");
	      }
	     break;
	  case '2':
	     c2 = strspl(npassname, "c2");
	     break;
	  case 'p':
	     cpp = strspl(npassname, "cpp");
	     break;
	}
     }
  }
 if (nc == 0)
  { goto nocom;
  }
 if (signal(SIGINT,SIG_IGN) != SIG_IGN)
  { signal(SIGINT,idexit);
  }
 if (signal(SIGTERM,SIG_IGN) != SIG_IGN)
  { signal(SIGTERM,idexit);
  }
 if (signal(SIGHUP,SIG_IGN) != SIG_IGN)
  { signal(SIGHUP,idexit);
  }
 if (pflag == 0)
  { sprintf(tmp0,"%s/ctm%05.5d",tmpdir,getpid());
  }
 tmp1 = strspl(tmp0,"1");
 tmp2 = strspl(tmp0,"2");
 tmp3 = strspl(tmp0,"3");
 if (pflag == 0)
  { tmp4 = strspl(tmp0,"4");
  }
 if (oflag)
  { tmp5 = strspl(tmp0,"5");
  }
 if (bflag)
  { tmp6 = strspl(tmp0,"6");
  }
 for (i=0;i<nc;i++)
  { if ((nc > 1) && !Mflag)
     { printf("%s:\n",clist[i]);
       fflush(stdout);
     }
    if (!exflag && (getsuf(clist[i]) == 's'))
     { assource = clist[i];
       goto assemble;
     }
    else
     { assource = tmp3;
     }
    if (pflag)
     { tmp4 = setsuf(clist[i],'i');
     }
    av[0] = "cpp";
    av[1] = clist[i];
    na = 2;
    if (! exflag)
     { av[na++] = tmp4;
       tmpunlink(tmp4);
     }
    for (j = 0; j < np; j++)
     { av[na++] = plist[j];
     }
    av[na++] = 0;
    if (callsys("C_CPP",cpp,av))
     { exfail++;
       eflag++;
     }
    if (pflag || exfail)
     { cflag++;
       continue;
     }
    if (sflag)
     { if ((nc == 1) && outfile)
	{ tmp3 = outfile;
	}
       else
	{ tmp3 = setsuf(clist[i],'s');
	}
       assource = tmp3;
     }
#ifdef vax /* again, really 4.3bsd */
    av[0] = fflag ? "sccom" : "ccom";
    ccom_env = fflag ? "C_SCCOM" : "C_CCOM";
    ccom_pgm = fflag ? sccom : ccom;
#else
    av[0] = "ccom";
    ccom_env = "C_CCOM";
    ccom_pgm = ccom;
#endif
    av[1] = tmp4;
    av[2] = oflag ? tmp5 : tmp3;
    tmpunlink(av[2]);
    na = 3;
    if (proflag)
     { av[na++] = "-XP";
     }
    if (gflag)
     { av[na++] = "-Xg";
     }
    else if (Gflag)
     { av[na++] = "-XG";
     }
    if (wflag)
     { av[na++] = "-w";
     }
#ifdef sun
    if (fopt >= 0)
     { av[na++] = foptions[fopt].foption;
     }
    if (fflag)
     { av[na++] = "-fsingle";
     }
#endif
    av[na] = 0;
    if (callsys(ccom_env,ccom_pgm,av))
     { cflag++;
       eflag++;
       continue;
     }
    if (oflag)
     { av[0] = "c2";
       av[1] = tmp5;
       av[2] = tmp3;
       tmpunlink(tmp3);
       av[3] = 0;
       if (callsys("C_C2",c2,av))
	{ unlink(tmp3);
	  tmp3 = assource = tmp5;
	}
       else
	{ unlink(tmp5);
	}
     }
    if (sflag)
     { continue;
     }
assemble:
    cunlink(tmp1);
    cunlink(tmp2);
    cunlink(tmp4);
    bfail = 0;
    if (bflag)
     { av[0] = "bsstodata";
       av[1] = assource;
       av[2] = tmp6;
       tmpunlink(tmp6);
       av[3] = 0;
       if (callsys("C_BSSTODATA",bsstodata,av) > 1)
	{ bfail = 1;
	}
       assource = tmp6;
     }
    if (! bfail)
     { av[0] = "as";
       av[1] = "-o";
       if (cflag && (nc == 1) && outfile)
	{ av[2] = outfile;
	}
       else
	{ av[2] = setsuf(clist[i],'o');
	}
       na = 3;
       if (Rflag)
	{ av[na++] = "-R";
	}
       if (dflag)
	{ av[na++] = dflag;
	}
       av[na++] = assource;
       av[na] = 0;
       if (callsys("C_AS",as,av) > 1)
	{ cflag++;
	  eflag++;
	  continue;
	}
     }
    if (bflag)
     { cunlink(tmp6);
     }
  }
nocom:
 if (cflag==0 && nl!=0)
  { av[0] = "ld";
    na = 1;
    av[na++] = "-X";
    if (lfirst >= 0)
     { av[na++] = llist[lfirst];
       av[na++] = "-e";
       av[na++] = "start";
     }
    av[na++] = crt0_tbl[crt0];
#ifdef sun
    if (fopt >= 0)
     { av[na++] = foptions[fopt].crt1;
     }
#endif
    if (outfile)
     { av[na++] = "-o";
       av[na++] = outfile;
     }
    for (i=0;i<nl;i++)
     { if (i != lfirst)
	{ av[na++] = llist[i];
	}
     }
    for (i=0;i<n_libs;i++)
     { av[na++] = libslist[i];
     }
    if (gflag || Gflag)
     { av[na++] = "-lg";
     }
    av[na++] = proflag ? "-lc_p" : "-lc";
    av[na++] = 0;
    eflag |= callsys("C_LD",ld,av);
    if (nc==1 && nxo==1 && eflag==0)
     { unlink(setsuf(clist[0],'o'));
     }
  }
 dexit();
}

idexit()
{
 eflag = 100;
 dexit();
}

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

error(s,x)
char *s;
char *x;
{
 FILE *diag = exflag ? stderr : stdout;

 fprintf(diag,"cc: ");
 fprintf(diag,s,x);
 putc('\n',diag);
 exfail++;
 cflag++;
 eflag++;
}

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 <= MAXNAMLEN && c > 2 && *s++ == '.')
  { return (*s);
  }
 return (0);
}

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

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

callsys(e,f,v)
char *e;
char *f;
char **v;
{
 int t;
 int status;
 char *p;

 p = getenv(e);
 if (Vflag > 0)
  { char **cp;
    fprintf(stderr,"<cc:");
    for (cp=v;*cp;cp++)
     { fprintf(stderr," %s",*cp);
     }
    fprintf(stderr,">\n");
    fflush(stderr); /* Sun misfeature */
  }
 if (debug)
  { char **cpp;
    fprintf(stderr,"%s: ",f);
    for (cpp=v;*cpp;cpp++)
     { fprintf(stderr," %s",*cpp);
     }
    fprintf(stderr,"\n");
  }
 t = vfork();
 if (t == -1)
  { printf("No more processes\n");
    return(100);
  }
 if (t == 0)
  { if (p)
     { execv(p,v);
     }
    execv(f,v);
    printf("Can't find %s\n",f);
    fflush(stdout);
    _exit(100);
  }
 while (t != wait(&status)) ;
 if ((t=(status&0377)) != 0 && t!=14)
  { if (t!=2)
     { printf("Fatal error in %s\n",f);
       eflag = 8;
     }
    dexit();
  }
 return ((status>>8) & 0377);
}

nodup(l,os)
char **l;
char *os;
{
 register char *t;
 register char *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);
}

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

 s = os;
 if (getsuf(s) != 'o')
  { fprintf(stderr,"cc: INTERNAL BUG: Bad suffix in finddup for %s\n",os);
    fprintf(stderr,"    -f option may be botched as a result\n");
    return(-1);
  }
 rv = 0;
 while (t = *l++)
  { while (c = *s++)
     { if (c != *t++)
	{ break;
	}
     }
    if (*t==0 && c==0)
     { return(rv);
     }
    s = os;
    rv ++;
  }
 fprintf(stderr,"cc: INTERNAL BUG: No dup in finddup for %s\n",os);
 fprintf(stderr,"    -f option may be botched as a result\n");
 return(-1);
}

#define NSAVETAB 1024
char *savetab;
int saveleft = 0;

char *savestr(cp)
register char *cp;
{
 register int len;

 len = strlen(cp) + 1;
 if (len > saveleft)
  { saveleft = NSAVETAB;
    if (len > saveleft)
     { saveleft = len;
     }
    savetab = malloc(saveleft);
    if (savetab == 0)
     { fprintf(stderr, "ran out of memory (savestr)\n");
       exit(1);
     }
  }
 strncpy(savetab,cp,len);
 cp = savetab;
 savetab += len;
 saveleft -= len;
 return(cp);
}

char *strspl(left,right)
char *left;
char *right;
{
 char buf[BUFSIZ];

 strcpy(buf,left);
 strcat(buf,right);
 return(savestr(buf));
}

tmpunlink(fn)
char *fn;
{
 if (strncmp(fn,"/tmp/",5) == 0)
  { if (Vflag > 1)
     { fprintf(stderr,"<cc: unlinking %s>\n",fn);
       fflush(stderr); /* Sun misfeature */
     }
    unlink(fn);
  }
}
