/*  preprocessor for zc (formerly hcc) , a68k assembler, blink
*/


#include <stdio.h>

#define toupper( x ) ( x > 'Z' ? x - ('a'-'A'): x )

char *malloc();
extern char *getenv();

	       /* flags if 1 then will allow that operation
		* to be performed on a file		   */
short dozc,doa68k,doblink,dodebug,debug;
short blinkxref,startup,standardblinklib;

doexec( x,errmsg,file )
char x[],*errmsg,*file;
{
if( debug ){
  printf("%s\n",x);
  return(1);
}
if( ! Execute(x,0L,0L) ){
  printf(errmsg,file);
  quit();
}
return(1);
}

char libdir[32];	      /* librarys' dir             */
char cmddir[32];	     /*  zc,a68k,blink 's dir     */
char xrefname[128];	    /* blink xref file output	 */
char blinklibs[128];	   /* blink lib direc		*/
char blinkname[128];	  /* output file from a68k     */
char a68kname[128];	 /* output file from zc       */
char zcopts[128];	/* zc options for each phase */
char a68kopts[128];    /* a68k options		    */
char zctemp[128];     /* CCTEMP dir		   */
char blinkopts[128]; /* blink options		  */
char buf[256];	    /* command line		 */
char buf2[256];    /* scratch buf		*/

/* memory allocation/deallocation routines */
struct mem {
struct mem *prev;
long len;
char *memory;
} *curmem;


char *jmalloc(n)
long n;
{
char *buf;
struct mem *memlist;
buf=(char *)malloc( n+(long )sizeof(struct mem));
if(!buf){
    printf("cc: outofmemory\n");
    quit();
}
memlist=(struct mem *)buf;
memlist->len=n;
memlist->memory=buf+sizeof(struct mem);
memlist->prev=curmem;
curmem=memlist;
return(memlist->memory);
}

quit()
{

  jfree();
  exit(0);
}

jfree(){
struct mem *nxmem;

while(curmem != NULL){
  nxmem=curmem->prev;
  free(curmem);  /* frees all the memory we so graciously gobbled up */
  curmem=nxmem;
}
}/*end of jfree */



/*********************************************************

  Handles file list compiles,assembles,links

**********************************************************/


/* lists of source file names, as well as type of operation */

struct filenm{
  struct filenm *next;
  struct filenm *prev;
  short ab;	 /* abnormal file name */
  short zc;	  /* 1 if .c file */
  short a68k;	  /* 1 if .s file */
  char *name;
}*files,*curfile;


#define copystring( x )  (char *)strcpy( jmalloc( (long )len((char *) x )), x )


/* adds a file to the file list
 *   computes whether an compile and/or assembly will be done
 *   also determines whether this is an abnormal file name.
 *   If so then only a link will be done on the file
 */
struct filenm *addfile(name)
char name[];
{
long l;
struct filenm *newfile;
char *filename;

newfile = (struct filenm *)jmalloc(sizeof(struct filenm));
newfile->prev = curfile;
newfile->next = NULL; /* last file so far */
if(curfile)
   curfile->next = newfile;
else
   files = newfile;
curfile = newfile;
filename = (char *) copystring( (char *)name);
curfile->name=filename;    /* contains the new filename */
l=len(filename);

curfile->ab=0; /* assume it is normal until proven otherwise */
curfile->zc=0;
curfile->a68k=0;

if(filename[l-2] == '.'){
  if(filename[l-1]== 'c'){
     curfile->zc = 1;
     curfile->a68k=1;
  }else if (filename[l-1] == 'a' || filename[l-1] == 's'){
     curfile->a68k=1;
  }else if(filename[l-1] != 'o')
     curfile->ab = 1; /* abnormal file name */
}else
  curfile->ab = 1; /* abnormal file name */
}/* end of addfile */



long len(x)
char x[];
{
long i;

for(i=0; x[i] != 0 ; i++);
return(i);
}




char *tofiletype(nm,buffer,ext1,ext2)
char nm[],buffer[],ext1[],ext2;
{
long l;

strcpy(buffer,nm);
l=len(buffer);
if(l < 2 || ( buffer[l-2] != '.') ){
  strcat(buffer,ext1);
  return(buffer);
}
if(ext2)
  buffer[l-1] = ext2;
else
  buffer[l-2] = '\0'; /* must be executable */
return(buffer);
}/*end of tofiletype */

