/*
 * lcc.c - LSI C compiler driver, for 8086
 */


static char rcsID[] = "$Header: /mnt2/proj/lsic/ver3/lcc/RCS/lcc.c,v 1.1 91/06/10 19:44:24 kmori Exp $";


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>

#pragma checksp

typedef	char	bool;
#define	YES	1
#define	NO	0



extern	char Program[];
extern void version();



bool Nonexec = NO;
int Verilev = 0;
int Warnlev = 1;
int State = 0;
char *Exec_dir;
char *Temp_dir;

char out[100], assem[100], w[100], temp1[100], temp2[100], temp3[100];

char *cppargv[100];
char **pcpp = cppargv;
char *cfargv[100];
char **pcf = cfargv;
char *cgargv[100];
char **pcg = cgargv;
char *ipargv[100];
char *rargv[100];
char **pr = rargv;
char *ldargv[1000];
char **pld = ldargv;
char *msopt[100];
char **pmsop = msopt;
char *firstsrc;

bool Cflag = NO;
bool Sflag = NO;
bool STflag = NO;
bool SCflag = NO;
bool Pflag = NO;
bool Gflag = NO;
int Olevel = 0;
bool Mslink = NO;
bool Jap = NO;
bool CommentNest = NO;
bool ImplicitInt = NO;


int no;
char MemModel = 's';






void usage()
{
	fprintf(stderr, "\n");
#ifdef	SMALLONLY
	fprintf(stderr, "---- S-Model ONLY ----\n");
	fprintf(stderr,
"Usage: %s [-afile][-c][-Dname[=value]][-g][-h][-Idir][-jN][-k linkopt]\n\
           [-llibrary][-Ldir][-m][-mslink][-nX][-noconf][-nomslink]\n\
           [-o output][-O][-p][-S[C]][-Tdir][-vN][-wN][-Xdir][-yN][-z]\n\
           sourcefile...\n\
\n\
soucefile: one of: *.c *.a86 *.asm *.obj *.lib\n", Program);
#else
	fprintf(stderr,
"Usage: %s [-afile][-c][-Dname[=value]][-g][-h][-Idir][-jN][-k linkopt]\n\
           [-llibrary][-Ldir][-m][-m[spdl]][-mslink][-nX][-noconf][-nomslink]\n\
           [-o output][-O][-p][-S[C]][-Tdir][-vN][-wN][-Xdir][-yN][-z]\n\
           sourcefile...\n\
\n\
soucefile: one of: *.c *.a86 *.asm *.obj *.lib\n", Program);
#endif
}



void xexit(int rc)
{
#ifdef	CPM
	if (rc)
		unlink("a:$$$.sub");
#endif
	exit(rc);
}


FILE *efopen(char *file, char *mode)
{
	FILE *fp;

	if ((fp = fopen(file, mode)) == NULL) {
		fprintf(stderr, "%s: ", Program);
		perror(file);
		xexit(1);
	}
	return (fp);
}



int strcmpc(char *s, char *t)
{
	while (tolower(*s) == tolower(*t)) {
		if (*s == '\0')
			return (0);
		s++;
		t++;
	}
	return (*s - *t);
}



char *strsave(char *s)
{
	char *p;

	p = malloc(strlen(s) + 1);
	if (p == NULL) {
		fprintf(stderr, "%s: Out of memory\n", Program);
		xexit(1);
	}
	return (strcpy(p, s));
}



char	*tobacksl(char *buf)
{
	char	*p;

	for (p = buf; *p; p++)
		if (*p == '/')
			*p = '\\';
	return (buf);
}



char *addsl(char *buf)
{
	if (buf[0] != '\0') {
		switch (buf[strlen(buf) - 1]) {
		case ':': case '/': case '\\':
			break;
		default:
			strcat(buf, "/");
		}
	}
	return (buf);
}


int pgetc(FILE *fp)
{
	static char *pushback /* = NULL */;
	int c, d;
	char varname[100], *p;

	for (;;) {
		if (pushback) {
			if ((c = *pushback++) != '\0')
				return (c);
			pushback = NULL;
		}
		c = getc(fp);
		if (c != '$' || (c = getc(fp)) == '$')
			return (c);
		/* environment variable */
		p = varname;
		if ((d = c) == '{')
			c = getc(fp);
		while (c != EOF	&& (d == '{' ? c != '}'
					     : isalpha(c) || isdigit(c) )) {
			if (p < varname + sizeof(varname) - 1)
				*p++ = c;
			c = getc(fp);
		}
		if (d != '{')
			ungetc(c, fp);
		*p = '\0';
		pushback = getenv(varname);
	}
}


