/* 
   Copyright 1994-2003 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
*/
#include        <stdio.h>
#include				<malloc.h>
#include 				<string.h>
#include 				<setjmp.h>
#include				<signal.h>
#include				<stdlib.h>
#include				"utype.h"	
#include				"cmdline.h"	
#include        "lists.h"
#include        "expr.h"
#include				"errors.h"
#include        "c.h"
#include 				"diag.h"
#include       "winmode.h"
#include       "..\version.h"

#ifdef __CCDL__
int _stklen = 100 * 1024 ;
#endif
// #define VERSION 206

extern char GLBDEFINE[],SOURCEXT[],OBJEXT[], ENVNAME[],PROGNAME[];
extern int packlevel ;
extern ERRORS *errlist;
extern TABLE    tagtable;
extern int      total_errors;
extern int diagcount;
extern char *errfile;
extern TABLE *gsyms;
FILE            *inputFile = 0, *listFile = 0, *outputFile = 0, *cppFile = 0, *errFile = 0;
int anyusedfloat ;
int stoponerr = 0;
int has_output_file ;
LIST *clist = 0;
LIST *deflist = 0;
int prm_linkreg = TRUE;
int prm_stackcheck = FALSE;
int prm_warning = TRUE, prm_extwarning = FALSE;
#ifdef ICODE
int prm_asmfile = TRUE ;
#else
#ifdef COLDFIRE
int prm_asmfile = FALSE;
#else
int prm_asmfile = TRUE ;
#endif
#endif
int prm_ansi = FALSE;
int prm_listfile = FALSE;
int prm_maxerr = 25;
int prm_diag = FALSE;
int prm_bss = TRUE;
int prm_cppfile = FALSE;
int prm_revbits = FALSE;
int prm_lines = TRUE;
int prm_cplusplus = FALSE;
int prm_cmangle = TRUE;
int prm_trigraph = FALSE ;
int basear = 1, basedr = 1, basefr = 1;
int prm_debug = FALSE;
char version[256];
char copyright[256] ;
int prm_errfile = FALSE;
int prm_profiler = FALSE;
int prm_peepopt = TRUE;
int prm_optmult = TRUE;
int prm_intrinsic = FALSE;
int prm_asmopt = FALSE;
int prm_xcept = FALSE;
int prm_quieterrors = FALSE ;
int prm_smartframes = TRUE ;
#ifdef i386
int prm_compileonly = FALSE ;
#ifdef MSDOS
int prm_winmode = DOS ;
#else
int prm_winmode = CONSOLE ;
#endif
int prm_crtdll ;
extern int prm_nasm ;
#endif

char *prm_searchpath = 0;
jmp_buf ctrlcreturn;
int file_count = 0;

char     *infile, listfile[256],outfile[256],cppfile[256],errorfile[256];
void bool_setup(char select, char *string);
void err_setup(char select, char *string);
void incl_setup(char select, char *string);
void def_setup(char select, char *string);
void codegen_setup(char select, char *string);
void optimize_setup(char select, char *string);
void warning_setup(char select, char *string);
void parsefile(char select, char *string);
void output_setup(char select, char *string) ;
#ifdef i386
void winmode_setup(char select, char *string) ;
#endif

/* setup for ARGS.C */
ARGLIST ArgList[] = {
	{ 'c', ARG_BOOL, bool_setup },
  { 'i', ARG_BOOL, bool_setup },
  { 'e', ARG_BOOL, bool_setup },
  { 'f', ARG_CONCATSTRING, parsefile },
  { 'l', ARG_BOOL, bool_setup },
  { 'o', ARG_CONCATSTRING, output_setup},
  { 'v', ARG_BOOL, bool_setup },
  { 'w', ARG_CONCATSTRING, warning_setup },
  { 'A', ARG_BOOL, bool_setup },
  { 'C', ARG_CONCATSTRING, codegen_setup },
  { 'O', ARG_CONCATSTRING, optimize_setup },
  { 'E', ARG_CONCATSTRING, err_setup },
  { 'I', ARG_CONCATSTRING, incl_setup },
	{ 'D', ARG_CONCATSTRING, def_setup },
  { 'S', ARG_BOOL, bool_setup },
  { 'X', ARG_BOOL, bool_setup },
	{ 'Q', ARG_BOOL, bool_setup },
#ifdef i386
   { 'W', ARG_CONCATSTRING, winmode_setup },
#endif
  { 0, 0, 0 }
};
#ifdef i386
void winmode_setup(char select , char *string)
{
   switch (string[0]) {
      case 'd' :
         prm_winmode = DLL ;
         break ;
      case 'c' :
         prm_winmode = CONSOLE ;
         break ;
      case 'g' :
         prm_winmode = GUI ;
         break ;
      case 'e' :
         prm_winmode = DOS ;
         break ;
      default :
         fatal("Invalid executable type") ;
   }
   if (string[1] == 'c') {
      if (prm_winmode == DOS)
         fatal("Invalid use of CRTDLL") ;
      prm_crtdll = TRUE ;
   }
}
#endif


