#include "c.h"
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
FILE *AssemblerFile;
static void compile ARGS((char *));
static int doargs ARGS((int, char **));
static void emitYYnull ARGS((void));
static void typestab ARGS((Symbol, void *));
static void globaltab(Symbol p,void *cl);
static void statictab(Symbol p,void *cl);
static void CreateXrefFile(char *name);
extern void SetFastCompile(Interface *);
#ifndef _S_IWRITE
#define _S_IWRITE 0x200
#endif
Interface      *IR = NULL;
FILE *xrefFile;
char *xrefFileName=NULL;
char *ilFileName=NULL;
FILE *ilFile;
static char    *infile, *outfile;
int             outputLine = 1; // whether to output #line or just # in cpp
int				verbose=0;
int             Aflag = 0;          /* >= 0 if -A specified */
int             Pflag;          /* != 0 if -P specified */
int             Xflag=1;          /* != 0 if -X specified to permit
                                 extensions */
int             glevel=0;         /* == [0-9] if -g[0-9] specified */
int             xref=0;           /* != 0 for cross-reference data */
Symbol          YYnull;         /* _YYnull  symbol if -n or -nvalidate
                                 specified */
Symbol          YYcheck;        /* _YYcheck symbol if -nvalidate,check
                                 specified */
int Savedargc;
char **Savedargv;
int GenerateAsm = 0;
int GenerateCpp = 0;
int IntermediateLanguageFile;
#ifdef PEEPHOLE
int OptimizeFlag = 0;
#endif
clock_t AsmTime=0,CppTime = 0;

static int IsAssemblerFile(char *fname)
{
	char *p;

	p = strrchr(fname,'.');
	if (p == NULL) return(0);
	p++;
	if (*p != 'a' && *p != 'A') return(0);
	p++;
	if (*p != 's' && *p != 'S') return(0);
	p++;
	if (*p != 'm' && *p != 'M') return(0);
	p++;
	if (*p) return(0);
	return(1);
}
static char *suffix(char *infile,char *s)
{
	char *outfile;
	char *p = strrchr(infile,'\\');

	if (p) p++;
	else p = infile;
	outfile = malloc(strlen(p)+10);
	memset(outfile,0,strlen(p)+10);
	strcpy(outfile,p);
	p = strrchr(outfile,'.');
	if (p) *p = 0;
	strcat(outfile,s);
	return outfile;
}
void AssembleFile(char *fname)
{
	FILE *f;
	char *buffer;
	int s;

	f = fopen(fname,"r");
	if (f == NULL) {
		printf("Impossible to topen %s\n",fname);
		return;
	}
	glevel = 0;
	AsmInit(fname);
	buffer = malloc(4097);
	while (!feof(f)) {
		s = fread(buffer,1,4096,f);
		buffer[4096] = 0;
		AsmReadBuffer(buffer,s);
	}
	free(buffer);
	AsmReadBuffer("",0);
	WriteCoffFile();
}

