/****************************************************************************
    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 11:11:30 2004
****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>
#include "tkgate.h"
#if USE_ICONV
#include <iconv.h>
#endif

#define MAXOPTIONS 0

EditState *seterror(int n,EditState *es);

void setGCcolors();
void makeMakeShortcuts();


int tracePageCountEst(const char *orient,int paper,int scale,int start,int end);

void scopeButtonPress(int x,int y,int n,int state);
void scopeButtonMotion(int x,int y,int n,int state);
void scopeButtonRelease(int x,int y,int n,int state);

/*
  Check to see if modifications are OK, and if not issue a message. 
 */
int modifyOK(EditState *es,unsigned flags)
{
  if (!(flags & 0x2) && es->isInterface) {
    message(0,msgLookup("err.badeditop"));	/* Illegal interface edit mode operation. */
    return 0;
  }
  if (!(flags & 0x1) && es->env->is_lib) {
    message(0,msgLookup("err.libmode"),es->env->Name);			/* Not a library module */      
    return 0;
  }
  return 1;
}

/*
 * Called to do final tkgate initilization
 */
int gat_init(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkGate_init(tcl);
  ob_mode(OM_ENABLED);
  
  return TCL_OK;
}

int gat_reinitDelay(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkGate_initDelay();
  return TCL_OK;
}



/*
   gat_new "name.v" topmod
*/
int gat_new(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState **es = &gw->parms->circuit->es;
  const char *file,*topMod;
  GModuleDef *M;
  int old_mode;

  assert(argc == 3);

  tkgate_setMajorMode(MM_EDIT);

  old_mode = ob_get_mode();
  if (old_mode != OM_DISABLED)
    ob_mode(OM_DISABLED);
  ob_clear();

  file = argv[1];
  topMod = argv[2];

  ob_touch(XGate.circuit);
  XGate.circuit->discardChanges = 0;
  XGate.circuit->useExtBars = 1;
  ClearModified();
  Circuit_setCurrentFile(file);

  editstate_flushModules(es);
  M = env_findAdd(topMod,1);
  ob_touch(M);
  M->is_top = 1;
  editstate_push(es,M,0);
  editstate_setCurrent(*es);
  ob_touch(XGate.circuit);
  XGate.circuit->root_mod = M;

  FlagRedraw();

  if (old_mode == OM_ENABLED)
    ob_mode(old_mode);
  return TCL_OK;
}

/*
   gat_load "name.v"
*/
int gat_load(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  const char *name;
  const char *modFlag;
  int old_mode;

  assert(argc == 2);
  name = argv[1];

  tkgate_setMajorMode(MM_EDIT);

  if ((modFlag = Tcl_GetVar(tcl,"tkg_modifiedFlag",TCL_GLOBAL_ONLY)) && *modFlag != '0') {
    DoTcl("tkg_confDelMods");
    if (!XGate.tcl->result || strcmp(XGate.tcl->result,"yes") != 0) {
      return TCL_OK;
    }
  }

  old_mode = ob_get_mode();
  if (old_mode != OM_DISABLED)
    ob_mode(OM_DISABLED);
  ob_clear();

  ob_touch(gw->parms->circuit);
  if (VerilogOpen(&gw->parms->circuit->es,name) < 0) {
    const char *curFile;

    if ((curFile = Tcl_GetVar(tcl,"tkg_currentFile",TCL_GLOBAL_ONLY)) && !*curFile) {
      Tcl_SetVar(XGate.tcl,"tkg_currentFile",name,TCL_GLOBAL_ONLY);
      DoTcl("tkg_setDisplayFile %s",name);
    } else
      message(1,msgLookup("err.badopen"),name);	/* Unable to open input file '%s' */

    if (old_mode == OM_ENABLED)
      ob_mode(old_mode);
    return TCL_OK;
  }

  sel_clearDelta();

  ClearModified();
  FlagRedraw();

  if (old_mode == OM_ENABLED)
    ob_mode(old_mode);

  return TCL_OK;
}

/*
   gat_loadLibrary "name.v"
*/
int gat_loadLibrary(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;
  const char *name;
  int old_mode;

  assert(argc == 2);
  name = argv[1];

  tkgate_setMajorMode(MM_EDIT);

  old_mode = ob_get_mode();
  if (old_mode != OM_DISABLED)
    ob_mode(OM_DISABLED);
  ob_clear();

  ob_touch(gw->parms->circuit);
  if (VerilogOpenLibrary(&gw->parms->circuit->es,name) < 0) {
    message(1,msgLookup("err.badlibopen"),name);		/* Unable to open library file '%s'. */
    return TCL_OK;
  }


  if (es->isInterface)
    modint_arrange(es);

  FlagRedraw();

  if (old_mode == OM_ENABLED)
    ob_mode(old_mode);

  return TCL_OK;
}

int gat_mode(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  int mode = 0;

  assert(argc == 2);

  sscanf(argv[1],"%d",&mode);

  setEditMode(gw->parms->circuit->es,mode);

  return TCL_OK;
}


/*
   gat_make and {opts}
*/
int gat_make(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;
  EditState **pes = &gw->parms->circuit->es;
  struct gateinfo *GI;
  int x,y,r;
  struct celemnt *g;
  const char *xstr,*ystr;

  assert(argc >=  2);

  if (tkgate_currentMode() != MM_EDIT) return TCL_OK;

  if (!modifyOK(es,0)) return TCL_OK;

  xstr = seekOption("-x",argv,argc);
  ystr = seekOption("-y",argv,argc);

  if (xstr && ystr) {
    sscanf(xstr,"%d",&x);
    sscanf(ystr,"%d",&y);
    x /= XGate.circuit->zoom_factor;
    y /= XGate.circuit->zoom_factor;
    x = wtoc_x(x);
    y = wtoc_y(y);
  } else if (XGate.ed->mark_vis) {
    x = XGate.ed->tx;
    y = XGate.ed->ty;
    r = XGate.circuit->rot;
  } else {
    message(0,msgLookup("err.nomark"));	/* Please set a mark with the left mouse button before selecting a gate type. */
    return TCL_OK;
  }

  GI = gateinfo_lookup(argv[1]);
  if (!GI) {
    message(1,msgLookup("err.badgate"));	/* Unknown gate type. */
    return TCL_OK;
  }

  badermessage(1,GI->englishName);

  mark_unpost();

  g = (*GI->MakeFunction)(pes,es->env,GI->Code,x,y,r,0,0,argv+2,argc-2);

  if (!g) return TCL_OK;			/* Make was canceled */

  if (tkg_defaultTech) {
    ob_touch(g);
    ob_free(g->tech);
    g->tech = ob_strdup(tkg_defaultTech);
  }

  gate_draw(g,0);

  if (g && pes) {
    sel_appendGate(*pes,g);
    sel_finish(*pes);
    ob_touch(XGate.circuit);
    XGate.circuit->select = g;
  }

  scrollbar_bbx_update();

  return TCL_OK;
}