char **addoptf(char **xargv, char *file, char ***argvp)
{
	FILE *fp;
	int c;
	char token[256], *p;
	char **ap;

	fp = efopen(file, "r");
	for (;;) {
		do {
			c = pgetc(fp);
		} while (isspace(c));
		if (c == EOF)
			break;
		if (c == '#') {
		/* skip comment */
			while (c != EOF && c != '\n')
				c = pgetc(fp);
		} else if (c == '&' && argvp != NULL) {
		/* place command line arguments here */
			for (ap = *argvp; *ap; ap++) {
				if (**ap == '@')
					xargv = addoptf(xargv, *ap + 1, NULL);
				else
					*xargv++ = *ap;
			}
			*argvp = ap;
		} else {
		/* ordinary argument */
			p = token;
			while (c != EOF && !isspace(c)) {
				if (p < token + sizeof(token) - 1)
					*p++ = c;
				c = pgetc(fp);
			}
			*p = '\0';
			*xargv++ = strsave(token);
		}
	}
	fclose(fp);
	return (xargv);
}



int	xcallv(char *name, char *argv[])
{
	int	i, n;
	char buf[100];

	if (Nonexec || Verilev != 0) {
		fprintf(stderr, "%s", name);
		for (i = 1; argv[i] != NULL; i++)
			fprintf(stderr, " %s", argv[i]);
		putc('\n', stderr);
	}
	if (! Nonexec) {
		fflush(stdout);
#ifndef	CPM
		dup2(2, 5);
		dup2(1, 2);
#endif
		n = spawnv(0, strcat(addsl(strcpy(buf, Exec_dir)), name), argv);
		if (n == -1)
			n = spawnvp(0, name, argv);
#ifndef	CPM
		dup2(5, 2);
#endif
		if (n < 0) {
			fprintf(stderr, "%s: can't exec: %s: %s\n",
					Program, buf, strerror(errno));
			xexit(1);
		}
		if (n == 2 && Warnlev < 2)
			n = 0;
		if (n != 0)
			State = 1;
		return (n);
	}
	return (0);
}



int	xcall(char *name, ...)
{
	va_list	p;

	va_start(p, name);
	return (xcallv(name, (char **)p));
}



void delete(char *name)
{
	if (! Nonexec)
		unlink(name);
}



char *suffix(char *s)
{
	char	c, *p;

	p = NULL;
	while (c = *s) {
		if (c == '/' || c == ':' || c == '\\')
			p = NULL;
		else if (c == '.')
			p = s;
		s++;
	}
	return (p != NULL ? p : s);
}


char *replace_suffix(char *buf, char *str, char *suf)
{
	strcpy(suffix(strcpy(buf, str)), suf);
	return (buf);
}



char *objname(char *s)
{
	static char obj[100];

	if (Cflag && out[0] != '\0')
		return (out);
	else
		return (replace_suffix(obj, s, ".obj"));
}