void bool_setup(char select, char *string)
/*
 * activation routine (callback) for boolean command line arguments
 */
{
	int bool = (int)string;
   if (select == 'S' && OBJEXT[0])
      prm_asmfile = bool;
  if (select == 'l')
		prm_listfile = bool;
	if (select == 'i')
		prm_cppfile = bool;
	if (select == 'e')
		prm_errfile = bool;
   if (select == 'v' && OBJEXT[0]) {
		prm_debug = bool;
      prm_smartframes = !bool ;
   }
   if (select == 'A')
		prm_ansi = bool;
	if (select == 'Q')
		prm_quieterrors = bool ;
#ifdef i386
   if (select == 'S') {
      prm_asmfile = bool;
      prm_nasm = bool ;
      prm_compileonly = TRUE ;
   }
   if (select == 'c') {
      prm_compileonly = bool ;
      prm_asmfile = FALSE ;
   }
#endif
}
void codegen_setup(char select, char *string)
/*
 * activation for code-gen type command line arguments
 */
{
		int bool = TRUE;
		while (*string) {
				switch (*string) {
//               case 'f':
//                  prm_smartframes = bool ;
//                  break ;
					case 'd':
						prm_diag = bool;
						break;
					case 'r':
						prm_revbits = bool;
						break;
					case 'b':
						prm_bss=bool;
						break;
					case 'l':
						prm_lines = bool;
						break;
					case 'm':
						prm_cmangle = bool;
						break;
#ifndef i386
					case 'R':
						prm_linkreg = bool;
						break;
#endif
					case 'S':
						prm_stackcheck = bool;
						break;
/*               case 'T': */
/*                  prm_trigraph = bool ; */
/*                  break ; */
/*          case 'X': This overrides something in confcodegen */
/*            prm_xcept = bool; */
						break;
					case 'Z':
						prm_profiler = bool;
						break;
					case '-':
						bool = FALSE;
						break;
					case '+':
						bool = TRUE;
						break;
					default:
						if (!confcodegen(*string,bool)) 
							fatal("Illegal codegen parameter ");
				}
			string++;
		}
}
void optimize_setup(char select, char *string)
/*
 * activation for optimizer command line arguments
 */
{
		int bool = TRUE;
		while (*string) {
			switch (*string) {
				case 'a':
					prm_asmopt = bool;
					break;
				case 'i':
					prm_intrinsic = bool;
					break;
				case 'p':
					prm_peepopt = bool;
					break;
				case 'm':
					prm_optmult = bool;
					break;
				case 'R':
				case 'r':
					string++;
					while (*string && *string != '+' && *string != '-') {
						switch (*string++) {
							case 'a':
							case 'A':
								basear = bool;
								break;
							case 'd':
							case 'D':
								basedr = bool;
								break;
							case 'f':
							case 'F':
								basefr = bool;
								break;
							default:
								goto errorx;
						}
						if (!*string)
							return;
					}
					break;
				case '-':
					bool = FALSE;
					break;
				case '+':
					bool = TRUE;
					break;
				default:
errorx:
#ifdef ICODE
               if (!confoptgen(*string,bool))
#endif
						fatal("Illegal optimizer parameter");
			}
			string++;
		}
}
void err_setup(char select, char *string)
/*
 * activation for the max errs argument
 */
{
	if (*string == '+') {
		prm_extwarning = TRUE;
		string++;
	}
	else
		if (*string == '-') {
			prm_extwarning = FALSE;
			string++;
		}
	prm_maxerr = atoi(string);
}
void incl_setup(char select, char * string)
/*
 * activation for include paths
 */
{
   if (prm_searchpath) {
      prm_searchpath = realloc(prm_searchpath,strlen(string)+strlen(prm_searchpath)+2);
      strcat(prm_searchpath,";") ;
   } else {
		prm_searchpath = malloc(strlen(string)+1);
		prm_searchpath[0] = 0;
	}
	strcat(prm_searchpath,string);
}
void def_setup(char select, char *string)
/*
 * activation for command line #defines
 */
{
	char *s = malloc(strlen(string)+1);
	LIST *l = malloc(sizeof(LIST));
	strcpy(s,string);
	l->link = deflist;
	deflist = l;
	l->data = s;
}
void output_setup(char select, char *string)
{
	strcpy(outfile,string);
	has_output_file = TRUE ;
}
void setglbdefs(void)
/*
 * function declares any global defines from the command line and also
 * declares a couple of other macros so we can tell what the compiler is
 * doing
 */
{
	LIST *l = deflist;
   char buf[256], *p ;
   int major,minor ;
	while (l) {
		char *s = l->data;
		char *n = s;
		while (*s && *s != '=')
			s++;
		if (*s == '=')
			*s++=0;
		glbdefine(n,s);
		l = l->link;
	}
  sscanf(CC_STRING_VERSION,"%d.%d",&major,&minor) ;
  sprintf(buf,"%d",major * 100 + minor) ;
  glbdefine("__CCDL__",buf);
#ifdef i386
  glbdefine("__386__","");
  glbdefine("__i386__","");
#endif
#ifdef CPLUSPLUS
	if (prm_cplusplus) {
		glbdefine("__cplusplus","");
	}
#endif
//   if (prm_ansi)
   glbdefine("__STDC__","1");
	glbdefine(GLBDEFINE,"");
}
void InsertAnyFile(FILE *inf, FILE *outf, char *filename, char *path, int drive)
/*
 * Insert a file name onto the list of files to process
 */

