/************************************************************************
 *									*
 *			Copyright (c) 1985, Fred Fish			*
 *			    All Rights Reserved				*
 *									*
 *	This software and/or documentation is released into the		*
 *	public domain for personal, non-commercial use only.		*
 *	Limited rights to use, modify, and redistribute are hereby	*
 *	granted for non-commercial purposes, provided that all		*
 *	copyright notices remain intact and all changes are clearly	*
 *	documented.  The author makes no warranty of any kind with	*
 *	respect to this product and explicitly disclaims any implied	*
 *	warranties of merchantability or fitness for any particular	*
 *	purpose.							*
 *									*
 ************************************************************************
 */


/*
 *	cc -- C compiler front end for Sozobon-C.
 *
 *	Very zc/A68k/Blink dependent.  ccx was adapted from the
 *	Lattice and Aztec C versions by Fred Fish on Fish #2
 *
 */

/*
 * Maintenance Notes:
 * 06Mar89 - (Jal) Only look in <current directory>, "ZC:", or "RAM:". 
 * 13Jun89 - Jal.  Replace Manx fexecv() with one that returns exit() value.
 * 17Jun89 - Jal.  Force A68k to print only every 100'th line.
 */

#include <stdio.h>

/*
 *	The following allow use on systems that don't have my macro based
 *	debugging package.  The default, for now, is to assume it is not
 *	available.
 */
 
#ifdef DBUG
#  include <local/dbug.h>
#else	/* !DBUG */
#  define DBUG_ENTER(a)
#  define DBUG_RETURN(a) return(a)
#  define DBUG_VOID_RETURN return
#  define DBUG_2(a,b)
#  define DBUG_3(a,b,c)
#  define DBUG_4(a,b,c,d)
#  define DBUG_5(a,b,c,d,e)
#  define DBUG_PUSH(a)
#endif	/* DBUG */

/*
 *	IMPLEMENTATION NOTES
 *
 *	Some of the builtin (artificial) limits could be removed by
 *	using dynamically allocated data structures, such as keeping the
 *	operand list as a linked list with space for each link supplied
 *	by malloc.  This would certainly increase the code space, while
 *	reducing the statically allocated data space.  The net result
 *	would probably be a program requiring about the same amount of
 *	total memory for most day to day usages.  When source is not
 *	available to the end user, maximum flexibility is a must and
 *	dynamic allocation is the only way to go.  In this case however
 *	it is not clear that the added complexity is worth it, since
 *	source should be available for anyone wishing to expand the
 *	limits.
 *
 *	One last note, if you are going to have builtin limits then
 *	check the @#$%&* things for overflow.  Running off the end of
 *	an array with no indication that something is wrong, other
 *	than a crash, if definitely unfriendly!
 *
 */
 

/*
 *	Manifest constants which can be tuned to fit requirements.
 */

#define CMDBUFFERSIZE (1024)	/* Size of command buffer for CLI command */
#define MAXCMDARGV (32)		/* Maximum arguments for Cmdargv[] */ 
#define MAXOPERANDS (64)	/* Maximum number of operands on cmd line */
#define MAXDEFINES (32)		/* Maximum number of -D<name> args */
#define MAXUNDEFINES (32)	/* Maximum number of -U<name> args */
#define MAXINCDIRS (16)		/* Maximum number of -I<filename> args */
#define MAXLIBS (16)		/* Maximum number of -l<lib> args */
#define ARGSIZE (64)		/* Size of temp args for cmd line */

/*
 *	Define QUADDEV to be the default place where you want the compiler
 *	intermediate files (quad files) to be created.  For systems with
 *	512K or more, the preferred place is the ram disk.  However,
 *	really big compiles may need to be done on a hard disk.
 *	In either case, the default can be overridden with the -q option.
 */
 
#define QUADDEV	"RAM:"		/* Keep intermediate files in ram */

/*
 *	Manifest constants which are generally the same on all systems.
 */

#define EOS '\000'		/* End of string character */

