[net.micro.amiga] cc.c

fnf@unisoft.UUCP (11/23/85)

As I promised in one of my earlier postings, enclosed is the source to my
unix like front end for the Lattice C compiler.  This is real useful
for most simple C programs without any amiga specific compilation or
linking requirements.

One of the features of this cc is that your development disk can be in
any drive, including ram, and cc will find it.

Does anyone know of a secret flag to get the Lattice compiler to emit
assembly language?  How about a flag to turn off those obnoxious
startup messages (I'm going to patch my compiler if necessary!).

Anyway, hope you find this useful.  I was going to post a version of
DECUS grep also, but looks like someone beat me to it.

-Fred		(415) 644-1230

============================  cc.c  follows  ==========================
/************************************************************************
 *									*
 *			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 Amiga and Lattice C.
 *
 *	Somewhat AMIGA/Lattice dependent, but can probably be adapted to
 *	other systems with a minimum of work.  I have attempted to keep
 *	portability in mind as much as possible.
 *
 */

char _sccsid[] = "@(#)cc.c	1.3";

#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 */

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

#define CMDBUFFERSIZE (256)	/* Size of command buffer for CLI command */
#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 QUADDEV to be the 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.
 */
 
#define QUADDEV	"ram:"		/* Keep intermediate files in ram */
/* #define QUADDEV "" */	/* Keep intermediate files in current dir */

/*
 *	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 */

/*
 *	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)
#define OFILE(op) (strcmp(op->Suffix,"o")==0)

#ifdef AMIGA
#  define system(a) (Execute(a,0,0))
#  define ENABLE_ABORT (Enable_Abort = 1)
#  define DISABLE_ABORT (Enable_Abort = 0)
extern int Enable_Abort;			/* Enable abort on CNTL-C */
#else
#  define ENABLE_ABORT				/* Null expansion */
#  define DISABLE_ABORT				/* Null expansion */
#endif	/* AMIGA */

/*
 *	Set list of places to search for various executables, libraries, etc.
 *	Searched in order, first match wins.
 */

static char *Devices[] = {
    "",
    "ram:",
    "df0:",
    "df1:",
    NULL
};

static char *BinDirs[] = {
    "",
    "ram:c/",
    "df0:c/",
    "df1:c/",
    NULL
};

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

static char *IncDirs[] = {
    "ram:include/",
    "df0:include/",
    "df1:include/",
    NULL
};

/*
 *	Flags set by command line arguments/
 */
 
static int cflag;			/* -c flag given */
static int Pflag;			/* -P flag given */
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 = "a.out";		/* Output file name from linker */
static char *QuadDev = QUADDEV;		/* Where to keep quad files */

static char *Locate ();			/* Find a file */
static void Check_Abort ();		/* Test for abort requested */
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 MakeObjects ();		/* Reduce to list of object files */
static void ParseCommandLine ();	/* Deal with command line */
static void Compile ();			/* Translate from .c to .o */
static void Assemble ();		/* Translate from .s to .o */
static void Link ();			/* Gather .o's into executable */

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

main (argc, argv)
int argc;
char *argv[];
{
    DBUG_ENTER ("main");
    ENABLE_ABORT;
    ParseCommandLine (argc, argv);
    MakeObjects ();
    if (!cflag && !Pflag && !Sflag && ErrCount == 0) {
	Link ();
    }
    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':
		    XARG (argc, argv, argp);
		    if (NDefines >= MAXDEFINES) {
			Fatal ("too many -D args (%d max)", MAXDEFINES);
		    }
		    Defines[NDefines++] = argp;
		    break;
		case 'E':
		    Warning ("-E unimplemented, converted to -P instead");
		    Pflag++;
		    break;
		case 'f':
		    break;	/* NOP for now, just eat it */
		case 'g':
		    break;	/* NOP for now, just eat it */
		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':
		    break;	/* NOP for now, just eat it */
		case 'o':
		    XARG (argc, argv, argp);
		    outfile = argp;
		    break;
		case 'P':
		    Pflag++;
		    break;
		case 'q':
		    XARG (argc, argv, argp);
		    QuadDev = argp;
		    break;
		case 'S':
		    Sflag++;
		    Warning ("-S option not yet implemented, ignored");
		    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;
	    }
	}
    }
    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;

    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)) {
	    Compile (op);
	} else if (SFILE (op)) {
	    Assemble (op);
	}
    }
    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 cmdbuf[CMDBUFFERSIZE];
    
    DBUG_ENTER ("Link");
    sprintf (cmdbuf, "%s", Locate ("alink", BinDirs));
    sprintf (cmdbuf, "%s %s", cmdbuf, Locate ("Lstartup.obj", LibDirs));
    for (index = 0; index < NOperands; index++) {
	op = &Operands[index];
	if (OFILE (op)) {
	    name = op -> Rootname;
	} else {
	    name = op -> Basename;
	}
	sprintf (cmdbuf, "%s+%s.o", cmdbuf, name);
    }
    sprintf (cmdbuf, "%s library ", cmdbuf);
    for (index = 0; index < NLibs; index++) {
	sprintf (cmdbuf, "%s%s.lib+", cmdbuf, Locate (Libs[index], LibDirs));
    }
    sprintf (cmdbuf, "%s%s+", cmdbuf, Locate ("lc.lib", LibDirs));
    sprintf (cmdbuf, "%s%s", cmdbuf, Locate ("amiga.lib", LibDirs));
    sprintf (cmdbuf, "%s to %s", cmdbuf, outfile);
    sprintf (cmdbuf, "%s map nil:", cmdbuf);
    (void) RunCommand (cmdbuf);
    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);
}