int gat_closeBox(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState **es = &gw->parms->circuit->es;

  if (!(*es)->parent) {
    message(0,msgLookup("err.closeroot"));	/* "Can't close top-level module.  Use quit." */
    return TCL_OK;
  }

  ob_touch(XGate.circuit);
  unselectGate(*es);
  editstate_pop(es);
  editstate_setCurrent(*es);
  ClearErrorMark();
  net_unselect(0);
  cpath_reshow();
  FlagRedraw();

  return TCL_OK;
}

int gat_openBox(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState **es = &gw->parms->circuit->es;
  GModuleDef *M = 0;
  GCElement *b = 0;
  const char *modName = 0;
  
  if (argc == 2) modName = argv[1];

  ob_touch(XGate.circuit);
  if ((*es)->isInterface) {
    if (!modName) {
      if (XGate.circuit->select && (XGate.circuit->select->typeinfo->Code == BLOCK)) {
	modName = XGate.circuit->select->u.block.BlockFunction;
	block_explode(XGate.circuit->select);
      }
    }
    if (!modName) {
      return TCL_OK;
    }

    gat_closeBox(_d,tcl,argc,argv);
  }

  if (modName) {
    /* The 'jump-to-module' feature can not be used in simulation or analysis mode. */


    if (tkgate_currentMode() != MM_EDIT) {
      message(1,msgLookup("err.nojump"));
      return TCL_OK;
    }
    M = env_findModule(modName);
    if (!M) {
      message(1,msgLookup("err.nomod"),modName);		/* Module '%s' is not defined. */
      return TCL_OK;
    }
  } else {
    b = XGate.circuit->select;
    if (!b || (b->typeinfo->Code != BLOCK))
      return TCL_OK;

    M = env_findAdd(b->u.block.BlockFunction,0);
  }

  if (M->prot && tkgate_currentMode() == MM_EDIT) { 
    message(1,msgLookup("msg.modlock"));			/* Logic Block is Locked! */
    return TCL_OK;
  }

  unselectGate(*es);

  if (!b) {
    editstate_navigateToModule(es,M);
  } else {
    block_explode(b);
    editstate_push(es,M,b);
  }
  editstate_setCurrent(*es);
  ClearErrorMark();
  net_unselect(0);
  cpath_reshow();
  FlagRedraw();

  return TCL_OK;
}