/*
 *	Command line arguments that represent files to be compiled, assembled,
 *	or linked, are kept track of via an "Operand" array.  If, for example,
 *	the file name is "df0:mydir/junk.c", then the Rootname is
 *	"df0:mydir/junk", the Basename is "junk", and the Suffix is "c".
 *	String suffixes are used, rather than single character suffixes, to
 *	allow use of names with multicharacter suffixes.
 */
 
struct Operand {		/* Info about each operand (non option) */
	char *Rootname;		/* Name minus any suffix */
	char *Basename;		/* Name minus any prefix or suffix */
	char *Suffix;		/* Suffix of operand */
};

static struct Operand Operands[MAXOPERANDS];	/* Table of operands */
static int NOperands = 0;			/* Number of operands found */
static char *Defines[MAXDEFINES];		/* Table of defines */
static int NDefines = 0;			/* Number of defines */
static char *UnDefines[MAXUNDEFINES];		/* Table of undefines */
static int NUnDefines = 0;			/* Number of undefines */
static char *UserInc[MAXINCDIRS];		/* Table of include dirs */
static int NUserInc = 0;			/* Number of include dirs */
static char *Libs[MAXLIBS];			/* Table of library args */
static int NLibs = 0;				/* Number of library args */
#ifdef AZTEC_C
static char *CmdArgv[MAXCMDARGV];		/* Table of program args */
static int NCmdArgs = 0;			/* Number of args in table */
#endif
static int GlobalStatus;			/* -1 = Ok, 0 = Oh, Oh. */

/*
 *	The command line buffer for child commands is allocated statically,
 *	rather than as an automatic, to forstall problems with stack limits
 *	in initial implementations.  Hopefully, automatic stack growth will
 *	be implemented in future release of the C compiler.  If nothing
 *	else, someday I will read the manuals and figure out how to explicitly
 *	grow the stack...
 *
 */

static char Command[CMDBUFFERSIZE];		/* Command line buffer */
static char *EndCommand = Command;		/* End of current command */

/*
 *	Macros to determine the suffix type of a file given a pointer to
 *	its operand structure.
 */
 
#define CFILE(op) (strcmp(op->Suffix,"c")==0)
#define SFILE(op) (strcmp(op->Suffix,"s")==0 || strcmp(op->Suffix,"a")==0)
#define OFILE(op) (strcmp(op->Suffix,"o")==0)
extern int strcmp ();

/*
 *	Now some macros to map from unix names to the AMIGA equivalents,
 *	and to enable abort on control-C.
 */
 
#ifdef amiga
#  define system(a) (Execute(a,0,0))
#  define ENABLE_ABORT (Enable_Abort = 1)
#  define DISABLE_ABORT (Enable_Abort = 0)
#  define CHECK_ABORT Check_Abort()
   extern int Enable_Abort;			/* Enable abort on CNTL-C */
   static void Check_Abort ();		/* Test for abort requested */
#else
#  define ENABLE_ABORT				/* Null expansion */
#  define DISABLE_ABORT				/* Null expansion */
#  define CHECK_ABORT				/* Null expansion */
#endif	/* amiga */

/*
 *	Set list of places to search for various executables, libraries, etc.
 *	Searched in order, first match wins, null string is current directory.
 *	Note that these names are used as prefixes exactly as given, so
 *	device names must end in ':' and directory names must end in '/'.
 *
 */

static char *Devices[] = {
	"",
	"ZC:",
	NULL
};

static char *BinDirs[] = {
	"",
	"ZC:c/",
	"RAM:c/",
	NULL
};

static char *LibDirs[] = {
	"",
	"zc:lib/",
	NULL
};

static char *IncDirs[] = {
	"ZC:include",
	NULL
};

/*
 *	Flags set by command line arguments/
 */
 
static int cflag;			/* -c flag given */
static int dflag;			/* -d debug flag */
static int vflag;			/* -v verbose name */
static int Cflag;			/* -C bss, data -> chip memory */
static int Fflag;			/* -F bss, data -> fast memory */
static int Oflag;			/* -O zc optimize flag */
static int Pflag;			/* -P profiler option */
static int Sflag;			/* -S flag given */
static int Vflag;			/* -V flag given (non-standard) */

