/* Copyright Per Bothner 1987. Read the file Q-INFO */
#include <stdio.h>
#include <fstream.h>
#include "Qcompile.h"
#include <procs.h>
#include "symbol.h"
#include <machines.h>
#include <debug.h>
//#include <format.h>
extern struct Module *DefaultModule;
extern struct Type Block;
extern struct DataToken *GetRealToken();
extern FILE *Open();

#undef CompileToC
#define CompileToC(cf) (!EvalCompile(cf))

struct SegDesc SegTable[NumSegments] = {
    { ".text", "text.start"},
    { ".data", "data.start"}
  };
#if 0
#define TmpDirName "[tmp]"

extern struct DataToken *GetHeadToken();

struct ExtSymbol *
AddSymbol(cf, name)
    CFile *cf; char *name;
  { register struct ExtSymbol **next = &cf->firstSymbol;
    register struct ExtSymbol *sym;
    int len = strlen(name);
    register int nSyms;
 /* check if symbol already in list */
    for (nSyms = 0; sym = *next, sym; nSyms++)
      {
	if (strcmp((char*)(sym+1), name) == 0)
	  {
	    return sym;
	  }
	next = &sym->next;
      }
 /* add new symbol */
    sym = (struct ExtSymbol *)malloc(sizeof(struct ExtSymbol) + len + 1);
    sym->next = 0;
    sym->symNum = nSyms;
    sym->s.stype = UNDEF;
    sym->s.slength = len;
    sym->s.svalue = 0;
    strcpy(sym+1, name);
    *next = sym;
    cf->symbolSize += sizeof(struct vaxsym) + len + 1;
    return sym;
  }
#endif

#if 0
CompRegOffsetToReg(cf, reg, offset, dest_reg)
  {
    struct DataToken src_token, dst_token;
    MakeRegOffsetToken(&src_token, reg, offset);
    MakeRegToken(&dst_token, dest_reg);
    CompileMove(cf, &src_token, &dst_token);
  }
#endif

#if 0
static WriteReloc(f, reloc)
    register struct Relocation *reloc;
    FILE *f;
  { struct vaxreloc rel;
    if (reloc->rsegment == RFORGET) return;
    rel.rsegment = reloc->rsegment;
    ByteSwapLongCopy(&reloc->rpos, &rel.rpos, sizeof(long));
    rel.rsize = reloc->rsize;
    rel.rdisp = reloc->rdisp;
    if (reloc->rsymbol)
	ByteSwapShortCopy(&reloc->rsymbol->symNum, &rel.rsymbol, sizeof(short));
    else rel.rsymbol = 0;
    fwrite(&rel, sizeof rel, 1, f);    
  }
#endif

/*
 * Temp. files are needed for writing out the text and (sometimes) data
 * segments. In interactive mode, a new one is needed for each command line.
 * For efficiency, we re-use the last one; instead of deleting it, we save
 * it for the next time we need a temp. file. We save the file (and its
 * name) on a pseudo-"free list" (pseudo because it has at most one element).
 * (If more files are released, they are immediatly deleted.) This is
 * reasonable for Q usage patterns where only one file is being compiled
 * at a time, but will work for nested or parallell compilations (except
 * that there is no synchroniation.)
 * (A two-element list might be somewhat better.)
 */
FILE *AvailTempFile = NULL;

#if 0
static
GetTempFile(cf, segment)
register CFile *cf;
  {
    if (AvailTempFile)
      {
	cf->segFile[segment] = AvailTempFile;
	AvailTempFile = NULL;
	fseek(cf->segFile[segment], 0, 0);
      }
    else
      { char buf[80];
	SystemCode error = OK;
	sprintf(buf, "%sQtmp.XXXXXX", TmpDirName);
	mktemp(buf);
	if ((cf->segFile[segment] = Open(buf, FCREATE, &error)) == NULL)
	  {
	    fprintf(stderr, "Cannot create work file %s - %s\n",
		buf, ErrorString(error));
	    return 0;
	  }
	DeleteFile(buf);
      }      
  }
#endif

#if 0
DeleteFile(name)
    char *name;
  {
    SystemCode error;
    if (LogVerbose > 0)
	fprintf(LogFile, "[Removing %s]\n", name);
    error = RemoveFile(name);
    if (error != OK)
	fprintf(stderr, "Failed to remove temporary file %s - %s\n",
	    name, ErrorString(error));
  }
#endif
#if 0
FreeCompilerTemp(cf, segment)
    register CFile *cf;
  {
    if (AvailTempFile == NULL)
	AvailTempFile = cf->segFile[segment];
    else
	fclose(cf->segFile[segment]);
    cf->segFile[segment] = NULL;
  }
#endif