/*
 *	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 may 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;
}

static void Compile (op)
struct Operand *op;
{
    DBUG_ENTER ("Compile");
    if (!Sflag && Pass0 (op) && !Pflag) {		/* Order important! */
	Check_Abort ();
	(void) Pass1 (op);
    }
    DBUG_VOID_RETURN;
}

/*
 *	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.
 */
 
static int Pass0 (op)
register struct Operand *op;
{
    register int status;
    register int index;
    auto char cmdbuf[CMDBUFFERSIZE];
    
    DBUG_ENTER ("Pass0");
    sprintf (cmdbuf, "%s", Locate ("lc1", BinDirs));
    if (Pflag) {
	sprintf (cmdbuf, "%s -o%s.i -p", cmdbuf, op -> Basename);
	sprintf (cmdbuf, "%s -DAMIGA -DM68000 -DSPTR", cmdbuf);
    } else {
	sprintf (cmdbuf, "%s -o%s%s.q", cmdbuf, QuadDev, op -> Basename);
    }
    for (index = 0; index <NUserInc; index++) {
	sprintf (cmdbuf, "%s -i%s/", cmdbuf, UserInc[index]);
    }
    if (NUnDefines != 0) {
	Warning ("-U options not currently handled, ignored!");
    }
    /*************************
    for (index = 0; index <NUnDefines; index++) {
	sprintf (cmdbuf, "%s -u%s", cmdbuf, UnDefines[index]);
    }
    **************************/
    for (index = 0; index <NDefines; index++) {
	sprintf (cmdbuf, "%s -d%s", cmdbuf, Defines[index]);
    }
    sprintf (cmdbuf, "%s -i%s/", cmdbuf, Locate ("include", Devices));
    sprintf (cmdbuf, "%s -i%s/", cmdbuf, Locate ("lattice", IncDirs));
    sprintf (cmdbuf, "%s %s", cmdbuf, op -> Rootname);
    status = RunCommand (cmdbuf);
    DBUG_RETURN (status);
}

static int Pass1 (op)
struct Operand *op;
{
    int status;
    auto char cmdbuf[CMDBUFFERSIZE];
    
    DBUG_ENTER ("Pass1");
    sprintf (cmdbuf, "%s", Locate ("lc2", BinDirs));
    sprintf (cmdbuf, "%s -o%s.o", cmdbuf, op -> Basename);
    sprintf (cmdbuf, "%s %s%s", cmdbuf, QuadDev, op -> Basename);
    status = RunCommand (cmdbuf);
    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 void Assemble (op)
struct Operand *op;
{
    DBUG_ENTER ("Assemble");
    Warning ("assembly pass not yet implemented");
    ErrCount++;
    DBUG_VOID_RETURN;
}

/*
 *	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.
 */
 
static int RunCommand (command)
char *command;
{
    int status;
    
    DBUG_ENTER ("RunCommand");
    DBUG_3 ("cmd", "execute '%s'", command);
    if (Vflag) {
	printf ("%s\n", command);
	(void) fflush (stdout);
    }
    Check_Abort ();
    status = system (command);
    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[64];
    
    DBUG_ENTER ("Locate");
    while (*vec != NULL) {
	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);
}

/*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);
}

/*
 *	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.
 *
 */
 
static void Check_Abort ()
{
    DBUG_ENTER ("Check_Abort");
#ifdef AMIGA
    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;
#endif
    DBUG_VOID_RETURN;
}

peter@graffiti.UUCP (Peter da Silva) (11/24/85)

> As I promised in one of my earlier postings, enclosed is the source to my
> unix like front end for the Lattice C compiler.  This is real useful
> for most simple C programs without any amiga specific compilation or
> linking requirements.

While on the subject of UNIX front-ends... a while ago there was an IBM-PC
version of Make posted to net.sources. If anyone would like to try porting
it to the AMIGA (and tell me how the port goes, so I can use the info in my
purchase decision), I may be able to get it to them (no guarantees, since it's
archived somewhere in my "old sources" cupboard).
-- 
Name: Peter da Silva
Graphic: `-_-'
UUCP: ...!shell!{graffiti,baylor}!peter
IAEF: ...!kitty!baylor!peter

fnf@unisoft.UUCP (11/28/85)

In article <467@graffiti.UUCP> peter@graffiti.UUCP (Peter da Silva) writes:
>
>> As I promised in one of my earlier postings, enclosed is the source to my
>> unix like front end for the Lattice C compiler..

Turns out the 200 character limitation in printf/sprintf strings causes real
problems with large command lines.  I'll be posting a new, expanded version
in a few days.

>
>While on the subject of UNIX front-ends... a while ago there was an IBM-PC
>version of Make posted to net.sources. If anyone would like to try porting
>it to the AMIGA (and tell me how the port goes, so I can use the info ...

As of this morning (about 3AM :-), this was up and running on my amiga.
I can use it to remake itself, so the basic functionality is there.
However, the are still a couple of bugs that are corrupting memory.

Great little machine, but it REALLY needs some form of memory protection.
Bugs that I would find in an hour on a protected machine are taking ten
times as long as this machine.  Luckily, my macro based C debugging system
is working very well in this sort of environment.  Just redefine the
macros to examine critical data structures at each function entry and
exit point, and the offending code is quickly isolated to a particular
function.  Maybe its time I posted this package also...

-Fred

===========================================================================
Fred Fish    UniSoft Systems Inc, 739 Allston Way, Berkeley, CA  94710  USA
{ucbvax,dual}!unisoft!fnf	(415) 644 1230 		TWX 11 910 366-2145
===========================================================================