/****************************************************************************
    Copyright (C) 1987-2005 by Jeffery P. Hansen

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

    Last edit by hansen on Thu Sep  2 08:50:46 2004
****************************************************************************/
#include "tkgate.h"

#define TCLSCRIPT "scripts/tkgate.tcl"
#define EPG_TCLSCRIPT "scripts/egate.tcl"

int do_panic_save = 1;
int want_exit = 0;
int is_verbose = 0;

char *print_options = 0;
char *print_file = 0;
char *print_printer = 0;


extern int blockmovestyle;
extern int smoothScroll;
extern int flashCPath;
extern int ob_max_undo;

int debugContinuousVerify = 0;
int debugSimInterface = 0;
int sync_Xserver = 0;

int noviceMode = 1;
int doBackupOnSave = 0;
int wantCheckpoint = 0;
int checkpointenabled = 0;

SHash *message_table;

/*
 * Global variables used in main/Tk_AppInit
 */
static  char **loadList;
static  int numLoads = 0;
static  char **libLoadList;
static  int numLibLoads = 0;
static  int start_sim = 0;
static  char *sim_script = 0;
static  int did_load = 0;
static  int debug = 0;
extern int guiActive;

int quietMode = 0;

char tkgate_homedir[STRMAX];
char *tkgate_exampledir;
char *tkgate_tutorialdir;

/*****************************************************************************
 *
 * Catch a ctrl-c interrupt and do something about it so we can shut down
 * cleanly.
 *
 *****************************************************************************/
static void catchInterrupt(int s)
{
  FlagRedraw();
  want_exit = 1;

  /*
  switch (tkgate_currentMode()) {
  case MM_EDIT :
    want_exit = 0;
    DoTcl("tkg_exit");
    break;
  case MM_ANALYZE :
    want_exit = 1;
    break;
  case MM_SIMULATE :
    want_exit = 1;
    ReqScopeRedisplay();
    break;
  }
  */
}

/*****************************************************************************
 *
 * Test to see if 'path' is a valid tkgate home directory.  We do this by 
 * trying to open some files we expect to be there and returning non-zero
 * if we find all of the expected files.  Returns 0 if one or more expected
 * files are not found.
 *
 *****************************************************************************/
static int testHome(char *path)
{
  char buf[STRMAX];
  FILE *f;

  sprintf(buf,"%s/sitename.txt",path);
  if (!(f = fopen(buf,"r"))) return 0;
  fclose(f);

  sprintf(buf,"%s/locale/en/messages",path);
  if (!(f = fopen(buf,"r"))) return 0;
  fclose(f);

  sprintf(buf,"%s/locale/en/tutorials/welcome_tut.v",path);
  if (!(f = fopen(buf,"r"))) return 0;
  fclose(f);

  return 1;
}

/*****************************************************************************
 *
 * Figure out which directory we should use as the tkgate home.
 *
 * 1) Directory specified by TKGATE_HOME environment variable
 * 2) Directory specified by TKGATE_HOMEDIR in config.h
 * 3) Directory specified by TKGATE_SECONDARYHOME in config.h
 *
 * The #define value TKGATE_HOMEDIR is normally generated automatically by the
 * configure script using TKGATE_HOMEDIRBASE as a prefix and tkgate-x.y (where
 * x.y is the version number) the subdirectory.  The #define value TKGATE_SECONDARYHOME
 * is normally set to the directory in which tkgate was configured/compiled.
 *
 *****************************************************************************/
static void tkg_findGateHome()
{
  char *p;

  *tkgate_homedir = 0;
  if ((p = getenv("TKGATE_HOME"))) strcpy(tkgate_homedir,p);

  if (!testHome(tkgate_homedir)) {
    strcpy(tkgate_homedir,TKGATE_HOMEDIR);
    if (!testHome(tkgate_homedir)) {
      strcpy(tkgate_homedir,TKGATE_SECONDARYHOME);
      if (!testHome(tkgate_homedir)) {
	printf("\n");
	printf("I could not locate the tkgate home directory.  I tried looking in:\n");
	if (p) printf("  %s (environment variable)\n",p);
	printf("  %s  (primary location)\n",TKGATE_HOMEDIR);
	printf("  %s  (secondary location)\n",TKGATE_SECONDARYHOME);
	printf("\n");
	printf("Try setting the environment variable TKGATE_HOME.\n");
	exit(1);
      }
    }
  }
}

static void PrintCopyright()
{
  extern char *release_date;

  printf("TKGate %s - %s (released %s)\n",VERSIONS[0].name,TKGATE_DESCRIPTION,release_date);
  printf("[Compiled %s %s]\n",__DATE__,__TIME__);
  printf("%s\n",TKGATE_COPYRIGHT);
  printf("  TKGate comes with ABSOLUTELY NO WARRANTY;  see 'Help...License' menu\n");
  printf("  for license and warranty details.  Report problems to %s\n",TKGATE_MAILCONTACT);

  fflush(stdout);
}

