/*
 * tmain.c - main program for translator and linker.
 */

#include "..\h\config.h"
#include "general.h"
#include "tproto.h"
#include "..\h\paths.h"

/*
 * Prototypes.
 */

hidden	novalue	execute	Params((char *ofile,char *efile,char * *args));
hidden	novalue	report Params((char *s));
hidden	novalue	rmfiles Params((char **p));
hidden	novalue	usage Params((noargs));

/*
 * The following code is operating-system dependent [@tmain.01].  Include
 *  files and such.
 */

#if PORT
Deliberate syntax error
#endif					/* PORT */

#if AMIGA || HIGHC_386 || MSDOS || MVS || UNIX || VM || VMS
/* nothing is needed */
#endif					/* AMIGA || HIGHC_386 || ... */

#if ATARI_ST
char *patharg;
#endif					/* ATARI_ST */

#if MACINTOSH
#if MPW
#include <fcntl.h>	\* MPW3 - for unlink() *\
#include <CursorCtl.h>
void SortOptions();
#endif					/* MPW */
#endif					/* MACINTOSH */

#if OS2
#include <process.h>
#endif					/* OS2 */
/*
 * End of operating-system specific code.
 */

#if IntBits == 16
#ifdef strlen
#undef strlen				/* pre-defined in some contexts */
#endif					/* strlen */
#endif					/* Intbits == 16 */

#ifndef Iconx
#define Iconx IconxPath
#endif					/* Iconx */

/*
 *  Define global variables.
 */

#define Global
#define Init(v) = v
#include "globals.h"

char *ofile = NULL;			/* linker output file name */

/*
 * getopt() variables
 */
extern int optind;		/* index into parent argv vector */
extern int optopt;		/* character checked for validity */
extern char *optarg;		/* argument associated with option */

/*
 *  main program
 */
