/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ifndef lint
static char rcsid[] = "$Header: pc.c 1.1 89/03/12 $";
#endif

#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/param.h>

/*
 * Pc - front end for Pascal compiler.
 */
char	*pc0 = "/usr/lib/pc0";
char	*pc1 = "/usr/lib/pc1";
#if ! defined(NOPC2)
char	*pc2 = "/usr/lib/pc2";
#endif
char	*c2 = "/lib/c2";
char	*pc3 = "/usr/lib/pc3";
char	*ld = "/bin/ld";
char	*as = "/bin/as";
char	*lpc = "-lpc";
char	*crt0 = "/lib/crt0.o";
char	*mcrt0 = "/lib/mcrt0.o";
char	*gcrt0 = "/usr/lib/gcrt0.o";

char	*mktemp();
char	*tmpdir = "/tmp";
char	tmp0[MAXPATHLEN], tmp1[MAXPATHLEN];
char	*tname[2];
char	*tfile[2];

char	*setsuf(), *savestr();

int	Jflag, Sflag, Oflag, Tlflag, cflag, gflag, pflag, wflag, tflag;
int	debug;

#define	NARGS	512
int	ldargx = 3;
int	pc0argx = 3;
char	*pc0args[NARGS] =	{ "pc0", "-o", "XXX" };
char	*pc1args[3] =		{ "pc1", 0, };
#if ! defined(NOPC2)
char	*pc2args[2] =		{ "pc2", 0 };
#endif
char	*c2args[4] =		{ "c2", 0, 0, 0 };
int	pc3argx = 1;
#define	pc3args	pc0args
#define	ldargs	pc0args
/* char	*pc3args[NARGS] =	{ "pc3", 0 }; */
/* char	*ldargs[NARGS] =	{ "ld", "-X", "/lib/crt0.o", 0, }; */

				/* as -J -t tmpdir -o objfile srcfile \0 */
int	asargx;
char	*asargs[8] =		{ "as", 0, };

char *mesg[] = {
	0,
	"Hangup",
	"Interrupt",	
	"Quit",
	"Illegal instruction",
	"Trace/BPT trap",
	"IOT trap",
	"EMT trap",
	"Floating exception",
	"Killed",
	"Bus error",
	"Segmentation fault",
	"Bad system call",
	"Broken pipe",
	"Alarm clock",
	"Terminated",
	"Signal 16",
	"Stopped (signal)",
	"Stopped",
	"Continued",
	"Child exited",
	"Stopped (tty input)",
	"Stopped (tty output)",
	"Tty input interrupt",
	"Cputime limit exceeded",
	"Filesize limit exceeded",
	"Signal 26",
	"Signal 27",
	"Signal 28",
	"Signal 29",
	"Signal 30",
	"Signal 31",
	"Signal 32"
};

/*
 * If the number of .p arguments (np) is 1, and the number of .o arguments
 * (nxo) is 0, and we successfully create an ``a.out'', then we remove
 * the one .ps .o file (onepso).
 */
int	np, nxo;
char	*onepso;
int	errs;

int	onintr();