static int ErrCount = 0;		/* Count of compile/assemble errors */
static char *outfile = NULL;		/* Output file name from linker */
static char *QuadDev = QUADDEV;		/* Where to keep quad files */

static char *predefines = "-Damiga -Dm68000 -Dzc";

static char *Locate ();			/* Find a file */
static void AddToCommand ();		/* Add argument to command buffer */
static void InitCommand ();		/* Initialize command buffer */
static void Fatal ();			/* Quit with fatal error */
static void Warning ();			/* Issue warning message */
static void AddOperandToList ();	/* Add .c, .s, or .o file to list */
static void CleanObjects ();		/* Remove .o for link and go mode */
static void MakeObjects ();		/* Reduce to list of object files */
static void ParseCommandLine ();	/* Deal with command line */
static int Compile ();			/* Translate from .c to .o */
static int Assemble ();			/* Translate from .s to .o */
static void Link ();			/* Gather .o's into executable */

extern void exit ();			/* See exit(2) */

/*
 *	Main entry point.  Note that despite common usage where main is
 *	essentially of type void, we declare it explicitly to return
 *	an int, and actually return a value.  In most implementations,
 *	the value returned from main is the exit status of the program.
 *	Whether this applies to Lattice C or not, I'm not sure yet.
 */
 
int main (argc, argv)
int argc;
char *argv[];
{
	DBUG_ENTER ("main");
	ENABLE_ABORT;
	DISABLE_ABORT;
	GlobalStatus = -1;
	ParseCommandLine (argc, argv);
	MakeObjects ();
	if (!cflag && !Sflag && ErrCount == 0 && GlobalStatus) {
		Link ();
		CleanObjects ();
	}
	DBUG_RETURN (0);
}

/*
 *	The following macro is used to allow optional whitespace between
 *	an option and it's argument.  Argp is left pointing at the option
 *	and argv and argc are adjusted accordingly if necessary.
 *
 *	Note that there is no check for missing option arguments.  In
 *	particular, -o -V will blindly take -V as the output file name.
 *
 */

#define XARG(argc,argv,argp) {if(*++argp==EOS){argp=(*argv++);argc--;}}

static void ParseCommandLine (argc, argv)
int argc;
char **argv;
{
	register char *argp;    

	DBUG_ENTER ("ParseCommandLine");
	argc--;
	argv++;
	while (argc-- > 0) {
	CHECK_ABORT;
	argp = *argv++;
	if (*argp != '-') {
		AddOperandToList (argp);
	} else {
		switch (*++argp) {
		case '#':
			XARG (argc, argv, argp);
			DBUG_PUSH (argp);
			break;
		case 'c':
			cflag++;
			break;
		case 'd':
			dflag++;
			break;
		case 'v':
			vflag++;
			break;
		case 'D':
			XARG (argc, argv, argp);
			if (NDefines >= MAXDEFINES)
				Fatal ("too many -D args (%d max)", MAXDEFINES);
			Defines[NDefines++] = argp;
			break;
		case 'I':
			XARG (argc, argv, argp);
			if (NUserInc >= MAXINCDIRS)
				Fatal ("too many -I args (%d max)", MAXINCDIRS);
			UserInc[NUserInc++] = argp;
			break;
		case 'l':
			XARG (argc, argv, argp);
			if (NLibs > MAXLIBS)
				Fatal ("too many -l args (%d max)", MAXLIBS);
			Libs[NLibs++] = argp;
			break;
		case 'O':
			Oflag++;
			break;
		case 'o':
			XARG (argc, argv, argp);
			outfile = argp;
			break;
		case 'q':		/* Warning, non-standard */
			XARG (argc, argv, argp);
			QuadDev = argp;
			break;
		case 'C':
			Cflag++;
			break;
		case 'F':
			Fflag++;
			break;
		case 'P':
			Pflag++;
			break;
		case 'A':
		case 'S':
			Sflag++;
			break;
		case 's':
			break;		/* NOP for now, just eat it */
		case 'U':
			XARG (argc, argv, argp);
			if (NUnDefines >= MAXUNDEFINES)
				Fatal ("too many -U args (%d max)", MAXUNDEFINES);
			UnDefines[NUnDefines++] = argp;
			break;
		case 'V':
			Vflag++;
			break;
		default:
			Warning ("unknown option '%c'", (char *) *argp);
			break;
		}
	}
	}

	/*
	 * If no -o option, use the first file in the list.
	 */

	if ( outfile == NULL )
	outfile = Operands[0].Basename;

	DBUG_VOID_RETURN;
}