CFile::CFile(
    char *name, /* module name - used for suffixes etc */
    Module *mod,
    int mode) /* mode&1: generate assembly; mode&2: generate binary */
{
    char buf[60];
    module = mod;
    moduleName = name; /* sets/clears EvalCompile */
#if 1
    machineType = ThisMachineType;
    machineDesc = NULL;
#else
    if (mode > 3)
      {
	switch (mode)
	  {
	  case 'm': case 'M': machineDesc = M68kDesc; break;
	  case 'v': case 'V': machineDesc = VaxDesc; break;
	  default:
	    RunError(0, "Unknown machine type: %c", mode);
	  }
	machineType = mode;
	mode = 1;
      }
    else
      {
	machineType = ThisMachineType;
	machineDesc = ThisMachineDesc;
      }
#endif
    availRegs = 0; /* set by ProcInit */
    if (mode & 1)
      {
	sprintf(buf, "%s.Q.C", name);
	_asm_stream = new ofstream(buf);
	if (!_asm_stream || !*_asm_stream) {
	    cerr << "Cannot create output file '" << buf << "'\n";
	    return;
	}
	asm_stream() <<
	    "#include <Qc_header.h>\n#include \"" << name << ".Q.h\"\n";
#if 1
	asm_stream() << "static int __do_init_syms();\n"
	  "static int __sym_init_dummy = __do_init_syms();\n";
#else
	asm_stream() << "static struct SymbolInitElement __sym_inits[];\n"
	    << "static char __sym_strs[];\n"
	    << "static SymbolInitDummy __sym_init_dummy(__sym_inits, __sym_strs);\n";
#endif
      }
    if (mode != 0) {
	sprintf(buf, CompileToC(this) ? "%s.Q.h" : "%s.Qx", name);
	_aux_stream = new ofstream(buf);
	if (_aux_stream == NULL || !*_aux_stream)
	  {
	    cerr << "cannot create aux output file '" << buf << "'\n";
	    _aux_stream = NULL;
	    return;
	   }
    }
    else {
	_aux_stream = NULL;
    }

    lastId = 0;
    sourcePos.lineNo = 0;
    includes = 0;
    segment = NullSegment;
    firstSymbol = 0;
    symbolSize = 0;
    errors = 0;
    curProc = NULL;
    pendingDumps = NULL;
    pendingSymbols = NULL;
/*    pendingList = NULL;*/
    indentation = 0;
/*  prevCompiledList = NULL; */
    instrBufLen = 0;
    commentBuffer[0] = '\0';
#if 0
    pendingTypeDefs = NULL;
    for (i = HighestReg; --i >= 0; ) regRefCount[i] = 0;
    for (i = NumSegments; --i >= 0; )
      {
	if (mode == 0 && i == DataSegment)
	    segFile[i] = 0;
	else
	  {
	    GetTempFile(this, i);
	    relocations[i] = 0;
	    firstReloc[i] = 0;
	    nextReloc[i] = &firstReloc[i];
	    segSize[i] = 0;
	    SwitchSegment(this, i);
	    CompLabel(this, SegTable[i].startLabelSuffix, 0);
	  }
      }
#endif
  }

#include "genmap.h"
void FinishSymbols(CFile* cf)
{
    struct PrevDumped *dump;
    cf->asm_stream() << "static char __sym_strs[] = \"\\\n";
    for (dump = cf->pendingSymbols; dump; dump = dump->next) {
	Symbol *sym = (Symbol*)dump->addr();
	PrintQuotedInterior(sym->chars(), sym->leng(), cf->asm_stream());
	cf->asm_stream() << "\\000\\\n";
    }
    cf->asm_stream() << "\";\n";

    cf->asm_stream() << "static SymbolInitElement __sym_inits[] = {\n";
    for (dump = cf->pendingSymbols; dump; dump = dump->next) {
	Symbol *sym = (Symbol*)dump->addr();
	int flags;
	if (sym->_package == &KeywordPackage) flags = KeywordPackageCode;
	else if (sym->_package == &BuiltinPackage) flags = BuiltinPackageCode;
	else if (sym->_package == &CLispPackage) flags = CLispPackageCode;
	else if (sym->_package == &SchemePackage) flags = SchemePackageCode;
	else if (sym->_package == &UserPackage) flags = UserPackageCode;
	else flags = 0; /* ??? */
	cf->asm_stream() << "  { &" <<  dump->name() << ", " << flags << ", " 
	    << sym->leng() << " },\n";
    }
    cf->asm_stream() << "  {0} };\n";
    cf->asm_stream() << "static int __do_init_syms() {return InitSymbols(__sym_inits, __sym_strs);}\n";
}

void CFile::finish()
{
    DumpPending(*this);
    FinishSymbols(this);
    //    DumpSymbolPtrs(this);
    if (_aux_stream) {
	delete _aux_stream;
	_aux_stream = NULL;
    }
    if (_asm_stream) {
	delete _asm_stream;
	_asm_stream = NULL;
    }
#if 0
    register struct PrevCompiled *nextC /* *prevC = prevCompiledList */;
    for ( ; prevC; prevC = nextC) {
	nextC = prevC->next;
	if (prevC->lab.flags == StringLabel) free(prevC->lab.label);
	free(prevC);
    }
#endif
}