#define toassem( x ) tofiletype( x ,a68kname,".s ",'s')
#define tolink( x ) tofiletype( x ,blinkname,".o ",'o')
#define toexec( x ) tofiletype( x ,blinkname,".lnk ",'\0')

doerrors()
{
FILE *err;
int nmerrors;

  if( (err=fopen("ram:zc.errors","r"))==NULL)
     return();
  nmerrors=0;
  fscanf(err,"%d",&nmerrors);
  close(err);
  if(nmerrors)
     quit();
  return(); /* no errors thank goodness */
}


/* this assembles the file */
assemfile(nm,outnm)
char nm[],outnm[];
{

strcpy(buf,cmddir);
strcat(buf,"a68k <* >* ");
strcat(buf,a68kopts);
strcat(buf,"-o");
strcat(buf,outnm);
strcat(buf," ");
strcat(buf,nm);
Chk_Abort();
doexec(buf,"cc: assemble error: %s\n",nm);
}/*end of assemfile*/

/* compiles and/or assembles the files in the file list */
compilefiles(){
struct filenm *file;
char nm[];

file=files;
while(file){
  toassem(file->name);/* use this as the input to a68k
		       * unless updated below	       */
  if(file->zc && dozc ){
     strcpy(buf,cmddir);
     strcat(buf,"zc >* <* -Eram:zc.errors ");
     strcat(buf,zcopts);
     strcat(buf," ");
     if( (file->a68k && doa68k) || file->ab){
       strcat(buf," -O");
       if(file->a68k && doa68k)
	 strcpy(buf2,zctemp);  /* use temp file if doing an assemble */
       else
	 buf2[0]='\0';
       strcat(buf2,file->name);
       strcat(buf,toassem(buf2));/* use designated outputfile */
     }
     strcat(buf," ");
     strcat(buf,file->name);
     Chk_Abort();
     doexec(buf,"cc: compile error: %s\n",file->name);/* start compiler */
     doerrors(); /* read error file to see if any errors */
  }   /* compiled okay */
  if(file->a68k && doa68k){
     assemfile(a68kname,tolink(file->name)); /* assemble file */
     if(file->zc){ /* if was a c file remove .s file */
       strcpy(buf,"c:delete >* <* ");
       strcat(buf,a68kname);
       Chk_Abort();
       doexec(buf,"cc: delete error: %s\n",a68kname);
     }
  }
  file=file->next;/* get next file */
}/* done with this file, compile & assemble next */
}/* done with files */


linkfiles(){
struct filenm *file;
char nm[];

if(!doblink || files == NULL ) return();/* don't do anything if not supposed to */
file=files;
strcpy(buf,cmddir);
strcat(buf,"blink <* >* ");
if(startup){
  strcat(buf,libdir);
  strcat(buf,"BothStartup.obj ");
}
while(file){
  strcat(buf,tolink(file->name));/* build linker list */
  file = file->next;
  strcat(buf," ");
}
strcat(buf," TO ");
strcat(buf,toexec(files->name));/* use first file name */
strcat(buf," LIB ");
strcat(buf,blinklibs);
if(standardblinklib){
   strcat(buf,libdir);
   strcat(buf,"amiga.lib ");
}
if(!dodebug)
  strcat(buf," NODEBUG ");
if(blinkxref){
  strcat(buf," XREF ");
  strcat(buf,xrefname);
}
Chk_Abort();
doexec(buf,"cc: linker error:\n",NULL);
printf("cc: executable is %s \n",toexec(files->name));
}/* done linking it */


help()
{

  printf("cc: Version 1.0 Preprocessor for \n%s%s%s%s%s%s%s%s%s%s%s%s%s",
	 "   cc: Sozobon-C compiler, \n",
	 "   as: a68k C.Gibbs Motorola Compatible Assembler, and\n",
	 "   blink:  Software Distillery Linker \n",
	 "           looks for lib:amiga.lib and lib:BothStartup.obj \n",
	 "   Syntax:\n",
	 "           cc [OPTIONS] <files.c> <files.s> <files.o> \n\n",
	 "   Options:\n",
	 "    -Dxxxx   define xxxx,   -Uxxxx undefine xxxx\n",
	 "    -C    data->chip,       -F data->fast\n",
	 "    -S  don't use startups, -T don't use amiga.lib\n",
	 "    -A    don't assemble,   -c don't link\n",
	 "    -Ixxxx  include dir,    -V printout actions only\n",
	 "    -lxxxx  use library  lib:xxxx.lib\n");
}