/*
 *	For each operand, do compilation or assembly as necessary, to
 *	reduce to an object file in the current directory.
 */

static void MakeObjects ()
{
	register int index;
	register struct Operand *op;
	auto char buffer[ARGSIZE];

	DBUG_ENTER ("MakeObjects");
	for (index = 0; index < NOperands; index++) {
		CHECK_ABORT;
		op = &Operands[index];
		if (NOperands > 1 && (CFILE (op) || SFILE (op)))
			printf ("%s.%s:\n", op -> Rootname, op -> Suffix);
		if (CFILE (op)) {
			if ( (GlobalStatus = Compile (op)) == 0)
				return;
			if (!Sflag) {
				if ( (GlobalStatus = Assemble (op)) == 0 )
					return;
				sprintf (buffer, "%s%s.s", QuadDev, op -> Basename);
				if (!DeleteFile (buffer))
					Warning ("can't delete '%s'", buffer);
			}
		} else if (SFILE (op))
			if ( (GlobalStatus = Assemble (op)) == 0 )
				return;
	}

	DBUG_VOID_RETURN;
}

/*
 *	Note that commands to cc of the form "-l<name>" get interpreted
 *	to mean use a library called "name.lib" from the library
 *	directory.
 */

static void Link ()
{
	register int index;
	register struct Operand *op;
	register char *name;
	auto char buffer[ARGSIZE];
	int status;
	
	DBUG_ENTER ("Link");
	InitCommand ();
	AddToCommand ("%s ", Locate ("Blink", BinDirs));
	AddToCommand ("%s", Locate ("begin.o", LibDirs));
	for (index = 0; index < NOperands; index++) {
		op = &Operands[index];

		if (OFILE (op))
			name = op -> Rootname;
		else
			name = op -> Basename;
		AddToCommand ("+%s.o", name);
	}
	AddToCommand ("%s", " library ");
	for (index = 0; index < NLibs; index++) {
		sprintf (buffer, "%s.lib", Libs[index]);
		AddToCommand ("%s+", Locate (buffer, LibDirs));
	}
	AddToCommand ("%s", Locate ("ZC.lib", LibDirs));
	AddToCommand ("%s", Locate ("Ami.lib", LibDirs));
	AddToCommand (" to %s map nil:", outfile);
	if ( !dflag )
		AddToCommand ( " NODEBUG");
	status = RunCommand ();
	DBUG_VOID_RETURN;
}
 

/*VARARGS1*/
static void Warning (fmt, arg1, arg2, arg3)
char *fmt;
char *arg1;
char *arg2;
char *arg3;
{
	fprintf (stderr, "cc -- warning: ");
	fprintf (stderr, fmt, arg1, arg2, arg3);
	fprintf (stderr, "\n");
	(void) fflush (stderr);
}

/*VARARGS1*/
static void Fatal (fmt, arg1, arg2, arg3)
char *fmt;
char *arg1;
char *arg2;
char *arg3;
{
	fprintf (stderr, "cc -- fatal error: ");
	fprintf (stderr, fmt, arg1, arg2, arg3);
	fprintf (stderr, "\n");
	(void) fflush (stderr);
	exit (1);
}

/*
 *	Split an operand name into rootname, basename, and suffix
 *	components.  The rootname is the full name, minus any suffix,
 *	but including any prefix.  The basename is the rootname minus
 *	any prefix.  The suffix is anything after the last '.' character.
 *	Only the suffix is allowed to be the null string.
 */

