/*


 Copyright (C) 1990 Texas Instruments Incorporated.

 Permission is granted to any individual or institution to use, copy, modify,
 and distribute this software, provided that this complete copyright and
 permission notice is maintained, intact, in all copies and supporting
 documentation.

 Texas Instruments Incorporated provides this software "as is" without
 express or implied warranty.


 * 
 * C++ compiler driver
 *
     CCC (capital CCC) translates C++ source code to C source code.
     The command uses cpp(1) for preprocessing, cfront for syntax
     and type checking, and cc(1) for code generation.

     CCC assumes arguments ending in

     .c   are C++ source programs; they are  compiled,  and  each
          object  program  is left on the file whose name is that
          of the source with .o substituted for .c.

     .s   are assembly source programs; they are  assembled  into
          .o files.

     CCC interprets the following options:

     -n   Print what would have been done, but don't do it.

     -v   Verbose mode.  Print the commands as they are called.

     -c   Suppress linking with ld(1) and produce a .o  file
          for each source file.  A single object file can be
          named explicitly using the -o option.

     -C   Prevent cpp and cfront from removing comments.

     -E   Run only cpp on the .c files and  send  the  result  to
          standard output.

     -F   Run only cpp and cfront on the .c files  and  send  the
          result to standard output.

     -Fc  Like the -F option, but the output  is  C  source  code
          suitable as a .c file for cc(1).

     -.suffix
          Instead of using standard output for the -E , -F or -Fc
          options,  place  the output from each .c file on a file
          with the corresponding .suffix.

     -k   Keep the files __ctdt.c and __ctdt.o.  They are used to
          initialize  global  and static class objects.  Normally
          they are removed.

     -NOMUNCH
     -NOPATCH
          Do not run munch or patch on the resulting  object  file.
          This  phase  is  normally  needed to detect static con-
          structors and destructors.

     -x1  cpp debug print of files #included
     -x2  cpp debug inserts #control as comments in output
     -x3  cpp debug does both -x1 and -x2

     +d   Suppress expansion of inline functions.

     +e1,+e0
          These flags affect how the compiler handles tables  for
          virtual  functions.  +e1 causes the tables to be exter-
          nal and initialized; +e0 causes the tables to be exter-
          nal  and  uninitialized.   If neither flag is specified
          (the default), the tables are local (static)  and  ini-
          tialized. One file with all virtuals known to it should
          be compiled with +e1; the rest should be compiled  with
          +e0.   Having  one  external  table  may  significantly
          reduce compile time, link time and object file size.

     +i   Keep C source code produced by the front end in  a  ..c
          file.  Line number information is stripped.

     +V   Accept  regular  C  function  declarations;   use   the
          /usr/include directory for #include files.  Support for
          this option is not guaranteed in future releases

     +L   Generate source line number information using the  for-
          mat "#line %d" instead of "#%d".

     +xfile
          Read a file of sizes and alignments. Each line contains
          three fields: a type name, the size (in bytes), and the
          alignment (in bytes). This option is useful  for  cross
          compilation.

     +S   Spy on cfront;  that  is,  print  some  information  on
          stderr.

     +Z30000 Set cfront lex input buffer size

     +zf127  Set number of files that can be concurrently opened in cfront

     -???
          All other - flags are sent to the "C" compiler

     /???
          All /xxx flags are sent to the linker

     +???
          All +xxx flalgs are sent to cfront

     If the environment variable INCLUDE exists, it is assumed to be a
     colon separated list of directories to be searched when trying to
     find '#include' files.  This is an alternative to the -I flag of
     cc(1).

     If set, the environment variables ccpC (preprocessor), cfrontC (C++
     front end), ccC (c compiler), assemblerC (assembler), linkerC
     (linker), and LIBRARY (C++ library) override the defaults.

     See ld(1) for loader options, as(1) for assembler options, cc(1)
     for code generation options, and cpp(1) for preprocessor options.
 */

/* exit status codes
 * 2    too many errors
 * 1    internal error
 * 0    all went well
 * -1   nothing done
 */

#if defined(MSDOS) && !defined(DOS) /* Lets us work with IBM or MS compilers */
#define DOS
#endif

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>

#if defined(DOS)
#include <process.h>
#else
#include <sys/types.h>
#include <sys/file.h>
#endif

#include "config.h"

#define nil 0
#define EOL '\0'

typedef char *String;
typedef char **StringVec;
typedef int FileDesc;
typedef enum {false, true} boolean;

#define BADEXEC -1

#define IsChild(pid) ((pid)==0)

#define stdinDesc       0
#define stdoutDesc      1

static String PREPROCESSOR;
static String CCfrontEnd;
static String Ccompiler;
static String ASSEMBLER;
static String LINKER;
static String MUNCHER;
static String PATCHER;
static String COMPRESS;
static String MV;
static String LIB;
static String StdCClib;
static String frontEndSuffix;
static String runtimeLib;
static String cclib;
static String TMPDIR;

static String ENVPATH = "INCLUDE=";
static String DEFAULTLINKER = CC_ld;
static String INCLINK = "inclink";
static String RELINK = "relink";

#define ARGSIZE         2048
#define FILENAMESIZE    256
#define MAXARGS         128
#define MAXTMPFILES     64
#define PATHLEN         1024
#define SET             true

boolean Cflag = false;
boolean Eflag = false;
boolean Fflag = false;
boolean FcFlag = false;
boolean plusIflag = false;
boolean suffixFlag = false;
boolean verboseFlag = false;
boolean fakeFlag = false;
boolean objFlag = false;
boolean ignErrFlag = false;
boolean skipAsmFlag = false;
boolean cFlag = false;
boolean noMunchFlag = false;
boolean gFlag = false;
boolean gprofFlag = false;
boolean profFlag = false;
boolean genRelocInfoFlag = false;
boolean keepctdtFlag = false;
boolean implementQuick = false;

boolean translator;			  /* true if generates C code */
boolean dotdotsuffix;			  /* true if ..c suffix */

boolean ccIsLinker;			  /* true if using cc for link phase */

String cppArgs[MAXARGS];
StringVec cppArgv = cppArgs;

String cppPathArgs[MAXARGS];
StringVec cppPathv = cppPathArgs;

String ccArgs[MAXARGS];
StringVec ccArgv = &ccArgs[0];

String frontEndArgs[MAXARGS];
StringVec frontEndArgv = frontEndArgs;

String linkArgs[MAXARGS];                 /* Link options */
StringVec linkArgv = linkArgs;

String linkObjs[MAXARGS];                 /* Objects to link */
StringVec linkObjv = linkObjs;

String linkLibs[MAXARGS];                 /* Link Libraries */
StringVec linkLibv = linkLibs;

String commandv[MAXARGS];
StringVec command;

String objSpaceArgs[MAXARGS];
StringVec objSpaceArgv = &objSpaceArgs[0];

String implementArgs[MAXARGS];
StringVec implementArgv = &implementArgs[0];

char implement_count[20];

char tmpFilesList[MAXTMPFILES][FILENAMESIZE];
char xBuff[FILENAMESIZE];		  /* for +x file flag */
int tmpFileCount = 0;

extern StringVec environ;

char libDirBuff[128] = CC_libdir;
static String libDir = libDirBuff;