main(argc,argv)
int argc;
char *argv[];
{
  int i, l;
  char *s;

  printf("cc: \n");
  if(argc < 2 || argv[1][0] == '?'){
     help();
     exit(0);
  }
  startup=standardblinklib=1;/* use BothStartup.obj & amiga.lib */
  blinklibs[0]='\0';       /* blink lib direc           */
  blinkname[0]='\0';      /* output file from a68k     */
  a68kname[0]='\0';      /* output file from zc       */
  zcopts[0]='\0';       /* zc options for each phase */
  a68kopts[0]='\0';    /* a68k options              */
  zctemp[0]='\0';     /* CCTEMP dir                */
  blinkopts[0]='\0'; /* blink options             */
  buf[0]='\0';      /* command line              */
  buf2[0]='\0';    /* scratch buf               */

  dodebug=debug=0;
  dozc=doa68k=doblink=1;/* will compile,assem,link unless told otherwise*/
  curmem=NULL;/* haven't grabbed any memory yet*/
  files=NULL;
  curfile=NULL;
  blinkxref=0;
  strcpy(xrefname," ");

  if( getenv("CCLIB") ){
      strcpy(libdir," ");
      strcat(libdir,getenv("CCLIB")); /* get libdirector */
  }else
      strcpy(libdir," lib:"); /* default directory */
  if( ! getenv("CCEXEC") )
      strcpy(cmddir,"c:");
  else{
      strcpy(cmddir,getenv("CCEXEC")); /*dir for zc,a68k,blink */
  }
  if( getenv("CCTEMP") ){
     strcpy(zctemp,getenv("CCTEMP"));
  }else{
     printf("cc:Warning, environment variable CCTEMP unset\n   Using ram: \n");
     strcpy(zctemp,"ram:");
  }
  if( ! getenv("INCLUDE")){
     printf("cc: Warning, environment variable INCLUDE unset\n");
  }else{
     strcpy(a68kopts," -I");
     strcat(a68kopts,getenv("INCLUDE") );
  }
  for(i = 1; i < argc; i++){/* parse arguments */
     Chk_Abort();  /* Do options */
     if(argv[i][0] == '+'){
	s = argv[i];
	switch(s[1]){
	   default:
	      printf("cc: No + switches allowed\n");
	      break;
	}
      }else if(argv[i][0] == '-' ){
	s = argv[i];
	switch(s[1]){
	   case 'A':
	   case 'a':
	     doblink = doa68k = 0;
	     break;
	   case 'C':  /* force data into chip memory */
	     strcat(zcopts," -C ");
	     break;
	   case 'c':
	     doblink = 0;
	     break;
	   case 'd':
	   case 'D':
	     strcat(zcopts," -D");
	     strcat(zcopts,&s[2]);
	     break;
	   case 'F':
	     strcat(zcopts," -F ");
	     break;
	   case 'i':
	   case 'I':
	     strcat(zcopts," -I");
	     strcat(zcopts,&s[2]);
	     break;
	   case 'l':
	     strcat(blinklibs," -l");
	     strcat(blinklibs,&s[2]);
	     strcat(blinklibs," ");
	     break;
	   case 'T':
	     standardblinklib=0; /* don't use amiga.lib */
	     break;
	   case 'S':
	     startup = 0;/* don't use BothStartup.obj */
	     break;
	   case 'u':
	   case 'U':
	     strcat(zcopts," -U");
	     strcat(zcopts,&s[2]);
	     break;
	   case 'v':
	   case 'V':
	     debug = 1;
	     break;
	   default:
	     printf("bad option: \"%s\"\n", argv[i]);
	     exit(1);
	     break;
	}
     }else  /* argument wasn't an option so must be a file  */
	addfile(argv[i]);/* add filename to list of files */
  }/* done with parsing arguments */
  compilefiles(); /* compile all the files */
  linkfiles();   /*  link all the files */
  quit();/* cleanup mess and go home */
}