static void AddOperandToList (filename)
char *filename;
{
	register char *split;
	register struct Operand *op;
	extern char *strrchr ();

	DBUG_ENTER ("AddOperandToList");
	DBUG_3 ("ops", "add file '%s' to operand list", filename);
	if (NOperands >= MAXOPERANDS) {
		Fatal ("too many files (%d max)\n", MAXOPERANDS);
	}
	op = &Operands[NOperands];
	op -> Rootname = filename;
	if ((split = strrchr (filename, '/')) == NULL) {
		split = strrchr (filename, ':');
	}
	if (split == NULL) {
		op -> Basename = filename;
	} else {
		op -> Basename = ++split;
	}
	if ((split = strrchr (filename, '.')) == NULL) {
		op -> Suffix = "";
	} else {
		*split++ = EOS;
		op -> Suffix = split;
	}
	DBUG_3 ("ops", "rootname '%s'", op -> Rootname);
	DBUG_3 ("ops", "basename '%s'", op -> Basename);
	DBUG_3 ("ops", "suffix '%s'", op -> Suffix);
	NOperands++;
	DBUG_VOID_RETURN;
}

/*
 *	Compile one operand from a C source program to an object module.
 */
 
static int Compile (op)
struct Operand *op;
{
	int status;
	DBUG_ENTER ("Compile");
	status = Pass1 (op);
	CHECK_ABORT;

	if ( status )
		status = Pass2(op);
	DBUG_RETURN(status);
}

/*
 *	Note that because of brain-damage in the fact that -p to lc1 removes
 *	all predefined defs, we must add them so replacing -c with -P in the
 *	cc command line will result in the same set of predefined symbols.
 *	This is rather ugly and leaves a hole for future problems if we
 *	get out of sync with respect to what names the compiler predefines.
 */
 
static void
AddIncludes()
{
	int index;

	for (index = 0; index <NUserInc; index++) {
		AddToCommand (" -I%s", UserInc[index]);
	}

	for (index = 0; index <NUnDefines; index++) {
		/*************************
		AddToCommand (" -u%s", UnDefines[index]);
		**************************/
		Warning ("-U%s ignored! (unimplemented)", UnDefines[index]);
	}

	for (index = 0; index <NDefines; index++) {
		AddToCommand (" -D%s", Defines[index]);
	}
	AddToCommand (" -I%s", Locate ("include", Devices));
}

static int Pass1 (op)
register struct Operand *op;
{
	register int status;
	register int index;
 	
	DBUG_ENTER ("Pass1");
	InitCommand ();
	AddToCommand ("%s", Locate ("ZC", LibDirs));

	AddIncludes();
	if ( vflag )
		AddToCommand ( "-V" );

	if ( Cflag )
		AddToCommand ( "-C" );

	if ( Fflag )
		AddToCommand ( "-F" );

	if (Sflag) {
		AddToCommand (" -O%s.s", op -> Basename);
	} else {
		AddToCommand (" -O%s%s.s", QuadDev, op -> Basename);
	}
	 AddToCommand (" %s.c", op->Basename);

	status = RunCommand ();
	DBUG_RETURN (status);
}

static int Pass2 (op) /* Optimize */
register struct Operand *op;
{
	register int status;
	register int index;
 	
	DBUG_ENTER ("Pass2");
	InitCommand ();
	AddToCommand ("%s", Locate ("top", LibDirs));

	if (!Oflag) /* Optimize not wanted, but we need to consolidate. */
		AddToCommand (" -a");

	if ( vflag )
		AddToCommand (" -v");

	if (Sflag) {
		AddToCommand (" %s.s", op -> Basename);
	} else {
		AddToCommand (" %s%s.s", QuadDev, op -> Basename);
	}

	status = RunCommand ();
	DBUG_RETURN (status);
}


/*
 *	I have not yet had occasion to use the macro assembler, so this
 *	part is not yet implemented.  If anyone wants to send me the
 *	appropriate code, I will be glad to install it.
 */
 