void compile_c(char *s)
{
	char	**p;

	/* generate c command */
	/* cpp */
	p = pcpp;
	if (Warnlev == 0)
		*p++ = "-w";
	if (Jap)
		*p++ = "-j";
	if (CommentNest)
		*p++ = "-c";
	*p++ = "-o";
	*p++ = strcat(addsl(strcpy(temp1, Temp_dir)), "1.$$$");
	*p++ = replace_suffix(w, s, ".c");
	*p = NULL;
	if (xcallv("cpp", cppargv)) {
		delete(temp1);
		return;
	}
	/* cf */
	p = pcf;
	*p++ = "-chct";
	if (Warnlev == 0)
		*p++ = "-w";
	else if (ImplicitInt)
		*p++ = "-wi";
	if (Jap)
		*p++ = "-j";
	*p++ = temp1;
	strcat(addsl(strcpy(temp2, Temp_dir)), "2.$$$");
	if (STflag) {
		if (out[0] != '\0')
			strcpy(temp2, out);
		else
			replace_suffix(temp2, s, ".t");
	}
	*p++ = temp2;
	*p++ = strcat(addsl(strcpy(temp3, Temp_dir)), "3.$$$");
	*p = NULL;
	if (xcallv("cf", cfargv)) {
		delete(temp1);
		delete(temp2);
		delete(temp3);
		return;
	}
	delete(temp1);
	delete(temp3);
	if (STflag)
		return;
	/* cg */
	p = pcg;
	if (Verilev >= 2)
		*p++ = "-v";
	if (Pflag || Gflag || SCflag)
		*p++ = "-g";
	*p++ = temp2;
	strcat(addsl(strcpy(assem, Temp_dir)), "3.$$$");
	if (Sflag && !SCflag) {
		if (out[0] != '\0')
			strcpy(assem, out);
		else
			replace_suffix(assem, s, ".a86");
	}
	*p++ = assem;
	*p = NULL;
	if (xcallv("cg86", cgargv)) {
		delete(temp2);
		delete(assem);
		return;
	}
	delete(temp2);
	/* merc */
	if (SCflag) {
		xcall("merc", "merc", "-o",
			out[0] ? out : replace_suffix(w, s, ".a86"),
			assem, NULL);
		delete(assem);
	}
	if (Sflag)
		return;
	/* ip86 */
	if (Pflag) {
		p = ipargv;
		*p++ = "ip86";
		if (Gflag)
			*p++ = "-g";
		*p++ = assem;
		*p++ = temp2;
		*p++ = s;
		*p = NULL;
		xcallv("ip86", ipargv);
		delete(assem);
		strcpy(assem, temp2);
	}
	/* r86 */
	p = pr;
	*p++ = "-o";
	*p++ = objname(s);
	*p++ = "-m";
	*p++ = s;
	if (Olevel == 0)
		*p++ = "-q";
	*p++ = assem;
	*p = NULL;
	xcallv("r86", rargv);
	delete(assem);
}



char *find_file(char *buf, char *file)
{
	int	fd;
	char	*p, *d;

	fd = -1;
	strcpy(buf, file);
	if ((fd = open(buf, 0)) < 0 && (p = getenv("PATH")) != NULL) {
		while (*p) {
			d = buf;
			while (*p && *p != ';')
				*d++ = *p++;
			*d = '\0';
			strcat(addsl(buf), file);
			if ((fd = open(buf, 0)) >= 0)
				break;
			if (*p == ';')
				p++;
		}
	}
	if (fd < 0)
		return (NULL);
	close(fd);
	return (buf);
}



void	invoke_link()
{
	FILE *fp;
	char **p;

	/* generate link command */
	*pld = NULL;
	fp = efopen("link.i", "w");
	if (!Mslink) {
		for (p = ldargv + 1; *p; p++) {
			if ((*p)[0] == '-' && (*p)[1] == 'l')
				fprintf(fp, "-l%c/%s ", MemModel, *p + 2);
			else
				fprintf(fp, "%s ", *p);
		}
		putc('\n', fp);
		fclose(fp);
		xcall("lld", "lld", "@link.i", NULL);
	} else {
		char *s;
		char libdir[100];
		bool first, map;

		if (out[0] == '\0')
			*suffix(strcpy(out, firstsrc)) = '\0';
		map = Gflag;
		if (Gflag)
			fputs("/li", fp);
		for (p = msopt; p < pmsop; p++)
			fprintf(fp, "%s", *p);
		fputs("/noi ", fp);
		libdir[0] = '\0';
		for (p = ldargv; *p; p++) {
			if ((*p)[0] == '-' && (*p)[1] == 'L') {
				addsl(strcpy(libdir, *p + 2));
				s = libdir + strlen(libdir);
				*s++ = MemModel;
				*s++ = '\\';
				*s = '\0';
			}
		}
		first = YES;
		for (p = ldargv; *p; p++) {
			if ((*p)[0] == '-' && (*p)[1] == 'M')
				map = YES;
			else if (strcmpc(suffix(*p), ".obj") == 0) {
				if ((*p)[0] == '-' && (*p)[1] == 'l')
					strcat(strcpy(w, libdir), *p + 2);
				else
					strcpy(w, *p);
				if (!first)
					fprintf(fp, "+\n");
				first = NO;
				fprintf(fp, "%s", tobacksl(w));
			}
		}
		fprintf(fp, "\n%s\n", tobacksl(out));
		if (map) {
			*suffix(out) = '\0';
			fprintf(fp, "%s/map", out);
		}
		putc('\n', fp);
		first = YES;
		for (p = ldargv; *p; p++) {
			if ((*p)[0] == '-' && (*p)[1] == 'l'
			    && strcmpc(suffix(*p), ".obj") != 0)
				strcat(strcpy(w, libdir), *p + 2);
			else if (strcmpc(suffix(*p), ".lib") == 0)
				strcpy(w, *p);
			else
				continue;
			if (!first)
				fprintf(fp, "+\n");
			first = NO;
			fprintf(fp, "%s", tobacksl(w));
		}
		fprintf(fp, ";\n");
		fclose(fp);
		xcall("link", "link", "@link.i", NULL);
	}
	if (State == 0 && no == 1 && strcmpc(suffix(firstsrc), ".obj") != 0)
		delete(replace_suffix(w, firstsrc, ".obj"));
	delete("link.i");
}



