/************************************************************************/
/*                                                                      */
/* Copyright (c) 1988 -                                                 */
/*     The Rector and Visitors of the University of Virginia            */
/*                                                                      */
/* This code may not be distributed further without permission          */
/*      from the University of Virginia.                                */
/* This code is distributed WITHOUT ANY WARRANTY.  No claims are        */
/*      made as to whether it serves any particular purpose or even     */
/*      works at all.                                                   */
/*                                                                      */
/************************************************************************/

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>

/* Change LIBDIR macro if /usr/uva/lib is not the correct lib directory */
 
#define LIBDIR(X) "/usr/uva/lib/X"

/* Check that AS, LD, STARTUP, PSTARTUP, SYPROF, are correct */

#define AS "/bin/as"              /* assembler */
#define LD "/bin/ld"              /* link editor */
#define STARTUP "/lib/crt0.o"     /* sys startup routine */
#define PSTARTUP "/lib/mcrt0.o"   /* sys startup routine (profiled) */
#define SYPROF "-L/usr/lib/libp"  /* flag to get profiled system libs */

#define MAXFILE  100   /* maximum files allowed on command line */

extern void exit(), perror();
extern char *optarg, *malloc();
extern int optind, opterr;

int
	opt_N = 0,
	opt_S = 0,
	opt_c = 0,
	opt_g = 0,
	opt_p = 0,
	opt_s = 0,
	opt_t = 0,
	opt_w = 0
;

char *opt_o = (char*)0;
char *myname;
char *trashfile[MAXFILE*2];
int  trashidx = 0;

#define cleanup() while(trashidx > 0) unlink(trashfile[--trashidx])

void
terminate()
{
	cleanup();
	exit(1);
}

/*
 * namefile() names the output file of a command.  The name is
 * the base name of the input file with the specified suffix char.
 * namefile() assumes the input file has a valid suffix.
 */

char *
namefile (file, suffix)
	register char *file, suffix;
{
	register char *newfile, *base;

	/* skip over directories */

	base = file;
	while (*file) {
		if (*file++ == '/')
			base = file;
	}

	/* malloc space for new file name */

	file = newfile = malloc((unsigned)(file-base+1));
	if (newfile == (char*)0) {
		fprintf(stderr, "%s: out of memory\n", myname);
		terminate();
	}

	/* copy base filename with new suffix char */

	while (*file++ = *base++)
		;
	*(file-2) = suffix;

	/* add file to list of files to be discarded */

	if (!(opt_S && suffix == 's') && !(opt_c && suffix == 'o'))
		trashfile[trashidx++] = newfile;
	return(newfile);
}

/*
 * suffix(s)  Returns character value of file suffix.  For example,
 * returns 'p' for "file.p".  Returns 0 if no suffix.
 */

int
suffix(s)
	register char *s;
{
	register int leng=0;
	while(*s) {
		if (*s == '/')
			leng = 0;
		else
			leng++;
		s++;
	}
	return ((leng > 2 && *(s-2) == '.') ? *(s-1) : 0);
}

			
/*
 * compile() invokes the Pascal compiler on a ".p" file,
 * generating a ".s" file.
 */

compile(dot_p, dot_s)
	register char *dot_p, *dot_s;
{
	char *argvec[10];
	register int i;
	int status, pid;

	switch(pid = fork()) {

	case -1:
		perror(myname);
		terminate();

	default:	/* parent */
		while(wait(&status) != pid)
			;
		if (status) {
			fprintf(stderr,
				"%s: compiling phase failed for \"%s\"\n",
				myname, dot_p);
			if (status & 0x80)
				fprintf(stderr, "\tcore dumped\n");
			terminate();
		}
		break;

	case 0:			/* child */
		close(1);
		if (open(dot_s, O_WRONLY|O_CREAT|O_TRUNC, 0666) == -1) {
			fprintf(stderr, "%s: could not open \"%s\"\n",
				myname, dot_s);
			perror(myname);
			terminate();
		}
		i = 0;
		argvec[i++] = "pcomp";
		if (opt_N)
			argvec[i++] = "-N";
		if (opt_g)
			argvec[i++] = "-g";
		if (opt_p)
			argvec[i++] = "-p";
		if (opt_s)
			argvec[i++] = "-s";
		if (opt_t)
			argvec[i++] = "-t";
		if (opt_w)
			argvec[i++] = "-w";
		argvec[i++] = dot_p;
		argvec[i] = (char*)0;
		execv(LIBDIR(pcomp), argvec);
		fprintf(stderr, "%s: could not exec \"%s\"\n", myname,
			LIBDIR(pcomp));
		perror(myname);
		terminate();
	}
}

/*
 * assemble invokes the assembler on a ".s" file, generating a ".o" file.
 */