main(argc, argv)
	int argc;
	char **argv;
{
	register char *argp;
	register int i;
	int savargx;
	char *t, c;
	int j;

	argc--, argv++;
	if (argc == 0) {
		execl("/bin/cat", "cat", "/usr/lib/how_pc", 0);
		exit(1);
	}
	if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
		signal(SIGINT, onintr);
		signal(SIGTERM, onintr);
	}
	for (i = 0; i < argc; i++) {
		argp = argv[i];
		if (argp[0] != '-')
			continue;
		switch (argp[1]) {

		case 'd':
			if (argp[2] == 0)
				debug++;
			continue;
		case 'i':
			pc0args[pc0argx++] = "-i";
			while (i+1 < argc && argv[i+1][0] != '-' &&
			    getsuf(argv[i+1]) != 'p') {
				pc0args[pc0argx++] = argv[i+1];
				i++;
			}
			if (i+1 == argc) {
				fprintf(stderr, "pc: bad -i construction\n");
				exit(1);
			}
			continue;
		case 'o':
			i++;
			if (i == argc) {
				fprintf(stderr, "pc: -o must specify file\n");
				exit(1);
			}
			c = getsuf(argv[i]);
			if (c == 'o' || c == 'p' || c == 'c') {
				fprintf(stderr, "pc: -o would overwrite %s\n",
				    argv[i]);
				exit(1);
			}
			continue;
		case 't':
			i++;
			if (i == argc) {
				fprintf(stderr, "pc: -t but no directory\n");
				exit(1);
			}
			if (argp[2] != '\0') {
				fprintf(stderr, "pc: bad -t option\n");
				exit(1);
			}
			tmpdir = argv[i];
			if (tmpdir[0] == '-') {
				fprintf(stderr, "pc: bad -t option\n");
				exit(1);
			}
			tflag = 1;
			continue;
		case 'O':
			/* this optimizer doesn't understand
			 * anything!!! sete, for example....
			 */
			/* Oflag = 1; */
			continue;
		case 'S':
			Sflag = 1;
			continue;
		case 'J':
#ifdef sequent
			fprintf(stderr, "pc: no -J option, ignored.\n");
			continue;
#else
			Jflag = 1;
			continue;
#endif
		case 'T':
			switch (argp[2]) {

			case '0':
				pc0 = "/usr/src/ucb/pascal/pc0/a.out";
				if (argp[3] != '\0') {
					pc0 = &argp[3];
				}
				continue;
			case '1':
				pc1 = "/usr/src/lib/pcc/fort";
				if (argp[3] != '\0') {
					pc1 = &argp[3];
				}
				continue;
			case '2':
#if ! defined(NOPC2)
				pc2 = "/usr/src/ucb/pascal/utilities/pc2";
				if (argp[3] != '\0') {
					pc2 = &argp[3];
				}
#endif
				continue;
			case '3':
				pc3 = "/usr/src/ucb/pascal/utilities/pc3";
				if (argp[3] != '\0') {
					pc3 = &argp[3];
				}
				continue;
			case 'l':
				Tlflag = 1;
				lpc = "/usr/src/usr.lib/libpc/libpc";
				if (argp[3] != '\0') {
					lpc = &argp[3];
				}
				continue;
			}
			continue;
		case 'c':
			cflag = 1;
			continue;
		case 'l':
			if (argp[2])
				continue;
			/* fall into ... */
		case 'b':
		case 's':
		case 'z':
		case 'C':
			pc0args[pc0argx++] = argp;
			continue;
		case 'w':
			wflag = 1;
			pc0args[pc0argx++] = argp;
			continue;
		case 'g':
			gflag = 1;
			pc0args[pc0argx++] = argp;
			continue;
		case 'p':
			if (argp[2] == 'g')
				crt0 = gcrt0;
			else
				crt0 = mcrt0;
			if (!Tlflag)
				lpc = "-lpc_p";
			pflag = 1;
			continue;
		}
	}
	if (gflag && Oflag) {
		fprintf(stderr, "pc: warning: -g overrides -O\n");
		Oflag = 0;
	}
	sprintf(tmp0, "%s/%s", tmpdir, "p0XXXXXX");
	tname[0] = mktemp(tmp0);
	sprintf(tmp1, "%s/%s", tmpdir, "p1XXXXXX");
	tname[1] = mktemp(tmp1);
	savargx = pc0argx;
	for (i = 0; i < argc; i++) {
		argp = argv[i];
		if (argp[0] == '-')
			continue;
		if (suffix(argp) == 's') {
			asargx = 1;
			if (Jflag)
				asargs[asargx++] = "-J";
#			if vax || ns32000
				if (tflag) {
					asargs[asargx++] = "-t";
					asargs[asargx++] = tmpdir;
				}
#			endif vax || ns32000
			asargs[asargx++] = argp;
			asargs[asargx++] = "-o";
			tfile[1] = setsuf(argp, 'o');
			asargs[asargx++] = tfile[1];
			asargs[asargx] = 0;
			if (dosys(as, asargs, 0, 0))
				continue;
			tfile[1] = 0;
			continue;
		}
		if (suffix(argp) != 'p')
			continue;
		tfile[0] = tname[0];
		pc0args[2] = tfile[0];
		pc0argx = savargx;
		if (pflag)
			pc0args[pc0argx++] = "-p";
		if (Jflag)
			pc0args[pc0argx++] = "-J";
		pc0args[pc0argx++] = argp;
		pc0args[pc0argx] = 0;
		if (dosys(pc0, pc0args, 0, 0))
			continue;
		pc1args[1] = tfile[0];
#if ! defined(NOPC2)
		tfile[1] = tname[1];
#else
		tfile[1] = (Sflag && ! Oflag) ? setsuf(argp, 's') : tname[1];
#endif
		if (dosys(pc1, pc1args, 0, tfile[1]))
			continue;
		unlink(tfile[0]);
		tfile[0] = tname[0];
		if (Oflag) {
#if defined(NOPC2)
			if (Sflag)
				tfile[0] = setsuf(argp, 's');
#endif
			if (dosys(c2, c2args, tfile[1], tfile[0]))
				continue;
			unlink(tfile[1]);
			tfile[1] = tfile[0];
			tfile[0] = tname[1];
		}
#if ! defined(NOPC2)
		if (Sflag)
			tfile[0] = setsuf(argp, 's');
		if (dosys(pc2, pc2args, tfile[1], tfile[0]))
			continue;
		unlink(tfile[1]);
#else
		tfile[0] = tfile[1];
#endif
		tfile[1] = 0;
		if (Sflag) {
			tfile[0] = 0;
			continue;
		}
		asargx = 1;
		if (Jflag)
			asargs[asargx++] = "-J";
#		if vax || ns32000
			if (tflag) {
				asargs[asargx++] = "-t";
				asargs[asargx++] = tmpdir;
			}
#		endif vax || ns32000
		asargs[asargx++] = tfile[0];
		asargs[asargx++] = "-o";
		tfile[1] = setsuf(argp, 'o');
		asargs[asargx++] = tfile[1];
		asargs[asargx] = 0;
		if (dosys(as, asargs, 0, 0))
			continue;
		tfile[1] = 0;
		remove();
	}
	if (errs || cflag || Sflag)
		done();