char fileName[FILENAMESIZE];              /* Temporary */
char cppName[FILENAMESIZE];               /* Name of cpp output file */
char frontEndName[FILENAMESIZE];
char compressName[FILENAMESIZE];
char filterName[FILENAMESIZE];
char destname[FILENAMESIZE];
char objName[FILENAMESIZE];
char libPath[FILENAMESIZE];
char implementName[FILENAMESIZE];         /* Name of implement input file */
char lastLib[FILENAMESIZE];               /* last -l file file */
char ccPath[PATHLEN];
String myname, linkFile;
String suffix;

int cppDesc, dest;
int numErrors = 0;

void Error (msg1, msg2)
String msg1, msg2;
{
    if (msg2 == nil || *msg2 == EOL) {
        fprintf(stderr, "%s: %s\n", myname, msg1);
    } else {
        fprintf(stderr, "%s: %s %s\n", myname, msg1, msg2);
    }
    exit(1);
}  /* Error */

void WarningMsg (msg1, msg2)
String msg1, msg2;
{
    if (msg2 == nil || *msg2 == EOL) {
        fprintf(stderr, "%s: %s\n", myname, msg1);
    } else {
        fprintf(stderr, "%s: %s %s\n", myname, msg1, msg2);
    }
}  /* WarningMsg */

int Rename (old, new)
String old; 
String new;
{
  if (verboseFlag) {
    fprintf(stderr, "mv %s %s\n", old, new);
  }
#if defined(M_XENIX)
  link(old, new);			  /* Xenix doesn't have rename */
  return unlink(old);
#else
  return rename(old, new);
#endif
}  /* Rename */

void Remove (file)
String file; 
{
  int i;

  if (keepctdtFlag == false && fakeFlag == false 
	     && file != nil && *file != EOL) {
    if (verboseFlag) {
      fprintf(stderr, "rm %s\n", file);
    }
    unlink(file);			  /* Delete the file */
  }
                                          /* Remove file from temporary list */
  for (i=0; i<tmpFileCount; i++)
    if (strcmp(tmpFilesList[i], file) == 0) {
      tmpFilesList[i][0] = EOL;
      if ((i+1) == tmpFileCount) tmpFileCount--;
      break;
    }
}  /* Remove */

void RemoveTmpFiles ()
{
  int i;
  for (i=0; i<tmpFileCount; i++) {
    if (tmpFilesList[i][0] != EOL)
      Remove(tmpFilesList[i]);
  }
  tmpFileCount = 0;
}  /* RemoveTmpFiles */

void Quit (exitStatus)
int exitStatus;
{
    RemoveTmpFiles();
    exit(exitStatus);
}  /* Quit */

void myclose (desc)
FileDesc desc;
{
    if (fakeFlag == false && close(desc) != 0) {
        fprintf(stderr, "(%d) ", desc);
        perror("CCC: close failed");
    }
}  /* myclose */

FileDesc myopen (filename, flags, mode)
String filename;
int flags, mode;
{
    int f;
    if (fakeFlag == false) {
        f = open(filename, flags, mode);
        if (f < 0) {
            fprintf(stderr, "(%s) ", filename);
            perror("CCC: open failed");
            Quit(1);
        }
    } else
        f = -1;
    return f;
}  /* myopen */

/*
 * Add an argument to the end of an argument list
 */
StringVec AddArg (l, s)
    StringVec l;
    String s;
{
  if (s != 0 && *s != EOL) {
    *l++ = s;
  }
  return l;
}  /* AddArg */

/*
 * Append a list of arguments to the end of an argument list
 * Input is a space-seperated list of arguments
 */
StringVec AddArgs(l, s)
    StringVec l;
    String s;
{
  String p = s;
  for(;;) {
    if (isspace(*p) || *p == EOL) {
      int len = p-s;
      if (len > 0) {
        String r = (String) malloc(len+1);
        strncpy(r, s, len);
        *(r+len) = EOL;
        l = AddArg(l, r);
      }
      if (*p == EOL) break;
      while(isspace(*(p+1))) p++;
      s = p+1;
    }
    if (*p++ == EOL) break;
  }
  return l;
}  /* AddArgs */

/*
 * Append a list of arguments to the end of an argument list
 */
StringVec AddList (v, list)
    StringVec v;
    StringVec list;
{
  if (list == 0 || *list == EOL) {
    /*  fprintf(stderr, "%s: AddList: null string\n", myname); */
  } else {
    while (*list != 0) {
      *v++ = *list++;
    }
  }
  return v;
}  /* AddList */

String newString (s)
String s;
{
     String p;
     p = (char*) malloc(FILENAMESIZE);
     strcpy(p, s);
     return p;
}  /* newString */

void RegisterTmpFile (filename)
String filename;
{
    int i;

    if (tmpFileCount >= MAXTMPFILES) {
        for (i = 0; i<MAXTMPFILES ; i++) {
            unlink(tmpFilesList[i]);
            tmpFilesList[i][0] = EOL;
        }
        tmpFileCount = 0;
    }
    strcpy(tmpFilesList[tmpFileCount], filename);
    tmpFileCount++;
}  /* RegisterTmpFile */

TmpFileName(tmpname, characteristic, type)
    String tmpname;
    String characteristic;
    String type;
{
 sprintf(tmpname, "%sC%d%s.%s", TMPDIR, getpid() % 9999, characteristic, type);
 RegisterTmpFile(tmpname);
}  /* TmpFileName */

FileDesc MakeTmpFile (tmpname, characteristic, type)
    String tmpname;
    String characteristic;
    String type;
{
  int t;
  TmpFileName(tmpname, characteristic, type);
  if (fakeFlag == false) {
    t = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (t < 1) {
      perror("CCC: cannot open tmp file");
      exit(1);
    }
  } else {
    t = stdoutDesc;
  }
  return t;
}  /* MakeTmpFile */

void PrintArgList (prog, argv)
String prog;
StringVec argv;
{
    int i;
    
    fprintf(stderr, "%s ", prog);
    for (i=1; argv[i] != 0; i++) {
        fprintf(stderr, "%s ", argv[i]);
    }
    fprintf(stderr,"\n");
}  /* PrintArgList */

void initexec (progname)
String progname;
{
    command = commandv;
    *command++ = progname;
}  /* initexec */

#if !defined(DOS)
void RedirectIn (in)
FileDesc in;
{
    if (in != stdinDesc) {
        dup2(in, stdinDesc);
        close(in);
    }
}  /* RedirectIn */

void RedirectOut (out)
FileDesc out;
{
    if (out != stdoutDesc) {
        dup2(out, stdoutDesc);
        close(out);
    }
}  /* RedirectOut */

/*  Special hack to convert vector of string pointers into a
    a simple string for use in NoWaitExec.
*/

#if defined(M_XENIX)
String makecfxxargs (v)
StringVec v;
{
  static char workstring[256];
  strcpy(workstring, "exec ");
  while (*v != 0) {
    strcat(workstring,*v++);
    strcat(workstring, " ");
  }
  return workstring;
}  /* makecfxxargs */
#endif

/* Return pid of child */
int NoWaitExec (name, argv, in, out)
String name;
StringVec argv;
FileDesc in;
FileDesc out;
{
    int pid;
    char *path = "/bin/sh";
    pid=fork();
    if IsChild(pid) {
        RedirectIn(in);
        RedirectOut(out);
                          /* This hack executes cfxx from a shell
                             instead of via execvp.  For some reason
                             cfxx fails if executed from execvp. Sigh.*/
#if defined(M_XENIX)
	if ( name == CCfrontEnd) {
	  execl (path, path, "-c", makecfxxargs(argv), (char*)0 );
        }
        else
#endif
        execvp(name, argv);
        fprintf(stderr, "%s: Can not execute %s\n", myname, name);
	perror("CCC: exec error");
        _exit(BADEXEC);
    }
    return pid;
}  /* NoWaitExec */

