/*

File:	 out.c
Purpose: Output routines

Last Update Time-stamp: <97/06/25 13:22:22 zdu>

Copyright (C) 1995, 1997 Zerksis D. Umrigar

See the file LICENSE for copying and distribution conditions.
THERE IS ABSOLUTELY NO WARRANTY FOR THIS PROGRAM.

*/

#include "options.h"
#include "out.h"
#include "scan.h"

#include "area.h"
#include "error.h"

/*			     OUTPUT ROUTINES				*/


typedef struct {
  ConstString fName;	/* source file name in which counter resides */
  Index lineNum;	/* line number at which counter resides */
} CounterInfo;

static struct {
  AreaX counterInfo;	/* contains information for all counters */
  FILE *outFile;
  ConstString ext;
  CounterType counter;
} globs;
#define G globs

#define COUNTER_INFO(i)	AX_ACCESS(G.counterInfo, CounterInfo, i)

VOID 
initOut() 
{
  INIT_AREAX(G.counterInfo, CounterInfo, 10, 0xFFFF);
}

VOID 
terminateOut() 
{
  delAreaX(&G.counterInfo);
}

/* Source Names */
#define CNTR_TYPE	"unsigned"	/* C type used for counters */
#define FNAME_TYPE	"FNameType"	/* type used for file-names array */
#define LINE_NUM_TYPE	"unsigned"	/* C type used for line #s */
#define PROF_TYPE	"ProfType"	/* type used for global info */
#define CNTRS		"Cntrs"		/* name of counter array */
#define LINE_NUMS	"LineNums"	/* name of line # array */
#define FILE_NAMES	"FNames"	/* name of file name breaks array */
#define ATEXIT_FN	"AtExit"	/* name of function to call at exit */
#define PROF_VAR	"Prof"
#define LIB_OUT_FN	"_bbProfOut"	/* runtime profile output function */

static FILE *
openOutputFile(fName)
  ConstString fName;
{
  CONST Count fNameLen= strlen(fName);
  ConstString dotP= strrchr(fName, '.');	/* look for extension */
  CONST Int rootLen= (dotP) ? dotP - fName : fNameLen;
  enum { MAX_BUF= 256 };
  char buf[MAX_BUF];
  if (fNameLen + strlen(optionsP->prefix) + 1 > MAX_BUF) {
    fatal("output file name too long");
  }
  else {
    sprintf(buf, "%.*s%s%s", rootLen, fName, optionsP->prefix, fName + rootLen);
  }
  if ((G.outFile= fopen(buf, "w")) == NULL) {
    fatal("could not open file %s", buf);
  }
  return G.outFile;
}

VOID
beginOutFile(fName)
  ConstString fName;
{
  FILE *outFile= openOutputFile(fName);

  fprintf(outFile, "typedef struct {\n");
  fprintf(outFile, "  const unsigned hi;\n"); 
  fprintf(outFile, "  const char *const fName;\n");
  fprintf(outFile, "} %s%s;\n\n", optionsP->prefix, FNAME_TYPE);

  fprintf(outFile, "typedef struct {\n");
  fprintf(outFile, "  const %s *const counters;\n", CNTR_TYPE);
  fprintf(outFile, "  const %s%s *const fNames;\n", 
	  optionsP->prefix, FNAME_TYPE);
  fprintf(outFile, "  const %s *const lineNums;\n", LINE_NUM_TYPE);
  fprintf(outFile, "  const unsigned nCounters;\n");
  fprintf(outFile, "  unsigned isInit;\n");
  fprintf(outFile, "} %s%s;\n\n", optionsP->prefix, PROF_TYPE);

  fprintf(outFile, "static %s%s %s%s;\n", optionsP->prefix, PROF_TYPE, 
	  optionsP->prefix, PROF_VAR);
  fprintf(outFile, "static %s %s%s[];\n", CNTR_TYPE, optionsP->prefix, CNTRS);
  fprintf(outFile, "static %s%s %s%s[];\n", optionsP->prefix, FNAME_TYPE, 
	  optionsP->prefix, FILE_NAMES);
  fprintf(outFile, "static %s %s%s[];\n", 
	  LINE_NUM_TYPE, optionsP->prefix, LINE_NUMS);
  fprintf(outFile, "static void %s%s();\n", optionsP->prefix, ATEXIT_FN);

  fprintf(outFile, "int atexit();\n\n");
  
  fprintf(outFile, "# 1 \"%s\"\n", fileName());
}