void	warn_obsolete_option(char *s)
{
	char newopt;

	switch (s[1]) {
	case 'A':
	case 'C':
	case 'd':
	case 'G':
	case 'H':
	case 'i':
	case 'J':
	case 'K':
	case 'M':
	case 'N':
	case 's':
	case 't':
	case 'V':
	case 'W':
	case 'x':
	case 'Y':
		newopt = s[1] ^ ('a' ^ 'A');
		goto alternate;
	case 'B':
	case 'b':
		newopt = 'L';
	alternate:
		fprintf(stderr, "%s: Warning: Option %s is obsolete; use -%c\n",
			Program, s, newopt);
		s[1] = newopt;
		break;
	case 'Q':
	case 'q':
		fprintf(stderr, "%s: Option -q is obsolete; use -O0\n",
			Program);
		xexit(1);
	}
}



int main(int argc, char *argv[])
{
	char *s;
	char **xp, **p;
	static char *xargv[1000];

	argv++;
	if (argv[0] == NULL) {
		version();
		usage();
		xexit(1);
	}
	xp = xargv;
	*xp = NULL;
	if (strcmp(argv[0], "-noconf") == 0)
		argv++;
	else if (find_file(w, "_lcc"))
		xp = addoptf(xp, w, &argv);
	for (; *argv != NULL; argv++) {
		if (**argv == '@')
			xp = addoptf(xp, *argv + 1, NULL);
		else
			*xp++ = *argv;
	}
	*xp = NULL;

	/* initialize options */
	no = 0;
	*pcpp++ = "cpp";
	*pcf++ = "cf";
	*pcg++ = "cg86";
	*pr++ = "r86";
	*pld++ = "lld";
#ifdef	CPM
	*pld++ = "-lccpm.obj";
#else
	*pld++ = "-lcdos.obj";
#endif
	out[0] = '\0';
	for (xp = xargv; (s = *xp++) != NULL; ) {
		if (strcmp(s, "-mslink") == 0)
			Mslink = YES;
		else if (strcmp(s, "-nomslink") == 0)
			Mslink = NO;
		else if (*s == '-') {
			/* do options */
			warn_obsolete_option(s);
			switch (s[1]) {
			case 'z':
				Nonexec = YES;
				break;
			case 'n':
				*pcf++ = s;
				break;
			case 'c':
				switch (s[2]) {
				case '\0':
					Cflag = YES;
					break;
				case 'n':
				case 'N':
					CommentNest = YES;
					break;
				default:
					*pcf++ = s;
				}
				break;
			case 'S':
				switch (s[2]) {
				case 'T':
					STflag = YES;
					break;
				case 'C':
					SCflag = YES;
					break;
				case '\0':
					break;
				default:
					goto badopt;
				}
				Sflag = YES;
				Cflag = YES;
				break;
			case 'v':
				switch (s[2]) {
				case '0':
				case '1':
				case '2':
					Verilev = s[2] - '0';
					break;
				default:
					Verilev = 1;
				}
				break;
			case 'w':
				switch (s[2]) {
				case '0':
				case '1':
				case '2':
					Warnlev = s[2] - '0';
					break;
				case 'i':
					ImplicitInt = YES;
					break;
				case 'x':
					ImplicitInt = NO;
					break;
				default:
					Warnlev = 0;
				}
				break;
			case 'j':
				Jap = s[2] - '0';
				break;
			case 'o':
				*pld++ = s;
				if (s[2] == '\0' && *xp != NULL) {
					strcpy(out, *xp);
					*pld++ = *xp++;
				} else
					strcpy(out, s + 2);
				break;
			case 'k':
				if (s[2] == '\0' && *xp != NULL) {
					if (Mslink)
						*pmsop++ = *xp++;
					else
						*pld++ = *xp++;
				} else {
					if (Mslink)
						*pmsop++ = s + 2;
					else
						*pld++ = s + 2;
				}
				break;
			case 'X':
				Exec_dir = s + 2;
				break;
			case 'T':
				Temp_dir = s + 2;
				break;
			case 'g':
				Gflag = YES;
				/* fall thru */
			case 'l':
			case 'L':
				*pld++ = s;
				break;
			case 'y':
				*pcf++ = s;
				/* fall thru */
			case 'I':
			case 'D':
				*pcpp++ = s;
				break;
			case 'h':
				*pcg++ = "-s";
				break;
			case 'm':
				if (s[2] == 'a' || s[2] == '\0') {
					*pld++ = "-M";
				} else {
#ifdef	SMALLONLY
					goto badopt;
#else
					*pcf++ = s;
					*pcg++ = s;
					MemModel = s[2];
					switch (MemModel) {
					case 'd':
					case 'l':
						*pcpp++ = "-D_D_FAR";
						if (s[2] == 'd')
							break;
						/* fall thru */
					case 'p':
						*pcpp++ = "-D_P_FAR";
						break;
					case 's':
						break;
					default:
						goto badopt;
					}
#endif
				}
				break;
			case 'O':
				if (s[2])
					Olevel = s[2] - '0';
				else
					Olevel = 1;
				break;
			case 'a':
				if (s[2])
					ldargv[1] = strsave(strcat(strcpy(w, "-l"), s + 2));
				else
					ldargv[1] = "";
				break;
			case 'p':
				ldargv[1] = "-lcpro.obj";
				Pflag = YES;
				break;
			default:
			badopt:
				fprintf(stderr, "%s: Bad option: %s\n",
						Program, s);
				usage();
				xexit(1);
			}
		} else if (strcmpc(suffix(s), ".lib") == 0) {
			/* add a library file */
			*pld++ = s;
		} else {
			*pld++ = strsave(replace_suffix(w, s, ".obj"));
			if (no == 0)
				firstsrc = s;
			no++;
			/* check file type */
			if (strcmpc(suffix(s), ".c") == 0 || *suffix(s) == 0) {
				compile_c(s);
			} else if (strcmpc(suffix(s), ".p86") == 0
					|| strcmpc(suffix(s), ".a86") == 0) {
				if (Sflag)
					continue;
				/* generate cpp, r86 command */
				p = pcpp;
				*p++ = "-o";
				*p++ = strcat(addsl(strcpy(temp1, Temp_dir)), "1.$$$");
				*p++ = s;
				*p++ = NULL;
				if (xcallv("cpp", cppargv) == 0) {
					p = pr;
					*p++ = "-o";
					*p++ = objname(s);
					*p++ = "-m";
					*p++ = s;
					if (Olevel == 0)
						*p++ = "-q";
					*p++ = temp1;
					*p = NULL;
					xcallv("r86", rargv);
				}
				delete(temp1);
#if	0
			} else if (strcmpc(suffix(s), ".a86") == 0) {
				if (Sflag)
					continue;
				/* generate r86 command */
				p = pr;
				*p++ = "-o";
				*p++ = objname(s);
				*p++ = "-m";
				*p++ = s;
				if (Olevel == 0)
					*p++ = "-q";
				*p++ = s;
				*p++ = NULL;
				xcallv("r86", rargv);
#endif
			} else if (strcmpc(suffix(s), ".asm") == 0) {
				if (Sflag)
					continue;
				/* generate masm command */
				strcat(strcat(strcat(strcpy(w, s), ","), objname(s)), ";");
				xcall("masm", "masm", "/mx", w, NULL);
			} else if (strcmpc(suffix(s), ".obj") == 0) {
				/* do nothing */
			} else {
				/* unknown file; error */
				fprintf(stderr, "%s: can't handle: %s\n", Program, s);
				xexit(1);
			}
		}
	}
	if (no <= 0) {
		fprintf(stderr, "%s: no sourcefile\n", Program);
		xexit(1);
	}
	if (State == 0 && !Cflag)
		invoke_link();
	xexit(State);
}