/* char	*pc3args[NARGS] =	{ "pc3", 0 }; */
	pc3args[0] = "pc3";
	if (wflag)
		pc3args[pc3argx++] = "-w";
	pc3args[pc3argx++] = "/usr/lib/pcexterns.o";
	for (i = 0; i < argc; i++) {
		argp = argv[i];
		if (!strcmp(argp, "-o"))
			i++;
		if (argp[0] == '-')
			continue;
		switch (getsuf(argp)) {

		case 'o':
			pc3args[pc3argx++] = argp;
			nxo++;
			continue;
		case 's':
		case 'p':
			onepso = pc3args[pc3argx++] =
			    savestr(setsuf(argp, 'o'));
			np++;
			continue;
		}
	}
	pc3args[pc3argx] = 0;
	if (dosys(pc3, pc3args, 0, 0) > 1)
		done();
	errs = 0;
/* char	*ldargs[NARGS] =	{ "ld", "-X", "/lib/crt0.o", 0, }; */
	ldargs[0] = "ld";
	ldargs[1] = "-X";
	ldargs[2] = crt0;
	for (i = 0; i < argc; i++) {
		argp = argv[i];
		if (argp[0] != '-') {
			switch (getsuf(argp)) {

			case 'p':
			case 's':
				ldargs[ldargx] = savestr(setsuf(argp, 'o'));
				break;
			default:
				ldargs[ldargx] = argp;
				break;
			}
			if (getsuf(ldargs[ldargx]) == 'o')
			for (j = 0; j < ldargx; j++)
				if (!strcmp(ldargs[j], ldargs[ldargx]))
					goto duplicate;
			ldargx++;
duplicate:
			continue;
		}
		switch (argp[1]) {

		case 'i':
			while (i+1 < argc && argv[i+1][0] != '-' &&
			    getsuf(argv[i+1]) != 'p')
				i++;
			continue;
		case 'd':
			if (argp[2] == 0)
				continue;
			ldargs[ldargx++] = argp;
			continue;
		case 'o':
			ldargs[ldargx++] = argp;
			i++;
			ldargs[ldargx++] = argv[i];
			continue;
		case 'l':
			if (argp[2])
				ldargs[ldargx++] = argp;
			continue;
		case 't':
			i++;
			continue;
		case 'g':
			ldargs[ldargx++] = "-h";
			continue;
		case 'c':
		case 'w':
		case 'p':
		case 'S':
		case 'J':
		case 'T':
		case 'O':
		case 'C':
		case 'b':
		case 's':
		case 'z':
			continue;
		default:
			ldargs[ldargx++] = argp;
			continue;
		}
	}
	ldargs[ldargx++] = lpc;
	if (gflag)
		ldargs[ldargx++] = "-lg";
	if (pflag) {
		ldargs[ldargx++] = "-lm_p";
		ldargs[ldargx++] = "-lc_p";
	} else {
		ldargs[ldargx++] = "-lm";
		ldargs[ldargx++] = "-lc";
	}
	ldargs[ldargx] = 0;
	if (dosys(ld, ldargs, 0, 0)==0 && np == 1 && nxo == 0)
		unlink(onepso);
	done();
}