assemble(dot_s, dot_o)
	register char *dot_s, *dot_o;
{
	char *argvec[10];
	register int i;
	int pid, status;

	switch(pid = fork()) {

	case -1:
		perror(myname);
		terminate();

	default:	/* parent */
		while (wait(&status) != pid)
			;
		if (status) {
			fprintf(stderr, "%s: assembing phase failed for \"%s\"\n",
				myname, dot_s);
			terminate();
		}
		break;

	case 0:		/* child */
		i = 0;
		argvec[i++] = "as";
		argvec[i++] = "-o";
		argvec[i++] = dot_o;
		argvec[i++] = dot_s;
		argvec[i] = (char*)0;
		execv(AS, argvec);
		fprintf(stderr, "%s: could not exec \"%s\"\n", myname, AS);
		perror(myname);
		terminate();
	}
}
	
/*
 * load() invokes the link editor on all ".o" and ".a" files.  
 * The output is "a.out" unless the user specified the "-o" option.
 */

load(dot_o)
	register char **dot_o;
{
	char *argvec[MAXFILE+15];
	register int i;
	int pid, status;

	switch(pid = fork()) {

	case -1:
		perror(myname);
		terminate();

	default:	/* parent */
		while(wait(&status) != pid)
			;
		if (status) {
			fprintf(stderr, "%s: linking phase failed\n", myname);
			terminate();
		}
		break;

	case 0:		/* child */
		i = 0;
		argvec[i++] = "ld";
		argvec[i++] = "-z";
		if (opt_p)
			argvec[i++] = SYPROF;
		if (opt_o) {
			argvec[i++] = "-o";
			argvec[i++] = opt_o;
		}
		if (opt_g)
			argvec[i++] = "-lg";
		if (opt_p)
			argvec[i++] = PSTARTUP;
		else
			argvec[i++] = STARTUP;
		while (*dot_o)
			argvec[i++] = *dot_o++;
		if (opt_p)
			argvec[i++] = LIBDIR(libp/libuvapc.a);
		else
			argvec[i++] = LIBDIR(libuvapc.a);
		argvec[i++] = "-lm";
		argvec[i++] = "-lc";
		argvec[i] = (char*)0;
		execv(LD, argvec);
		fprintf(stderr, "%s: could not exec \"%s\"\n", myname, LD);
		perror(myname);
		terminate();
	}
}

main(argc, argv)
	int argc; char *argv[];
{
	char *dot_o[MAXFILE];
	register char *dot_s, *filearg;
	register int i, errflag = 0;

	signal(SIGINT, terminate);
	signal(SIGHUP, terminate);
	myname = argv[0];
	opterr = 0;
	while ((i = getopt(argc, argv, "NScgo:pstw")) != -1) {
		switch(i) {

		case 'N':       /* disable peephole optimizer */
			opt_N = 1;
			break;

		case 'S':       /* generate .s file(s) and quit */
			opt_S = 1;
			break;

		case 'c':       /* generate .o file(s) and quit */
			opt_c = 1;
			break;

		case 'g':       /* generate symbolic info for debugger */
			opt_g = 1;
			break;

		case 'o':       /* give "a.out" a different name */
			opt_o = optarg;
			if (suffix(opt_o) == 'p') {
				fprintf(stderr,
					"%s: attempt to overwrite source file \"%s\"\n",
					myname, opt_o);
				errflag++;
			}
			break;

		case 'p':       /* enable program profiling */
			opt_p = 1;
			break;

		case 's':       /* complain when non-standard features used */
			opt_s = 1;
			break;

		case 't':       /* do not generate run time error checks */
			opt_t = 1;
			break;

		case 'w':       /* turn off warning diagnostics */
			opt_w = 1;
			break;

		case '?':
			errflag++;
			break;
		}
	}
	if (errflag || optind >= argc) {
		fprintf(stderr,
			"Usage: %s [ -o file ] [ -NScgpstw ] file [ file ... ]\n",
			myname);
		terminate();
	}

	/* process file arguments */

	if (argc - optind >= MAXFILE) {
		fprintf(stderr, "%s: too many file arguments\n", myname);
		terminate();
	}
	for (i = 0; optind < argc; optind++) {
		dot_s = filearg = argv[optind];
		switch(suffix(filearg)) {

		case 'p':
			compile(filearg, dot_s = namefile(filearg, 's'));
			/* fall through */

		case 's':
			if (!opt_S) {
				assemble(dot_s, dot_o[i++] = namefile(filearg, 'o'));
				if (dot_s != filearg)
					unlink(dot_s);   /* don't wait for cleanup() */
			}
			break;

		case 'o':
		case 'a':
			dot_o[i++] = filearg;
			break;

		default:
			fprintf(stderr,
				"%s: file argument \"%s\" has invalid/missing suffix\n",
				myname, filearg);
			terminate();
		}
	}
	if (!opt_S && !opt_c) {
		dot_o[i] = (char*)0;
		load(dot_o);
	}
	cleanup();
	exit(0);
	/* NOTREACHED */
}