novalue main(argc,argv)
int argc;
char **argv;
   {
   int nolink = 0;			/* suppress linking? */
   int errors = 0;			/* translator and linker errors */
   char **tfiles, **tptr;		/* list of files to translate */
   char **lfiles, **lptr;		/* list of files to link */
   char **rfiles, **rptr;		/* list of files to remove */
   char *efile = NULL;			/* stderr file */
   char buf[MaxFileName];		/* file name construction buffer */
   int c, n;
   struct fileparts *fp;

#if MACINTOSH
#if MPW
   InitCursorCtl(NULL);
   SortOptions(argv);
#endif					/* MPW */
#endif					/* MACINTOSH */

   /*
    * Process options.
    */
   while ((c = getopt(argc,argv,Options)) != EOF)
      switch (c) {
         case 'c':			/* -c: compile only (no linking) */
            nolink = 1;
            break;
         case 'e':			/* -e file: redirect stderr */
            efile = optarg;
            break;
         case 'm':			/* -m: preprocess using m4(1) [UNIX] */
            m4pre = 1;
            break;
         case 'o':			/* -o file: name output file */
            ofile = optarg;
            break;

#if ATARI_ST
         case 'p':			/* -p path: iconx path [ATARI] */
            patharg = optarg;
            break;
#endif					/* ATARI_ST */

         case 's':			/* -s: suppress informative messages */
            silent = 1;
            break;
         case 'u':			/* -u: warn about undeclared ids */
            uwarn = 1;
            break;
         case 't':			/* -t: turn on procedure tracing */
            trace = -1;
            break;


         case 'L':			/* -L: enable linker debugging */

#ifdef DeBugLinker
            Dflag = 1;
#endif					/* DeBugLinker */

            break;

         case 'S':			/* -Sxnnnn: set a size */
            sizearg(optarg,argv);
            break;
         default:
         case 'x':			/* -x illegal until after file list */
            usage();
         }

   /*
    * Allocate space for lists of file names.
    */
   n = argc - optind + 1;
   tptr = tfiles = (char **)alloc((unsigned int)(n * sizeof(char *)));
   lptr = lfiles = (char **)alloc((unsigned int)(n * sizeof(char *)));
   rptr = rfiles = (char **)alloc((unsigned int)(2 * n * sizeof(char *)));

   /*
    * Scan file name arguments.
    */
   while (optind < argc)  {
      if (strcmp(argv[optind],"-x") == 0)	/* stop at -x */
         break;
      else if (strcmp(argv[optind],"-") == 0) {
         *tptr++ = "-";				/* "-" means standard input */
         *lptr++ = *rptr++ = "stdin.u1";
         *rptr++ = "stdin.u2";
         }
      else {
         fp = fparse(argv[optind]);		/* parse file name */
         if (*fp->ext == '\0' || smatch(fp->ext, SourceSuffix)) {
            makename(buf,SourceDir,argv[optind], SourceSuffix);
            *tptr++ = salloc(buf);		/* translate the .icn file */
            makename(buf,TargetDir,argv[optind],U1Suffix);
            *lptr++ = *rptr++ = salloc(buf);	/* link & remove .u1 */
            makename(buf,TargetDir,argv[optind],U2Suffix);
            *rptr++ = salloc(buf);		/* also remove .u2 */
            }
         else if (smatch(fp->ext,U1Suffix) || smatch(fp->ext,U2Suffix)
               || smatch(fp->ext,USuffix)) {
            makename(buf,TargetDir,argv[optind],U1Suffix);
            *lptr++ = salloc(buf);
            }
         else
            quitf("bad argument %s",argv[optind]);
         }
      optind++;
      }
 
   *tptr = *lptr = *rptr = NULL;	/* terminate filename lists */
   if (lptr == lfiles)
      usage();				/* error -- no files named */

   /*
    * Round hash table sizes to next power of two, and set masks for hashing.
    */
   chsize = round2(chsize);  cmask = chsize - 1;
   fhsize = round2(fhsize);  fmask = fhsize - 1;
   ghsize = round2(ghsize);  gmask = ghsize - 1;
   ihsize = round2(ihsize);  imask = ihsize - 1;
   lhsize = round2(lhsize);  lmask = lhsize - 1;

   /*
    * Translate .icn files to make .u1 and .u2 files.
    */
   if (tptr > tfiles) {
      if (!silent)
         report("Translating");
      errors = trans(tfiles);
      if (errors > 0)			/* exit if errors seen */
         exit(ErrorExit);
      }

   /*
    * Link .u1 and .u2 files to make an executable.
    */
   if (nolink)				/* exit if no linking wanted */

#if MACINTOSH
#if MPW
      /*
       *  Set type of translator output ucode (.u) files
       *  to 'TEXT', so they can be easily viewed by editors.
       */
      {
      char **p;
      void setfile();
      for (p = rfiles; *p; ++p)
         setfile(*p,'TEXT','icon');
      }
#endif					/* MPW */
#endif					/* MACINTOSH */

      exit(NormalExit);

   if (ofile == NULL)  {		/* if no -o file, synthesize a name */
      ofile = salloc(makename(buf,TargetDir,lfiles[0],IcodeSuffix));
   } else {				/* add extension in necessary */
      fp = fparse(ofile);

#if !(MACINTOSH && MPW) /* This code provokes code generation bug in */
		/* pre-release MPW 3.0 C compiler */
      if (*fp->ext == '\0' && *IcodeSuffix != '\0') /* if no ext given, but wanted */
         ofile = salloc(makename(buf,TargetDir,ofile,IcodeSuffix));
#endif					/* !(MACINTOSH && MPW) */

      if (*fp->ext == '\0' && *IcodeSuffix != '\0') /* if no ext given */
         ofile = salloc(makename(buf,TargetDir,ofile,IcodeSuffix));
   }

   if (!silent)
      report("Linking");
   errors = ilink(lfiles,ofile);	/* link .u files to make icode file */

   /*
    * Finish by removing intermediate files.
    *  Execute the linked program if so requested and if there were no errors.
    */

#if MACINTOSH
#if MPW
   /* Set file type to TEXT so it will be executable as a script. */
   setfile(ofile,'TEXT','icon');
#endif					/* MPW */
#endif					/* MACINTOSH */

   rmfiles(rfiles);			/* remove intermediate files */
   if (errors > 0) {			/* exit if linker errors seen */
      unlink(ofile);
      exit(ErrorExit);
      }

   if (optind < argc)  {
      if (!silent)
         report("Executing");
      execute (ofile, efile, argv+optind+1);
      }

   exit(NormalExit);
   }

/*
 * execute - execute iconx to run the icon program
 */