dosys(cmd, argv, in, out)
	char *cmd, **argv, *in, *out;
{
	union wait status;
	int pid;

	if (debug) {
		int i;
		printf("%s:", cmd);
		for (i = 0; argv[i]; i++)
			printf(" %s", argv[i]);
		if (in)
			printf(" <%s", in);
		if (out)
			printf(" >%s", out);
		printf("\n");
	}
	/*
	 * warning: vfork doesn't work here, because the call to signal() 
	 * done by the child process destroys the parent's SIGINT handler.
	 */
	pid = fork();
	if (pid < 0) {
		fprintf(stderr, "pc: No more processes\n");
		done();
	}
	if (pid == 0) {
		if (in) {
			close(0);
			if (open(in, 0) != 0) {
				perror(in);
				exit(1);
			}
		}
		if (out) {
			close(1);
			unlink(out);
			if (creat(out, 0666) != 1) {
				perror(out);
				exit(1);
			}
		}
		signal(SIGINT, SIG_DFL);
		execv(cmd, argv);
		perror(cmd);
		exit(1);
	}
	while (wait(&status) != pid)
		;
	if (WIFSIGNALED(status)) {
		if (status.w_termsig != SIGINT) {
			fprintf(stderr, "%s: %s", cmd, mesg[status.w_termsig]);
			if (status.w_coredump)
				fprintf(stderr, " (core dumped)");
			fprintf(stderr, "\n");
		}
		errs = 100;
		done();
		/*NOTREACHED*/
	}
	if (status.w_retcode) {
		errs = 1;
		remove();
	}
	return (status.w_retcode);
}

done()
{

	remove();
	exit(errs);
}

remove()
{

	if (tfile[0])
		unlink(tfile[0]);
	if (tfile[1])
		unlink(tfile[1]);
}

onintr()
{

	errs = 1;
	done();
}

getsuf(cp)
	char *cp;
{

	if (*cp == 0)
		return;
	while (cp[1])
		cp++;
	if (cp[-1] != '.')
		return (0);
	return (*cp);
}

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

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

#define	NSAVETAB	512
char	*savetab;
int	saveleft;

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

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

suffix(cp)
	char *cp;
{

	if (cp[0] == 0 || cp[1] == 0)
		return (0);
	while (cp[1])
		cp++;
	if (cp[-1] == '.')
		return (*cp);
	return (0);
}