static int Assemble (op)
struct Operand *op;
{
	register int status;
	register int index;
	
	DBUG_ENTER ("Assemble");
	InitCommand ();
	AddToCommand ("%s", Locate ("A68k", BinDirs));

	if ( dflag )
		AddToCommand( " -d" );

	AddToCommand (" -q100 -o%s.o", op -> Basename);
	if (CFILE (op)) {
		AddToCommand (" %s%s.s", QuadDev, op -> Basename);
	} else {
		AddToCommand (" %s.%s", op -> Rootname, op -> Suffix);
	}
	status = RunCommand ();
	DBUG_RETURN (status);
}

/*
 *	As far as I can tell, the child status is not returned, only
 *	whether or not the child could be run.  So, how do we find out
 *	whether there was an error or not?  It's probably in the manuals
 *	somewhere, I just haven't had time to dig yet.
 *
 *	Note that because Lattice printf is not capable of printing more
 *	than 200 characters at a time, we must spit them out one at a time
 *	to make sure the entire command line gets printed when -V is used.
 *
 */
 
static int RunCommand ()
{
	int status;
	register char *cmdp;
	register int i;
	extern int errno;

#ifdef AZTEC_C
	int   fexecv();
#endif
	
	DBUG_ENTER ("RunCommand");
	DBUG_3 ("cmd", "execute '%s'", Command);
	CHECK_ABORT;
	/*
	 * Allow use of Aztec's fexecv() function
	 */
#ifndef AZTEC_C */
	if (Vflag) {
	for (cmdp = Command; *cmdp != EOS; cmdp++) {
		putchar (*cmdp);				/* see above */
	}
	putchar ('\n');
	(void) fflush (stdout);
	}
	status = system (Command);
#else
	if ( NCmdArgs >= MAXCMDARGV )
	Fatal( "Number of Command line arguments exceeds maximum of %d\n",
		    MAXCMDARGV);
	CmdArgv[NCmdArgs] = NULL;
	if (Vflag){
		for (i=0; i<NCmdArgs; ++i )
			printf("%s ", CmdArgv[i]);
		printf("\n");
	}

	status = fexecv( CmdArgv[0], CmdArgv );

	if (status == 0)
		status = -1;
	else
		status = 0;
#endif
	DBUG_3 ("sys", "subcommand returns status %d", status);
	if (!status) {
		ErrCount++;
	}
	DBUG_RETURN (status);
}

/*
 *	Look through the list of paths pointed to by "vec" until we find
 *	a file with name given pointed to by "namep".  If none is found,
 *	the name pointed to by namep is returned.
 */
 
static char *Locate (namep, vec)
char *namep;
char **vec;
{
	static char namebuf[ARGSIZE];
	
	DBUG_ENTER ("Locate");
	while (*vec != NULL) {
		(void) sprintf (namebuf, "%s%s", *vec, namep);
		DBUG_3 ("try", "look for '%s'", namebuf);
		if (Readable (namebuf)) {
			namep = namebuf;
			break;
		}
		vec++;
	}
	DBUG_RETURN (namep);
}

/*
 *	Check to see if the file exists and is readable.
 */

#ifdef unix
#  include <fcntl.h>
#else
#  include <libraries/dos.h>
#endif

static int Readable (name)
char *name;
{
	register int status = 0;
	register int fildes;
	
	DBUG_ENTER ("Readable");
#ifdef unix
	fildes = open (name, O_RDONLY);
	if (fildes >= 0) {
		(void) close (fildes);
		status = 1;
	}
#else
	fildes = Lock (name, ACCESS_READ);
	if (fildes != 0) {
		UnLock (fildes);
		status = 1;
	}
#endif
	DBUG_RETURN (status);
}

/*
 *	Do explicit check for abort.  When Enable_Abort is non-zero,
 *	Chk_Abort() cause program termination if CNTRL-C or CNTRL-D has
 *	been received.  Thus, we temporarily set it back to zero while we
 *	do the explicit test, so we can do our own clean up and exit.
 *	Note that if the -V flag was used, we spit out a confirming message
 *	that we are quitting.
 *
 *	Since we previously set Check_Abort to non-zero, this routine may be
 *	overkill.
 */
 