int mywait (pid)
int pid;
{
    int status;
    
    if (pid != -1) {
        while ( wait(&status) != pid ) {
            ;
        }
        if (status != 0) {
            if ((status & 0xff)== 0) {
                status = status >> 8;
            }
        }
    } else {
        status = -1;
    }
    return status;
}  /* mywait */
#endif 

int myexec(name, argv, in, out)
String name;
String argv[];
FileDesc in;
FileDesc out;
{
    int pid, status;
#if !defined(DOS)
    pid = NoWaitExec(name, argv, in, out);
    status = mywait(pid);
#else
    status = spawnvp(P_WAIT, name, argv);
#endif
    return status;
}  /* myexec */

/* 
 * If 0 is passed as in or out, use the defaults, stdin and stdout. 
 */
int execute (progfile, argv, in, out) 
String progfile;
StringVec argv;
FileDesc in, out;
{
    int status;

    *command++ = nil;			  /* Terminate arg list HACK !! */
    if (verboseFlag) {
        PrintArgList(progfile, argv);
    }
    status = 0;
    if (fakeFlag == false) {
        status = myexec(progfile, argv, in, out);
        if (status != 0) {
            numErrors++;
        }
        if (verboseFlag && status != 0) {
            fprintf(stderr,"%s: %s exited with status of %d\n",
                myname, progfile, status);
        }
    }

    return status;
}  /* execute */

void NoMoreOpt (opt, arg)
char opt;
String arg;
{
    if (opt != EOL) {
        fprintf(stderr,
            "%s: %c not understood in option %s\n", myname, opt, arg
        );
        Quit(1);
    }
}  /* NoMoreOpt */

/****************  This is case_insensitive *****************/
#define TO_UPPER(x) (islower(x) ? toupper(x) : x) 
boolean is_equal (c1, c2)
    char* c1;
    char* c2;
{
  for ( ; *c1 == *c2 || TO_UPPER (*c1) == TO_UPPER (*c2); c1++, c2++)
    if (*c1 == EOL)                   
      return true;                   
  return (*c1 == *c2 || TO_UPPER (*c1) == TO_UPPER (*c2)) ? true : false;
}  /* is_equal */

typedef enum 
    {INVALIDFILE, CFILE, CPLUSPLUSFILE, OBJECTFILE,
       ASSEMBLERFILE, LIBRARYFILE, HEADERFILE}
    FileType;

struct FileInfo {
    char* suffix;
    FileType type;
};

struct FileInfo fileTypeInfo[] = {
    {"C", CPLUSPLUSFILE},
    {"cxx", CPLUSPLUSFILE},
    {"i", CFILE},
    {"c", CFILE},
    {"o", OBJECTFILE},
    {"obj", OBJECTFILE},
    {"s", ASSEMBLERFILE},
    {"asm", ASSEMBLERFILE},
    {"a", LIBRARYFILE},
    {"lib", LIBRARYFILE},
    {"h", HEADERFILE},
    {"hxx", HEADERFILE},
    {EOL, INVALIDFILE}
};

/* 
 * dir, tail, ext are VAR parameters
 */
FileType DetermineFileType (filename, dir, tail, ext)
String filename;
String *dir;                              /* Directory */
String *tail;                             /* File name (includes type) */
String *ext;                              /* File type (includes leading .) */
{
  String p;
  FileType kind;
  struct FileInfo *fi;

  *dir = nil;                             /* defaults */
  *ext = nil;
  *tail = fileName;
    
  p = strlen(filename) + filename;
  while (p != fileName) {
    if (*p == '.' && *ext == nil) {
      *ext = p;
    } else if (*p == '/' || *p == '\\') {
      *tail = p + 1;
      *p = EOL;
      *dir = fileName;
      break;
    }
    p--;
  }
  /* determine kind of file it is */
  if ( *ext == nil ) {
    kind = LIBRARYFILE;                   /* Assume lib file */
  } else {
    for(fi = fileTypeInfo; fi->suffix != EOL; fi++)
#if defined(DOS)		/* DOS filenames aren't case sensitive */
      if (is_equal((*ext)+1, fi->suffix)) break;
#else
      if (!strcmp((*ext)+1, fi->suffix)) break;
#endif
    kind = fi->type;
  }
  return kind;
}  /* DetermineFileType */

void SetUpHandler (sig, handler)
int sig;
void (*handler)();
{
#if defined(DOS)
    int (*signalHandler)();		  /* Pointer to integer function */
#else
    void (*signalHandler)();		  /* Pointer to integer function */
#endif
    signalHandler = signal(sig, handler);
    if (signalHandler == SIG_IGN) {
        (void) signal(sig, SIG_IGN);
    } else if ((int)signalHandler == -1) {
        perror("CCC: could not set up signal handler");
    }
}  /* SetUpHandler */

void interupt ()
{
    RemoveTmpFiles();
    exit(3);
}  /* interupt */

void PrintVersionAndPath ()
{
    static boolean printed = false;
    if (printed == false) {
        printf("CCPATH = %s\n", ccPath);
        printed = true;			  /* Only print this once */
    }
}  /* PrintVersionAndPath */