static novalue execute(ofile,efile,args)
char *ofile, *efile, **args;
   {
   int n;
   char **argv, **p;

   for (n = 0; args[n] != NULL; n++)	/* count arguments */
      ;
   p = argv = (char **)alloc((unsigned int)((n + 5) * sizeof(char *)));

   *p++ = Iconx;			/* set iconx pathname */
   if (efile != NULL) {			/* if -e given, copy it */
      *p++ = "-e";
      *p++ = efile;
      }
   *p++ = ofile;			/* pass icode file name */

#if AMIGA && LATTICE
   *p = *args;
   while (*p++) {
      *p = *args;
      args++;
   }
#else					/* AMIGA && LATTICE */
   while (*p++ = *args++)		/* copy args into argument vector */
      ;
#endif					/* AMIGA && LATTICE */

   *p = NULL;

/*
 * The following code is operating-system dependent [@tmain.02].  It calls
 *  iconx on the way out.
 */

#if PORT
   /* something is needed */
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA
#if AZTEC_C
      execvp(Iconx,argv);
      return;
#endif					/* AZTEC_C */
#if LATTICE
      {
      struct ProcID procid;
      if (forkv(Iconx,argv,NULL,&procid) == 0) { 
         wait(&procid);
         return;
         }
      }
#endif					/* LATTICE */
#endif					/* AMIGA */

#if ATARI_ST || MVS || VM
      /* Forkvp(Iconx,argv); */
      /* return; */
      fprintf(stderr,"-x not supported\n");
      fflush(stderr);
     /* not implemented yet */
#endif					/* ATARI_ST || MVS || VM */

#if HIGHC_386
      fprintf(stderr,"-x not supported\n");
      fflush(stderr);
     /* not implemented yet */
#endif					/* HIGHC_386 */

#if MACINTOSH
#if MPW
      /*
       *  Something slightly different ...
       */
      {
      char *s;
      char *mpwquote();

      fputs(Iconx,stdout);
      for (p = argv + 1; *p; ++p) {
         putchar(' ');
	 fputs(s = mpwquote(*p),stdout);
	 free(s);
	 }
      putchar('\n');
      return;
      }
#endif					/* MPW */
#if LSC
      fprintf(stderr,"-x not supported\n");
      fflush(stderr);
#endif					/* LSC */
#endif					/* MACINTOSH */

#if MSDOS
#if LATTICE || MICROSOFT || TURBO
      execvp(Iconx,argv);	/* execute with path search */
#endif					/* LATTICE || MICROSOFT || TURBO */
#if MWC
      fprintf(stderr,"-x not supported\n");
      fflush(stderr);
      /* execall(Iconx,argv); */
#endif					/* MWC */
#endif					/* MSDOS */

#if MVS || VM
#if SASC
	  exit(sysexec(Iconx, argv));
#endif					/* SASC */
#endif                                  /* MVS || VM */

#if OS2
      execvp(Iconx,argv);	/* execute with path search */
#endif					/* OS2 */

#if UNIX
      /*
       * If an ICONX environment variable is defined, use that.
       *  If not, first try the predefined path, then search $PATH via execvp. 
       */
      if ((argv[0] = getenv("ICONX")) != NULL && argv[0][0] != '\0') {
         execv(argv[0], argv);	/* exec file specified by $ICONX */
         quitf("cannot execute $ICONX (%s)", argv[0]);
         }

#ifdef HardWiredPaths
#ifdef CRAY
      argv[0] = "iconx";
      execv(Iconx, argv);
#else					/* CRAY */
      argv[0] = Iconx;		/* try predefined file */
      execv(argv[0], argv);
#endif					/* CRAY */
#endif					/* HardWiredPaths */

      argv[0] = "iconx";
      execvp(argv[0], argv);	/* if no Iconx, search path for "iconx" */

#ifdef HardWiredPaths
      quitf("cannot run %s", Iconx);
#else					/* HardWiredPaths */
      quitf("cannot find iconx", "");
#endif					/* HardWiredPaths */
#endif					/* UNIX */

#if VMS
      execv(Iconx,argv);
#endif					/* VMS */

/*
 * End of operating-system specific code.
 */

   quitf("could not run %s",Iconx);
   }

static novalue report(s)
char *s;
   {

/*
 * The following code is operating-system dependent [@tmain.03].  Report
 *  phase.
 */

#if PORT
   fprintf(stderr,"%s:\n",s);
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA || ATARI_ST || HIGHC_386 || MSDOS || MVS || OS2 || UNIX || VM || VMS
   fprintf(stderr,"%s:\n",s);
#endif					/* AMIGA || ATARI_ST || HIGHC_386 ... */

#if MACINTOSH
#if MPW
   printf("Echo '%s:' > Dev:StdErr\n",s);
#endif					/* MPW */
#if LSC
   fprintf(stderr,"%s:\n",s);
#endif					/* LSC */
#endif					/* MACINTOSH */

/*
 * End of operating-system specific code.
 */

   }

/*
 * rmfiles - remove a list of files
 */

static novalue rmfiles(p)
char **p;
   {
   for (; *p; p++) {
/*
 * The following code is operating-system dependent [@tmain.04].
 *  remove files.
 */

#if PORT
      unlink(*p);
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA || ATARI_ST || HIGHC_386 || MSDOS || MVS || OS2 || UNIX || VM || VMS
      unlink(*p);
#endif					/* AMIGA || ATARI_ST ... */

#if MACINTOSH
#if MPW
      /*
       * MPW generates commands rather than doing the actions
       *  at this time.
       */
      fprintf(stdout,"Delete %s\n", *p);
#endif					/* MPW */
#if LSC
      unlink(*p);
#endif					/* LSC */
#endif					/* MACINTOSH */

/*
 * End of operating-system specific code.
 */
      }
   }

/*
 * Print an error message if called incorrectly.  The message depends
 *  on the legal options for this system.
 */
static novalue usage()
   {
   fprintf(stderr,"usage: %s %s file ... [-x args]\n", progname, Usage);
   exit(ErrorExit);
   }
