[net.micro.6809] Alternate cc

jejones@ea.UUCP (10/28/84)

The following is an alternative cc for Microware C. It is only feasible for
Level II, thanks to the sizes of the pieces of the compiler. Note also the
cautions in the source. (It is quieter than cc[12] unless you ask for chat,
and adds some options: -x to simply echo the commands it would have run to
stdout, -z=<pathname> for alternate cstart routines. Also, -l=<pathname>
works, unfortunately unlike the current cc[12].)

It pipes the commands to a shell. The functions supporting that accompany
cc.c, and are posted by courteous permission of Steve Blasingame.

Final note: if you agree with me that OS-9 should do lazy unlinking (i.e.
don't wipe modules with reference count = 0 until you need the memory),
you might mention it to Microware.

						James Jones

------------------------------TEAR HERE------------------------------
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# bool.h cc.c pipe.c

echo x - bool.h
sed 's/^	//' > "bool.h" << '//E*O*F bool.h//'
	/*
	 * bool.h -- a header for those of us who think that C really
	 * should have a Boolean type...
	 */
	
	typedef int bool;
	
	#define TRUE	1
	#define FALSE	0
//E*O*F bool.h//

echo x - cc.c
sed 's/^	//' > "cc.c" << '//E*O*F cc.c//'
	/*
	 * cc -- alternate compiler executive for the Microware C compiler
	 *		(c) 1984 Gonzo Software
	 * Use and copy and hack as you will, as long as you don't sell it.
	 * I'd like to get a copy of any improvements, though...--jej
	 *
	 * This version has the various compiler components talk to one another
	 * through pipes rather than temporary files, to save time. (Actually,
	 * for short files, the big win is loading the pieces of the compiler
	 * only once.)
	 *
	 * usage: just like cc2, EXCEPT for the following flags:
	 *
	 * -q	don't be quiet about what is going on
	 * -x	instead of executing the commands that would do the compilation
	 *	etc., just echo them to standard output
	 * -z=<pathname>
	 *	specify pathname for the startup routine, overriding the
	 *	default of cstart.r
	 *
	 * This program builds a script, and hence is rather stupid about
	 * what to do in the face of errors. Therefore one should typically
	 * use it only for things one is fairly sure will compile. (Not to
	 * mention that what with the preprocessor and compiler sending
	 * error messages to stdout, the pipes will be SNAFUd if there are
	 * syntax errors.)
	 */
	
	#include <stdio.h>
	#include <bool.h>
	
	#define error(msg)	fprintf(stderr, "cc: %s\n", msg)
	#define MAXFILES	30
	#define MAXLIBS		10
	#define MAXDEFS		20
	#define MAXPROGS	5
	#define MAXNAME		64
	#define TEMPPATH	"/d0/tmp/"
	#define LIBDIR		"/d0/lib"
	#define LIBDEFAULT	"CLib.l"
	#define STARTDEFAULT	"CStart.r"
	
	/* names of programs we invoke */
	
	#define PREPROC		"c.prep"
	#define COMPILER	"c.comp"
	#define OPTIM		"c.opt"
	#define ASM		"c.asm"
	#define LINKER		"c.link"
	#define SHELL		"shell -p -t"
	
	/* command line option flags */
	
	#define NOOPT		'o'
	#define NOASM		'a'
	#define NOLINK		'r'
	#define CHAT		'q'
	#define NOSTACK		's'
	#define PROFILE		'p'
	#define SCOM		'c'
	#define EDITION		'e'
	#define MEMORY		'm'
	#define EXECPATH	'f'
	#define LIB		'l'
	#define ALTSTART	'z'
	#define DEFINE		'd'
	#define NOEXEC		'x'
	
	/* a structure that will keep track of the .whatever files we use */
	
	typedef struct {
		char	*BaseName;
		bool	IsTemp;
		char	Type;	/* i.e. is it a .c, a .a, or a .r? */
	} Descr;
	
	/*
	 * options (assigned their default settings)
	 * (We should note that WantComp isn't an option that one can explicitly
	 * set--instead, it's something we set depending on whether there are
	 * any .c files specified on the command line.)
	 */
	
	bool	WantChat  = FALSE,	/* tell what you're doing (on stderr)	*/
		WantSCom  = FALSE,	/* insert source code as asm comments	*/
		WantComp  = FALSE,	/* should we run the compiler?		*/
		WantOpt   = TRUE,	/* how about the optimizer?		*/
		WantProf  = FALSE,	/* insert profiling code		*/
		WantStack = TRUE,	/* insert stack-checking code		*/
		WantAsm   = TRUE,	/* assemble the compiler output		*/
		WantLink  = TRUE,	/* link the whole mess at the end	*/
		WantExec  = TRUE;	/* do we *really* want to do all this?	*/
	
	/*
	 * Now for some things that ideally we shouldn't have to do. It would
	 * be nice if OS-9 would leave modules with reference count zero in
	 * memory until the memory was really needed. That way, one wouldn't
	 * have to explicitly load/unlink the pieces of the compiler such as
	 * we are going to do here...
	 */
	
	bool	LoadComp,
		LoadAsm;
	
	/* here we keep track of the various files we are working with */
	
	Descr		FD[MAXFILES];
	int		FDAvail = 0;
	
	/* here we keep track of the user-specified libraries... */
	
	char		*Lib[MAXLIBS];
	int		LibAvail = 0;
	
	/* here we save the #defines from the command line... */
	
	char		*Def[MAXDEFS];
	int		DefAvail = 0;
	
	/* various pointers */
	
	char		*Memory,	/* to the -m= option (if selected)	*/
			*Edition,	/* to the -e= option (if selected)	*/
			*ExecPath,	/* where to put the executable (if any)	*/
			*StartPath;	/* to alternate startup function	*/
	
	/*
	 * count of various sorts of files, to mimic cc[12]'s naming of
	 * executables and, since (alas) OS-9 doesn't do lazy unlinking,
	 * to determine whether to explicitly load/unlink compiler parts
	 */
	
	int		DotCCnt = 0,
			DotACnt = 0;
	
	/*
	 * We will write the commands to do the compilation etc. to the file
	 * CmdOut points at. If we really want to do them, the file will be a
	 * pipe pointed at a shell.
	 */
	
	FILE		*CmdOut;
	
	main(argc, argv)
	int	argc;
	char	*argv[];
	{
		int	i;
		int	CleanUp();
		bool	OK, DoOption(), DoFile();
	
		OK = TRUE;
	
		for (i = 1; i < argc; i++)
			if (*argv[i] == '-')
				OK &= DoOption(argv[i]);
			else
				OK &= DoFile(argv[i]);
	
		if (!OK)
			exit(1);
	
		if (!WantExec)
			CmdOut = stdout;
		else if ((CmdOut = popen(SHELL, "w")) == (FILE *) NULL) {
			error("can't fork shell");
			exit(1);
		}
	
		LoadComp = DotCCnt > 1;
		LoadAsm = WantAsm && (DotCCnt + DotACnt > 1);
	
		if (LoadComp || LoadAsm)
			DoLoad("load");
	
		if (WantComp) {
			for (i = 0; i < FDAvail; i++)
				if (FD[i].Type == 'c')
					DoCompile(i);
		}
	
		if (WantAsm) {
			for (i = 0;  i < FDAvail; i++)
				if (FD[i].Type == 'a')
					DoAsm(i);
		}
	
		if (WantLink) {
			if (ExecPath == (char *) NULL) {
				if (DotCCnt != 1)
					ExecPath = "output";
				else {
					for (i = 0; i < FDAvail; i++)
						if (FD[i].Type == 'c') {
							ExecPath = FD[i].BaseName;
							break;
						}
				}
			}
			DoLink();
		}
	
		CleanUp();
	
	}
	
	CleanUp()
	{
		int	i;
		bool	First;
	
		First = TRUE;
		for (i = 0; i < FDAvail; i++)
			if (FD[i].IsTemp) {
				fprintf(CmdOut, "%s %s%s.%c",
					(First ? "del" : ""),
					TEMPPATH, FD[i].BaseName, FD[i].Type);
				First = FALSE;
			}
	
		if (!First)
			putc('\n', CmdOut);
	
		if (LoadComp || LoadAsm)
			DoLoad("unlink");
	
		if (WantExec)
			pclose(CmdOut);
	
	
	
	}
	
	bool
	DoOption(OpString)
	char	*OpString;
	{
		char	**LongOption;
	
	
		for (++OpString; *OpString; ++OpString) {
			switch (*OpString) {
			case NOOPT:
				WantOpt = FALSE;
				continue;
			case NOASM:
				WantAsm = WantLink = FALSE;
				continue;
			case NOLINK:
				WantLink = FALSE;
				continue;
			case CHAT:
				WantChat = TRUE;
				continue;
			case PROFILE:
				WantProf = TRUE;
				continue;
			case SCOM:
				WantSCom = TRUE;
				continue;
			case NOSTACK:
				WantStack = FALSE;
				continue;
			case NOEXEC:
				WantExec = FALSE;
				continue;
			case EDITION:
				LongOption = &Edition;
				break;
			case MEMORY:
				LongOption = &Memory;
				break;
			case EXECPATH:
				LongOption = &ExecPath;
				break;
			case ALTSTART:
				LongOption = &StartPath;
				break;
			case LIB:
				if (LibAvail >= MAXLIBS) {
					error("too many libraries");
					return(FALSE);
				}
				LongOption = &Lib[LibAvail++];
				break;
			case DEFINE:
				if (DefAvail >= MAXDEFS) {
					error("too many defines");
					return(FALSE);
				}
				LongOption = &Def[DefAvail++];
				break;
			default:
				error("unknown option");
				return(FALSE);
			}
			if (*OpString != DEFINE && *(OpString + 1) != '=') {
				fprintf(stderr, "cc: invalid -%c option\n", *OpString);
				return(FALSE);
			}
			*LongOption = OpString + 2;
			return(TRUE);
		}
	
		return(TRUE);
	
	}
	
	bool
	DoFile(Name)
	char	*Name;
	{
		char	*suffix;
		bool	OK;
	
		OK = TRUE;
	
		suffix = Name + strlen(Name) - 1;
		switch (*suffix) {
		case 'c':
			WantComp = TRUE;
			DotCCnt++;
			break;
		case 'a':
			DotACnt++;
			break;
		case 'r':
			break;
		default:
			OK = FALSE;
			break;
		}
	
		if (!OK || *(suffix - 1) != '.') {
			error("unknown file type");
			return(FALSE);
		}
	
		*(suffix - 1) = '\0';
	
		return(AddFile(Name, FALSE, *suffix));
	
	}
	
	bool
	AddFile(Name, Temp, Type)
	char	*Name;
	bool	Temp;
	char	Type;
	{
		int	i;
	
		if (FDAvail >= MAXFILES) {
			error("too many files");
			return(FALSE);
		}
	
		i = FDAvail++;
		FD[i].BaseName = Name;
		FD[i].IsTemp = Temp;
		FD[i].Type = Type;
	
		return(TRUE);
	}
	
	/*
	 * The following functions write commands to CmdOut that would get
	 * the stuff compiled/assembled/linked as needed.
	 */
	
	DoCompile(FDIndex)
	int	FDIndex;
	{
		int	i;
	
		if (WantChat)
			fprintf(stderr, "compiling %s.c\n", FD[FDIndex].BaseName);
	
		/* c.prep [-l] [-d<etc.>]* name.c ... */
	
		fputs(PREPROC, CmdOut);
		if (WantSCom)
			fputs(" -l", CmdOut);
		for (i = 0; i < DefAvail; i++)
			fprintf(CmdOut, " -d%s", Def[i]);
		fprintf(CmdOut, " %s.c ! ", FD[FDIndex].BaseName);
	
		/* ! c.comp [-s] [-p] ... */
	
		fputs(COMPILER, CmdOut);
		if (!WantStack)
			fputs(" -s", CmdOut);
		if (WantProf)
			fputs(" -p", CmdOut);
	
		/* ! c.opt (if optimization was requested)... */
	
		if (WantOpt)
			fprintf(CmdOut, " ! %s", OPTIM);
	
		/* >Name.a */
	
		fprintf(CmdOut, " >%s%s.a\n",
			(WantAsm ? TEMPPATH : ""), FD[FDIndex].BaseName);
	
		/* remember the .a file (we may use it later). */
	
		if (!AddFile(FD[FDIndex].BaseName, WantAsm, 'a')) {
			CleanUp();
			exit(1);
		}
	
	}
	
	DoAsm(FDIndex)
	int	FDIndex;
	{
		if (WantChat)
			fprintf(stderr, "assembling %s.a\n", FD[FDIndex].BaseName);
	
		/* c.asm -o=Name.r */
	
		fprintf(CmdOut,"%s -o=%s%s.r %s%s.a\n", ASM,
			(WantLink ? TEMPPATH : ""), FD[FDIndex].BaseName,
			(FD[FDIndex].IsTemp ? TEMPPATH : ""), FD[FDIndex].BaseName);
	
		/* ...and remember the .r file. */
	
		if (!AddFile(FD[FDIndex].BaseName, WantLink, 'r')) {
			CleanUp();
			exit(1);
		}
	
	}
	
	DoLink()
	{
		int	i, j;
	
		if (WantChat)
			fprintf(stderr, "linking\n");
	
		/* c.link -o=<wherever> [-l=<library>]* <start> [<Name>.r]+ */
	
		fprintf(CmdOut, "%s -o=%s", LINKER, ExecPath);
	
		for (i = 0; i < LibAvail; i++)
			fprintf(CmdOut, " -l=%s", Lib[i]);
	
		/* ...you always get the default library */
		fprintf(CmdOut, " -l=%s/%s ", LIBDIR, LIBDEFAULT);
	
		if (StartPath == NULL)
			fprintf(CmdOut, "%s/%s", LIBDIR, STARTDEFAULT);
		else
			fprintf(CmdOut, "%s", StartPath);
	
		for (i = 0; i < FDAvail; i++)
			if (FD[i].Type == 'r')
				fprintf(CmdOut, " %s%s.r",
					(FD[i].IsTemp ? TEMPPATH : ""),
					FD[i].BaseName);
	
		if (Edition)
			fprintf(CmdOut, " -e=%s", Edition);
		if (Memory)
			fprintf(CmdOut, " -M=%s", Memory);
		putc('\n', CmdOut);
	
	}
	
	DoLoad(cmd)
	char	*cmd;
	{
		if (LoadComp) {
			fprintf(CmdOut, "%s %s\n", cmd, PREPROC);
			fprintf(CmdOut, "%s %s\n", cmd, COMPILER);
			if (WantOpt)
				fprintf(CmdOut, "%s %s\n", cmd, OPTIM);
		}
		if (LoadAsm)
			fprintf(CmdOut, "%s %s\n", cmd,  ASM);
	}
//E*O*F cc.c//

echo x - pipe.c
sed 's/^	//' > "pipe.c" << '//E*O*F pipe.c//'
	#include <stdio.h>
	#include <signal.h>
	
	int pipeno[20];	/* array of pipe process ids */
	
	/* Pipe - create an interprocess communication channel. This
	 * function is made to behave like the function of the same
	 * name under UNIX.
	 * Last Edit 6/15/84 Steve Blasingame
	 */
	int
	pipe(files)
	int	files[2];	/* array of file descriptors */
	{
		int ndx;
		for (ndx=0; ndx<2; ndx++)
			if ((files[ndx] = open("/pipe",ndx)) == -1)
				return(-1);
		return(0);
	}
	
	/* Popen - create a standard I/O channel to a subprocess. This
	 * function is designed to behave like the function of the same
	 * name under UNIX.
	 * Last Edit 6/15/84 Steve Blasingame
	 */
	FILE *
	popen(com, mode)
	char	*com;		/* command line to exec in subprocess	*/
	char	*mode;		/* mode for I/O "r" or "w" only apply	*/
	{
		int	sfd;	/* standard path file descriptor to use	*/
		int	fd;	/* file descriptor of the pipe stream	*/
		int	fdsave;	/* a place to save the standard stream	*/
		int	pid;	/* process id of child process		*/
		int	bsiz;	/* size of command line string		*/
		static char cbf[256];/* storage for command line buff	*/
	
		sfd = (mode[0] == 'r') ? 1 : 0; /* determine pipe type	*/
	
		if ((fd = open("/pipe",3)) == -1) /* open the stream	*/
			return(-1);
		fdsave = dup(sfd);	/* save the standard path	*/
		close(sfd);		/* now close the standard path	*/
		dup(fd);		/* now dup the stdio to a pipe	*/
	
		strcpy(cbf,"ex ");	/* execl over the shell		*/
		strcat(cbf,com);	/* stash line in local buffer	*/
		strcat(cbf,"\n");	/* append a newline character	*/
		bsiz = strlen(cbf); /* calculate length of command line	*/
	
		if ((pid=os9fork("/d0/Cmds/Shell",bsiz,cbf,1,1,0)) != -1) {
			close(sfd);	/* now close the standard path	*/
			dup(fdsave);	/* get back the old channel	*/
			pipeno[fd] = pid;	/* pid of child saved	*/
			return fdopen(fd,mode);	/* now return a stream	*/
		}
		else	return NULL;	/* the fork bombed... */
	}
	/* Pclose - Close a stdio stream created with Popen. This is
	 * necessary to synchronize processes on both sides of the stream.
	 * Edit of 6/30/84 Steve Blasingame
	 */
	pclose(ptr)
	FILE *ptr;
	{
	        register int	ndx;		/* array index of pipe numbers	*/
		register int	waitvalue;	/* value returned from wait	*/
			 int	(*intrf)();	/* current function of SIG_INT	*/
			 int	result;		/* return status from process	*/
	
	        ndx = fileno(ptr);	/* Get fd as index into pipeno[] of pid	*/
		fclose(ptr);
	        close(ndx);		/* close the stream */
	
		/* Turn off interrupts while waiting for the exit of the
		 * process to exit to insure synchronous behavior */
	        intrf = signal(SIGINT, SIG_IGN);
	
		/* wait on the process to exit and return error indx it doesn't exist */
	        while((waitvalue = wait(&result)) != pipeno[ndx] && waitvalue != -1) ;
	
		/* Now turn back on interrupts and return	*/
	        signal(SIGINT, intrf);	/* Turn back on interrupts	*/
		return((waitvalue == -1) ? waitvalue : result);
	}
//E*O*F pipe.c//

exit 0