void linkvars(Tcl_Interp *tcl)
{
  extern int stepsize;
  static char randomstate[128];

  initstate(getpid() | time(0),randomstate,128);

  Tcl_SetVar(tcl,"tkg_progVer",VERSIONS[0].name,TCL_GLOBAL_ONLY);
  Tcl_SetVar(tcl,"tkg_description",TKGATE_DESCRIPTION,TCL_GLOBAL_ONLY);
  Tcl_SetVar(tcl,"tkg_copyright",TKGATE_COPYRIGHT,TCL_GLOBAL_ONLY);
  Tcl_SetVar(tcl,"tkg_mailContact",TKGATE_MAILCONTACT,TCL_GLOBAL_ONLY);
  Tcl_SetVar(tcl,"tkg_homepage",TKGATE_HOMEPAGE,TCL_GLOBAL_ONLY);
  Tcl_SetVar(tcl,"tkg_localdoc",TKGATE_LOCALDOC,TCL_GLOBAL_ONLY);

  Tcl_LinkVar(tcl,"tkg_errorLogFile",(char*)&tkg_errorLogFile,TCL_LINK_STRING);
  Tcl_LinkVar(tcl,"tkg_siteName",(char*)&tkg_siteName,TCL_LINK_STRING);
  Tcl_LinkVar(tcl,"tkg_defaultTech",(char*)&tkg_defaultTech,TCL_LINK_STRING);

  Tcl_LinkVar(tcl,"tkg_contVerify",(char*)&debugContinuousVerify,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_simStepSize",(char*)&stepsize,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_checkpointEnabled",(char*)&checkpointenabled,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_wantCheckpoint",(char*)&wantCheckpoint,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_simDebugInterface",(char*)&debugSimInterface,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_simRememberProbes",(char*)&XGate.circuit->c_rememberProbes,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_baderMode",(char*)&baderp,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_trekMode",(char*)&startrekp,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_batMode",(char*)&batp,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"simOn",(char*)&XGate.circuit->simulator.active,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_modifiedFlag",(char*)&changedp,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_regionUpdate",(char*)&regionUpdate,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_blockMoveStyle",(char*)&blockmovestyle,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_noviceMode",(char*)&noviceMode,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_discardChanges",(char*)&XGate.circuit->discardChanges,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_useExtBars",(char*)&XGate.circuit->useExtBars,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_doBackupOnSave",(char*)&doBackupOnSave,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_smoothScroll",(char*)&smoothScroll,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_flashCPath",(char*)&flashCPath,TCL_LINK_INT);
  Tcl_LinkVar(tcl,"tkg_undoLength",(char*)&ob_max_undo,TCL_LINK_INT);
}

void readLongMsg(FILE *f,char *msg,int n)
{
  char *p;
  int l;

  p = msg;
  while (fgets(p,n,f)) {
    l = strlen(p);
    if (strcmp(p,"-end-\n") == 0) {
      if (p != msg)
	p[-1] = 0;
      else
	*p = 0;
      break;
    }
    p += l;
    n -= l;
    if (n <= 0) {
      printf("Fatal Error: Message too long.\n");
      exit(1);
      break;
    }
  }
}

/*
 * Determine which locale we are going to run under and read the appropriate
 * message files.  If the locale is not "en" (English), then the both the
 * locale specific message file and the English message file is read.  If there
 * are any messages in the English message file that are not in the locale 
 * specific message file, a warning is printed and the English message
 * is used in place of the missing locale-specific message.
 */
void localization_Setup(Tcl_Interp *tcl)
{
  SHash *H = new_SHash();
  char fileName[1024],buf[1024],tag[1024],msg[8096];
  char msg_lang[1024];
  FILE *f;
  int en_backup = 0;
  char *lang = 0;
  int no_msg_count = 0;

  XGate.japaneseMode = 0;
  
  lang = getenv("LANG");
  if (!lang || !*lang)
    lang = "en";

  if (!tcl) lang = "en";

  strncpy(msg_lang,lang,1024);

  if (strcmp(msg_lang,"ASCII") == 0 || strcmp(msg_lang,"US-ASCII") == 0)
    strcpy(msg_lang,"en");

  if (strncmp(lang,"ja",2) == 0 || strncmp(lang,"jp",2) == 0) {
#if TKGATE_JSUPPORT
    strcpy(msg_lang,"ja");
    XGate.japaneseMode = 1;
#else
    logError(ERL_ERROR,"Not configured for Japanese support.  Reverting to English.");
    putenv("LANG=ASCII");
    strcpy(msg_lang,"en");
#endif
  }

  msg_lang[2] = 0;


  if (strcmp(msg_lang,"en") != 0) {
    sprintf(fileName,"%s/locale/%s/messages",tkgate_homedir,msg_lang);
    f = fopen(fileName,"r");
    if (f) {
      while (fgets(buf,1024,f)) {
	if (sscanf(buf,"%s %[^\n]",tag,msg) == 2 && *tag != '#') {
	  if (strcmp(msg,"-begin-") == 0)
	    readLongMsg(f,msg,8096);

	  if (SHash_find(H,tag)) {
	    logError(ERL_ERROR,"Duplicate add of message '%s'.",tag);
	  }

	  SHash_insert(H,tag,ob_strdup(msg));
	}
      }
      fclose(f);
      en_backup = 1;
    } else {
      printf("[No support for locale '%s'.  Reverting to English.]\n",msg_lang);
      strcpy(msg_lang,"en");
    }
  }

  sprintf(fileName,"%s/locale/en/messages",tkgate_homedir);
  f = fopen(fileName,"r");
  if (!f) {
    logError(ERL_ERROR,"Can not find required locale/xx/messages file.\n");
    exit(1);
  }
  while (fgets(buf,1024,f)) {
    if (sscanf(buf,"%s %[^\n]",tag,msg) == 2 && *tag != '#') {
      if (strcmp(msg,"-begin-") == 0)
	readLongMsg(f,msg,8096);

      if (en_backup) {
	if (!SHash_find(H,tag)) {
	  SHash_insert(H,tag,ob_strdup(msg));
	  if (is_verbose)
	    logError(ERL_ERROR,"No localized string for symbol '%s'.  Using English value.",tag);
	  else
	    no_msg_count++;
	}
      } else {
	if (SHash_find(H,tag)) {
	  logError(ERL_ERROR,"Duplicate add of message '%s'.",tag);
	}
	SHash_insert(H,tag,ob_strdup(msg));
      }
    }
  }

  fclose(f);

  if (!is_verbose && no_msg_count > 0) {
    logError(ERL_ERROR,"No localized strings for %d messages.  Use 'tkgate -v' for details.",no_msg_count);
  }

  /*
   * Hack to get right font encoding for Czech.
   */
  if (strcmp(msg_lang,"cs") == 0) {
    extern char *font_encoding;
    font_encoding = "iso8859-2";
  }

  message_table = H;

  if (tcl)
    Tcl_SetVar(tcl,"lang",msg_lang,TCL_GLOBAL_ONLY);

#if TKGATE_JSUPPORT
  /*
   * If we have Japanese support and kinput is running, an invalid locale
   * will cause a crash in the tcl/tk initialization step.  If the locale
   * is not set, force it to a valid locale.
   */
  if (!getenv("LANG"))
    putenv("LANG=ASCII");
#endif
}

/*
 * tcl handler code for message lookup function 'm'.
 */
int gat_msgLookup(ClientData d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  char *msg;

  if (argc < 2) return TCL_OK;

  msg = msgLookup(argv[1]);
  if (strlen(msg) < 127)
    strcpy(tcl->result,msg);
  else
    tcl->result = ob_strdup(msg);

  return TCL_OK;
}

/*
 * Set handlers on various signals we might receive.  Most abnormal signals
 * are caught by panicSave which attempts to save the current circuit.
 */
void tkgate_signalSetup()
{
  /*
    Make sure they really want to exit when ctl-c is typed.
   */
  signal(SIGINT,catchInterrupt);

  /*
    Do a panic save on any core dump producing signal, or
    on a SIGHUP.
   */
  if (do_panic_save) {
    signal(SIGHUP,panicSave);
    signal(SIGQUIT,panicSave);
    signal(SIGILL,panicSave);
    signal(SIGTRAP,panicSave);
    signal(SIGFPE,panicSave);
    signal(SIGBUS,panicSave);
    signal(SIGSEGV,panicSave);
#ifdef SIGEMT
    signal(SIGEMT,panicSave);
#endif
#ifdef SIGSYS
    signal(SIGSYS,panicSave);
#endif
  }
}

Circuit *new_Circuit(int ob)
{
  Circuit *C;

  if (ob)
    C = (Circuit*) ob_malloc(sizeof(Circuit),"Circuit");
  else
    C = (Circuit*) malloc(sizeof(Circuit));
  C->currentFile = 0;
  C->fileVersion = 0;
  C->mid_mod = new_GModuleDef("<module-interfaces>",1);
  C->cut_buffer = 0;
  C->mg_selection = 0;
  C->rot = 0;
  C->mode = MODE_MOVE;
  C->numInitScripts = 0;
  C->initScripts = 0;
  C->zoom_factor = 1;
  C->discardChanges = 0;
  C->useExtBars = 1;
  C->is_modified = 0;
  C->es = new_EditState();
  C->moduleTable = new_SHash();
  C->c_rememberProbes = 1;
  C->c_numProbes = 0;
  C->c_probes = 0;

  return C;
}

void delete_Circuit(Circuit *C)
{
}

void Circuit_setTitle(const char *title)
{
}

void Circuit_setCurrentFile(const char *name)
{
  ob_touch(XGate.circuit);
  if (XGate.circuit->currentFile)
    ob_free(XGate.circuit->currentFile);
  XGate.circuit->currentFile = ob_strdup(name);

  if (XGate.tcl) {
    Tcl_SetVar(XGate.tcl,"tkg_currentFile",name,TCL_GLOBAL_ONLY);
    DoTcl("tkg_setDisplayFile %s",name);
  }
}

void Circuit_setCurrentFileVersion(const char *vnum)
{
  ob_touch(XGate.circuit);
  if (XGate.circuit->fileVersion)
    ob_free(XGate.circuit->fileVersion);
  XGate.circuit->fileVersion = ob_strdup(vnum);

  if (XGate.tcl)
    Tcl_SetVar(XGate.tcl,"ecp_fileVersion",vnum,TCL_GLOBAL_ONLY);
}

/*****************************************************************************
 *
 * Clear any probes in the probe memory.
 *
 *****************************************************************************/
void Circuit_clearProbes(Circuit *C)
{
  int i;

  ob_touch(C);
  C->c_numProbes = 0;
  for (i = 0;i < C->c_numProbes;i++) {
    ob_free(C->c_probes[i]);
  }
  if (C->c_probes)
    ob_free(C->c_probes);
  C->c_probes = 0;
  C->c_numProbes = 0;
}

void Circuit_extractProbesAux(Circuit *C,GSimModule *M)
{
  HashElem *E;
#define CPROBE_ALLOCSTEP	256

  for (E = Hash_first(M->probes);E;E = Hash_next(M->probes,E)) {
    GSimProbe *P = (GSimProbe*) HashElem_obj(E);

    ob_touch(C);
    if (C->c_numProbes == 0) {
      C->c_probes = (char**) ob_malloc(sizeof(char*)*CPROBE_ALLOCSTEP,"char**");
    } else if ((C->c_numProbes % CPROBE_ALLOCSTEP) == 0) {
      C->c_probes = (char**) ob_realloc(C->c_probes,sizeof(char*)*(C->c_numProbes+CPROBE_ALLOCSTEP));
    }
    C->c_probes[C->c_numProbes++] = ob_strdup(P->name);
  }

  for (E = Hash_first(M->children);E;E = Hash_next(M->children,E)) {
    GSimModule *cM = (GSimModule*) HashElem_obj(E);

    Circuit_extractProbesAux(C,cM);
  }
}


/*****************************************************************************
 *
 * Extract the set of probes already set and put them in the probe memory
 *
 *****************************************************************************/
void Circuit_extractProbes(Circuit *C)
{
  Circuit_clearProbes(C);
  if (!C->c_rememberProbes) return;
  Circuit_extractProbesAux(C, C->simulator.sim_root);
}

void Circuit_repostProbes(Circuit *C)
{
  SimInterface *si = &XGate.circuit->simulator;
  int i;

  if (C->c_rememberProbes) {
    for (i = 0;i < C->c_numProbes;i++) {
      char *name = C->c_probes[i];
      GSimModule *SM;
      GWire *w;

      if (SimInterface_lookupWire(si,name,&SM,&w) != 0)
	continue;

      if (SimInterface_probeExists(si,SM,name))
	continue;

      SimInterface_addDelProbe(si,SM,name,w,w->nodes,w->nodes->x,w->nodes->y);
    }
  }

  Circuit_clearProbes(C);
}


/*****************************************************************************
 * Load the circuits on the command line only to check their integrety.
 *****************************************************************************/
void doVerifyLoad()
{
  extern int VerilogErrorCount;
  int i;

  XGate.tcl = 0;

  MakeHashTables();
  icon_init();
  init_gates();

  XGate.ed = (EditData*) malloc(sizeof(EditData));
  XGate.errl = (ErrorList*) malloc(sizeof(ErrorList));

  XGate.circuit = new_Circuit(0);

  localization_Setup(0);

  for (i = 0;i < numLoads;i++) {
    VerilogOpen(&XGate.circuit->es,loadList[i]);

    /*
     * We got an error reading a circuit file.
     */
    if (VerilogErrorCount > 0)
      exit(1);
  }

  /*
   * We found inconsistencies in the file.
   */
  if (verify_circuit() > 0)
    exit(2);

  exit(0);
}

/*
 * Do defaults setup for command line printing.  We need to find out what the site
 * name is and get the options from the user's preferences file.  We need to read
 * the preferences file manually since we are not starting the tcl/tk interpreter.
 */
void commandLinePrintSetup(GPrintOpt *PO)
{
  FILE *f;
  char buf[STRMAX];
  char *p;

  /*
   * Get the default site name from the
   */
  sprintf(buf,"%s/sitename.txt",tkgate_homedir);
  f = fopen(buf,"r");
  if (f) {
    if (fgets(buf,STRMAX,f)) {
      p = strchr(buf,'\n');
      if (p) *p = 0;
      tkg_siteName = strdup(buf);
    }
    fclose(f);
  }

  GPrintOpt_clDefault(PO);

  /*
   * If we have a home directory, open the preferences file and read
   * printing related defaults from it.
   */
  p = getenv("HOME");
  if (p) {
    extern PaperSize paperSizes[];

    sprintf(buf,"%s/.tkgate-preferences",p);
    f = fopen(buf,"r");
    if (f) {
      const char *lang = getenv("LANG");
      char str[STRMAX];
      int d;

      while (fgets(buf,STRMAX,f)) {
	if (sscanf(buf,"set tkg_siteName \"%[^\"]",str) == 1) {
	  tkg_siteName = strdup(str);
	} else if (sscanf(buf,"set tkg_printCommand \"%[^\"]",str) == 1) {
	  PO->po_cmd = strdup(str);
	} else if (sscanf(buf,"set tkg_printPgSizeA4 %d",&d) == 1) {
	  if (strncmp(lang,"en",2) != 0)
	    PO->po_paper = strdup(paperSizes[d].ps_size);
	} else if (sscanf(buf,"set tkg_printPgSize %d",&d) == 1) {
	  if (strncmp(lang,"en",2) == 0)
	    PO->po_paper = strdup(paperSizes[d].ps_size);
	} else if (sscanf(buf,"set tkg_printPgOrient %s",str) == 1) {
	  PO->po_orient = strdup(str);
	} else if (sscanf(buf,"set tkg_printStyle %s",str) == 1) {
	  PO->po_style = strdup(str);
	} else if (sscanf(buf,"set tkg_printDuplex %s",str) == 1) {
	  PO->po_isDuplex = strdup(str);
	} else if (sscanf(buf,"set tkg_printIndex %s",str) == 1) {
	  PO->po_index = strdup(str);
	} else if (sscanf(buf,"set tkg_printGraph %s",str) == 1) {
	  PO->po_graph = strdup(str);
	}
      }
      fclose(f);
    }
  }

  if (!tkg_siteName)
    tkg_siteName = strdup("CLP");

}

/*
 * Set up for doing printing from the command line.
 */
void commandLinePrint()
{
  extern int VerilogErrorCount;
  char *p;
  int i;
  GPrintOpt PO;
  extern PaperSize paperSizes[];


  XGate.tcl = 0;
  MakeHashTables();
  icon_init();
  init_gates();
  XGate.ed = (EditData*) malloc(sizeof(EditData));
  XGate.errl = (ErrorList*) malloc(sizeof(ErrorList));
  XGate.circuit = new_Circuit(0);
  localization_Setup(0);

  for (i = 0;i < numLoads;i++) {
    VerilogOpen(&XGate.circuit->es,loadList[i]);

    /*
     * We got an error reading a circuit file.
     */
    if (VerilogErrorCount > 0)
      exit(1);
  }

  /*
   * Apply default options
   */
  commandLinePrintSetup(&PO);


  if (print_file) {
    PO.po_file = print_file;
    PO.po_cmd = 0;
  } else {
    PO.po_cmd = print_printer;
    PO.po_file = 0;
  }

  /*
   * Apply printer options
   */
  for (p = strtok(print_options,":");p;p = strtok(0,":")) {
    char name[STRMAX];
    char sval[STRMAX];
    int n;

    if (!*p) continue;	/* Ignore empty options */

    if (sscanf(p,"%[^=]=%s",name,sval) == 2) {
    } else if (sscanf(p,"%[^=]",name) == 1) {
      strcpy(sval,"1");
    } else {
      fprintf(stderr,"tkgate: Badly formatted print option '%s'\n",p);
      fprintf(stderr,"  run 'tkgate -h' for help.\n");
      exit(1);
    }

    n = strlen(name);
    if (strncmp(name,"duplex",n) == 0) {
      PO.po_isDuplex = strdup(sval);
    } else if (strncmp(name,"epsf",n) == 0) {
      PO.po_epsf = strdup(sval);
    } else if (strncmp(name,"index",n) == 0) {
      PO.po_index = strdup(sval);
    } else if (strncmp(name,"hierarchy",n) == 0) {
      PO.po_graph = strdup(sval);
    } else if (strncmp(name,"paper",n) == 0) {
      PO.po_paper = strdup(sval);
    } else if (strncmp(name,"modules",n) == 0) {
      PO.po_select = "sel";
      PO.po_modlist = strdup(sval);
    } else if (strncmp(name,"4up",n) == 0) {
      PO.po_4up = strdup(sval);
    } else {
      fprintf(stderr,"tkgate: Unknown print option '%s'\n",name);
      fprintf(stderr,"  run 'tkgate -h' for help.\n");
      exit(1);
    }
  }


  /*
   * Check for valid paper size and convert to expected case if necessary.
   */
  for (i = 0;paperSizes[i].ps_size;i++)
    if (strcasecmp(PO.po_paper,paperSizes[i].ps_size) == 0) {
      PO.po_paper = paperSizes[i].ps_size;
      break;
    }
  if (!paperSizes[i].ps_size) {
      fprintf(stderr,"tkgate: Unknown paper size '%s'\n",PO.po_paper);
      exit(1);
  }


  ob_begin_frame("Print");
  GPrintOpt_print(&PO);
  ob_end_frame();

  exit(0);
}

void usage()
{
  fprintf(stderr,"Usage: tkgate [options][file]\n\n");
  fprintf(stderr,"  Options:\n");
  fprintf(stderr,"    -h         Print this summary of options.\n");
  fprintf(stderr,"    -s         Run in X11 synchronous mode (slow).\n");
  fprintf(stderr,"    -q         Suppress some messages to the tty.\n");
  fprintf(stderr,"    -x         Start simulator immediately.\n");
  fprintf(stderr,"    -X file    Start simulator immediately and execute script.\n");
  fprintf(stderr,"    -l file    Load file as a library.\n");
  fprintf(stderr,"    -L lang    Specify language (if configured).\n");
  fprintf(stderr,"    -p file    Print to a file.\n");
  fprintf(stderr,"    -P prn     Output to a printer.\n");
  fprintf(stderr,"    -O opts    Printer options.\n");
  fprintf(stderr,"\n");
  fprintf(stderr,"  Printer options are specified as a colon separated list of\n");
  fprintf(stderr,"  the following items (identifiers can be abreviated):\n");
  fprintf(stderr,"\n");
  fprintf(stderr,"    epsf=bool      Use encapsulated Postscript format\n");
  fprintf(stderr,"    duplex=bool    Use duplex printing\n");
  fprintf(stderr,"    index=bool     Include index\n");
  fprintf(stderr,"    hier=bool      Include hierarchy graph\n");
  fprintf(stderr,"    paper=type     Use specified paper type\n");
  fprintf(stderr,"    4up=bool       Do 4-up printing of small modules\n");
  fprintf(stderr,"    modules=list   List of modules to print (comma separated)\n");
  fprintf(stderr,"\n");
  exit(1);
}


void parse_options(int argc,char *argv[])
{
  int c;
  int verify_only = 0;
  extern char *optarg;
  extern int optind;
#if OPTRESET
  extern int optreset;
#endif

  loadList = (char**) calloc(argc,sizeof(char*));
  libLoadList = (char**) calloc(argc,sizeof(char*));

  while (argc > 0) {
    while ((c = getopt(argc,argv,"vVAhdxqsX:l:L:p:P:O:")) != EOF) {
      switch (c) {
      case 'v' :
	is_verbose = 1;
	break;
      case 'V' :
	verify_only = 1;
	break;
      case 'A' :
	do_panic_save = 0;
	break;
      case 'X' :
	start_sim = 1;
	sim_script = optarg;
	break;
      case 'O' :
	if (print_options) {
	  print_options = realloc(print_options,strlen(print_options)+strlen(optarg)+2);
	  strcat(print_options,":");
	  strcat(print_options,optarg);
	} else
	  print_options = strdup(optarg);
	break;
      case 'p' :
	print_file = optarg;
	break;
      case 'P' :
	print_printer = optarg;
	break;
      case 'x' :
	start_sim = 1;
	break;
      case 'q' :
	quietMode = 1;
	break;
      case 's' :
	sync_Xserver = 1;
	break;
      case 'd' :
	debug = 1;
	break;
      case 'l' :
	libLoadList[numLibLoads++] = optarg;
	break;
      case 'L' :
	{
	  static char buf[128];
	  if (strlen(optarg) < 100) {
	    /*
	     * Solaris putenv man page claims the actual string passed
	     * to putenv is used in the environment so we must use
	     * a 'static' char array here.
	     */
	    sprintf(buf,"LANG=%s",optarg);
	    putenv(buf);
	  } else
	    putenv("LANG=ASCII");
	}
	break;
      case 'h' :
      default :
	usage();
	break;
      }
    }
    argc -= optind;
    argv += optind;
#if OPTRESET
    optreset = 1;
#endif
    optind = 0;
    if (argc > 0) {
      loadList[numLoads++] = argv[0];
      argc--;
      argv++;
    }
  }

  if (verify_only)
    doVerifyLoad();
  if (print_file || print_printer)
    commandLinePrint();
}

/*
 * See if Miles Bader is running this program and harass him just a bit.
 */
void bader_check()
{
  struct passwd *pw = getpwuid(getuid());
  char buf[STRMAX],*p;
  extern int baderp,startrekp,batp;

  strcpy(buf,pw->pw_gecos);
  for (p = buf;*p;p++)
    if (isupper(*p)) *p = tolower(*p);

  if (strstr(buf,"miles") != 0 && strstr(buf,"bader") != 0) {
    baderp = 1;
    printf("Hi sMiles!\n");
  } else if ((strstr(buf,"james") != 0 || strstr(buf,"jim") != 0) && strstr(buf,"kirk") != 0) {
    startrekp = 1;
    printf("Kirk, this place is swarming with Klingons!\n");
  } else if ((strstr(buf,"william") != 0 || strstr(buf,"bill") != 0) && strstr(buf,"shatner") != 0) {
    startrekp = 1;
    printf("Do you have a point Spock?\n");
  } else if (strstr(buf,"bruce") != 0 && strstr(buf,"wayne") != 0) {
    batp = 1;
    printf("Let's go, Robin. The longer we tarry, the more dire the peril.\n");
  }
}

/*
 * Read delay files and associate them with gates
 */
void TkGate_initDelay()
{
  char buf[STRMAX];
  HashElem *E;
  int i;
  const char *p;
  char *q;

  GDelayDef_flush();

  p = Tcl_GetVar(XGate.tcl,"tkg_simDefaultDelayFile",TCL_GLOBAL_ONLY);
  if (GDelayDef_readFile(p) < 0)
    message(1,"Can not open default delay file '%s'.",p);

  p = Tcl_GetVar(XGate.tcl,"tkg_simDelayFile",TCL_GLOBAL_ONLY);
  strcpy(buf,p);
  for (p = strtok(buf," ");p;p = strtok(0," ")) {
    if (GDelayDef_readFile(p) < 0)
      message(1,"tkgate: Can not open delay file '%s'.",p);
  }

  for (E = Hash_first(GateIdxHash);E;E = Hash_next(GateIdxHash,E)) {
    GGateInfo *gi = (GGateInfo*) HashElem_obj(E);

    for (i = 0;gi->delayNames[i];i++);
    gi->num_delays = i;

    if (!gi->num_delays) continue;

    strcpy(buf,gi->vnames);
    if ((q = strchr(buf,':'))) *q = 0;

    gi->delay_defs = GDelayDef_findList(buf);
    if (!gi->delay_defs) {
      message(1,"No delay definitions found for primitive '%s'.",buf);
    } else {
      GDelayDef *dd = GDelayDef_findTech(gi->delay_defs,"default");
      if (strcmp(dd->dd_tech,"default") != 0)
	message(1,"No 'default' delay definition found for primitive '%s'.",buf);
    }
  }

  DoTcl("resetTechList");
}

/*
 * Called to do final tkgate initilization (called from tcl script)
 */
void TkGate_init(Tcl_Interp *tcl)
{
  int i;

  TkGate_initDelay();

  sel_updateMenuState();

  for (i = 0;i < numLoads;i++) {
    DoTcl("gat_load %s",loadList[i]);
    did_load = 1;
  }

  for (i = 0;i < numLibLoads;i++) {
    DoTcl("gat_loadLibrary %s",libLoadList[i]);
    did_load = 1;
  }

  free(libLoadList);
  free(loadList);

  guiActive = 1;

  if (start_sim) {
    if (sim_script)
      Tcl_SetVar(XGate.tcl,"sopts_simInitScript",sim_script,TCL_GLOBAL_ONLY);
    tkgate_setMajorMode(MM_SIMULATE);
  }

  if (noviceMode && !did_load)
    DoTcl("tkg_loadNoviceCircuit");

  bader_check();
  tkgate_signalSetup();			/* set traps on signals */
}

/*
 * Set up tutorial and example directories.
 */
void tkgate_setup_tutorials(Tcl_Interp *tcl)
{
  char buf[STRMAX];
  const char *lang = Tcl_GetVar(tcl,"lang",TCL_GLOBAL_ONLY);
  struct stat sb;

  sprintf(buf,"%s/locale/%s/tutorials",tkgate_homedir,lang);
  if (stat(buf,&sb) || ((sb.st_mode & S_IFMT) != S_IFDIR)) {
    sprintf(buf,"%s/locale/en/tutorials",tkgate_homedir);
    printf("[No tutorials found for locale '%s'.  Using English tutorials.]\n",lang);
    if (stat(buf,&sb) || ((sb.st_mode & S_IFMT) != S_IFDIR)) {
      printf("[No tutorials found for English either'.  Help!]\n");
      exit(1);
    }
  }

  tkgate_tutorialdir = ob_strdup(buf);
  Tcl_SetVar(tcl,"tkgate_tutorialdir",buf,TCL_GLOBAL_ONLY);

  sprintf(buf,"%s/locale/%s/examples",tkgate_homedir,lang);
  if (stat(buf,&sb) || ((sb.st_mode & S_IFMT) != S_IFDIR)) {
    sprintf(buf,"%s/locale/en/examples",tkgate_homedir);
    printf("[No examples found for locale '%s'.  Using English examples.]\n",lang);
    if (stat(buf,&sb) || ((sb.st_mode & S_IFMT) != S_IFDIR)) {
      printf("[No examples found for English either'.  Help!]\n");
      exit(1);
    }
  }

  tkgate_exampledir = ob_strdup(buf);
  Tcl_SetVar(tcl,"tkgate_exampledir",buf,TCL_GLOBAL_ONLY);

}

/*
 * This contains the inital setup for tcl/tk.
 */
int Tcl_AppInit(Tcl_Interp *tcl)
{
  char buf[STRMAX];
  int r;


  /*
   * Initialize the application description object to all zeros.
   */
  memset(&XGate,0,sizeof(XGate));


  r = Tcl_Init(tcl);
  if (r == TCL_ERROR) {
    fprintf(stderr,"Tcl_Init Error in tkgate:\n%s\n",tcl->result);
    fprintf(stderr,"Perhaps you could try setting the environment variable TCL_LIBRARY\n");
    fprintf(stderr,"to the directory in which init.tcl can be found.  You can also set\n");
    fprintf(stderr,"this variable in config.h\n");
    exit(1);
  }

  localization_Setup(tcl);

  r = Tk_Init(tcl);
  if (r == TCL_ERROR) {
    fprintf(stderr,"Tk_Init Error in tkgate:\n%s\n",tcl->result);
    fprintf(stderr,"Perhaps you could try setting the environment variable TK_LIBRARY\n");
    fprintf(stderr,"to the directory in which tk init files can be found.  You can also set\n");
    fprintf(stderr,"this variable in config.h\n");
    exit(1);
  }

  Tcl_SetVar(tcl,"tkg_gateHome",tkgate_homedir,TCL_GLOBAL_ONLY);

  sprintf(buf,"%s/bitmaps",tkgate_homedir);
  Tcl_SetVar(tcl,"bd",buf,TCL_GLOBAL_ONLY);

  sprintf(buf,"%s/scripts",tkgate_homedir);
  Tcl_SetVar(tcl,"sd",buf,TCL_GLOBAL_ONLY);

  tkgate_setup_tutorials(tcl);

  configureMainWindow(tcl);

  linkvars(tcl);


  Tcl_CreateCommand(tcl,"m",gat_msgLookup,(ClientData)0,0);

  icon_init();
  SetUpCursors();
  MakeHashTables();

  init_gates();				/* initialized built-in gate types */
  InitTclProcs(tcl);			/* install tkgate tcl commands */
  cpath_initNetDelayTable();		/* initialize delay tables for critical path analysis */

#if TKGATE_UNICODE_JSUPPORT
  if (XGate.japaneseMode)
    DoTcl("catch { option add *font k14 }");
#endif

  return TCL_OK;
}

/*****************************************************************************
 *
 * This is the main() for tkgate.
 *
 *****************************************************************************/
int main(int argc,char *argv[])
{
  char tkgate_startup_script[STRMAX];
  char *tk_argv[2];

  ob_init();			/* Initialize the undo/redo object manager */

  /*
   * If the tcl/tk library file path environment variables are not set, use
   * the ones that we found when tkgate was compiled.
   */
#ifdef TCL_LIBRARY
  if (!getenv("TCL_LIBRARY")) putenv("TCL_LIBRARY=" TCL_LIBRARY);
#endif
#ifdef TK_LIBRARY
  if (!getenv("TK_LIBRARY")) putenv("TK_LIBRARY=" TK_LIBRARY);
#endif

  tkg_findGateHome();		/* Figure out which directory we should use as the tkgate home */
  parse_options(argc,argv);	/* Parse the command line options */
  if (!quietMode) PrintCopyright();	/* Print the copyright notice (unless we are in quiet mode) */

  /*
   * Build a mock command line with the main tcl script for tkgate and start up the tcl/tk
   * interpeter with that script.  All of the rest of the initialization will be controlled
   * by the tcl script.
   */
  sprintf(tkgate_startup_script,"%s/%s",tkgate_homedir,TCLSCRIPT);
  tk_argv[0] = argv[0];
  tk_argv[1] = tkgate_startup_script;
  Tk_Main(2,tk_argv,Tcl_AppInit);	/* Here we go, off to tcl/tk land. */

  return 0;
}