#if 0
static
WriteBinFile(cf, outName)
    register CFile *cf; char *outName;
  { int segment;
    SystemCode error = OK;
    register FILE *f;
    register i;
    struct ExtSymbol *sym;

    f = Open(outName, FCREATE, &error);
    if (error != OK)
      {
	fprintf(stderr, "Output file %s - %s\n", outName, ErrorString(error));
	return 0;
       }

 /* first write out the header */
  { struct bhdr filhdr;
    filhdr.fmagic = FMAGIC;
    filhdr.tsize = cf->segSize[TextSegment];
    filhdr.dsize = cf->segSize[DataSegment];
    filhdr.bsize = 0;
    filhdr.ssize = cf->symbolSize;
    filhdr.rtsize = cf->relocations[TextSegment] * sizeof(struct vaxreloc);
    filhdr.rdsize = cf->relocations[DataSegment] * sizeof(struct vaxreloc);
    filhdr.entry = 0;
    ByteSwapLongInPlace(&filhdr, sizeof(struct bhdr));
    fwrite(&filhdr, sizeof(struct bhdr), 1, f);
  }

 /* write text, then data segments
    This is complicated by the fact that we must add the text size to
    to all relocatable words that refer to the data segment.
    Also, we must check if labels have become defined.
 */
    for (segment = 0; segment <= 1; segment++)
      { register FILE *segFile = cf->segFile[segment];
	register struct Relocation *reloc = cf->firstReloc[segment];
	int rpos = 0;
	int more = cf->segSize[segment];
	char buf[40];

#if 0
	fclose(segFile);
	sprintf(buf, "%s%.20s%s-XXXXXX", TmpDirName,
	    cf->moduleName, SegTable[segment].startLabelSuffix);
	mktemp(buf);
 	segFile = fopen(buf, "r");
#endif
	fseek(segFile, 0, 0);
	for (;;)
	  { int adjust = 0;
	 /* look for a relocation command which needs to be adjuster */
	    while (reloc)
	      {
	     /* if we relocate relative to a known label, fix this */
		if (reloc->rsegment == REXT
		&& (sym = reloc->rsymbol)->s.stype != UNDEF)
		  { 
		    reloc->rsymbol = 0;
		    adjust = sym->s.svalue;
		    if ((sym->s.stype & ~EXTERN) == TEXT)
		      {
			reloc->rsegment = RTEXT;
			break;
		      }
		    else if ((sym->s.stype & ~EXTERN) == DATA)
			reloc->rsegment = RDATA;
		    else if ((sym->s.stype & ~EXTERN) == ABS)
		      {
			reloc->rsegment = RFORGET;
			break;
		      }
		  }
	     /* relocations are relative to start of text segment */
		if (reloc->rsegment == RDATA)
		  {
		    adjust += cf->segSize[TextSegment];
		    break;
		  }
		reloc = reloc->next;
	      }
	    if (!reloc)
		break;
	/*  copy code until reloc->rpos is reached */
	    i = reloc->rpos - rpos;
	    more -= i;
	    while (--i >= 0)
		putc(getc(segFile), f);
	    rpos = reloc->rpos;
	/*  adjust the word according to the reloc command */
	    switch (reloc->rsize)
	      {
	      case RLONG:
		PutSigned4(GetSigned4(segFile) + adjust, f);
		i = 4;
		break;
	      case RWORD:
		PutSigned2(GetSigned2(segFile) + adjust, f);
		i = 2;
		break;
	      case RBYTE:
		PutSigned1(GetSigned1(segFile) + adjust, f);
		i = 1;
		break;

	      }
	    more -= i;
	    rpos += i;
	    reloc = reloc->next;
	  }
	for (i = more; --i >= 0; )
		putc(getc(segFile), f);
      }

 /* write symbol table */
    for (sym = cf->firstSymbol; sym; sym = sym->next)
      {
	if ((sym->s.stype & ~EXTERN) == DATA)
	    sym->s.svalue += cf->segSize[TextSegment];
	if (sym->s.stype == UNDEF) sym->s.stype = EXTERN|UNDEF;
	ByteSwapLongInPlace(&sym->s.svalue, sizeof(long));	
	fwrite(&sym->s, sizeof(struct vaxsym) + sym->s.slength + 1, 1, f);
      }

 /* write relocation tables */
    for (segment = 0; segment <= 1; segment++)
      { register struct Relocation *reloc = cf->firstReloc[segment];
	while (reloc)
	  { struct Relocation *next = reloc->next;
	    WriteReloc(f, reloc);
	    free(reloc);
	    reloc = next;
	  }
      }

 /* free symbol table */
    for (sym = cf->firstSymbol; sym; )
      { struct ExtSymbol *next = sym->next;
	free(sym);
	sym = next;
      }

    fclose(f);
  }
#endif

CFile::~CFile()
{
    finish();
}