/*
   setrot {0/90/180/270}

   set rotation for new gates
*/
int gat_setrot(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{ 
  int N = 0;

  assert(argc == 2);

  sscanf(argv[1],"%d",&N);

  ob_touch(XGate.circuit);
  XGate.circuit->rot = N;

  return TCL_OK;
}

int gat_simWrite(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  assert(argc == 2);

  if (XGate.circuit->simulator.active)
    DoTcl("tkg_simWrite {%s}",argv[1]);
  else
    message(0,msgLookup("err.simonly"));	/* "Command not valid when not in simulation mode." */
  return TCL_OK;
}


/*
   Relay a command from the simulator to the scope
*/
int gat_scopeCommand(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  SimInterface_command(&XGate.circuit->simulator,argv[1]);
  return TCL_OK;
}

/*
   Relay a command from the analyzer to cpath 
*/
int gat_analyzerCommand(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  cpath_command(argv[1]);
  return TCL_OK;
}

int gat_errBoxReport(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState **es = &gw->parms->circuit->es;
  int N = 0;
  
  ob_touch(XGate.circuit);

  assert(argc == 2);
  sscanf(argv[1],"%d",&N);

  if (N >= 0)
    *es = seterror(N,*es);
  else {
    Error_purge();
  }

  return TCL_OK;
}

int gat_saveCircuit(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  const char *fileName = argv[1];

  assert(argc == 2);

  VerilogSave(fileName);

  return TCL_OK;
}

int gat_addOut(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (!modifyOK(es,2)) return TCL_OK;

  if (!XGate.circuit->select)
    return TCL_OK;

  (*XGate.circuit->select->typeinfo->AddOutput)(es,XGate.circuit->select);

  return TCL_OK;
}

int gat_addIn(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (!modifyOK(es,2)) return TCL_OK;

  if (!XGate.circuit->select)
    return TCL_OK;

  (*XGate.circuit->select->typeinfo->AddInput)(es,XGate.circuit->select);

  return TCL_OK;
}

int gat_addInOut(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (!modifyOK(es,2)) return TCL_OK;

  if (!XGate.circuit->select)
    return TCL_OK;

  (*XGate.circuit->select->typeinfo->AddTri)(es,XGate.circuit->select);

  return TCL_OK;
}

int gat_changePinDir(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (!XGate.circuit->select)
    return TCL_OK;

  (*XGate.circuit->select->typeinfo->ChangePin)(es,XGate.circuit->select);

  return TCL_OK;
}

/*
    Delete selected gate.
*/
int gat_deleteSelected(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;
  int n;

  if (!es) return TCL_OK;

  if (!modifyOK(es,2)) return TCL_OK;

  if ((es)->isInterface) {
    sel_interfaceReset(es);
    return TCL_OK;
  }

  if (XGate.circuit->select || XGate.circuit->mg_selection) {
    sel_delete(es);
    scrollbar_bbx_update();
  } else if (DoTcl(".sbar.blklst.f.list curselection"), (tcl->result && sscanf(tcl->result,"%d",&n) == 1)) {
    DoTcl("tkg_blockDelete");
  } else {
    message(1,msgLookup("err.nodel"));		/* "No deletable selection." */
    return TCL_OK;
  }


  return TCL_OK;
}

int gat_replicate(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (!modifyOK(es,0)) return TCL_OK;

  setEditMode(es,MODE_REPLICATE);
  scrollbar_bbx_update();

  return TCL_OK;
}

int gat_listBlocks(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  HashElem *E;

  DoTcl("tkg_blockListClear");
  for (E = Hash_first(XGate.circuit->moduleTable);E;E = Hash_next(XGate.circuit->moduleTable,E)) {
    GModuleDef *M = (GModuleDef*) HashElem_obj(E);
    char *mflags = "";
  
    if (M->is_top) mflags = "+";
    if (M->is_lib)
      DoTcl("tkg_blockListAdd \"(%s)%s\"\n",M->Name,mflags);
    else
      DoTcl("tkg_blockListAdd %s%s\n",M->Name,mflags);
  }

  return TCL_OK;
}

int gat_renameBlock(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  const char *OldName,*NewName;

  assert(argc == 3);
  OldName = argv[1];
  NewName = argv[2];

  if (checkValidName(argv[2],0) != 0) {
    message(1,msgLookup("err.badid"),argv[2]);
    return TCL_OK;
  }

  if (!env_findModule(OldName)) {
    message(1,msgLookup("err.modnotdef"),OldName);	/* "Module '%s' is undefined." */
    return TCL_OK;
  }

  if (env_findModule(NewName)) {
    message(1,msgLookup("err.moddef"),NewName);		/* "The block '%s' already exists." */
    return TCL_OK;
  }

  env_rename(OldName,NewName);
  DoTcl("gat_listBlocks -u");
  SetModified();

  return TCL_OK;
}

int gat_copyBlock(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (checkValidName(argv[2],0) != 0) {
    message(1,msgLookup("err.badid"),argv[2]);
    return TCL_OK;
  }

  env_copy(es,argv[1],argv[2]);

  return TCL_OK;
}

int gat_deleteBlock(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  env_delete(es,argv[1]);

  return TCL_OK;
}

int gat_claimBlock(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  GModuleDef *M;

  if (argc < 2) return TCL_OK;

  M = env_findModule(argv[1]);
  if (M) {
    if (!M->is_lib) {
      message(1,msgLookup("msg.notlib"),argv[1]);			/* Not a library module */      
      return TCL_OK;
    }

    DoTcl("tkg_blockListSetLibFlag %s 0",argv[1]);
    ob_touch(M);
    M->is_lib = 0;
    SetModified();
  }

  return TCL_OK;
}

int gat_newBlock(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  if (checkValidName(argv[1],0) != 0) {
    message(1,msgLookup("err.badid"),argv[1]);
    return TCL_OK;
  }

  env_defineModule(argv[1],0);

  return TCL_OK;
}

int gat_batInc(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  extern int batp;

  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (batp) {
    SetBatCursor(es);
    Tcl_Eval(tcl,"after $tkg_batRate gat_batInc");
  }

  return TCL_OK;
}

int gate_properties_lookup(const char *func,const char *name,void *data,int *rval)
{
  GCElement *g = (GCElement*) data;
  GGateInfo *gi = g->typeinfo;
  int b = 0, n = 0, c = 0;
  int i;

  if (!func) return EE_BADFUNC;
  if (!name) return EE_NOTDEF;

  for (i = 0;i < gi->NumPads;i++) {
    if (strcmp(gi->Pad[i].Name,name) == 0) {
      GWire *w;
      for (w = g->wires[i];w;w = w->next) {
	GNet *net = w->net;
	if (net->nbits > b) b = net->nbits;
	if (w->invert) c++;
	n++;
      }
    }
  }

  if (n == 0) {
    expr_errsym = name;
    return EE_NOTDEF;
  }

  if (strcmp(func,"bits") == 0) {
    *rval = b;
    return 0;
  } else if (strcmp(func,"inv") == 0) {
    *rval = c;
    return 0;
  } else if (strcmp(func,"num") == 0) {
    *rval = n;
    return 0;
  } else {
    expr_errsym = func;
    return EE_BADFUNC;
  }

  return 0;
}

/*
  Set variables for the delays of the currently seleced gate in the specified tecnology
 */
int gat_setGateTechDelays(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  GCElement *g = XGate.circuit->select;
  GDelayDef *dd;
  GGateInfo *gi;
  int i,rr,n;

  if (!g) return TCL_OK;
  gi = g->typeinfo;

  dd = GDelayDef_findTech(gi->delay_defs,argv[1]);
  if (!dd) return TCL_OK;

  for (i = 0;i < gi->num_delays;i++) {
    if (!(rr = Expr_eval(dd->dd_delay[i],&n,gate_properties_lookup,g))) {
      DoTcl("set edgat_techdelay(%s) %d",gi->delayNames[i],n);
    }
  }
  DoTcl("update");
  return TCL_OK; 
}

int gat_editProps(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;
  GCElement *g = XGate.circuit->select;
  GGateInfo *gi;
  const char *temp;
  int ok;

  if (!modifyOK(es,1)) return TCL_OK;
  if (tkgate_currentMode() != MM_EDIT) return TCL_OK;

  if ((!g || g->typeinfo->Code == JOINT) && XGate.circuit->nsel) {
    DoTcl("gat_editNet %s",XGate.circuit->nsel->signame);
    return TCL_OK;
  }

  if (!g) {
    DoTcl("tkg_editModuleOrNet");
    return TCL_OK;
  }
  gi = g->typeinfo;

  switch (gi->Code) {
  case BLOCK :
    {
      GWire *w = block_hitPort(g,XGate.ed->tx,XGate.ed->ty);
      if (w) {
	block_setPortName(g,w,es);
	return TCL_OK;
      }
    }
    break;
  case JOINT :
    return TCL_OK;
  case LOGICIN :
  case LOGICTRI :
  case LOGICOUT :
    net_editProps(g->wires[0]->net,XGate.ed->tx,XGate.ed->ty);
    return TCL_OK;
  }

  if (!gi->EditProps) {
    message(1,msgLookup("err.noprop"));	/* Selected gate has no editable properites. */
    return TCL_OK;
  }

  (*gi->EditProps)(g,1);
  DoTcl("tkg_editGate");
  if ((temp = Tcl_GetVar(tcl,"edgat_ok",TCL_GLOBAL_ONLY)) && sscanf(temp,"%d",&ok) == 1 && ok) {
    if (XGate.circuit->es->env->is_lib) {
      message(0,msgLookup("err.libmode"));		/* "Can not edit library module." */
      return TCL_OK;
    }

    if (tkgate_currentMode() == MM_EDIT)
      (*gi->EditProps)(g,0);
    else
      message(1,msgLookup("err.noteditpchg"));
  }

  return TCL_OK;
}

/*
   Update net properties
*/
int gat_changeNet(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;
  const char *oldName = argv[1];
  const char *newName = argv[2];
  int hide = 0;
  int nbits = 1;
  int io = 0;
  GNet *n;
  GWire *w;

  if (XGate.circuit->es->env->is_lib) {
    message(0,msgLookup("err.libmode"));		/* "Can not edit library module." */
    return TCL_OK;
  }

  sscanf(argv[3],"%d",&hide);
  sscanf(argv[4],"%d",&nbits);
  sscanf(argv[5],"%d",&io);

  n = (GNet*) SHash_find(es->env->nets,oldName);

  if (!n) return TCL_OK;

  w = n->driver;
  net_setSize(w->net,nbits);
  net_rename(n,newName,!hide);

  if (w->net->ionet) {
    GGateInfo *gi = gtype_get(io);

    if (gi)
      gate_transmute(w->net->ionet,gi);
  }

  FlagRedraw();

  return TCL_OK;
}

int gat_unselectGates(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  unselectAll(es);
  return TCL_OK;
}

int gat_setBlockDesc(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  GCElement *g = XGate.circuit->select;

  if (tkgate_currentMode() != MM_EDIT) {
    message(0,msgLookup("err.editonly"));
    return TCL_OK;
  }

  if (g && g->typeinfo->Code == BLOCK) {
    GModuleDef *M = env_findAdd(g->u.block.BlockFunction,0);
    modint_setInterface(M,g);
    message(0,msgLookup("msg.setinterface"),g->u.block.BlockFunction);	/* Set module interface for '%s'. */
  } else
    message(0,msgLookup("msg.needsel"));		/* Please select a module instance. */

  return TCL_OK;
}

int gat_editBlockDesc(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState **es = &gw->parms->circuit->es;

  ob_touch(XGate.circuit);

  if (tkgate_currentMode() != MM_EDIT) {
    message(0,msgLookup("err.editonly"));
    return TCL_OK;
  }

  if ((*es)->isInterface) {
    gat_closeBox(_d,tcl,argc,argv);
    return TCL_OK;
  } else {
    modint_edit(es,0);
    return TCL_OK;
  }
}

int gat_moveGate(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;
  int dx = 0,dy =0;

  assert(argc == 3);

  sscanf(argv[1],"%d",&dx);
  sscanf(argv[2],"%d",&dy);

  if (XGate.circuit->select)
    gate_moveObject(XGate.circuit->select,dx,dy);
  else if (XGate.circuit->mg_selection) {
    sel_draw(es);
    sel_move(es,dx,dy);
    sel_draw(es);
  }

  return TCL_OK;
}

int gat_editNet(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  GNet *net;
  char buf[STRMAX],*p;

  if (tkgate_currentMode() != MM_EDIT) return TCL_OK;

  strcpy(buf,argv[1]);
  if ((p = strchr(buf,'['))) *p = 0;
  if ((p = strchr(buf,'@'))) *p = 0;

  net = GModuleDef_findNet(XGate.circuit->es->env,buf);
  if (net)
    net_editProps(net,net->driver->nodes->x,net->driver->nodes->y);

  return TCL_OK;
}

int gat_cutToBuf(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (es->isInterface) {
    sel_interfaceReset(es);
  } else {
    sel_kill(es);
    FlagRedraw();
  }

  return TCL_OK;
}

int gat_copyToBuf(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  sel_copy(es);

  return TCL_OK;
}

int gat_yankFromBuf(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (!modifyOK(es,0)) return TCL_OK;

  sel_yank(es);
  FlagRedraw();

  return TCL_OK;
}

int gat_selectAll(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  mark_unpost();
  sel_clear(es);
  sel_selectAll(es);

  ob_touch(XGate.circuit);
  XGate.circuit->mode = MODE_MOVESEL;
  return TCL_OK;
}

/*
  Check to see if breakpoint expression is valid.

  The first argument specifies the breakpoint operation to perform.

  -load				Load breakpoints into tk listbox
  -replace n bp			Replace a breakpoint
  -insert n bp			Insert a breakpoint
  -delete n			Delete a breakpoint

  The 'replace' and 'insert' commands will return a negative value on error.
  Otherwise they will return the id number of the breakpoint.
 */
int gat_breakpoint(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  SimInterface *si = &XGate.circuit->simulator;
  int i,r;

  r = 0;

  assert(argc > 1);

  if (strcmp(argv[1],"-load") == 0) {
    assert(argc == 3);
    for (i = 0;i < si->numBPs;i++)
      DoTcl("%s insert end \"%s\"",argv[2],si->breakpoints[i].text);
    r = 0;
  } else if (strcmp(argv[1],"-clear") == 0) {
    SimInterface_clearBreaks(si);
  } else if (strcmp(argv[1],"-replace") == 0) {
    assert(argc == 4);
    if (breakpoint_check(argv[3]) < 0)  return -1;
    if (sscanf(argv[2],"%d",&i) != 1) return -1;
    SimInterface_deleteBreakPoint(si,i);
    r = SimInterface_insertBreakPoint(si,i,argv[3]);
  } else if (strcmp(argv[1],"-insert") == 0) {
    assert(argc == 4);
    if (sscanf(argv[2],"%d",&i) != 1) return -1;
    r = SimInterface_insertBreakPoint(si,i,argv[3]);
  } else if (strcmp(argv[1],"-delete") == 0) {
    assert(argc == 3);
    if (sscanf(argv[2],"%d",&i) != 1) return -1;
    SimInterface_deleteBreakPoint(si,i);
    r = 0;
  }

  sprintf(tcl->result,"%d",r);
  return TCL_OK;
}

int gat_simScript(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  SimInterface *si = &XGate.circuit->simulator;
  FILE *f;

  if (argc != 2) {
    strcpy(tcl->result,"bad script ");
    return TCL_ERROR;
  }

  if (!(f = openInPath(argv[1]))) {
    message(1,msgLookup("err.openscript"),argv[1]);		/* "Can't open simulation script file '%s'" */
    return TCL_OK;
  } 

  SimInterface_execSimScript(si,f);	/* File closed internally */

  return TCL_OK;
}

int gat_simSelected(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  GCElement *g = XGate.circuit->select;
  int i;
  int match = 0;

  if (!g) {
    *tcl->result = 0;
    return TCL_OK;
  }

  if (argc == 1) 
    match = 1;
  else {
    for (i = 1;i < argc;i++)
      if (g->typeinfo == gateinfo_lookup(argv[i])) {
	match = 1;
	break;
      }
  }

  if (!match) {
    *tcl->result = 0;
    return TCL_OK;
  }

  GSimModule_getFullPath(XGate.circuit->es->smod,g,tcl->result);

  return TCL_OK;
}

int gat_print(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int i;
  GPrintOpt PO;

  GPrintOpt_init(&PO);

  for (i = 1;i < argc;i+=2) {
    if (i+1 >= argc) break;
    if (strcmp(argv[i],"-printer") == 0)
      PO.po_cmd = argv[i+1];
    else if (strcmp(argv[i],"-file") == 0)
      PO.po_file = argv[i+1];
    else if (strcmp(argv[i],"-paper") == 0)
      PO.po_paper = argv[i+1];
    else if (strcmp(argv[i],"-orient") == 0)
      PO.po_orient = argv[i+1];
    else if (strcmp(argv[i],"-style") == 0)
      PO.po_style = argv[i+1];
    else if (strcmp(argv[i],"-select") == 0)
      PO.po_select = argv[i+1];
    else if (strcmp(argv[i],"-modlist") == 0)
      PO.po_modlist = argv[i+1];
    else if (strcmp(argv[i],"-index") == 0)
      PO.po_index = argv[i+1];
    else if (strcmp(argv[i],"-graph") == 0)
      PO.po_graph = argv[i+1];
    else if (strcmp(argv[i],"-epsf") == 0)
      PO.po_epsf = argv[i+1];
    else if (strcmp(argv[i],"-title") == 0)
      PO.po_title = argv[i+1];
    else if (strcmp(argv[i],"-merge") == 0)
      PO.po_4up = argv[i+1];
    else if (strcmp(argv[i],"-duplex") == 0)
      PO.po_isDuplex = argv[i+1];
    else if (strcmp(argv[i],"-inclib") == 0)
      PO.po_incLib = argv[i+1];
    else if (strcmp(argv[i],"-encoding") == 0)
      PO.po_encoding = argv[i+1];
  }

  GPrintOpt_print(&PO);

  return TCL_OK;
}

int gat_cleanUp(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  gateCleanUp();
  return TCL_OK;
}


int gat_tracePrint(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int i;
  GPrintOpt PO;

  GPrintOpt_init(&PO);

  for (i = 1;i < argc;i+=2) {
    if (i+1 >= argc) break;
    if (strcmp(argv[i],"-printer") == 0)
      PO.po_cmd = argv[i+1];
    else if (strcmp(argv[i],"-file") == 0)
      PO.po_file = argv[i+1];
    else if (strcmp(argv[i],"-paper") == 0)
      PO.po_paper = argv[i+1];
    else if (strcmp(argv[i],"-orient") == 0)
      PO.po_orient = argv[i+1];
    else if (strcmp(argv[i],"-epsf") == 0)
      PO.po_epsf = argv[i+1];
    else if (strcmp(argv[i],"-title") == 0)
      PO.po_title = argv[i+1];
    else if (strcmp(argv[i],"-duplex") == 0)
      PO.po_isDuplex = argv[i+1];
    else if (strcmp(argv[i],"-start") == 0)
      PO.po_start = argv[i+1];
    else if (strcmp(argv[i],"-end") == 0)
      PO.po_end = argv[i+1];
    else if (strcmp(argv[i],"-scale") == 0)
      PO.po_scale = argv[i+1];
  }

  GPrintOpt_tracePrint(&PO);

  return TCL_OK;
}

/*
 * Get trace range data.  Returns a tripple of the form {sel_start sel_end trace_end}.
 */
int gat_getTraceData(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  extern GScope *Scope;

  if (Scope) {
    if (Scope->mark_count == 2) {
      int tstart = Scope->mark_val[0];
      int tend = Scope->mark_val[1];

      if (tend < tstart) {
	int x = tstart; tstart = tend; tend = x;
      }
      tstart = imax(tstart,0);
      tend = imin(tend,Scope->Time);

      sprintf(tcl->result,"%d %d %d",tstart,tend,Scope->Time);
    } else
      sprintf(tcl->result,"0 %d %d",Scope->Time,Scope->Time);
  } else {
    sprintf(tcl->result,"0 0 0");
  }

  return TCL_OK;
}

int gat_tracePageEst(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int page_count = 0;
  int scale = 100;
  int start = 0;
  int end = 1;
  int paper = 0;
  int i;
  const char *orient = "landscape";

  for (i = 1;i < argc;i+=2) {
    if (i+1 >= argc) break;

    if (strcmp(argv[i],"-paper") == 0)
      sscanf(argv[i+1],"%d",&paper);
    else if (strcmp(argv[i],"-scale") == 0)
      sscanf(argv[i+1],"%d",&scale);
    else if (strcmp(argv[i],"-start") == 0)
      sscanf(argv[i+1],"%d",&start);
    else if (strcmp(argv[i],"-end") == 0)
      sscanf(argv[i+1],"%d",&end);
    else if (strcmp(argv[i],"-orient") == 0)
      orient = argv[i+1];
  }

  page_count = tracePageCountEst(orient,paper,scale,start,end);
  sprintf(tcl->result,"%d",page_count);
  return TCL_OK;
}


int gat_setCircProp(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  if (argc == 1) return TCL_OK;

  ob_touch(XGate.circuit);
  ob_touch(XGate.circuit->initScripts);

  if (strcmp(argv[1],"-clearscripts") == 0) {
    int i;

    for (i = 0;i < XGate.circuit->numInitScripts;i++)
      ob_free(XGate.circuit->initScripts[i]);
    if (XGate.circuit->initScripts)
      ob_free(XGate.circuit->initScripts);
    XGate.circuit->numInitScripts = 0;
    XGate.circuit->initScripts = 0;
  } else if (strcmp(argv[1],"-script") == 0 && argc > 2) {
    if (XGate.circuit->numInitScripts) {
      XGate.circuit->initScripts = (char**) ob_realloc(XGate.circuit->initScripts,sizeof(char*)*(XGate.circuit->numInitScripts+1));
    } else
      XGate.circuit->initScripts = (char**) ob_malloc(sizeof(char*),"char*");
    XGate.circuit->initScripts[XGate.circuit->numInitScripts++] = ob_strdup(argv[2]);
  } else if (strcmp(argv[1],"-top") == 0 && argc > 2) {
    GModuleDef *M = env_findModule(argv[2]);
    if (M) {
      if (XGate.circuit->root_mod) {
	ob_touch(XGate.circuit->root_mod);
	XGate.circuit->root_mod->is_top = 0;
      }
      XGate.circuit->root_mod = M;
      ob_touch(M);
      XGate.circuit->root_mod->is_top = 1;
    }
  }

  return TCL_OK;
}

int gat_getCircProp(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  *tcl->result = 0;

  if (strcmp(argv[1],"-script") == 0 && argc > 2) {
    int i;

    if (sscanf(argv[2],"%d",&i) != 1) return TCL_OK;
    if (i >= XGate.circuit->numInitScripts) return TCL_OK;
    strcpy(tcl->result,XGate.circuit->initScripts[i]);
  }

  return TCL_OK;
}

int gat_setDip(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  GCElement *g;
  GSimSwitch *ss;
  GSimModule *M;

  if (argc != 3) return TCL_OK;

  if (SimInterface_lookupGate(&XGate.circuit->simulator,argv[1],&M,&g,&ss) == 0) {
    if (M == XGate.circuit->es->smod) gate_draw(g,0);

    ob_touch(g);
    if (sscanf(argv[2],"%x",&g->u.sw.dipval) != 1) {
      g->u.sw.dipval = 0;
    }

    g->u.sw.dipval &= nmask(g->wires[DIP_OUT]->net->nbits);

    if (M == XGate.circuit->es->smod) gate_draw(g,0);

    DoTcl("tkg_simNetSet %s %d'h%x",
	  ss->name,
	  g->wires[0]->net->nbits,
	  g->u.sw.dipval);

    sprintf(tcl->result,"%x",g->u.sw.dipval);
  } else {
    sprintf(tcl->result,"0");
  }


  return TCL_OK;
}

int gat_find(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int mode = 0;

  sscanf(argv[1],"%d",&mode);

  search_find(XGate.circuit->search,argv[2],mode);

  return TCL_OK;
}

int gat_anchor(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;
  int mode = 0;
  int old_mode;

  if (XGate.circuit->es->env->is_lib) {
    message(0,msgLookup("err.libmode"));		/* "Can not edit library module." */
    return TCL_OK;
  }

  if (XGate.circuit->select)
    old_mode = XGate.circuit->select->anchored;
  else if (XGate.circuit->mg_selection)
    old_mode = XGate.circuit->mg_selection->s_hasAnchored;
  else
    old_mode = 0;

  sprintf(tcl->result,"%d",old_mode);

  if (argc > 1) {
    sscanf(argv[1],"%d",&mode);
    sel_anchor(es,mode);
  }

  return TCL_OK;
}

int gat_setcolors(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  setGCcolors();
  FlagRedraw();
  if (tkgate_currentMode() == MM_SIMULATE)
    ReqScopeRedisplay();

  return TCL_OK;
}

int gat_scopeMotion(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int x = 0,y = 0,state = 0,n=1;

  if (argc < 5) return TCL_OK;

  sscanf(argv[1],"%d",&x);
  sscanf(argv[2],"%d",&y);
  sscanf(argv[3],"%d",&n);
  sscanf(argv[4],"%d",&state);

  scopeButtonMotion(x,y,n,state);

  return TCL_OK;
}

int gat_scopeButton(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int x = 0,y = 0,state = 0,n=1;

  if (argc < 5) return TCL_OK;

  sscanf(argv[1],"%d",&x);
  sscanf(argv[2],"%d",&y);
  sscanf(argv[3],"%d",&n);
  sscanf(argv[4],"%d",&state);

  scopeButtonPress(x,y,n,state);

  return TCL_OK;
}

int gat_scopeRelease(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int x = 0,y = 0,state = 0,n=1;

  if (argc < 5) return TCL_OK;

  sscanf(argv[1],"%d",&x);
  sscanf(argv[2],"%d",&y);
  sscanf(argv[3],"%d",&n);
  sscanf(argv[4],"%d",&state);

  scopeButtonRelease(x,y,n,state);

  return TCL_OK;
}

int gat_zoom(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int x = 0;
  int old_zf;

  if (argc < 2) return TCL_OK;

  sscanf(argv[1],"%d",&x);

  old_zf = XGate.circuit->zoom_factor;
  x = XGate.circuit->zoom_factor + x;		/* The new zoom factor */

  if (x < 1 || x > ZOOM_MAX || x == XGate.circuit->zoom_factor)
    return TCL_OK;

  ob_touch(XGate.circuit);
  XGate.circuit->zoom_factor = x; 
  if (x == 1) {
    DoTcl(".tbar.z.zoomout configure -state disabled");
    DoTcl(".tbar.z.zoomin configure -state normal");
  } else if (x == ZOOM_MAX) {
    DoTcl(".tbar.z.zoomout configure -state normal");
    DoTcl(".tbar.z.zoomin configure -state disabled");
  } else {
    DoTcl(".tbar.z.zoomout configure -state normal");
    DoTcl(".tbar.z.zoomin configure -state normal");
  }

  XGate_setOrigin(XGate.circuit->org_x - (XGate.width/old_zf/2 - XGate.width/XGate.circuit->zoom_factor/2),
		  XGate.circuit->org_y - (XGate.height/old_zf/2 - XGate.height/XGate.circuit->zoom_factor/2));

  if (XGate.circuit->zoom_factor > 1) {
    XSetLineAttributes(XGate.D,XGate.moduleGC,XGate.circuit->zoom_factor,LineSolid,CapButt,JoinMiter);
    XSetLineAttributes(XGate.D,XGate.instGC,XGate.circuit->zoom_factor,LineSolid,CapButt,JoinMiter);
  } else {
    XSetLineAttributes(XGate.D,XGate.moduleGC,0,LineSolid,CapButt,JoinMiter);
    XSetLineAttributes(XGate.D,XGate.instGC,0,LineSolid,CapButt,JoinMiter);
  }

  FlagRedraw();

  return TCL_OK;
}

int gat_align(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;
  int x = 0;

  if (argc < 2) return TCL_OK;

  sscanf(argv[1],"%d",&x);

  if (x)
    sel_alignHorz(es);
  else
    sel_alignVert(es);

  return TCL_OK;
}

int gat_setpop(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int x,y;

  if (tkgate_currentMode() == MM_SIMULATE) {
    strcpy(tcl->result,"simu");
    return TCL_OK;
  }

  if (tkgate_currentMode() == MM_ANALYZE) {
    strcpy(tcl->result,"anal");
    return TCL_OK;
  }
    

  if (XGate.circuit->mode != MODE_MOVE && XGate.circuit->mode != MODE_MOVESEL) {
    strcpy(tcl->result,"notmove");
    return TCL_OK;
  }

  if (XGate.circuit->mg_selection && !XGate.circuit->select)
    strcpy(tcl->result,"multi");
  else if (XGate.circuit->select) {
    int can_add = 0;
    int i;

    for (i = 0;i < XGate.circuit->select->typeinfo->NumPads;i++)
      if (XGate.circuit->select->typeinfo->Pad[i].CanAdd)
	can_add = 1;

    if (XGate.circuit->select->typeinfo->Code == BLOCK)
      strcpy(tcl->result,"block");
    else if (XGate.circuit->select->typeinfo->Code == JOINT) {
      if (!XGate.circuit->select->wires[0]
	  || !XGate.circuit->select->wires[1]
	  || !XGate.circuit->select->wires[2]
	  || !XGate.circuit->select->wires[3])
	strcpy(tcl->result,"joint3");
      else
	strcpy(tcl->result,"joint4");
    } else if (can_add)
      strcpy(tcl->result,"MIgate");
    else
      strcpy(tcl->result,"gate");
  } else if (XGate.circuit->wnsel)
    strcpy(tcl->result,"wire");
  else
    strcpy(tcl->result,"canv");

  x = y = 0;
  if (argc > 1) sscanf(argv[1],"%d",&x);
  if (argc > 2) sscanf(argv[2],"%d",&y);

  /*     ob_touch(XGate.popstate); (popstate may need to be made undoable?) */

  XGate.popstate.g = XGate.circuit->select;
  XGate.popstate.w = XGate.circuit->wsel;
  XGate.popstate.n = XGate.circuit->wnsel;
  XGate.popstate.wx = wtoc_x(x);
  XGate.popstate.wy = wtoc_y(y);
  XGate.popstate.x = wtoc_x(x/XGate.circuit->zoom_factor);
  XGate.popstate.y = wtoc_y(y/XGate.circuit->zoom_factor);

  return TCL_OK;
}

int gat_setMajorMode(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  if (argc < 2) {
    strcpy(tcl->result,"bad mode ");
    return TCL_ERROR;
  }

  if (strcmp(argv[1],"simulate") == 0)
    tkgate_setMajorMode(MM_SIMULATE);
  else if (strcmp(argv[1],"edit") == 0)
    tkgate_setMajorMode(MM_EDIT);
  else if (strcmp(argv[1],"analyze") == 0)
    tkgate_setMajorMode(MM_ANALYZE);
  else {
    strcpy(tcl->result,"bad mode ");
    return TCL_ERROR;
  }

  return TCL_OK;
}

int gat_gotoCPathNet(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  const char *p;
  GNet *n;
  int x,y;
  GWireNode *wn1,*wn2;

  if (argc < 2) {
    strcpy(tcl->result,"bad cpath");
    return TCL_ERROR;
  }

  Error_navigateToModule(&XGate.circuit->es,argv[1]);
  editstate_setCurrent(XGate.circuit->es);
  FlagRedraw();

  p = strrchr (argv[1],'.');
  if (p)
    p++;
  else
    p = argv[1];

  n = GModuleDef_findNet(XGate.circuit->es->env,p);
  if (!n) {
    sprintf(tcl->result,"net '%s' not found.",p);
    return TCL_ERROR;
  }

  wn1 = n->driver->nodes;
  wn2 = wn1->out ? wn1->out : wn1->in;
  x = (wn1->x + wn2->x)/2;
  y = (wn1->y + wn2->y)/2;

  XGate_setOrigin(XGate.width/2 - x,XGate.height/2 - y);
  ob_touch(XGate.circuit->es);
  editstate_saveOrig(XGate.circuit->es);
  SetErrorPosition(x,y);

  return TCL_OK;
}

int gat_popupBlockIo(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (argc < 2) {
    strcpy(tcl->result,"bad popup");
    return TCL_ERROR;
  }

#if TOUCH_XGATE_ED
  ob_touch(XGate.ed);
#endif
  XGate.ed->tx = XGate.popstate.x;
  XGate.ed->ty = XGate.popstate.y;

  if (strcmp(argv[1],"in") == 0)
    block_newin(es);
  else if (strcmp(argv[1],"out") == 0)
    block_newout(es);
  else if (strcmp(argv[1],"inout") == 0)
    block_newtri(es);
  else if (strcmp(argv[1],"change") == 0) {
    if (XGate.circuit->select && XGate.circuit->select->typeinfo->Code == BLOCK) {
      GWire *w = block_hitPort(XGate.circuit->select,XGate.ed->tx,XGate.ed->ty);
      if (w)
	block_changedir(w->nodes,es);
    }
  }

  return TCL_OK;
}

int gat_popupWireProps(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  GNet *net;

  net = XGate.popstate.w->net;
  if (net) {
#if TOUCH_XGATE_ED
    ob_touch(XGate.ed);
#endif
    XGate.ed->tx = XGate.popstate.wx;
    XGate.ed->ty = XGate.popstate.wy;
    net_editProps(net,XGate.ed->tx,XGate.ed->ty);
  }

  return TCL_OK;
}

int gat_popupWireAddStub(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (XGate.popstate.g) {
    if (XGate.popstate.g->typeinfo->Code == JOINT)
      joint_addstub(XGate.popstate.g,es);
  } else
  wire_addstub(es,XGate.popstate.x,XGate.popstate.y);

  return TCL_OK;
}

int gat_dumpWires(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  wire_dump(es);

  return TCL_OK;
}

int gat_getTechList(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  char **techs = GDelayDef_getTechList();
  char *s,*p;
  int l,i;

  if (!techs) {
    strcpy(XGate.tcl->result,"default");
    return TCL_OK;
  }

  l = 0;
  for (i = 0;techs[i];i++)
    l += strlen(techs[i])+1;
  l++;

  p = s = (char*)ob_malloc(l,"char*");
  for (i = 0;techs[i];i++)
    p += sprintf(p," %s",techs[i]);

  XGate.tcl->result = s;

  return TCL_OK;
}

/*
  Set the technology for all selected gates.
 */
int gat_setTech(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  TkgGateWin *gw = XGate.gw;
  EditState *es = gw->parms->circuit->es;

  if (XGate.circuit->es->env->is_lib) {
    message(0,msgLookup("err.libmode"));		/* "Can not edit library module." */
    return TCL_OK;
  }
  
  if (argc > 1) {
    ob_begin_frame("SetTech");
    sel_setTech(es,argv[1]);
    ob_end_frame();
  }

  return TCL_OK;
}

/*
  Get the technology of the selected gate
 */
int gat_getTech(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  char *tech = 0;

  if (XGate.circuit->select) {
    tech = XGate.circuit->select->tech;
  } else if (XGate.circuit->mg_selection) {
    HashElem *E;
    char *s_tech = 0;

    for (E = Hash_first(XGate.circuit->mg_selection->s_gates);E;E = Hash_next(XGate.circuit->mg_selection->s_gates,E)) {
      GCElement *g = (GCElement*) HashElem_obj(E);
      s_tech = g->tech ? g->tech : "";
      if (!tech)
	tech = s_tech;
      else {
	if (strcmp(tech,s_tech) != 0) {
	  tech = "";
	  break;
	}
      }
    }
  }

  if (!tech) tech = "";
  strcpy(tcl->result,tech);

  return TCL_OK;
}

int gat_makeMakeMenu(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  if (argc != 2) {
    strcpy(tcl->result,"bad make menu");
    return TCL_ERROR;
  }

  makeMakeMenu(argv[1]);

  return TCL_OK;
}

int gat_makeMakeShortcuts(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  makeMakeShortcuts();
  return TCL_OK;
}

int gat_showPath(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  cpath_show(argc-1,argv+1);
  FlagRedraw();
  return TCL_OK;
}

int gat_clearPath(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  cpath_clear();
  cpath_flush();
  FlagRedraw();
  return TCL_OK;
}

int gat_cpathFlash(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  cpath_flash();
  return TCL_OK;
}

int gat_refreshScreen(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  FlagRedraw();
  return TCL_OK;
}

/*
 * Verify that internal data structures are correct.
 */
int gat_verify(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  verify_circuit();
  return TCL_OK;
}

/*
 * Toggle the existence of a probe on the selected net.
 */
int gat_toggleProbe(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  SimInterface *si = &XGate.circuit->simulator;

  if (tkgate_currentMode() != MM_SIMULATE)
    return TCL_OK;

  if (XGate.circuit->nsel) {
    char buf[STRMAX];
    GWire *w;
    GSimModule *SM;

    GSimModule_getNetPathName(XGate.circuit->es->smod,XGate.circuit->nsel,buf);
    if (SimInterface_lookupWire(si,buf,&SM,&w) == 0)
      SimInterface_addDelProbe(si,XGate.circuit->es->smod,buf,w,w->nodes,w->nodes->x,w->nodes->y);
  }

  return TCL_OK;
}

/*
 * Undo the last edit
 */
int gat_undo(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int n = 1;

  if (argc > 1)
    sscanf(argv[1],"%d",&n);

  ob_undo(n);
  FlagRedraw();
  return TCL_OK;
}

/*
 * Redo the last undone edit
 */
int gat_redo(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int n = 1;

  if (argc > 1)
    sscanf(argv[1],"%d",&n);

  ob_redo(n);
  FlagRedraw();
  return TCL_OK;
}

int gat_obBeginFrame(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  unsigned flags = 0;

  if (argc < 2) {
    abort();
    return TCL_OK;
  }

  if (argc > 2)
    sscanf(argv[2],"%u",&flags);

  if (strcmp(argv[1],"-") == 0)
    ob_begin_framef(0,flags);
  else
    ob_begin_framef(argv[1],flags);
  return TCL_OK;
}

int gat_obAppendFrame(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  if (argc != 2) return TCL_OK;
  if (strcmp(argv[1],"-") == 0)
    ob_append_frame(0);
  else
    ob_append_frame(argv[1]);
  return TCL_OK;
}

int gat_obEndFrame(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  ob_end_frame();
  return TCL_OK;
}

int gat_getUndoList(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  const char *undoList[1024];
  char *p;
  int N,i;
  int nc = 0;

  N = ob_getUndoList(undoList,1024);


  nc = 10;
  for (i = 0;i < N;i++)
    nc += strlen(undoList[i])+1;

  tcl->result = malloc(nc);	/* Really use malloc/free here */
  tcl->freeProc = (Tcl_FreeProc*)free;

  p = tcl->result;
  *p = 0;
  for (i = 0;i < N;i++)
    p += sprintf(p," %s",undoList[i]);


  return TCL_OK;
}

int gat_getRedoList(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  const char *redoList[1024];
  char *p;
  int N,i;
  int nc = 0;

  N = ob_getRedoList(redoList,1024);


  nc = 10;
  for (i = 0;i < N;i++)
    nc += strlen(redoList[i])+1;

  tcl->result = malloc(nc);	/* Really use malloc/free here */
  tcl->freeProc = (Tcl_FreeProc*)free;

  p = tcl->result;
  *p = 0;
  for (i = 0;i < N;i++)
    p += sprintf(p," %s",redoList[i]);

  return TCL_OK;
}

int gat_obMode(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int m = 1;
  
  if (argc == 1) {
    m = ob_get_mode();
    sprintf(tcl->result,"%d",m);
  } else {
    sscanf(argv[1],"%d",&m);
    ob_mode(m);
  }

  return TCL_OK;
}

int gat_obFlush(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  ob_clear();
  return TCL_OK;
}

int gat_obUnstick(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  ob_unstick();
  return TCL_OK;
}

int gat_iconv(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
#if USE_ICONV
  static iconv_t dec_ico = 0;
  static iconv_t enc_ico = 0;
  iconv_t ico;
  size_t lp,lq;
  const char *p;
  char *q;
  char buf[STRMAX];
#endif

  if (argc < 3) return TCL_ERROR;

#if USE_ICONV
  if (dec_ico == 0) dec_ico = iconv_open("euc-jp","utf-8");
  if (enc_ico == 0) enc_ico = iconv_open("utf-8","euc-jp");

  if (dec_ico < 0 || enc_ico < 0) {
    tcl->result = strdup(argv[2]);
    return TCL_OK;
  }

  if (strcmp(argv[1],"encode") == 0)
    ico = enc_ico;
  else
    ico = dec_ico;

  p = argv[2];
  q = buf;
  lp = strlen(p);
  lq = STRMAX;
  iconv(ico,&p,&lp,&q,&lq);
  *q = 0;

  if (lq+1 < TCL_RESULT_SIZE)
    strcpy(tcl->result,buf);
  else
    tcl->result = strdup(buf);
  return TCL_OK;
#else
  tcl->result = strdup(argv[2]);
  return TCL_OK;
#endif
}


int gat_setShowXHairState(ClientData _d,Tcl_Interp *tcl,int argc,const char *argv[])
{
  int n;

  assert(argc == 2);
  sscanf(argv[1],"%d",&n);

  GScope_setShowXHairState(n);

  return TCL_OK;
}

static Tkg_Command tkg_procTable[] = {
  {"gat_addIn",			gat_addIn},
  {"gat_addOut",		gat_addOut},
  {"gat_addInOut",		gat_addInOut},
  {"gat_align",			gat_align},
  {"gat_analyzerCommand",	gat_analyzerCommand},
  {"gat_anchor",		gat_anchor},
  {"gat_batInc",		gat_batInc},
  {"gat_breakpoint",		gat_breakpoint},
  {"gat_changeNet",		gat_changeNet},
  {"gat_changePinDir",		gat_changePinDir},
  {"gat_claimBlock",		gat_claimBlock},
  {"gat_cleanUp",		gat_cleanUp},
  {"gat_clearPath",		gat_clearPath},
  {"gat_closeBox",		gat_closeBox},
  {"gat_copyBlock",		gat_copyBlock},
  {"gat_copyToBuf",		gat_copyToBuf},
  {"gat_cpathFlash",		gat_cpathFlash},
  {"gat_cutToBuf",		gat_cutToBuf},
  {"gat_deleteBlock",		gat_deleteBlock},
  {"gat_deleteSelected",	gat_deleteSelected},
  {"gat_dumpWires",		gat_dumpWires},
  {"gat_editBlockDesc", 	gat_editBlockDesc},
  {"gat_editNet",		gat_editNet},
  {"gat_editProps",		gat_editProps},
  {"gat_errBoxReport",		gat_errBoxReport},
  {"gat_find",			gat_find},
  {"gat_getCircProp",		gat_getCircProp},
  {"gat_getRedoList",		gat_getRedoList},
  {"gat_getTech",		gat_getTech},
  {"gat_getTechList",		gat_getTechList},
  {"gat_getTraceData",		gat_getTraceData},
  {"gat_getUndoList",		gat_getUndoList},
  {"gat_gotoCPathNet",		gat_gotoCPathNet},
  {"gat_init",			gat_init},
  {"gat_listBlocks",		gat_listBlocks},
  {"gat_load",			gat_load},
  {"gat_loadLibrary",		gat_loadLibrary},
  {"gat_make",			gat_make},
  {"gat_makeMakeMenu",		gat_makeMakeMenu},
  {"gat_makeMakeShortcuts",	gat_makeMakeShortcuts},
  {"gat_mode",			gat_mode},
  {"gat_moveGate",		gat_moveGate},
  {"gat_new",			gat_new},
  {"gat_newBlock",		gat_newBlock},
  {"gat_openBox",		gat_openBox},
  {"gat_obAppendFrame",		gat_obAppendFrame},
  {"gat_obBeginFrame",		gat_obBeginFrame},
  {"gat_obEndFrame",		gat_obEndFrame},
  {"gat_obFlush",		gat_obFlush},
  {"gat_obMode",		gat_obMode},
  {"gat_obUnstick",		gat_obUnstick},
  {"gat_popupBlockIo",		gat_popupBlockIo},
  {"gat_popupWireProps",	gat_popupWireProps},
  {"gat_popupWireAddStub",	gat_popupWireAddStub},
  {"gat_print",			gat_print},
  {"gat_redo",			gat_redo},
  {"gat_refreshScreen",		gat_refreshScreen},
  {"gat_reinitDelay",		gat_reinitDelay},
  {"gat_renameBlock",		gat_renameBlock},
  {"gat_replicate",		gat_replicate},
  {"gat_saveCircuit",		gat_saveCircuit},
  {"gat_scopeButton",		gat_scopeButton},
  {"gat_scopeMotion",		gat_scopeMotion},
  {"gat_scopeRelease",		gat_scopeRelease},
  {"gat_selectAll",		gat_selectAll},
  {"gat_setBlockDesc",		gat_setBlockDesc},
  {"gat_setCircProp",		gat_setCircProp},
  {"gat_setcolors",		gat_setcolors},
  {"gat_setDip",		gat_setDip},
  {"gat_setGateTechDelays",	gat_setGateTechDelays},
  {"gat_setMajorMode",		gat_setMajorMode},
  {"gat_setpop",		gat_setpop},
  {"gat_setShowXHairState",	gat_setShowXHairState},
  {"gat_setTech",		gat_setTech},
  {"gat_setrot",		gat_setrot},
  {"gat_showPath",		gat_showPath},
  {"gat_simScript",		gat_simScript},
  {"gat_simSelected",		gat_simSelected},
  {"gat_simWrite",		gat_simWrite},
  {"gat_scopeCommand",		gat_scopeCommand},
  {"gat_tracePageEst",		gat_tracePageEst},
  {"gat_tracePrint",		gat_tracePrint},
  {"gat_toggleProbe",		gat_toggleProbe},
  {"gat_undo",			gat_undo},
  {"gat_unselectGates",		gat_unselectGates},
  {"gat_verify",		gat_verify},
  {"gat_yankFromBuf",		gat_yankFromBuf},
  {"gat_zoom",			gat_zoom},
  {"iconv",			gat_iconv},
};

static int tkg_procTableLen = sizeof(tkg_procTable)/sizeof(tkg_procTable[0]);

void InitTclProcs(Tcl_Interp *tcl)
{
  int i;

  for (i = 0;i < tkg_procTableLen;i++) {
    Tcl_CreateCommand(tcl,tkg_procTable[i].command,tkg_procTable[i].func,0,0);
  }
}