extern long CharsRead;
extern _stdcall GetLastError(void);
extern int NrOfSpills;
int             main(int argc,char *argv[])
{
	int i;
	clock_t t0,t1;
	FILE *outf;
	char namebuf[256];
	extern int ReadTime;

	t0 = clock();
    IR = bindings[0].ir;
	Savedargc = argc;
	Savedargv = (char **)malloc(argc * sizeof(char *));
	for (i=0; i<argc;i++) {
		Savedargv[i] = malloc(strlen(argv[i])+1);
		strcpy(Savedargv[i],argv[i]);
	}
	CppInit();
    argc = doargs(argc, argv);
	if (infile == NULL) {
		fprintf(stderr,"no input files\n");
		return(-1);
	}
	if (IntermediateLanguageFile && infile) {
		ilFileName = suffix(infile,".lil");
		ilFile = fopen(ilFileName,"w");

	}
	if (IsAssemblerFile(infile)) {
		AssembleFile(infile);
		goto doexit;
	}
	if (!OptimizeFlag)
		SetFastCompile(IR);
	StartCpp(infile);
	if (GenerateCpp) {
		char *p = strrchr(infile,'\\');
		unsigned char *buffer;

		if (p) p++;
		else p = infile;
		outfile = malloc(strlen(p)+10);
		memset(outfile,0,strlen(p)+10);
		strcpy(outfile,p);
		p = strrchr(outfile,'.');
		if (p) *p = 0;
		strcat(outfile,".i");
		outf = fopen(outfile,"w");
        if (outf == NULL) {
            fprint(2, "%s: can't write `%s'\n",
                   argv[0], outfile);
            exit(1);
        }
		buffer = malloc(16000);
		while ((i=ReadFromCpp(buffer,16000)) != 0) {
			fwrite(buffer,i,1,outf);
		}
		fclose(outf);
		free(buffer);
		t1 = clock();
		printf("Cpp time: %g sec.\n",(double)(t1-t0)/1000.0);
		return(0);
	}
	AsmInit(infile);
    typeInit();
	if (outfile == NULL) {
		if (GenerateAsm && infile) {
			outfile = suffix(infile,".asm");
		}
		else if (GenerateAsm) outfile = tmpnam(NULL);
	}
    if (GenerateAsm && outfile) {
		AssemblerFile = fopen(outfile,"w");
        if (AssemblerFile == NULL) {
            printf("%s: can't write `%s: error %d'\n",
                   argv[0], outfile,GetLastError());
            exit(1);
        }
	}
	if (xref) {
		CreateXrefFile(xrefFileName);
	}
    inputInit();
    outputInit();
    if (errfd != 2) {
	sprintf(namebuf,"%s:\n",infile);
	write(errfd,namebuf,strlen(namebuf));
    }
    t = gettok();
    (*IR->progbeg) (argc, argv);
    if (IR->stabinit)
        (*IR->stabinit) (firstfile, argc, argv);
    program();
    if (events.end)
        apply(events.end, NULL, NULL);
    memset(&events, 0, sizeof events);
    emitYYnull();
	CheckStaticUses();
    if (xref && xrefFile) {
        Symbol          symroot = NULL;
        Coordinate      src;
        foreach(types, GLOBAL, typestab, &symroot);
        foreach(identifiers, GLOBAL, typestab, &symroot);
		foreach(globals,GLOBAL,globaltab,&symroot);
		foreach(globals,GLOBAL,statictab,&symroot);
        src.file = firstfile;
        src.x = 0;
        src.y = lineno;
        if (IR->stabend)
            (*IR->stabend) (&src, symroot,
                            ltov(&loci, PERM),
                            ltov(&symbols, PERM), NULL);
		DumpDefines();
		DumpDefinedFunctions();
		fclose(xrefFile);
    }
    finalize();
    (*IR->progend) ();
    outflush();
    deallocate(PERM);
	AsmReadBuffer("",0);
	if (IntermediateLanguageFile)
		fclose(ilFile);
	if (errcnt == 0) WriteCoffFile();
	t1 = clock();
	if (verbose)
		printf("Spills:%d Chars read: %d,Memory used:%d,Time = %g sec.\n",
			NrOfSpills,CharsRead,memused,(double)(t1-t0)/1000.0);
	if (errcnt > 0 || warningCount > 0) {
		printf("%d errors, %d warnings\n",errcnt,warningCount);
	}
doexit:
	if (GenerateAsm) fclose(AssemblerFile);
	fflush(stdout);
	fclose(stdout);
	fclose(stderr);
    close(outfd);
    close(errfd);
    return errcnt > 0;
}