void HandleArgs (argc, argv)
int argc;
String *argv;
{
    register int i;
    for (i = 1; i < argc; i++) {	/* Handle options of the form '-XXX' */
        if (argv[i][0]==EOL) {		/* Skip null argument */
        } 
        else if (argv[i][0]=='-') {	/* Look at args beginning with - */
            switch (argv[i][1]) {
#if defined(DOS)
                case 'A':                 /* -Ax select storage model */
                    NoMoreOpt(argv[i][3], argv[i]);
                    ccArgv = AddArg(ccArgv, argv[i]);
                    switch (argv[i][2]) { /* Set default library */
                      case 'S':           /* small */
#if defined(cfront2)
                        StdCClib = "SLcxxe";
#else
                        StdCClib = "SLcxx5ep";
#endif
                        ccArgv = AddArg(ccArgv, "-DM_I86SM");
			cppArgv = AddArg(cppArgv, "-DM_I86SM");
                        break;
                      case 'M':           /* medium */
#if defined(cfront2)
                        StdCClib = "MLcxxe";
#else
                        StdCClib = "MLcxx5ep";
#endif
                        ccArgv = AddArg(ccArgv, "-DM_I86MM");
			cppArgv = AddArg(cppArgv, "-DM_I86MM");
                        frontEndArgv = AddArg(frontEndArgv, "+MM");
                        break;
                      case 'L':           /* large */
#if defined(cfront2)
                        StdCClib = "Lcxxce";
#else
                        StdCClib = "LLcxx5ep";
#endif
                        ccArgv = AddArg(ccArgv, "-DM_I86LM");
			cppArgv = AddArg(cppArgv, "-DM_I86LM");
                        frontEndArgv = AddArg(frontEndArgv, "+ML");
                        break;
                      case 'C':           /* compact */
#if defined(cfront2)
                        StdCClib = "CLcxxe";
#else
                        StdCClib = "CLcxx5ep";
#endif
                        ccArgv = AddArg(ccArgv, "-DM_I86CM");
			cppArgv = AddArg(cppArgv, "-DM_I86CM");
                        frontEndArgv = AddArg(frontEndArgv, "+MC");
                        break;
                      default:
                        NoMoreOpt(argv[i][2], argv[i]);
                    }
                    break;
                case 'G':                 /* -Gn instruction set */
                    NoMoreOpt(argv[i][3], argv[i]);
                    ccArgv = AddArg(ccArgv, argv[i]);
                    break;
                case 'Z':                 /* -Zx language extensions */
                    NoMoreOpt(argv[i][3], argv[i]);
                    ccArgv = AddArg(ccArgv, argv[i]);
                    break;
                case 'g':                 /* -g debug mode */
                    NoMoreOpt(argv[i][2], argv[i]);
                 /* frontEndArgv = AddArg(frontEndArgv, "+L"); */
                    ccArgv = AddArg(ccArgv, "-Zi"); /* include debug info */
                    ccArgv = AddArg(ccArgv, "-Od"); /* turn off optimization */
                    linkArgv = AddArg(linkArgv, "/CO"); /* CodeView link */
                    break;
                case 'w':                 /* -wn supress warning messages */
                    NoMoreOpt(argv[i][3], argv[i]);
                    ccArgv = AddArg(ccArgv, argv[i]);
                    break;
#endif
                case 'c':                 /* -c supress linking */
                    cFlag = SET;
                    NoMoreOpt(argv[i][2], argv[i]);
                    break;
                case 'C':                 /* -C Keep comments */
                    NoMoreOpt(argv[i][2], argv[i]);
                    cppArgv = AddArg(cppArgv, argv[i]);
                    break;
                case 'd':                 /* -d ??? (passed to cc) */
                    NoMoreOpt(argv[i][2], argv[i]);
                    ccArgv = AddArg(ccArgv, argv[i]);
                    break;
                case 'E':                 /* -E cpp only */
                    Eflag = SET;
                    NoMoreOpt(argv[i][2], argv[i]);
                    break;
                case 'F':                 /* -F cpp & cfront only */
                    Fflag = SET;
                    if (argv[i][2] == 'c') { /* -Fc output sutable for cc */
                        NoMoreOpt(argv[i][3], argv[i]);
                        FcFlag = SET;
                    } else {
                        NoMoreOpt(argv[i][2], argv[i]);
                    } /* if */
                    break;
                case 'i':                 /* added this */
                    ignErrFlag = SET;
                    break;
                case 'k':                 /* keep __ctdt.c and __ctdt.o */
                    keepctdtFlag = SET;
                    break;
                case 'l':                 /* -lfile link with library file */
                    strcpy(lastLib, argv[i]+2);
#if defined(DOS)
                    linkLibv = AddArg(linkLibv, argv[i]+2);
#else
                    linkLibv = AddArg(linkLibv, argv[i]);
#endif
                    break;
#if defined(M_XENIX)
                case 'L':                 /* -L in Xenix handled special */
                    strcpy(libPath, argv[i]+2);
                break;
#endif
                case 'N':		  /* -NOMUNCH -NOPATCH */
					  /* don't munch/patch result */
                    if (strcmp(argv[i],"-NOMUNCH")==0) {
                        noMunchFlag = SET;
                    } else {
                        ccArgv = AddArg(ccArgv, argv[i]);
                    }
                    break;
                case 'n':		  /* print, but don't do it */
                    NoMoreOpt(argv[i][2], argv[i]);
                    verboseFlag = SET;
                    fakeFlag = SET;
                    PrintVersionAndPath();
                    break;
                case 'o':                 /* -ofile name of output file */
                    if (argv[i][2] != '\0')
                      strcpy(objName, argv[i]+2);
                    else
                      strcpy(objName, argv[++i]);
                    if (*objName == EOL) {
                        WarningMsg(
                            "No object specified after -o, using a.out", nil);
                    } else {
                        objFlag = SET;
#if !defined(DOS)
                        linkArgv = AddArg(linkArgv, "-o");
                        linkArgv = AddArg(linkArgv, objName);
#endif
                    }
                    break;
                case 'p':
                    if (argv[i][2] == 'g') { /* -pg gprof*/
                        NoMoreOpt(argv[i][3], argv[i]);
                        gprofFlag = SET;
                        profFlag = false;
                    } else {		  /* -p prof */
                        NoMoreOpt(argv[i][2], argv[i]);
                        profFlag = SET;
                        gprofFlag = false;
                    }
                    ccArgv = AddArg(ccArgv, argv[i]);
                    break;
                case 'r':                 /* -r ??? (passed to cc) */
                    genRelocInfoFlag = SET;
                    NoMoreOpt(argv[i][2], argv[i]);
                    ccArgv = AddArg(ccArgv, argv[i]);
                    break;
                case 'S':                 /* -S produce assembly source */
                    skipAsmFlag = SET;
                    NoMoreOpt(argv[i][2], argv[i]);
                    ccArgv = AddArg(ccArgv, argv[i]);
                    break;
                case 'v':		  /* -v verbose */
                    verboseFlag = SET;
                    NoMoreOpt(argv[i][2], argv[i]);
                    /* PrintVersionAndPath(); */
                    break;
                case 'I':                 /* -Ifile include directory */
                    cppArgv = AddArg(cppArgv, argv[i]);
                    break;
                case 'x':                 /* -xn cpp debug mode */
                case 'D':                 /* -Dsym define cpp symbol */
                case 'U':                 /* -Usym undefine cpp symbol */
                    cppArgv = AddArg(cppArgv, argv[i]);
                    break;
                case '.':                 /* -.suffix suffix for output */
                    if (suffixFlag) {
                        Error("Not allowed to specify more than one suffix", 
			      nil);
                    } /* if */
                    suffixFlag = SET;
                    suffix = &argv[i][1];
                    break;
                case 'X':                 /*  -X"class<parms>" - implement */
                    Implement(argv[i]+2);
                    break;
                case 'Q':                 /*  -Q - implement quick */
                    NoMoreOpt(argv[i][2], argv[i]);
                    implementQuick = true;
                    break;
                default:                  /* Otherwise, send it to cc */
                    ccArgv = AddArg(ccArgv, argv[i]);
                    break;
            }   /* switch (argv[i][1]) */

        } else if (argv[i][0] == '/') {   /* /foo options go to the linker */
          linkArgv = AddArg(linkArgv, argv[i]);
          
        } else if (argv[i][0] == '+') {
					/* handle options of the form '+XXX' */
            switch (argv[i][1]) {
                case 'd':               /* +d Don't expand inlines */
                    frontEndArgv = AddArg(frontEndArgv, argv[i]);
                    break;
                case 'e':                 /* +e1 +e0 C++ vtbl generation */
                    frontEndArgv = AddArg(frontEndArgv, argv[i]);
                    break;
                case 'V':                 /* +V Accept K&R C function decl */
                    NoMoreOpt(argv[i][2], argv[i]);
                    strcpy(libDir, " -I/usr/include");
                    break;
                case 'L':                 /* +L Use #line instead of # */
                    NoMoreOpt(argv[i][2], argv[i]);
                    break;
                case 'x':                 /* +xfile Alignment file */
                    if (argv[i][2] != '\0') {
                        frontEndArgv = AddArg(frontEndArgv, argv[i]);
                    } else {
                        i++;
                        if (argv[i] == EOL) {
                            Error("No arg specified after +x", nil);
                        } else {
                            strcpy(xBuff, "+x");
                            strcat(xBuff, argv[i]);
                            frontEndArgv = AddArg(frontEndArgv, xBuff);
                        }
                    }
                    break;
                case 'S':                 /* +S spy on cfront */
                    NoMoreOpt(argv[i][2], argv[i]);
                    frontEndArgv = AddArg(frontEndArgv, argv[i]);
                    break;
                case 'i':                 /* +i keep cc source in ..c */
                    plusIflag = SET;
                    break;
                case 'z':                 /* +zxxx cfront size options */
                    frontEndArgv = AddArg(frontEndArgv, argv[i]);
                    break;
                default:                  /* otherwise send it to cfront */
                    frontEndArgv = AddArg(frontEndArgv, argv[i]);
                    break;
            }
        } else {
            if (processFile(argv[i]) != 0) {
                Quit(1);
            }
        }
    }
}  /* HandleArgs */