{
  char *newbuffer, buffer[100],*p = buffer;
	LIST *r = &clist;

	file_count++;

	if (drive != -1) {
		*p++ = (char)(drive + 'A');
		*p++ = ':';
	}
	if (path) {
		strcpy(p,path);
		strcat(p,"\\");
	}
	else
		*p = 0;
  /* Allocate buffer and make .C if no extension */
	strcat(buffer,filename);
#ifdef i386
   if (prm_compileonly || !InsertExternalFile(buffer)) 
#endif
   {
      AddExt(buffer,".C");
      newbuffer = (char *) malloc(strlen(buffer) + 1);
      if (!newbuffer)
         return ;
      strcpy(newbuffer,buffer);

  /* Insert file */
  /* this is screwy, r is actually a pointer to a pointer.  It only works
   * because the link field is the first field
   */
      while (r->link)
         r = r->link;
      r->link = malloc(sizeof(LIST));
      r = r->link;
      if (!r)
         return ;
      r->link = 0;
      r->data = newbuffer;
   }
}
void dumperrs(FILE *file);
void setfile(char *buf,char *orgbuf,char *ext)
/*
 * Get rid of a file path an add an extension to the file name
 */
{
	char *p = strrchr(orgbuf,'\\');
	if (!p) p = orgbuf;
	else p++;
	strcpy(buf,p);
	StripExt(buf);
	AddExt(buf,ext);
}
void outputfile(char *buf, char *orgbuf,char *ext)
{
   if (has_output_file)
		AddExt(buf,ext) ;
	else
		setfile(buf,orgbuf,ext);
}
void scan_env(char *output,char *string)
{
   char name[256],*p = name ;
   while (*string) {
      if (*string == '%') {
         p = name ;
         string++ ;
         while (*string && *string != '%')
            *p++ = *string++ ;
         if (*string)
            string++ ;
         *p = 0 ;
         p = getenv(name) ;
         if (p) {
            strcpy(output,p) ;
            output += strlen(output) ;
         }

      } else
         *output++ = *string++ ;
   }
   *output = 0 ;
}
int parse_arbitrary(char *string)
/*
 * take a C string and and convert it to ARGC, ARGV format and then run
 * it through the argument parser
 */
{
	char *argv[40];
   char output[1024] ;
	int rv,i;
	int argc = 1;
	if (!string || !*string)
		return 1;
   scan_env(output,string) ;
   string = output ;
	while (1) {
      int quoted = ' ' ;
		while (*string == ' ') 
			string++;
		if (!*string)
			break;
      if (*string == '\"')
         quoted = *string++ ;
		argv[argc++] = string;
      while (*string && *string != quoted) string++;
		if (!*string)
			break;
		*string = 0;
		string++;
	}
  rv = parse_args(&argc,argv,TRUE);
  for (i= 1; i < argc; i++)
		InsertAnyFile(0,0,argv[i],0,-1);
	return rv;
}
void parsefile(char select, char *string)
/*
 * parse arguments from an input file
 */
{
	FILE *temp;
	if (!(temp = fopen(string,"r")))
		fatal("Argument file not found");
	while (!feof(temp)) {
		char buf[256];
		buf[0] = 0;
		fgets(buf,256,temp);
		if (buf[strlen(buf)-1] == '\n')
			buf[strlen(buf)-1] = 0;
		if (!parse_arbitrary(buf))
			break;
	}
	fclose(temp);
}
void addinclude(void)
/*
 * Look up the INCLUDE environment variable and append it to the
 * search path
 */
{
#ifdef COLDFIRE
	char *string = getenv("cfccincl");
#else
	char *string = getenv("CCINCL");
#endif
	if (string && string[0]) {
		char temp[500];
		strcpy(temp,string);
		if (prm_searchpath) {
			strcat(temp,";");
			strcat(temp,prm_searchpath);
			free(prm_searchpath);
		}
		prm_searchpath = malloc(strlen(temp)+1);
		strcpy(prm_searchpath,temp);
	}
}     
int parseenv(char *name)
/*
 * Parse the environment argument string
 */
{
	char *string = getenv(name);
	return parse_arbitrary(string);
}
int parseconfigfile(char *name)
{
   char buf[256],*p ;
   strcpy(buf,name) ;
   p = strrchr(buf,'\\') ;
   if (p) {
      FILE *temp;
      strcpy(p+1,PROGNAME) ;
      strcat(p,".CFG") ;
      if (!(temp = fopen(buf,"r")))
         return 0 ;
      while (!feof(temp)) {
         buf[0] = 0;
         fgets(buf,256,temp);
         if (buf[strlen(buf)-1] == '\n')
            buf[strlen(buf)-1] = 0;
         if (!parse_arbitrary(buf))
            break;
      }
      fclose(temp);
   }
   return 0 ;

}
void dumperrs(FILE *file)
{
#ifdef DIAGNOSTICS
   if (diagcount && !total_errors)
		fprintf(file,"%d Diagnostics detected\n",diagcount);
#endif
	if (total_errors)
		fprintf(file,"%d Total errors\n",total_errors);
	if (prm_diag)
		mem_summary();
}
void summary(void)
{       
	if (prm_listfile) {
      fprintf(listFile,"\f\n *** global scope symbol table ***\n\n");
      list_table(gsyms,0);
      if (tagtable.head) {
         fprintf(listFile,"\n *** structures and unions ***\n\n");
         list_table(&tagtable,0);
		}
		dumperrs(listFile);
  }
}
void ctrlchandler(int aa)
{
	longjmp(ctrlcreturn,1);
}
void internalError(int a)
{
   printf("Internal Error - Aborting compile") ;
   exit(1) ;
}
int main(int argc,char *argv[])
{
   char buffer[256];
	char *p;
   strcpy(copyright,PRODUCT_COPYRIGHT) ;
   strcpy(version,CC_STRING_VERSION) ;
  banner("%s Version %s %s",PROGNAME,version,copyright);

#ifndef ICODE
//   signal(SIGSEGV,internalError) ;
//   signal(SIGFPE, internalError) ;
#endif
	if (OBJEXT[0])
		prm_asmfile = FALSE ;

  outfile[0] = 0;

	/* parse the environment and command line */
	/* inpout cfg file has already been parsed */
  if (!parseenv(ENVNAME))
    usage(argv[0]);
  parseconfigfile(argv[0]) ;
  if (!parse_args(&argc, argv, TRUE) || (!file_count && argc == 1))
    usage(argv[0]);


	/* tack the environment includes in */
	addinclude();

	/* processor-dependent initialization */
	confsetup();

#ifdef i386
   if (has_output_file && !prm_compileonly) {
      InsertOutputFile(outfile) ;
      has_output_file = FALSE ;
   }
#endif
  /* Scan the command line for file names or response files */
	{ int i;
		for (i=1; i <argc; i++) 
			InsertAnyFile(0,0,argv[i],0,-1);
	}

   if (file_count > 1 && has_output_file)
		fatal("Can't specify output file for multiple input files\n");

  /* Set up a ctrl-C handler so we can exit the prog */
	signal(SIGINT,ctrlchandler);
	if (setjmp(ctrlcreturn))
		exit(1);
	/* loop through and compile all the files on the file list */
	while (clist) {
      anyusedfloat = 0 ;
      packlevel = 0 ;
#ifndef ICODE
		regini();
		IntrinsIni();
		inasmini();
#endif
		memini();
      symini();
		stmtini();
		initini();
		preprocini();
		exprini();
		declini();
    defclassini();
		funcini();
		peepini();
		outcodeini();
		outcode_file_init() ;
		dbginit() ;
      browse_init() ;
//      if (!prm_quieterrors)
//         printf("file: %s\n",clist->data);
	  strcpy(buffer,clist->data);
		if (prm_asmfile)
			outputfile(outfile,buffer,SOURCEXT);
		else
			outputfile(outfile,buffer,OBJEXT);
		setfile(cppfile,buffer,".I");
		setfile(listfile,buffer,".LST");
		setfile(errorfile,buffer,".ERR");
		prm_cplusplus = FALSE;
	  AddExt(buffer,".C");
#ifdef CPLUSPLUS
		p = strrchr(buffer,'.');
		if (*(p-1) != '.') {
			if (p[1] == 'c' || p[1] == 'C')
				if (p[2] == 'p' || p[2] == 'P')
					if (p[3] == 'p' || p[3] == 'P')
						prm_cplusplus = TRUE;
		}
#endif
		infile = errfile = litlate(buffer);
		kwini();

		if (prm_cppfile) {
			if (!(cppFile = fopen(cppfile,"w")))
				fatal("Can't open cpp file %s",cppfile);
		}
  	if (!(inputFile = fopen(infile,"r")))
    	fatal("Can't open input file %s",infile);
   browse_startfile(infile) ;
  	if (!(outputFile = fopen(outfile,prm_asmfile ? "w" : "wb" ))) {
    	fclose(inputFile);
    	fatal("Can't open output file %s",outfile);
	  }
		if (prm_asmfile)
			asm_header();
	  if (prm_listfile)
  		if (!(listFile = fopen(listfile,"w"))) {
    		fclose(inputFile);
		    fclose(outputFile);
    		fatal("Can't open list file %s",listfile);
  		}
	  if (prm_errfile)
  		if (!(errFile = fopen(errorfile,"w"))) {
				fclose(listFile);
    		fclose(inputFile);
		    fclose(outputFile);
    		fatal("Can't open error file %s",errorfile);
  		}
		initerr();
	  initsym();
		setglbdefs();
  	getch();
  	getsym();
	  compile();
		summary();
  	release_global();
		dumperrs(stdout);
	  fclose(inputFile);
//      if (prm_asmfile) {
  		fclose(outputFile);
//      }
  	if (prm_listfile)
    	fclose(listFile);
  	if (prm_errfile)
    	fclose(errFile);
  	if (prm_cppfile)
    	fclose(cppFile);
		clist = clist->link;

		/* Remove the ASM file if there are errors */
	if (total_errors)
			remove(outfile);

		/* Remove the ERR file if no warnings / errors */
		if (!errlist && prm_errfile)
			remove(errorfile);

		/* Flag to stop if there are any errors */
		stoponerr |= total_errors;
	}
  if (stoponerr)
   	return(1);
#ifdef i386
  else if (!prm_compileonly)
    return RunExternalFiles() ;
#endif
  else
    return(0);
}