/* compile - compile str */
static void     compile(char *str)
{
    inputstring(str);
    t = gettok();
    program();
}
extern int Mflag;
extern int dotokfile;
extern int TraceDependencies;
extern void DoCmdLineDirective(char *,int);
extern void CmdLineAddInclude(char *);
static void CreateXrefFile(char *name)
{
	char *p,tmpfName[256],*q;

	if (xrefFile) return;
	if (*name) {
		if (!strcmp(infile,name)) {
			fprintf(stderr,"Clobbering input file %s with xref file %s?\n",
				infile,name);
			return;
		}
		xrefFile = fopen(name,"w");
	}
	p = strrchr(infile,'.');
	if (p) {
		*p = 0;
		q = strrchr(infile,'\\');
		if (q) q++;
		else q = infile;
		sprintf(tmpfName,"%s.xrf",q);
		*p = '.';
		xrefFile = fopen(tmpfName,"w");
	}
	if (xrefFile) {
		fprintf(xrefFile,"F %s\n",infile);
		AddToFileTable(infile);
	}
}
/* doargs - process program arguments, removing top-half arguments from argv */
static int      doargs(int argc,char *argv[])
{
    int             i, result;

	result = -1;
    for (i = 1; i < argc; i++) {
		if (argv[i][0] == '/') argv[i][0] = '-';
        if (strcmp(argv[i], "-g") == 0)
            glevel = 2;
        else if (strncmp(argv[i], "-g", 2) == 0
                 && argv[i][2] && argv[i][2] >= '0' && argv[i][2] <= '9') {
            glevel = argv[i][2] - '0';
            /* if (glevel >= 4) IR->stabinit = cdbstabinit; */
        }
        else if (strncmp(argv[i], "-x",2) == 0) {
            xref++;
			xrefFileName = &argv[i][2];
		}
        else if (strcmp(argv[i], "-A") == 0) {
            if (++Aflag >= 2)
                Xflag = 0;
        }
		else if (strncmp(argv[i],"-D",2) == 0) {
			DoCmdLineDirective(argv[i]+2,'D');
		}
		else if (strncmp(argv[i],"-U",2) == 0) {
			DoCmdLineDirective(argv[i]+2,'U');
		}
		else if (strncmp(argv[i],"-M",2) == 0) {
			Mflag = 1;
			if (argv[i][2]) Mflag++;
			TraceDependencies = 1;
		}
		else if (strncmp(argv[i],"-I",2) == 0) {
			CmdLineAddInclude(argv[i]+2);
		}
		else if (strcmp(argv[i],"-z") == 0) {
			IntermediateLanguageFile=1;
		}
		else if (strncmp(argv[i],"-O",2) == 0) {
			OptimizeFlag = 1;
		}
		else if (strcmp(argv[i],"-T") == 0) {
			dotokfile = 1;
		}
        else if (strcmp(argv[i], "-X") == 0)
            Xflag++;
        else if (strcmp(argv[i], "-P") == 0)
            Pflag++;
        else if (strcmp(argv[i], "-w") == 0)
            wflag++;
        else if (strcmp(argv[i], "-b") == 0
                 || strcmp(argv[i], "-C") == 0
                 || strncmp(argv[i], "-a", 2) == 0)
            profInit(argv[i]);
        else if (strcmp(argv[i], "-n") == 0) {
            if (!YYnull) {
                YYnull = install(string("_YYnull"), &globals, GLOBAL, PERM);
                YYnull->type = ftype(voidtype, inttype);
                YYnull->sclass = STATIC;
                (*IR->defsymbol) (YYnull);
            }
        }
        else if (strncmp(argv[i], "-n", 2) == 0) {  /* -nvalid[,check] */
            char           *p = strchr(argv[i], ',');
            if (p) {
                YYcheck = install(string(p + 1), &globals, GLOBAL, PERM);
                YYcheck->type = func(voidptype, NULL, 1);
                YYcheck->sclass = EXTERN;
                (*IR->defsymbol) (YYcheck);
                p = stringn(argv[i] + 2, p - (argv[i] + 2));
            }
            else
                p = string(argv[i] + 2);
            YYnull = install(p, &globals, GLOBAL, PERM);
            YYnull->type = func(voidptype, NULL, 1);
            YYnull->sclass = EXTERN;
            (*IR->defsymbol) (YYnull);
        }
        else if (strncmp(argv[i], "-target=", 8) == 0);
        else if (strncmp(argv[i], "-t", 2) == 0)
            traceInit(&argv[i][2]);
        else if (strcmp(argv[i], "-v") == 0) {
			verbose=1;
        }
        else if (strncmp(argv[i], "-s", 2) == 0)
            density = (float)strtod(&argv[i][2], NULL);
        else if (strncmp(argv[i], "-errout=", 8) == 0) {
            char           *errfile = argv[i] + 8;
            {
                errfd = open(errfile, ( _O_WRONLY|_O_APPEND | _O_CREAT ),_S_IWRITE);
                if (errfd < 0) {
                    errfd = 2;
                    fprint(2, "%s: can't write errors to `%s'\n", argv[0], errfile);
                    exit(1);
                }
            }

        }
		else if (strncmp(argv[i],"-S",2) == 0) {
			GenerateAsm = 1;
		}
        else if (strncmp(argv[i], "-e", 2) == 0) {
            int             x;
            if ((x = strtol(&argv[i][2], NULL, 0)) > 0)
                errlimit = x;
        }
		else if (strncmp(argv[i],"-E",2) == 0) {
			GenerateCpp = 1;
			if (argv[i][2] == '+')
				outputLine = 0;
		}
		else {
			result = i;
			infile = argv[i];
		}
	}
    return result;
}
/* emitYYnull - compile definition for _YYnull, if it's referenced and named "_YYnull" */
static void     emitYYnull() {
    if (YYnull && YYnull->ref > 0.0
            && strcmp(YYnull->name, "_YYnull") == 0) {
        Aflag = 0;
        YYnull->defined = 0;
        YYnull = NULL;
        compile(stringf("static char *_YYfile = \"%s\";\n", file));
        compile("static void _YYnull(int line,...) {\nchar buf[200];\nsprintf(buf, \"null pointer dereferenced @%s:%d\\n\", _YYfile, line);\nwrite(2, buf, strlen(buf));\nabort();\n}\n");
    }
}

/* typestab - emit stab entries for p */
static void     typestab(Symbol p,void *cl)
{
    if (*(Symbol *) cl == 0 && p->sclass && p->sclass != TYPEDEF)
        *(Symbol *) cl = p;
    if ((p->sclass == TYPEDEF || p->sclass == 0) && IR->stabtype)
        (*IR->stabtype) (p);
}

static void globaltab(Symbol p,void *cl)
{
	if (p->scope == GLOBAL && p->sclass != STATIC) {
		if (p->type->op != FUNCTION && p->ref != 0.0)
			fprintf(xrefFile,"E %s %d\n",p->name,p->src.y);
	}
}

static void statictab(Symbol p,void *cl)
{
	if (p->sclass == STATIC) {
		if (p->type->op != FUNCTION && p->ref != 0.0)
			fprintf(xrefFile,"S %s %d\n",p->name,p->src.y);
	}
}