String SetDefaultFromEnv();
void GetCCPath();

void Init (envp)
StringVec envp;
{
#if defined(DOS) || defined(MSDOS)
    PREPROCESSOR = SetDefaultFromEnv(envp, "CCPC=", CC_cpp);
    CCfrontEnd = SetDefaultFromEnv(envp, "CFRONTC=", CC_compiler);
    frontEndSuffix = SetDefaultFromEnv(envp, "SUFFIXC=", CC_suffix);
    StdCClib = SetDefaultFromEnv(envp, "LIBRARY=", CC_library);
    runtimeLib = SetDefaultFromEnv(envp, "RUNTIMELIBC", CC_rt);
    translator = (boolean)(strcmp(frontEndSuffix, ".s") != 0);
    dotdotsuffix = (boolean)(strcmp(frontEndSuffix, "..c") == 0);
    Ccompiler = SetDefaultFromEnv(envp, "CC=", CC_cc);
    ASSEMBLER = SetDefaultFromEnv(envp, "ASSEMBLERC=", CC_as);
    LINKER = SetDefaultFromEnv(envp, "LINKERC=", DEFAULTLINKER);
    MUNCHER = SetDefaultFromEnv(envp, "MUNCHC=", CC_munch);
    PATCHER = SetDefaultFromEnv(envp, "PATCHC=", CC_patch);
    LIB = SetDefaultFromEnv(envp, "LIBC=", CC_lib);
    TMPDIR = SetDefaultFromEnv(envp, "TMP=", CC_tmp);
    COMPRESS = SetDefaultFromEnv(envp, "COMPRESS=", CC_compress);
    MV = SetDefaultFromEnv(envp, "MV=", CC_mv);
#else
    PREPROCESSOR = SetDefaultFromEnv(envp, "ccpC=", CC_cpp);
    CCfrontEnd = SetDefaultFromEnv(envp, "cfrontC=", CC_compiler);
    frontEndSuffix = SetDefaultFromEnv(envp, "suffixC=", CC_suffix);
    StdCClib = SetDefaultFromEnv(envp, "LIBRARY=", CC_library);
    runtimeLib = SetDefaultFromEnv(envp, "runTimeLibC", CC_rt);
    translator = (boolean)(strcmp(frontEndSuffix, ".s") != 0);
    dotdotsuffix = (boolean)(strcmp(frontEndSuffix, "..c") == 0);
    Ccompiler = SetDefaultFromEnv(envp, "ccC=", CC_cc);
    ASSEMBLER = SetDefaultFromEnv(envp, "assemblerC=", CC_as);
    LINKER = SetDefaultFromEnv(envp, "linkerC=", DEFAULTLINKER);
    MUNCHER = SetDefaultFromEnv(envp, "munchC=", CC_munch);
    PATCHER = SetDefaultFromEnv(envp, "patchC=", CC_patch);
    LIB = SetDefaultFromEnv(envp, "libC=", CC_lib);
    TMPDIR = SetDefaultFromEnv(envp, "TMP=", CC_tmp);
    COMPRESS = SetDefaultFromEnv(envp, "COMPRESS=", CC_compress);
#endif
    cclib = "-lc";
    GetCCPath(environ);
    ccArgv = AddArgs(ccArgv, CCARGS);
    frontEndArgv = AddArg(frontEndArgv, CCFRONTARGS);
    implement_count[0] = '\0';
}  /* Init */


int main (argc, argv, envp)
    int argc;
    StringVec argv, envp;
{
  myname = argv[0];
  if (argc <= 1) {
    fprintf(stderr,"usage: %s [options] [ file.{c,o,s} ] \n", myname);
    exit(-1);
  }
  SetUpHandler(SIGINT, interupt);
#if defined(DOS)
  SetUpHandler(SIGTERM, interupt);
  SetUpHandler(SIGBREAK, interupt);
#else
  SetUpHandler(SIGQUIT, interupt);
  SetUpHandler(SIGHUP, interupt);
#endif
  Init(envp);
  HandleArgs(argc, argv);
  if (objFlag == false) {
    strcpy(objName, "a.out");
  }
  if (numErrors != 0) {
    Quit(2);
  }
  if (Eflag || Fflag || FcFlag || skipAsmFlag) {
    Quit(0);
  }
  if (cFlag) {
    Quit(0);
  }
  Quit(LinkPhase());
}  /* main */

#define INCLUDEFLAG "-I"
#define INCLUDEFLAGLEN 2

#if defined(DOS)
#define INCLUDE_SEPERATOR ';'
#else
#define INCLUDE_SEPERATOR ':'
#endif

void AddIncludes (line)
String line;
{
    String a, b, t;
    boolean done;
    
    if (line == nil) {
        return;
    }
    a = &line[0];
    b = a;
    done = false;
    while (done == false) {
        done = (boolean) (*b == EOL || *b == '\n' || (b-&line[0]) >= ARGSIZE);
        if (*b == INCLUDE_SEPERATOR || *b == '\n' || *b == EOL) {
	  if (b>a) {
            *b = EOL;
            t = (char *) malloc(b-a + 1 + INCLUDEFLAGLEN);  /* +1=EOL +2=-I */
            strcpy(t, INCLUDEFLAG);
            strcat(t, a);
            cppPathv = AddArg(cppPathv, t);
            *b = INCLUDE_SEPERATOR;       /* restore so can print ccPath */
	  }
	  a = b+1;
        }
        b++;
    }
}  /* AddIncludes */

String ReadEnv (env, pathVar)
StringVec env;
String pathVar;
{
    int i, len;
    
    len = strlen(pathVar);
    for (i = 0; env[i] != nil; i++) {
        if (strncmp(env[i], pathVar, len) == 0) {
            return &env[i][len];
        }
    }
    return nil;
}  /* ReadEnv */

String SetDefaultFromEnv (envp, envName, defaultVal)
    StringVec envp;
    String envName;
    String defaultVal;
{
  String tmp;

  tmp = ReadEnv(envp, envName);
  if (tmp == nil) {
    tmp = defaultVal;
  }
  return tmp;
}  /* SetDefaultFromEnv */

void GetCCPath (env)
StringVec env;
{
  String tmp;
  tmp = ReadEnv(env, ENVPATH);
  if (tmp != nil) {
    strcpy(ccPath, tmp);
    AddIncludes(ccPath);
  }
}  /* GetCCPath */

int Move (filename, oldsuffix, newsuffix)
String filename, oldsuffix, newsuffix;
{
    char aBuffer[FILENAMESIZE], aBuffer2[FILENAMESIZE];

    strcpy(aBuffer, filename);
    strcat(aBuffer, oldsuffix);
    strcpy(aBuffer2, filename);
    strcat(aBuffer2, newsuffix);
    return Rename(aBuffer, aBuffer2);
}  /* Move */

/* 
 * process a given file.
 * either cc, as or just link it. 
 */