VOID
endOutFile()
{
  CONST Count nCounters= AX_NENTRIES(G.counterInfo);
  FILE *outFile= G.outFile;
  if (nCounters > 0) {
    fprintf(outFile, "static %s %s%s[%d];\n", 
	    CNTR_TYPE, optionsP->prefix, CNTRS, nCounters);

    {
      enum { N_FILE_NAMES_PER_LINE= 4 };
      int i, c;
      ConstString fName= COUNTER_INFO(0).fName;
      fprintf(outFile, "static %s%s %s%s[]= {\n  ", 
	      optionsP->prefix, FNAME_TYPE, optionsP->prefix, FILE_NAMES);
      for (i=0, c= 0; c < nCounters; c++) {
	if (i == N_FILE_NAMES_PER_LINE) {
	  fputs("\n  ", outFile); i= 0;
	}
	if (COUNTER_INFO(c).fName != fName) { /* ok, since intern'd */
	  fprintf(outFile, "{ %d, \"%s\" }, ", c, fName);
	  fName= COUNTER_INFO(c).fName; i++;
	}
      }
      fprintf(outFile, "%s{ %d, \"%s\" }\n};\n\n", 
	      (i == N_FILE_NAMES_PER_LINE) ? "\n  " : "", 
	      c, fName);
    }

    {
      enum { N_LINE_NUMS_PER_LINE= 8 };
      int c;
      fprintf(outFile, "static %s %s%s[]= {", 
	      LINE_NUM_TYPE, optionsP->prefix, LINE_NUMS);
      for (c= 0; c < nCounters; c++) {
	if (c % N_LINE_NUMS_PER_LINE == 0) {
	  fputs("\n  ", outFile);
	}
	fprintf(outFile, "%d%s ", COUNTER_INFO(c).lineNum, 
		(c < nCounters - 1) ? ", " : "");
      }
      fputs("\n};\n\n", outFile);
    }

    fprintf(outFile, "static %s%s %s%s= {\n", 
	    optionsP->prefix, PROF_TYPE, optionsP->prefix, PROF_VAR);
    fprintf(outFile, " &%s%s[0], &%s%s[0], &%s%s[0], %d, 0\n",
	    optionsP->prefix, CNTRS,
	    optionsP->prefix, FILE_NAMES,
	    optionsP->prefix, LINE_NUMS,
	    nCounters
           );
    fputs("};\n\n", outFile);

    fprintf(outFile, "extern void %s(%s%s *);\n",
	    LIB_OUT_FN, optionsP->prefix, PROF_TYPE);
    fprintf(outFile, "static void %s%s(void) { %s(&%s%s); }\n",
	    optionsP->prefix, ATEXIT_FN,
	    LIB_OUT_FN,
	    optionsP->prefix, PROF_VAR);
    AX_CUT(G.counterInfo, 0);
  } /* if (nCounters > 0) */
  fclose(G.outFile); G.outFile= NULL;
  VOID_RET();   
}

/* Output text of current token in tokSemP.  After token whitespace
 * but before actual token lexeme insert appropriate counter code, if
 * G.counter specifies a counter.
 */
VOID 
putOut(tokSemP)
  CONST TokSem *CONST tokSemP;
{
  if (G.counter == NO_COUNTER) {
    fwrite(tokSemP->text, sizeof(Char), tokSemP->len, G.outFile);
  }
  else {
    Index c= AX_NEXT(G.counterInfo);
    COUNTER_INFO(c).fName= tokSemP->fName; 
    COUNTER_INFO(c).lineNum= tokSemP->lineNum;
    fwrite(tokSemP->text, sizeof(Char), tokSemP->len1, G.outFile);
    switch (G.counter) {
      case COMPOUND_COUNTER:
	fprintf(G.outFile, "%s%s[%d]++; ", optionsP->prefix, CNTRS, c);
	break;
      case COND_COUNTER:
	if (optionsP->isCond) {
	  fprintf(G.outFile, "( %s%s[%d]++ , ", optionsP->prefix, CNTRS, c);
	}
	break;
      case FN_COUNTER:
	fprintf(G.outFile, 
	    "int %sInit= %s%s.isInit || ((%s%s.isInit= 1) && atexit(%s%s)); ",
		optionsP->prefix, optionsP->prefix, PROF_VAR, 
		optionsP->prefix, PROF_VAR, optionsP->prefix, ATEXIT_FN);
	fprintf(G.outFile, "int %sCntrInc= %s%s[%d]++; ", optionsP->prefix, 
		optionsP->prefix, CNTRS, c);
	break;
      case STMT_COUNTER:
	fprintf(G.outFile, "{ %s%s[%d]++; ", optionsP->prefix, CNTRS, c);
	break;
      default:
	assert(0);
    }
    fwrite(tokSemP->text + tokSemP->len1, sizeof(Char), 
	   tokSemP->len - tokSemP->len1, G.outFile);
    G.counter= NO_COUNTER;
  }
  VOID_RET();
}

/* Since we want the counter to be associated with the *next* token,
 * we merely set a flag here.  Then when we output the next token,
 * we make sure we output the appropriate counter increment only
 * after any whitespace preceeding the next token.
 */
VOID 
beginCounter(counterType)
  CounterType counterType;
{
  G.counter= counterType;
  VOID_RET();
}

VOID 
endCounter(counterType)
  CounterType counterType;
{
  switch (counterType) {
    case COND_COUNTER:
      if (optionsP->isCond) {
	fputs(" ) ", G.outFile);
      }
      break;
    case STMT_COUNTER:
      fputs(" } ", G.outFile);
      break;
    default:
      break;
  }
  VOID_RET();
}