#ifdef amiga
static void Check_Abort ()
{
	extern int Chk_Abort ();
	
	DBUG_ENTER ("Check_Abort");
	DBUG_2 ("abort", "do explicit test for CNTRL-C");
	DISABLE_ABORT;
	if (Chk_Abort () != 0) {
		if (Vflag) {
			printf ("cc - terminated by request\n");
		}
		exit (1);
	}
	ENABLE_ABORT;
	DBUG_VOID_RETURN;
}
#endif

/*
 *	Initialize the command line buffer and associated variables to
 *	discard any previous command line.
 */

static void InitCommand ()
{
	Command[0] = EOS;
	EndCommand = &Command[0];
#ifdef AZTEC_C
	NCmdArgs   = 0;
#endif
}

/*
 *	Build string to add to end of current command line, checking
 *	for overflow in the command buffer and maintaining the pointer
 *	to the end of the current command.
 *
 *	Note that we are a "printf type" of command, and can be called
 *	with up to three "char *" arguments.  There is a portability
 *	problem here, but Lattice hasn't yet made "varargs" a standard
 *	part of their distribution.
 *
 *	Also, note that the return argument of sprintf is supposed to be
 *	the number of characters to be added to the buffer.  This is
 *	not always true for some C implementations.  In particular,
 *	sprintf in BSD4.1 returns a pointer.  Thus we don't use the
 *	return argument.
 *
 */

/*VARARGS1*/
static void AddToCommand (fmt, arg1, arg2, arg3)
char *fmt;
char *arg1, *arg2, *arg3;
{
	register int length;
	register char *s;
	auto char buffer[ARGSIZE];
	auto char word[64];
	char *getword();

	(void) sprintf (buffer, fmt, arg1, arg2, arg3);
	length = strlen (buffer);
	if ((EndCommand - Command) + length >= sizeof (Command)) {
		Fatal ("command line too long (%d char max)", sizeof (Command));
	} else {
#ifndef AZTEC_C
		(void) strcat (EndCommand, buffer);
		EndCommand += length;
#else
	/*
	 * Break the buffer into words and store pointer to it in CmdArgv.
	 */

		s = &buffer[0];
		while (1){
			s = getword( s, word );
			if ( *word == '\0' )
				break;
			if (NCmdArgs < MAXCMDARGV )
				CmdArgv[NCmdArgs++] = EndCommand;
			else
		  		Fatal("Can't handle more than %d command line arguments.\n",
			      MAXCMDARGV);  	
			(void)strcpy( EndCommand, word );
			EndCommand += strlen(word)+1;	
#endif
		}
	}
}

/*
 *	If an executable is made from a single C file, the normal behavior
 *	for the unix environment is to treat the .o file as an intermediate
 *	file and remove it, so we follow suit.
 */
 
static void CleanObjects ()
{
	auto char buffer[ARGSIZE];
	register struct Operand *op;
	
	DBUG_ENTER ("CleanObjects");
	if (NOperands == 1) {
		op = &Operands[0];
		if (CFILE (op) || SFILE (op)) {
			sprintf (buffer, "%s.o", op -> Basename);
			if (!DeleteFile (buffer))
				Warning ("can't delete '%s'", buffer);
		}
	}
	DBUG_VOID_RETURN;
}
#ifdef AZTEC_C
/*
 * getword: fill in first word pointed at by s in word
 * 		return position of first char after the word
 */
	
static char *getword(s, word)
register char *s, *word;
{
	/*
	 * Get past any white space.
	 */

	for (; *s; ++s)
		if ( *s != ' ' && *s != '\t' )
			break;

	/*
	 * Copy all non-blank words into "word".
	 */
	for (; *s; ++s){
		if ( *s != ' ' && *s != '\t' )
			*word++ = *s;
		else
			break;
	}

	*word = '\0';
	return s;
}
#endif