int processFile (fullPathName)
    String fullPathName;
{
  String dir;                             /* Directory */
  String fileTail;                        /* File name (includes type) */
  String ext;                             /* File type (includes leading .) */
  FileType fileType;
  int status;

  linkFile = nil;
  strcpy(fileName, fullPathName);
  fileType = DetermineFileType(fileName, &dir, &fileTail, &ext);
  /*
   * set linkFile name
   */
  if (fileType == CPLUSPLUSFILE ||
      fileType == ASSEMBLERFILE || 
      fileType == CFILE) {
    strcpy(ext, OBJ_Suffix);              /* change xxx.c to xxx.o */
    linkFile = newString(fileTail);

/*  Set up to delete the object file if it will be linked. */
    if (fileType == CPLUSPLUSFILE && cFlag == false) {
      RegisterTmpFile(linkFile);
    }
  } else if (fileType == OBJECTFILE) {
    linkFile = newString(fullPathName);
  } else if (fileType == LIBRARYFILE ||
             fileType == CFILE ||
             fileType == INVALIDFILE) {
    linkFile = newString(fullPathName);
  } else if (fileType == HEADERFILE) {
    implementArgv = AddArg(implementArgv, fullPathName);
  }
  if (ext != nil) {
    *ext = EOL;                           /* truncate so fileTail has no ext */
  }
  /*
   * set front end name
   */
  if (Fflag) {
    if (suffixFlag) {
      strcpy(frontEndName, fileTail);
      strcat(frontEndName, suffix);
    } else {
      *frontEndName = EOL;
    }
  } else {
    strcpy(frontEndName, fileTail);
    strcat(frontEndName, frontEndSuffix);
  }
  /*
   * preprocess, cfront, compile
   */
  switch (fileType) {
  case LIBRARYFILE:
#if defined(DOS)
    linkLibv = AddArg(linkLibv, fullPathName);
    break;
#endif    
  case OBJECTFILE:
  case INVALIDFILE:
    linkObjv = AddArg(linkObjv, linkFile);
    return 0;
  case CPLUSPLUSFILE:
  case CFILE:
    linkObjv = AddArg(linkObjv, linkFile);
    status = CPPphase(fullPathName, fileTail); /* Preprocess */
    if (Eflag || status != 0) {
      return status;
    }
    return compilePhase(fullPathName, fileTail, fileType); /* Compile */
  case ASSEMBLERFILE:
    if (skipAsmFlag == false) {
      return asmPhase(fullPathName, fileTail); /* Assemble */
    }
  }
  return 0;
}  /* processFile */

compilePhase (fullPathName, tail, fileType)
    String fullPathName;                  /* fullPathname to preprocess */
    String tail;                          /* file name in fullPathName */
    FileType fileType;                    /* type of original source   */
{
  int status;
                                          /* translate (cfront) */
  if (fileType == CPLUSPLUSFILE) {
    status = FrontEndPhase(fullPathName, frontEndName, cppName);
/*
#if defined(DOS) || defined(M_XENIX)
    TmpFileName(compressName, "cmp", "i");
    status = FrontEndPhase(fullPathName, compressName, cppName);
#else
    status = FrontEndPhase(fullPathName, frontEndName, cppName);
#endif
*/
    if (status != 0) {
      if (plusIflag == false && fileType == CPLUSPLUSFILE) {
	Remove(frontEndName);
/*
#if defined(DOS) || defined(M_XENIX)
	Remove(compressName);
#else
	Remove(frontEndName);
#endif
*/
      }
#if defined(sparc)
      /* cfront21 on unix creates a .c instead of a ..c file */
      if (plusIflag && !dotdotsuffix && fileType == CPLUSPLUSFILE)
	Move(tail, ".c", "..c");
#endif
      return status;
    }
    Remove(cppName);

                                          /* Compress output */
/*
#if defined(DOS) || defined(M_XENIX)
    status = CompressPhase(frontEndName, compressName);
    if (status != 0) {
      return status;
    }
    Remove(compressName);
#endif
*/
                                           /* Filter output */
    if (plusIflag && (Fflag == false || suffixFlag)) {
      status = FilterPhase(frontEndName, tail);
      if (status != 0) {
        return status;
      }
    }
    if (Fflag) {
      return status;
    }
  }                                        /* End frontEnd processing */

  status = ccPhase(tail, fileType);        /* compile (cc) */

  if (plusIflag == false && fileType == CPLUSPLUSFILE) {
    Remove(frontEndName);
  }

  if (plusIflag && !dotdotsuffix && fileType == CPLUSPLUSFILE)
    Move(tail, ".c", "..c");

  if (status != 0) {
    return status;
  }

#if defined(DOS)
  if (translator) {
    return constPhase();		  /* constructor (DOS ONLY) */
  }
#endif

  if (dotdotsuffix) {
    if (skipAsmFlag) {
      return Move(tail, "..s", ".s");
    } else {
      return Move(tail, "..o", OBJ_Suffix);
    }
  }
  return status;
}  /* compilePhase */

/*
 *  The following tranformations are done by '/bin/cc'.
 *  We have to do them ourselves.
 *
 *      '-g'  ==> '-lg' at end of args
 *      '-pg' ==> '/lib/gcrt0.o'
 *      '-p'  ==> '/lib/mcrt0.o'
 */
int TransformCCArgs () {
    register int i, j = 0;
    boolean copy;

    for (i = 0; i < MAXARGS; i++) {
        if (ccArgs[i] == 0) {
            break;
        }
        copy = true;
        if (ccArgs[i][0] == '-') {
            switch (ccArgs[i][1]) {
            case 'g':
                gFlag = true;
                copy = false;
                break;
            case 'p':
                if (ccArgs[i][2] == 'g') {
                    runtimeLib = "/lib/gcrt0.o";
                } else {
                    runtimeLib = "/lib/mcrt0.o";
                }
                copy = false;
                break;
            }
        }
        if (copy) {
            ccArgs[j++] = ccArgs[i];
        }
    }
    ccArgs[j++] = runtimeLib;
    while (j < MAXARGS && ccArgs[j] != 0) {
        ccArgs[j++] = 0;
    }
}  /* TransformCCArgs */

int CPPphase (fullPathName, tail)
    String fullPathName;                  /* fullPathname to preprocess */
    String tail;                          /* file name in fullPathName */
{
  int status;

  initexec(PREPROCESSOR);
  command = AddList(command, cppArgs);
#if defined(cfront2) || defined(cfront21)
  command = AddArg(command, "-D__cplusplus=1");  /* for Ansi compatibility */
#endif
  command = AddArg(command, "-Dc_plusplus=1");
  command = AddArg(command, implement_count);
  command = AddArgs(command, CPPARGS);
  command = AddList(command, cppPathArgs);
  if (translator) {
    command = AddArg(command, libDir);
  }
  command = AddArg(command, fullPathName); /* Input file name */
  if (Eflag) {
    if (suffixFlag) {
      strcpy(destname, tail);
      strcat(destname, suffix);
      command = AddArg(command, destname); /* output file name */
    }
    status = execute(PREPROCESSOR, commandv, stdinDesc, stdoutDesc);
  } else {
      TmpFileName(cppName, "cpp", "C");
      command = AddArg(command, cppName); /* output file name */
      status = execute(PREPROCESSOR, commandv, stdinDesc, stdoutDesc);
  }
  return status;
}  /* CPPphase */

