/*
 * Copyright (c) 1983 Regents of the University of California.
 * All rights reserved. The Berkeley Software License Agreement
 * specifies the terms and conditions for redistribution.
 */
static char rcsid[] = "$Header:pc.c 12.0$";
#ifndef lint
static	char sccsid[] = "@(#)pc.c	3.25 (Berkeley) 7/2/83";
#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 = "/lib/f1";
char	*pc2 = "/usr/lib/pc2";
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, };
char	*pc2args[2] =		{ "pc2", 0 };
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");
		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':
			Oflag = 1;
			continue;
		case 'S':
			Sflag = 1;
			continue;
		case 'J':
			Jflag = 1;
			continue;
		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':
				pc2 = "/usr/src/ucb/pascal/utilities/pc2";
				if (argp[3] != '\0') {
					pc2 = &argp[3];
				}
				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";
#			ifdef vax
				if (tflag) {
					asargs[asargx++] = "-t";
					asargs[asargx++] = tmpdir;
				}
#			endif vax
			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];
		tfile[1] = tname[1];
		if (dosys(pc1, pc1args, 0, tfile[1]))
			continue;
		unlink(tfile[0]);
		tfile[0] = tname[0];
		if (Oflag) {
			if (dosys(c2, c2args, tfile[1], tfile[0]))
				continue;
			unlink(tfile[1]);
			tfile[1] = tfile[0];
			tfile[0] = tname[1];
		}
		if (Sflag)
			tfile[0] = setsuf(argp, 's');
		if (dosys(pc2, pc2args, tfile[1], tfile[0]))
			continue;
		unlink(tfile[1]);
		tfile[1] = 0;
		if (Sflag) {
			tfile[0] = 0;
			continue;
		}
		asargx = 1;
		if (Jflag)
			asargs[asargx++] = "-J";
#		ifdef vax
			if (tflag) {
				asargs[asargx++] = "-t";
				asargs[asargx++] = tmpdir;
			}
#		endif vax
		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 'c':
		case 'g':
		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");
	}
	pid = vfork();
	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);
}