int FrontEndPhase (fullPathName, out, in)
    String fullPathName;
    String out;
    String in;
{
  int status;
  char aBuffer[FILENAMESIZE];

  if (verboseFlag == false) {
    fprintf(stderr, "C++ Compile: %s\n", fullPathName);
  }

  initexec(CCfrontEnd);
  command = AddList(command, frontEndArgs);
#if defined(DOS) || defined(M_XENIX)
  strcpy(aBuffer, "+f");
  strcat(aBuffer, fullPathName);
  command = AddArg(command, strdup(aBuffer));
  strcpy(aBuffer, "+i");
  strcat(aBuffer, in);
  command = AddArg(command, strdup(aBuffer));
  if (*out != EOL) {
    strcpy(aBuffer, "+o");
    strcat(aBuffer, out);
    command = AddArg(command, strdup(aBuffer));
  }

  status = execute(CCfrontEnd, commandv,  stdinDesc, stdoutDesc);
  if (status == 217) {
    status = 0;                           /* ******** HACK ALERT ********** */
    numErrors--;
  }
#else
  cppDesc = myopen(in, O_RDONLY, 0644);
  if (translator) {                       /* cfront */
    command = AddArg(command, "+L");
    strcpy(aBuffer, "+f");
    strcat(aBuffer, fullPathName);
    command = AddArg(command, strdup(aBuffer));


  } else {                                /* g++ */
    command = AddArg(command, "-quiet");
  }
  if (Fflag) {
    if (suffixFlag) {
      dest = myopen(out, O_WRONLY | O_CREAT | O_TRUNC, 0644);
      status = execute(CCfrontEnd, commandv, cppDesc, dest);
      myclose(dest);
    } else {
      dest = stdoutDesc;
      status = execute(CCfrontEnd, commandv, cppDesc, dest);
    }
  } else {
    int frontEndDesc;
    frontEndDesc = myopen(out, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    status = execute(CCfrontEnd, commandv, cppDesc, frontEndDesc);
    myclose(frontEndDesc);
  }
  myclose(cppDesc);
#endif
  return status;
}  /* FrontEndPhase */

int CompressPhase (out, in)
    String out;
    String in;
{
  int status;
  initexec(COMPRESS);
  command = AddArg(command, in);
  command = AddArg(command, out);
  status = execute(COMPRESS, commandv, stdinDesc, stdoutDesc);
  if (status != 0) {
    WarningMsg(COMPRESS, "error");
  }
  return status;
}  /* CompressPhase */


/*
 * Filter out #line directives
 */
int FilterPhase (fullPathName, fileTail)
    String fullPathName;
    String fileTail;
{
#define BUFSIZE 1028
  int status;

  if (plusIflag) {
    register char prev = '\n';
    char buffer[BUFSIZE];
    int inDesc;
    int filterDesc;
    boolean skip_newline = false;

    strcpy(filterName, fileTail);
    strcat(filterName, ".f");
    filterDesc = myopen(filterName, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    inDesc = myopen(fullPathName, O_RDONLY, 0644);

    if (verboseFlag) {
      fprintf(stderr, "filter %s %s\n", fullPathName, filterName);
    }
    if (fakeFlag == false)
      for (;;) {
        register char* in = buffer;
        register char* out = buffer;
        register char* end = in + read(inDesc, in, BUFSIZE);
        if (end == in) break;             /* stop at end of file */
        if (skip_newline) {               /* if skipping at end of prev buf */
          while (in < end && *in != '\n') in++; /* Skip to new line */
          prev = *in;
          skip_newline = false;
        }
        while (in < end) {                /* search for \n# */
          if (prev != '\n' || *in != '#')
            *out++ = prev = *in++;
          else {
            while (in < end && *in != '\n') in++; /* Skip to new line */
            if (in < end)
              prev = *in++;
            else
              skip_newline = true;
          }
        }
        if (out > buffer)                 /* Write buffer */
          write(filterDesc, buffer, out - buffer);
      }
    myclose(inDesc);
    myclose(filterDesc);
    Remove(frontEndName);		  /* required by DOS rename command */
    status = Rename(filterName, frontEndName);
    if (status != 0) {
      perror("CCC: error in filter phase");
    }
  }
  return status;
}  /* FilterPhase */

int ccPhase (tail, fileType)
    String tail;
    FileType fileType;			  /* type of original source file */
{
  int status;
  char aBuffer[FILENAMESIZE];
  char bBuffer[FILENAMESIZE];

  initexec(Ccompiler);
  command = AddList(command, ccArgs);
  command = AddArg(command, "-c");

#if defined(DOS)
  command = AddArg(command, "-NM");
  command = AddArg(command, tail);
  strcpy(bBuffer, "-Fo");
  strcat(bBuffer, linkFile);
  command = AddArg(command, bBuffer);
  strcpy(aBuffer, "-Tc");
  strcat(aBuffer, frontEndName);
  command = AddArg(command, aBuffer);
#else
  if (objFlag && cFlag) {
    command = AddArg(command, "-o");
    command = AddArg(command, objName);
  }
					  /* Allow C files to skip front end */
  if (fileType == CFILE)
    command = AddArg(command, cppName);
  else
    command = AddArg(command, frontEndName);
#endif

  status = execute(Ccompiler, commandv, stdinDesc, stdoutDesc);
  if (status != 0) {
    Remove(linkFile);
  }
  return status;
}  /* ccPhase */

#if defined(DOS) || defined(M_XENIX)
/*
 * Implement constructors and destructors for an object file
 */
int constPhase ()
{
  int status;
  initexec("mxx");
#if defined(DOS)
  command = AddArg(command, "-4");
#endif
  command = AddArg(command, linkFile);
  status = execute("mxx", commandv, stdinDesc, stdoutDesc);
  return status;
}  /* constPhase */
#endif

int asmPhase (fullPathName, tail)
String fullPathName, tail;
{
    char aBuffer[FILENAMESIZE];

    initexec(ASSEMBLER);
    command = AddArg(command, "-o");
    strcpy(aBuffer, tail);
    strcat(aBuffer, ".o");		  /* out from cc -S */
    command = AddArg(command, aBuffer);
    command = AddArg(command, fullPathName);
    return execute(ASSEMBLER, commandv, stdinDesc, stdoutDesc);
}  /* asmPhase */

int RecompileWithMunchPhase ()
{
    int status;

    initexec(Ccompiler);
    command = AddArg(command, "-c");
    command = AddArg(command, "__ctdt.c");
    if (keepctdtFlag == false) {
        RegisterTmpFile("__ctdt.o");
    }
    (void) execute(Ccompiler, commandv, stdinDesc, stdoutDesc);
    
    initexec(LINKER);
    command = AddList(command, ccArgs);
    command = AddArg(command, "__ctdt.o");
    command = AddList(command, linkObjs);
    command = AddList(command, linkArgs);
    command = AddArg(command, StdCClib);
    
    status = execute(LINKER, commandv, stdinDesc, stdoutDesc);
    return status;
}  /* RecompileWithMunchPhase */


int LinkPhase () {
  int status;
#if defined(DOS)
  status = dosLink(DEFAULTLINKER);
#else
  if (strcmp(LINKER, INCLINK) == 0) {
    ccIsLinker = false;
    status = Inclink();
  } else {
    ccIsLinker = true;
    status = DefaultLink();
  }
#endif
  return status;
}  /* LinkPhase */

int Inclink () {
    TransformCCArgs();
    return PassOne(RELINK);
}  /* Inclink */

int DefaultLink () {
    int status;

    if (runtimeLib[0] != '\0') {
        ccIsLinker = false;
        TransformCCArgs();
        linkArgv = AddArg(linkArgv, "-C");
    }
    status = PassOne(LINKER);
    if (translator) {			  /* cfront translator */
      if (status != 0) {
        return status;
      }
      status = PassTwo();
    }
    return status;
}  /* DefaultLink */

#if defined(DOS)
int dosLink (linker)
    char* linker;
{
  int status;
  /* 
   * format is:
   *   LINK objects , runfile , listfile , librarylist , options ;
   */
  initexec(linker);                       /* LINK */
  command = AddList(command, linkObjs);   /* objects */
  command = AddArg(command, ",");
  if (objFlag) {                          /* runfile */
    command = AddArg(command, objName);
  }
  command = AddArg(command, "/NOIGNORECASE");
  command = AddArg(command, ",");
                                          /* listfile */
  command = AddArg(command, ",");
  command = AddList(command, linkLibs);   /* librarylist */
  command = AddArgs(command, StdCClib);
  command = AddList(command, linkArgs);
  command = AddArg(command, ";");

  status = execute(linker, commandv, stdinDesc, stdoutDesc);
  return status;
}  /* dosLink */
#endif

int PassOne (linker)
    char* linker;
{
  int status;

  initexec(linker);
  command = AddList(command, ccArgs);
  command = AddList(command, linkObjs);
  command = AddList(command, linkLibs);
#if defined(InterViews)			  /* what is this for??? */ 
  if (keepctdtFlag)
    command = AddArg(command, "-k");
#endif
  command = AddList(command, linkArgs);
  command = AddArg(command, StdCClib);
  if (ccIsLinker == false) {
    if (gFlag) {
      command = AddArg(command, "-lg");
    }
    command = AddArg(command, cclib);
    if (linker == RELINK) {
      command = AddList(command, objSpaceArgs);
    }
  }
  status = execute(linker, commandv, stdinDesc, stdoutDesc);
  return status;
}  /* PassOne */

int PassTwo () {
  int status;
#if defined(CC_domunch)
  status = MunchPhase();
#else
  status = PatchPhase();
#endif
  return status;
}  /* PassTwo */

int PatchPhase () {
  int status;
  if (noMunchFlag == false) {
    initexec(PATCHER);
    command = AddArg(command, objName);
    status = execute(PATCHER, commandv, stdinDesc, stdoutDesc);
  }
  return status;
}  /* PatchPhase */

int MunchPhase () {
  int status;
  int nmDesc, munchDesc;
  char nmName[FILENAMESIZE];

  if (genRelocInfoFlag  == false && noMunchFlag == false) {
#if !defined(M_XENIX)
    nmDesc = MakeTmpFile(nmName, "nm", "txt");
    initexec("nm");
    command = AddArg(command, objName);
    status = execute("/bin/nm", commandv, stdinDesc, nmDesc);
    if (status != 0) {
      return status;
    }
    close(nmDesc);      
    close(stdoutDesc);
    /* input */
    nmDesc = myopen(nmName, O_RDONLY, 0644);
    /* output */
    munchDesc = myopen("__ctdt.c", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (keepctdtFlag == false) {
      RegisterTmpFile("__ctdt.c");    
    }
#endif
    initexec(MUNCHER);
    if (profFlag) {
      command = AddArg(command, "-p");
    } else if (gprofFlag) {
      command = AddArg(command, "-pg");
    }
#if defined(M_XENIX)
    command = AddArg(command, objName);
#endif
    status = execute(MUNCHER, commandv, nmDesc, munchDesc);
#if !defined(M_XENIX)
    if (status != 0) {
      status = RecompileWithMunchPhase();
      if (status != 0) {
        return status;
      }
    }
#endif
  }
  return 0;
}  /* MunchPhase */

libPhase(fileName) 
    String fileName;
{
  char cmd[100];
  int status;
  initexec(LIB);
#if defined(DOS)
  sprintf(cmd, "%s -+%s;", lastLib, fileName);
  command = AddArg(command, cmd);
#else
  command = AddArg(command, "rl");
  command = AddArg(command, lastLib);
  command = AddArg(command, fileName);
#endif
  status = execute(LIB, commandv, stdinDesc, stdoutDesc);         
  return status;
}  /* libPhase */

Implement(what)
    String what;
{
  int status;
  int i;
  StringVec argv = implementArgs;
  FILE* implementfile;
  if (*what == EOL)
    Error ("You must specify \"class<parms>\" to implement with the -X option",
	   NULL);
  if (*objName == EOL)
    Error ("When using -X, you must specify an object name with the -o option",
	   NULL);
  if (*lastLib == EOL)
    Error ("When using -X, you must specify a library name with the -l option",
	   NULL);
  objFlag = false;
  TmpFileName(implementName, "imp", "C");
  implementfile = fopen(implementName, "w");
  fprintf(implementfile, "// CCC implementation input file\n");
  for (i=0; argv[i] != 0; i++)
    fprintf(implementfile, "#include <%s>\n", argv[i]);
  Eflag = false;                          /* Disable the -E option */
  cFlag = SET;                            /* Don't try to link */
  /*
   * Check for the fast track - a single compile
   */
  if (implementQuick) {
    char* what_name = strdup(what);
    char* p = strchr(what_name, '<');
    sprintf(frontEndName, "%s%s", objName, frontEndSuffix);
    if (p != NULL) *p = '\0';     /* Get the name of the class to implement */
    fprintf(implementfile,
            "#pragma defmacro IMPLEMENT_1 implement delimiter=> lines %s\n",
            what_name);
    fprintf(implementfile, "IMPLEMENT_1 %s\n", what);
    fclose(implementfile);
    linkFile = newString(objName);
    strcat(linkFile, OBJ_Suffix);
    /* Preprocess */
    status = CPPphase(implementName, objName);
    if (status != 0) return status;
    status = compilePhase(implementName, objName, CPLUSPLUSFILE);
    Remove(implementName);    
    libPhase(linkFile);
    Remove(linkFile);
  } else {
    int count;
    fprintf(implementfile,
	    "#pragma defmacro IMPLEMENT_N implement_n delimiter=> lines\n");
    fprintf(implementfile, 
   "#pragma defmacro DECLARE_ONCE declare_once delimiter=> recursive lines\n");
    fprintf(implementfile, "DECLARE_ONCE %s\n", what);
    fprintf(implementfile, "MACRO EXPANDING EXPAND_IMPLEMENT(count)\n");
    fprintf(implementfile, "{IMPLEMENT_N count %s}\n", what);
    fprintf(implementfile, "EXPAND_IMPLEMENT(Count)\n");
    fclose(implementfile);
    linkFile = newString(objName);
    if (strlen(objName) > 7) objName[7] = '\0';
    for(count=0;;count++) {
      char newImpName[FILENAMESIZE];
      char moduleName[FILENAMESIZE];
      if (strlen(objName) > 6 && count > 9) objName[6] = '\0';
      sprintf(frontEndName, "%s%d%s", objName, count, frontEndSuffix);
      sprintf(linkFile, "%s%d%s", objName, count, OBJ_Suffix);
      sprintf(moduleName, "%s%d", objName, count);
      sprintf(newImpName, "%s%s.%s", TMPDIR, moduleName, "C");
      sprintf(implement_count, "-DCount=%d", count);
      Rename(implementName,newImpName);
      status = CPPphase(newImpName, objName);
      if (status != 0) break;
      status = compilePhase(newImpName, moduleName, CPLUSPLUSFILE);
      libPhase(linkFile);
      Remove(linkFile);
      strcpy(implementName,newImpName);
    }
    if (count > 0) {
      numErrors--;                        /* We expect 1 error from cpp */
      status = 0;
    }
    Remove(implementName);    
  }    
  return status;
}  /* Implement */
