/*
 * Electric(tm) VLSI Design System
 *
 * File: dblangtcl.c
 * TCL interface module
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) 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.
 *
 * Electric(tm) 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 Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "config.h"
#if LANGTCL

#include "global.h"
#include "dblang.h"
#include "database.h"
#include "usr.h"
#ifdef	USETK
#  ifdef BLACK
#    undef BLACK
#  endif
#  ifdef WHITE
#    undef WHITE
#  endif
#  include "tk.h"
#  include "tkMenu.h"
#  include "tkInt.h"
#endif

#define	BUFSIZE 500

char *tcl_outputbuffer, *tcl_outputloc, *tcl_inputbuffer, *tcl_inputloc;
Tcl_Interp *tcl_interp = 0;

/* prototypes for local routines */
void tcl_dumpoutput(char *str);
INTSML tcl_getinput(void);
void tcl_converttoanyelectric(char *tclstr, INTBIG *addr, INTBIG *type);
void tcl_converttoscalartcl(INTBIG addr, INTBIG type);
char *tcl_converttotcl(INTBIG addr, INTBIG type);
INTBIG tcl_converttowarnedelectric(char *name, INTBIG want, char *msg);
int tcl_tkconsoleclose(ClientData instanceData, Tcl_Interp *interp);
int tcl_tkconsoleinput(ClientData instanceData, char *buf, int bufSize, int *errorCode);
int tcl_tkconsoleoutput(ClientData instanceData, char *buf, int toWrite, int *errorCode);
void tcl_tkconsolewatch(ClientData instanceData, int mask);

int tcl_curlib(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_curtech(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_getval(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_setval(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_setind(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_delval(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_initsearch(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_nextobject(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_getaid(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_maxaid(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_indexaid(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_aidturnon(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_aidturnoff(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_getlibrary(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_newlibrary(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_killlibrary(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_eraselibrary(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_selectlibrary(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_getnodeproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_newnodeproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_killnodeproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_copynodeproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_iconview(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_contentsview(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_newnodeinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_modifynodeinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_killnodeinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_replacenodeinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_nodefunction(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_newarcinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_modifyarcinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_killarcinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_replacearcinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_newportproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_portposition(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_getportproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_killportproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_moveportproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_undoabatch(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_noundoallowed(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_getview(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_newview(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_killview(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_tellaid(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_askaid(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_getarcproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_getcell(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_gettechnology(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_getpinproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_getnetwork(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);
int tcl_domenu(ClientData dummy, Tcl_Interp *interp, int argc, char **argv);

/************************* TCL: ELECTRIC ROUTINES *************************/
/*
 * Routine to free all memory associated with this module.
 */
void db_freetclmemory(void)
{
	efree((char *)tcl_outputbuffer);
	efree((char *)tcl_inputbuffer);
}

void el_tclinterpreter(Tcl_Interp *interp)
{
	Tcl_Channel consoleChannel;
	char *path;
	static Tcl_ChannelType consoleChannelType =
	{
		"console",				/* Type name. */
		NULL,					/* Always non-blocking.*/
		tcl_tkconsoleclose,		/* Close proc. */
		tcl_tkconsoleinput,		/* Input proc. */
		tcl_tkconsoleoutput,	/* Output proc. */
		NULL,					/* Seek proc. */
		NULL,					/* Set option proc. */
		NULL,					/* Get option proc. */
		tcl_tkconsolewatch,		/* Watch for events on console. */
		NULL,		/* Are events present? */
	};

	/* remember the interpreter */
	tcl_interp = interp;

	/* allocate terminal I/O buffers */
	tcl_outputbuffer = (char *)emalloc(BUFSIZE, db_cluster);
	tcl_inputbuffer = (char *)emalloc(BUFSIZE, db_cluster);
	if (tcl_outputbuffer == 0 || tcl_inputbuffer == 0) return;
	tcl_outputloc = tcl_outputbuffer;
	tcl_inputloc = 0;

	/* set console channels */
	consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console0", (ClientData)TCL_STDIN, TCL_READABLE);
	if (consoleChannel != NULL)
	{
		Tcl_SetChannelOption(NULL, consoleChannel, "-translation", "lf");
		Tcl_SetChannelOption(NULL, consoleChannel, "-buffering", "none");
	}
	Tcl_SetStdChannel(consoleChannel, TCL_STDIN);
	consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console1", (ClientData)TCL_STDOUT, TCL_WRITABLE);
	if (consoleChannel != NULL)
	{
		Tcl_SetChannelOption(NULL, consoleChannel, "-translation", "lf");
		Tcl_SetChannelOption(NULL, consoleChannel, "-buffering", "none");
	}
	Tcl_SetStdChannel(consoleChannel, TCL_STDOUT);
	consoleChannel = Tcl_CreateChannel(&consoleChannelType, "console2", (ClientData)TCL_STDERR, TCL_WRITABLE);
	if (consoleChannel != NULL)
	{
		Tcl_SetChannelOption(NULL, consoleChannel, "-translation", "lf");
		Tcl_SetChannelOption(NULL, consoleChannel, "-buffering", "none");
	}
	Tcl_SetStdChannel(consoleChannel, TCL_STDERR);

	/* set the system version */
	if (Tcl_SetVar(tcl_interp, "el_version", el_version, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL)
	{
		ttyputerr(_("Tcl_SetVar failed: %s"), tcl_interp->result);
		ttyputmsg(_("couldn't set el_version variable"));
	}

	/* set the Electric library directory */
	if (Tcl_SetVar(tcl_interp, "el_libdir", el_libdir, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL)
	{
		ttyputerr(_("Tcl_SetVar failed: %s"), tcl_interp->result);
		ttyputmsg(_("couldn't set el_libdir variable"));
	}

	/* set the Electric LISP directory */
	(void)initinfstr();
	(void)addstringtoinfstr(el_libdir);
	(void)addstringtoinfstr("lisp");
	(void)addtoinfstr(DIRSEP);
	if (Tcl_SetVar(tcl_interp, "el_lispdir", returninfstr(), TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL)
	{
		ttyputerr(_("Tcl_SetVar failed: %s"), tcl_interp->result);
		ttyputmsg(_("couldn't set el_lispdir variable"));
	}

	/* set the Electric Tcl directory */
	(void)initinfstr();
	(void)addstringtoinfstr(el_libdir);
	(void)addstringtoinfstr("tcl");
	(void)addtoinfstr(DIRSEP);
	path = returninfstr();
	if (Tcl_SetVar(tcl_interp, "el_tcldir", path, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL)
	{
		ttyputerr(_("Tcl_SetVar failed: %s"), tcl_interp->result);
		ttyputmsg(_("couldn't set el_tcldir variable"));
	}

	/* set the Tcl library directory - for internal Tcl use */
	if (Tcl_SetVar(tcl_interp, "tcl_library", path, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL)
	{
		ttyputerr(_("Tcl_SetVar failed: %s"), tcl_interp->result);
		ttyputmsg(_("couldn't set tcl_library variable"));
	}

#ifdef USETK
	/* set the Electric Tk directory */
	(void)initinfstr();
	(void)addstringtoinfstr(el_libdir);
	(void)addstringtoinfstr("tk");
	(void)addtoinfstr(DIRSEP);
	path = returninfstr();
	if (Tcl_SetVar(tcl_interp, "el_tkdir", path, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL)
	{
		ttyputerr(_("Tcl_SetVar failed: %s"), tcl_interp->result);
		ttyputmsg(_("couldn't set el_tkdir variable"));
	}

	/* set the Tk library directory - for internal Tk use */
	if (Tcl_SetVar(tcl_interp, "tk_library", path, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL)
	{
		ttyputerr(_("Tcl_SetVar failed: %s"), tcl_interp->result);
		ttyputmsg(_("couldn't set tk_library variable"));
	}
#endif

	/* create the Electric commands */
	Tcl_CreateCommand(tcl_interp, "curlib",          tcl_curlib,          (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "curtech",         tcl_curtech,         (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "getval",          tcl_getval,          (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "setval",          tcl_setval,          (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "setind",          tcl_setind,          (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "delval",          tcl_delval,          (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "initsearch",      tcl_initsearch,      (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "nextobject",      tcl_nextobject,      (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "getaid",          tcl_getaid,          (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "maxaid",          tcl_maxaid,          (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "indexaid",        tcl_indexaid,        (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "aidturnon",       tcl_aidturnon,       (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "aidturnoff",      tcl_aidturnoff,      (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "getlibrary",      tcl_getlibrary,      (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "newlibrary",      tcl_newlibrary,      (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "killlibrary",     tcl_killlibrary,     (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "eraselibrary",    tcl_eraselibrary,    (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "selectlibrary",   tcl_selectlibrary,   (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "getnodeproto",    tcl_getnodeproto,    (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "newnodeproto",    tcl_newnodeproto,    (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "killnodeproto",   tcl_killnodeproto,   (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "copynodeproto",   tcl_copynodeproto,   (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "iconview",        tcl_iconview,        (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "contentsview",    tcl_contentsview,    (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "newnodeinst",     tcl_newnodeinst,     (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "modifynodeinst",  tcl_modifynodeinst,  (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "killnodeinst",    tcl_killnodeinst,    (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "replacenodeinst", tcl_replacenodeinst, (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "nodefunction",    tcl_nodefunction,    (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "newarcinst",      tcl_newarcinst,      (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "modifyarcinst",   tcl_modifyarcinst,   (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "killarcinst",     tcl_killarcinst,     (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "replacearcinst",  tcl_replacearcinst,  (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "newportproto",    tcl_newportproto,    (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "portposition",    tcl_portposition,    (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "getportproto",    tcl_getportproto,    (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "killportproto",   tcl_killportproto,   (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "moveportproto",   tcl_moveportproto,   (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "undoabatch",      tcl_undoabatch,      (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "noundoallowed",   tcl_noundoallowed,   (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "getview",         tcl_getview,         (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "newview",         tcl_newview,         (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "killview",        tcl_killview,        (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "tellaid",         tcl_tellaid,         (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "askaid",          tcl_askaid,          (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "getarcproto",     tcl_getarcproto,     (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "getcell",         tcl_getcell,         (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "gettechnology",   tcl_gettechnology,   (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "getpinproto",     tcl_getpinproto,     (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
	Tcl_CreateCommand(tcl_interp, "getnetwork",      tcl_getnetwork,      (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
#ifdef USETK
	Tcl_CreateCommand(tcl_interp, "domenu",          tcl_domenu,          (ClientData)0, (Tcl_CmdDeleteProc *)NULL);
#endif
}

void tcl_converttoanyelectric(char *tclstr, INTBIG *addr, INTBIG *type)
{
	REGISTER char *pt;
	REGISTER float fval;

	/* simple if it is a number */
	if (isanumber(tclstr) != 0)
	{
		for(pt = tclstr; *pt != 0; pt++)
			if (*pt == '.')
		{
			/* with a decimal point, use floating point representation */
			fval = (float)atof(tclstr);
			*addr = castint(fval);
			*type = VFLOAT;
			return;
		}

		/* just an integer */
		*addr = myatoi(tclstr);
		*type = VINTEGER;
		return;
	}

	/* check for special Electric object names */
	if (namesamen(tclstr, "#nodeinst",     9) == 0) { *addr = myatoi(&tclstr[9]);  *type = VNODEINST;    return; }
	if (namesamen(tclstr, "#nodeproto",   10) == 0) { *addr = myatoi(&tclstr[10]); *type = VNODEPROTO;   return; }
	if (namesamen(tclstr, "#portarcinst", 12) == 0) { *addr = myatoi(&tclstr[12]); *type = VPORTARCINST; return; }
	if (namesamen(tclstr, "#portexpinst", 12) == 0) { *addr = myatoi(&tclstr[12]); *type = VPORTEXPINST; return; }
	if (namesamen(tclstr, "#portproto",   10) == 0) { *addr = myatoi(&tclstr[10]); *type = VPORTPROTO;   return; }
	if (namesamen(tclstr, "#arcinst",      8) == 0) { *addr = myatoi(&tclstr[8]);  *type = VARCINST;     return; }
	if (namesamen(tclstr, "#arcproto",     9) == 0) { *addr = myatoi(&tclstr[9]);  *type = VARCPROTO;    return; }
	if (namesamen(tclstr, "#geom",         5) == 0) { *addr = myatoi(&tclstr[5]);  *type = VGEOM;        return; }
	if (namesamen(tclstr, "#library",      8) == 0) { *addr = myatoi(&tclstr[8]);  *type = VLIBRARY;     return; }
	if (namesamen(tclstr, "#technology",  11) == 0) { *addr = myatoi(&tclstr[11]); *type = VTECHNOLOGY;  return; }
	if (namesamen(tclstr, "#aid",          4) == 0) { *addr = myatoi(&tclstr[4]);  *type = VAID;         return; }
	if (namesamen(tclstr, "#rtnode",       7) == 0) { *addr = myatoi(&tclstr[7]);  *type = VRTNODE;      return; }
	if (namesamen(tclstr, "#network",      8) == 0) { *addr = myatoi(&tclstr[8]);  *type = VNETWORK;     return; }
	if (namesamen(tclstr, "#cell",         5) == 0) { *addr = myatoi(&tclstr[5]);  *type = VCELL;        return; }
	if (namesamen(tclstr, "#view",         5) == 0) { *addr = myatoi(&tclstr[5]);  *type = VVIEW;        return; }
	if (namesamen(tclstr, "#window",       7) == 0) { *addr = myatoi(&tclstr[7]);  *type = VWINDOWPART;  return; }
	if (namesamen(tclstr, "#graphics",     9) == 0) { *addr = myatoi(&tclstr[9]);  *type = VGRAPHICS;    return; }
	if (namesamen(tclstr, "#constraint",  11) == 0) { *addr = myatoi(&tclstr[11]); *type = VCONSTRAINT;  return; }
	if (namesamen(tclstr, "#windowframe", 12) == 0) { *addr = myatoi(&tclstr[7]);  *type = VWINDOWFRAME; return; }

	/* just a string */
	(void)initinfstr();
	(void)addstringtoinfstr(tclstr);
	*addr = (INTBIG)returninfstr();
	*type = VSTRING;
}

INTBIG tcl_converttoelectric(char *tclstr, INTBIG type)
{
	switch (type&VTYPE)
	{
		case VSTRING:
			(void)initinfstr();
			(void)addstringtoinfstr(tclstr);
			return((INTBIG)returninfstr());

		case VINTEGER:
		case VSHORT:
		case VADDRESS:
			return(myatoi(tclstr));

		case VFLOAT:
		case VDOUBLE:
			return(castint((float)atof(tclstr)));

		case VFRACT:
			return(myatoi(tclstr) * WHOLE);

		case VNODEINST:    if (namesamen(tclstr, "#nodeinst",     9) == 0) return(myatoi(&tclstr[9]));  break;
		case VNODEPROTO:   if (namesamen(tclstr, "#nodeproto",   10) == 0) return(myatoi(&tclstr[10])); break;
		case VPORTARCINST: if (namesamen(tclstr, "#portarcinst", 12) == 0) return(myatoi(&tclstr[12])); break;
		case VPORTEXPINST: if (namesamen(tclstr, "#portexpinst", 12) == 0) return(myatoi(&tclstr[12])); break;
		case VPORTPROTO:   if (namesamen(tclstr, "#portproto",   10) == 0) return(myatoi(&tclstr[10])); break;
		case VARCINST:     if (namesamen(tclstr, "#arcinst",      8) == 0) return(myatoi(&tclstr[8]));  break;
		case VARCPROTO:    if (namesamen(tclstr, "#arcproto",     9) == 0) return(myatoi(&tclstr[9]));  break;
		case VGEOM:        if (namesamen(tclstr, "#geom",         5) == 0) return(myatoi(&tclstr[5]));  break;
		case VLIBRARY:     if (namesamen(tclstr, "#library",      8) == 0) return(myatoi(&tclstr[8]));  break;
		case VTECHNOLOGY:  if (namesamen(tclstr, "#technology",  11) == 0) return(myatoi(&tclstr[11])); break;
		case VAID:         if (namesamen(tclstr, "#aid",          4) == 0) return(myatoi(&tclstr[4]));  break;
		case VRTNODE:      if (namesamen(tclstr, "#rtnode",       7) == 0) return(myatoi(&tclstr[7]));  break;
		case VNETWORK:     if (namesamen(tclstr, "#network",      8) == 0) return(myatoi(&tclstr[8]));  break;
		case VCELL:        if (namesamen(tclstr, "#cell",         5) == 0) return(myatoi(&tclstr[5]));  break;
		case VVIEW:        if (namesamen(tclstr, "#view",         5) == 0) return(myatoi(&tclstr[5]));  break;
		case VWINDOWPART:  if (namesamen(tclstr, "#window",       7) == 0) return(myatoi(&tclstr[7]));  break;
		case VGRAPHICS:    if (namesamen(tclstr, "#graphics",     9) == 0) return(myatoi(&tclstr[9]));  break;
		case VCONSTRAINT:  if (namesamen(tclstr, "#constraint",  11) == 0) return(myatoi(&tclstr[11])); break;
		case VWINDOWFRAME: if (namesamen(tclstr, "#windowframe", 12) == 0) return(myatoi(&tclstr[12])); break;
	}
	return(-1);
}

INTBIG tcl_converttowarnedelectric(char *name, INTBIG want, char *msg)
{
	INTBIG addr, type;

	tcl_converttoanyelectric(name, &addr, &type);
	if (type != want)
	{
		Tcl_AppendResult(tcl_interp, msg, _(" argument has the wrong type"), (char *)NULL);
		return(-1);
	}
	return(addr);
}

char *tcl_converttotcl(INTBIG addr, INTBIG type)
{
	REGISTER INTBIG saddr, stype, len, i;

	/* handle scalars easily */
	if ((type&VISARRAY) == 0)
	{
		(void)initinfstr();
		tcl_converttoscalartcl(addr, type);
		return(returninfstr());
	}

	/* handle arrays */
	(void)initinfstr();
	len = (type&VLENGTH) >> VLENGTHSH;
	if (len != 0)
	{
		stype = type & VTYPE;
		for(i=0; i<len; i++)
		{
			if ((type&VTYPE) == VGENERAL)
			{
				stype = ((INTBIG *)addr)[i*2+1];
				saddr = ((INTBIG *)addr)[i*2];
			} else
			{
				if ((type&VTYPE) == VCHAR) saddr = ((char *)addr)[i]; else
					if ((type&VTYPE) == VDOUBLE) saddr = (INTBIG)(((double *)addr)[i]); else
						if ((type&VTYPE) == VSHORT) saddr = ((INTSML *)addr)[i]; else
							saddr = ((INTBIG *)addr)[i];
			}
			if (i != 0) (void)addtoinfstr(' ');
			tcl_converttoscalartcl(saddr, stype);
		}
	} else
	{
		for(i=0; ; i++)
		{
			if ((type&VTYPE) == VCHAR)
			{
				if ((((char *)addr)[i]&0377) == 0377) break;
				saddr = ((char *)addr)[i];
			} else if ((type&VTYPE) == VDOUBLE)
			{
				if (((double *)addr)[i] == -1) break;
				saddr = (INTBIG)(((double *)addr)[i]);
			} else if ((type&VTYPE) == VSHORT)
			{
				if (((INTSML *)addr)[i] == -1) break;
				saddr = ((INTSML *)addr)[i];
			} else
			{
				saddr = ((INTBIG *)addr)[i];
				if (saddr == -1) break;
			}
			if (i != 0) (void)addtoinfstr(' ');
			tcl_converttoscalartcl(saddr, type);
		}
	}
	return(returninfstr());
}

void tcl_converttoscalartcl(INTBIG addr, INTBIG type)
{
	static char line[50];

	switch (type&VTYPE)
	{
		case VSTRING:
			(void)addtoinfstr('"');
			(void)addstringtoinfstr((char *)addr);
			(void)addtoinfstr('"');
			return;
		case VINTEGER:
		case VSHORT:
		case VADDRESS:     sprintf(line, "%ld", addr);                break;
		case VFLOAT:
		case VDOUBLE:      sprintf(line, "%g", castfloat(addr));      break;
		case VFRACT:       sprintf(line, "%g", (float)addr / WHOLE);  break;
		case VNODEINST:    sprintf(line, "#nodeinst%ld",    addr);    break;
		case VNODEPROTO:   sprintf(line, "#nodeproto%ld",   addr);    break;
		case VPORTARCINST: sprintf(line, "#portarcinst%ld", addr);    break;
		case VPORTEXPINST: sprintf(line, "#portexpinst%ld", addr);    break;
		case VPORTPROTO:   sprintf(line, "#portproto%ld",   addr);    break;
		case VARCINST:     sprintf(line, "#arcinst%ld",     addr);    break;
		case VARCPROTO:    sprintf(line, "#arcproto%ld",    addr);    break;
		case VGEOM:        sprintf(line, "#geom%ld",        addr);    break;
		case VLIBRARY:     sprintf(line, "#library%ld",     addr);    break;
		case VTECHNOLOGY:  sprintf(line, "#technology%ld",  addr);    break;
		case VAID:         sprintf(line, "#aid%ld",         addr);    break;
		case VRTNODE:      sprintf(line, "#rtnode%ld",      addr);    break;
		case VNETWORK:     sprintf(line, "#network%ld",     addr);    break;
		case VCELL:        sprintf(line, "#cell%ld",        addr);    break;
		case VVIEW:        sprintf(line, "#view%ld",        addr);    break;
		case VWINDOWPART:  sprintf(line, "#window%ld",      addr);    break;
		case VGRAPHICS:    sprintf(line, "#graphics%ld",    addr);    break;
		case VCONSTRAINT:  sprintf(line, "#constraint%ld",  addr);    break;
		case VWINDOWFRAME: sprintf(line, "#windowframe%ld", addr);    break;
		default: return;
	}
	(void)addstringtoinfstr(line);
}

int tcl_tkconsoleclose(ClientData instanceData, Tcl_Interp *interp) { return 0; }

int tcl_tkconsoleinput(ClientData instanceData, char *buf, int bufSize, int *errorCode)
{
	REGISTER char *pt;
	REGISTER INTBIG len, i;

	*errorCode = 0;
	*tcl_outputloc = 0;
	tcl_inputloc = 0;
	pt = ttygetlinemessages(tcl_outputbuffer);
	tcl_outputloc = tcl_outputbuffer;
	len = strlen(pt);
	if (len > bufSize) len = bufSize;
	for(i=0; i<len; i++) buf[i] = pt[i];
	return(len);
}

int tcl_tkconsoleoutput(ClientData instanceData, char *buf, int toWrite, int *errorCode)
{
	char save;

	*errorCode = 0;
	Tcl_SetErrno(0);

	save = buf[toWrite];
	buf[toWrite] = 0;
	tcl_dumpoutput(buf);
	buf[toWrite] = save;
	return(toWrite);
}

void tcl_tkconsolewatch(ClientData instanceData, int mask) {}

void tcl_dumpoutput(char *str)
{
	while (*str != 0)
	{
		if (*str == '\n')
		{
			*tcl_outputloc = 0;
			ttyputmsg("%s", tcl_outputbuffer);
			tcl_outputloc = tcl_outputbuffer;
		} else
		{
			if (tcl_outputloc >= tcl_outputbuffer+BUFSIZE-2)
			{
				*tcl_outputloc = 0;
				ttyputmsg("%s", tcl_outputbuffer);
				tcl_outputloc = tcl_outputbuffer;
			}
			*tcl_outputloc++ = *str;
		}
		str++;
	}
}

INTSML tcl_getinput(void)
{
	char *pt;
	INTSML ret;

	if (tcl_inputloc == 0)
	{
		/* nothing in the buffer: get a line */
		*tcl_outputloc = 0;
		pt = ttygetlinemessages(tcl_outputbuffer);
		tcl_outputloc = tcl_outputbuffer;
		if (pt == 0) return(EOF);
		(void)strcpy(tcl_inputbuffer, pt);
		tcl_inputloc = tcl_inputbuffer;
	}

	/* get the next character */
	ret = *tcl_inputloc++;
	if (ret == 0)
	{
		ret = '\n';
		tcl_inputloc = 0;
	}
	return(ret);
}

/************************* DATABASE EXAMINATION ROUTINES *************************/

int tcl_curlib(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	if (argc != 1)
	{
		Tcl_AppendResult(interp, "Usage: curlib", (char *)NULL);
		return TCL_ERROR;
	}

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)el_curlib, VLIBRARY), (char *) NULL);
	return TCL_OK;
}

int tcl_curtech(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	if (argc != 1)
	{
		Tcl_AppendResult(interp, "Usage: curtech", (char *)NULL);
		return TCL_ERROR;
	}

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)el_curtech, VTECHNOLOGY), (char *) NULL);
	return TCL_OK;
}

int tcl_getval(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	INTBIG addr, type;
	char *retval;
	REGISTER VARIABLE *var;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: getval OBJECT QUAL", (char *)NULL);
		return TCL_ERROR;
	}
	tcl_converttoanyelectric(argv[1], &addr, &type);

	/* call Electric */
	var = getval(addr, type, -1, argv[2]);

	/* convert result */
	if (var != NOVARIABLE)
	{
		retval = tcl_converttotcl(var->addr, var->type);
		Tcl_AppendResult(interp, retval, (char *)NULL);
	}
	return TCL_OK;
}

int tcl_setval(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	INTBIG addr, type, naddr, ntype, i, *naddrlist;
	REGISTER VARIABLE *ret;
	char *retval, **setArgv;
	int setArgc;

	/* get input */
	if (argc != 4 && argc != 5)
	{
		Tcl_AppendResult(interp, "Usage: setval OBJECT QUAL NEWVALUE [OPTIONS]", (char *)NULL);
		return TCL_ERROR;
	}
	tcl_converttoanyelectric(argv[1], &addr, &type);

	/* see how many entries are being set */
	i = Tcl_SplitList(interp, argv[3], &setArgc, &setArgv);
	if (i != TCL_OK) return TCL_ERROR;

	/* see if the "newvalue" is an array */
	if (setArgc > 1)
	{
		/* setting an array */
		naddrlist = (INTBIG *)emalloc(setArgc * SIZEOFINTBIG * 2, el_tempcluster);
		if (naddrlist == 0) return TCL_ERROR;
		for(i=0; i<setArgc; i++)
			tcl_converttoanyelectric(setArgv[i], &naddrlist[i*2], &naddrlist[i*2+1]);
		ckfree((char *)setArgv);

		/* see if the array is uniform */
		for(i=1; i<setArgc; i++)
			if (naddrlist[(i-1)*2+1] != naddrlist[i*2+1]) break;
		if (i < setArgc)
		{
			/* general array */
			ret = setval(addr, type, argv[2], (INTBIG)naddrlist,
				VGENERAL|VISARRAY|((setArgc*2) << VLENGTHSH));
		} else
		{
			/* a uniform array */
			ntype = naddrlist[1];
			for(i=1; i<setArgc; i++) naddrlist[i] = naddrlist[i*2];
			ret = setval(addr, type, argv[2], (INTBIG)naddrlist,
				ntype|VISARRAY|(setArgc << VLENGTHSH));
		}
		efree((char *)naddrlist);
	} else
	{
		/* just setting a single value */
		tcl_converttoanyelectric(argv[3], &naddr, &ntype);

		/* call Electric */
		ret = setval(addr, type, argv[2], naddr, ntype);
	}
	if (argc == 5)
	{
		if (namesame(argv[4], "displayable") == 0) ntype |= VDISPLAY;
	}

	/* convert result */
	if (ret != NOVARIABLE)
	{
		retval = tcl_converttotcl(ret->addr, ret->type);
		Tcl_AppendResult(interp, retval, (char *)NULL);
	}
	return TCL_OK;
}

int tcl_setind(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	INTBIG addr, type, ret, aindex, naddr, ntype;

	/* get input */
	if (argc != 5)
	{
		Tcl_AppendResult(interp, "Usage: setind OBJECT QUAL INDEX NEWVALUE", (char *)NULL);
		return TCL_ERROR;
	}
	tcl_converttoanyelectric(argv[1], &addr, &type);
	aindex = myatoi(argv[3]);
	tcl_converttoanyelectric(argv[4], &naddr, &ntype);

	/* call Electric */
	ret = setind(addr, type, argv[2], aindex, naddr);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_delval(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	INTBIG addr, type, ret;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: delval OBJECT QUAL", (char *)NULL);
		return TCL_ERROR;
	}
	tcl_converttoanyelectric(argv[1], &addr, &type);

	/* call Electric */
	ret = delval(addr, type, argv[2]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_initsearch(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER INTBIG lx, hx, ly, hy, sea;
	REGISTER NODEPROTO *np;

	/* get input */
	if (argc != 6)
	{
		Tcl_AppendResult(interp, "Usage: initsearch LX HX LY HY FACET", (char *)NULL);
		return TCL_ERROR;
	}
	lx = myatoi(argv[1]);
	hx = myatoi(argv[2]);
	ly = myatoi(argv[3]);
	hy = myatoi(argv[4]);
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[5], VNODEPROTO, "fifth");
	if (np == NONODEPROTO) return TCL_ERROR;

	/* call Electric */
	sea = initsearch(lx, hx, ly, hy, np);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(sea, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_nextobject(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER INTBIG sea;
	REGISTER GEOM *g;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: nextobject SEARCH", (char *)NULL);
		return TCL_ERROR;
	}
	sea = myatoi(argv[1]);

	/* call Electric */
	g = nextobject(sea);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)g, VGEOM), (char *) NULL);
	return TCL_OK;
}

/****************************** AID ROUTINES ******************************/

int tcl_getaid(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER AIDENTRY *aid;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: getaid NAME", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	aid = getaid(argv[1]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)aid, VAID), (char *) NULL);
	return TCL_OK;
}

int tcl_maxaid(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	/* get input */
	if (argc != 1)
	{
		Tcl_AppendResult(interp, "Usage: maxaid", (char *)NULL);
		return TCL_ERROR;
	}

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(el_maxaid, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_indexaid(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER AIDENTRY *aid;
	REGISTER INTBIG aindex;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: indexaid INDEX", (char *)NULL);
		return TCL_ERROR;
	}
	aindex = myatoi(argv[1]);

	/* call Electric */
	if (aindex < 0 || aindex >= el_maxaid) aid = NOAID; else aid = &el_aids[aindex];

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)aid, VAID), (char *) NULL);
	return TCL_OK;
}

int tcl_aidturnon(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER AIDENTRY *aid;
	REGISTER INTBIG nocatchup;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: aidturnon AID NOCATCHUP", (char *)NULL);
		return TCL_ERROR;
	}
	aid = (AIDENTRY *)tcl_converttowarnedelectric(argv[1], VAID, "first");
	if (aid == NOAID) return TCL_ERROR;
	nocatchup = myatoi(argv[2]);

	/* call Electric */
	aidturnon(aid, (INTSML)nocatchup);
	return TCL_OK;
}

int tcl_aidturnoff(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER AIDENTRY *aid;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: aidturnoff AID", (char *)NULL);
		return TCL_ERROR;
	}
	aid = (AIDENTRY *)tcl_converttowarnedelectric(argv[1], VAID, "first");
	if (aid == NOAID) return TCL_ERROR;

	/* call Electric */
	aidturnoff(aid, 1);
	return TCL_OK;
}

/****************************** LIBRARY ROUTINES ******************************/

int tcl_getlibrary(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER LIBRARY *lib;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: getlibrary NAME", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	lib = getlibrary(argv[1]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)lib, VLIBRARY), (char *) NULL);
	return TCL_OK;
}

int tcl_newlibrary(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER LIBRARY *lib;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: newlibrary NAME FILE", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	lib = newlibrary(argv[1], argv[2]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)lib, VLIBRARY), (char *) NULL);
	return TCL_OK;
}

int tcl_killlibrary(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER LIBRARY *lib;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: killlibrary LIBRARY", (char *)NULL);
		return TCL_ERROR;
	}
	lib = (LIBRARY *)tcl_converttowarnedelectric(argv[1], VLIBRARY, "first");
	if (lib == NOLIBRARY) return TCL_ERROR;

	/* call Electric */
	killlibrary(lib);
	return TCL_OK;
}

int tcl_eraselibrary(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER LIBRARY *lib;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: eraselibrary LIBRARY", (char *)NULL);
		return TCL_ERROR;
	}
	lib = (LIBRARY *)tcl_converttowarnedelectric(argv[1], VLIBRARY, "first");
	if (lib == NOLIBRARY) return TCL_ERROR;

	/* call Electric */
	eraselibrary(lib);
	return TCL_OK;
}

int tcl_selectlibrary(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER LIBRARY *lib;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: selectlibrary LIBRARY", (char *)NULL);
		return TCL_ERROR;
	}
	lib = (LIBRARY *)tcl_converttowarnedelectric(argv[1], VLIBRARY, "first");
	if (lib == NOLIBRARY) return TCL_ERROR;

	/* call Electric */
	selectlibrary(lib);
	return TCL_OK;
}

/****************************** NODEPROTO ROUTINES ******************************/

int tcl_getnodeproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEPROTO *np;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: getnodeproto NAME", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	np = getnodeproto(argv[1]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)np, VNODEPROTO), (char *) NULL);
	return TCL_OK;
}

int tcl_newnodeproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER LIBRARY *lib;
	REGISTER NODEPROTO *np;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: newnodeproto NAME LIBRARY", (char *)NULL);
		return TCL_ERROR;
	}
	lib = (LIBRARY *)tcl_converttowarnedelectric(argv[2], VLIBRARY, "second");
	if (lib == NOLIBRARY) return TCL_ERROR;

	/* call Electric */
	np = newnodeproto(argv[1], lib);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)np, VNODEPROTO), (char *) NULL);
	return TCL_OK;
}

int tcl_killnodeproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEPROTO *np;
	REGISTER INTBIG ret;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: killnodeproto FACET", (char *)NULL);
		return TCL_ERROR;
	}
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[1], VNODEPROTO, "first");
	if (np == NONODEPROTO) return TCL_ERROR;

	/* call Electric */
	ret = killnodeproto(np);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_copynodeproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER LIBRARY *lib;
	REGISTER NODEPROTO *np, *nnp;

	/* get input */
	if (argc != 4)
	{
		Tcl_AppendResult(interp, "Usage: copynodeproto FACET LIBRARY NAME", (char *)NULL);
		return TCL_ERROR;
	}
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[1], VNODEPROTO, "first");
	lib = (LIBRARY *)tcl_converttowarnedelectric(argv[2], VLIBRARY, "second");
	if (lib == NOLIBRARY || np == NONODEPROTO) return TCL_ERROR;

	/* call Electric */
	nnp = copynodeproto(np, lib, argv[3]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)nnp, VNODEPROTO), (char *) NULL);
	return TCL_OK;
}

int tcl_iconview(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEPROTO *np, *inp;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: iconview FACET", (char *)NULL);
		return TCL_ERROR;
	}
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[1], VNODEPROTO, "first");
	if (np == NONODEPROTO) return TCL_ERROR;

	/* call Electric */
	inp = iconview(np);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)inp, VNODEPROTO), (char *) NULL);
	return TCL_OK;
}

int tcl_contentsview(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEPROTO *np, *cnp;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: contentsview FACET", (char *)NULL);
		return TCL_ERROR;
	}
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[1], VNODEPROTO, "first");
	if (np == NONODEPROTO) return TCL_ERROR;

	/* call Electric */
	cnp = contentsview(np);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)cnp, VNODEPROTO), (char *) NULL);
	return TCL_OK;
}

/****************************** NODEINST ROUTINES ******************************/

int tcl_newnodeinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np, *facet;
	REGISTER INTBIG lx, hx, ly, hy, rot, tran;

	/* get input */
	if (argc != 9)
	{
		Tcl_AppendResult(interp, "Usage: newnodeinst PROTO LX HX LY HY TRAN ROT FACET", (char *)NULL);
		return TCL_ERROR;
	}
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[1], VNODEPROTO, "first");
	lx = myatoi(argv[2]);
	hx = myatoi(argv[3]);
	ly = myatoi(argv[4]);
	hy = myatoi(argv[5]);
	tran = myatoi(argv[6]);
	rot = myatoi(argv[7]);
	facet = (NODEPROTO *)tcl_converttowarnedelectric(argv[8], VNODEPROTO, "eighth");
	if (np == NONODEPROTO || facet == NONODEPROTO) return TCL_ERROR;

	/* call Electric */
	ni = newnodeinst(np, lx, hx, ly, hy, (INTSML)tran, (INTSML)rot, facet);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)ni, VNODEINST), (char *) NULL);
	return TCL_OK;
}

int tcl_modifynodeinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEINST *ni;
	REGISTER INTBIG dlx, dly, dhx, dhy, drot, dtran;

	/* get input */
	if (argc != 8)
	{
		Tcl_AppendResult(interp, "Usage: modifynodeinst NODE dLX dLY dHX dHY dROT dTRAN", (char *)NULL);
		return TCL_ERROR;
	}
	ni = (NODEINST *)tcl_converttowarnedelectric(argv[1], VNODEINST, "first");
	dlx = myatoi(argv[2]);
	dly = myatoi(argv[3]);
	dhx = myatoi(argv[4]);
	dhy = myatoi(argv[5]);
	drot = myatoi(argv[6]);
	dtran = myatoi(argv[7]);
	if (ni == NONODEINST) return TCL_ERROR;

	/* call Electric */
	modifynodeinst(ni, dlx, dly, dhx, dhy, (INTSML)drot, (INTSML)dtran);
	return TCL_OK;
}

int tcl_killnodeinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEINST *ni;
	REGISTER INTBIG ret;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: killnodeinst NODE", (char *)NULL);
		return TCL_ERROR;
	}
	ni = (NODEINST *)tcl_converttowarnedelectric(argv[1], VNODEINST, "first");
	if (ni == NONODEINST) return TCL_ERROR;

	/* call Electric */
	ret = killnodeinst(ni);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_replacenodeinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEPROTO *np;
	REGISTER NODEINST *ni, *nni;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: replacenodeinst NODE NEWPROTO", (char *)NULL);
		return TCL_ERROR;
	}
	ni = (NODEINST *)tcl_converttowarnedelectric(argv[1], VNODEINST, "first");
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[2], VNODEPROTO, "second");
	if (ni == NONODEINST || np == NONODEPROTO) return TCL_ERROR;

	/* call Electric */
	nni = replacenodeinst(ni, np, 0);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)nni, VNODEINST), (char *) NULL);
	return TCL_OK;
}

int tcl_nodefunction(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEINST *ni;
	char *edummy;
	REGISTER INTBIG fun;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: nodefunction NODE", (char *)NULL);
		return TCL_ERROR;
	}
	ni = (NODEINST *)tcl_converttowarnedelectric(argv[1], VNODEINST, "first");
	if (ni == NONODEINST) return TCL_ERROR;

	/* call Electric */
	fun = nodefunction(ni, &edummy);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(fun, VINTEGER), (char *) NULL);
	return TCL_OK;
}

/****************************** ARCINST ROUTINES ******************************/

int tcl_newarcinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER ARCINST *ai;
	REGISTER NODEINST *ni1, *ni2;
	REGISTER PORTPROTO *pp1, *pp2;
	REGISTER ARCPROTO *ap;
	REGISTER NODEPROTO *np;
	REGISTER INTBIG w, bits, x1, y1, x2, y2;

	/* get input */
	if (argc != 13)
	{
		Tcl_AppendResult(interp, "Usage: newarcinst PROTO WID BITS NODE1 PORT1 X1 Y1 NODE2 PORT2 X2 Y2 FACET",
			(char *)NULL);
		return TCL_ERROR;
	}
	ap = (ARCPROTO *)tcl_converttowarnedelectric(argv[1], VARCPROTO, "first");
	w = myatoi(argv[2]);
	bits = myatoi(argv[3]);
	ni1 = (NODEINST *)tcl_converttowarnedelectric(argv[4], VNODEINST, "fourth");
	pp1 = (PORTPROTO *)tcl_converttowarnedelectric(argv[5], VPORTPROTO, "fifth");
	x1 = myatoi(argv[6]);
	y1 = myatoi(argv[7]);
	ni2 = (NODEINST *)tcl_converttowarnedelectric(argv[8], VNODEINST, "eighth");
	pp2 = (PORTPROTO *)tcl_converttowarnedelectric(argv[9], VPORTPROTO, "ninth");
	x2 = myatoi(argv[10]);
	y2 = myatoi(argv[11]);
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[12], VNODEPROTO, "twelvth");
	if (ap == NOARCPROTO || ni1 == NONODEINST || pp1 == NOPORTPROTO || ni2 == NONODEINST ||
		pp2 == NOPORTPROTO || np == NONODEPROTO) return TCL_ERROR;

	/* call Electric */
	ai = newarcinst(ap, w, bits, ni1, pp1, x1, y1, ni2, pp2, x2, y2, np);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)ai, VARCINST), (char *) NULL);
	return TCL_OK;
}

int tcl_modifyarcinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER ARCINST *ai;
	REGISTER INTBIG dw, dx1, dy1, dx2, dy2;
	REGISTER INTBIG ret;

	/* get input */
	if (argc != 7)
	{
		Tcl_AppendResult(interp, "Usage: modifyarcinst ARC dW dX1 dY1 dX2 dY2", (char *)NULL);
		return TCL_ERROR;
	}
	ai = (ARCINST *)tcl_converttowarnedelectric(argv[1], VARCINST, "first");
	if (ai == NOARCINST) return TCL_ERROR;
	dw = myatoi(argv[2]);
	dx1 = myatoi(argv[3]);
	dy1 = myatoi(argv[4]);
	dx2 = myatoi(argv[5]);
	dy2 = myatoi(argv[6]);

	/* call Electric */
	ret = modifyarcinst(ai, dw, dx1, dy1, dx2, dy2);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_killarcinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER ARCINST *ai;
	REGISTER INTBIG ret;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: killarcinst ARC", (char *)NULL);
		return TCL_ERROR;
	}
	ai = (ARCINST *)tcl_converttowarnedelectric(argv[1], VARCINST, "first");
	if (ai == NOARCINST) return TCL_ERROR;

	/* call Electric */
	ret = killarcinst(ai);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_replacearcinst(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER ARCPROTO *ap;
	REGISTER ARCINST *ai, *nai;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: replacearcinst ARC NEWPROTO", (char *)NULL);
		return TCL_ERROR;
	}
	ai = (ARCINST *)tcl_converttowarnedelectric(argv[1], VARCINST, "first");
	ap = (ARCPROTO *)tcl_converttowarnedelectric(argv[2], VARCPROTO, "second");
	if (ai == NOARCINST || ap == NOARCPROTO) return TCL_ERROR;

	/* call Electric */
	nai = replacearcinst(ai, ap);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)nai, VARCINST), (char *) NULL);
	return TCL_OK;
}

/****************************** PORTPROTO ROUTINES ******************************/

int tcl_newportproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEPROTO *np;
	REGISTER NODEINST *ni;
	REGISTER PORTPROTO *pp, *npp;

	/* get input */
	if (argc != 5)
	{
		Tcl_AppendResult(interp, "Usage: newportproto FACET NODE PORT NAME", (char *)NULL);
		return TCL_ERROR;
	}
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[1], VNODEPROTO, "first");
	ni = (NODEINST *)tcl_converttowarnedelectric(argv[2], VNODEINST, "second");
	pp = (PORTPROTO *)tcl_converttowarnedelectric(argv[3], VPORTPROTO, "third");
	if (np == NONODEPROTO || ni == NONODEINST || pp == NOPORTPROTO) return TCL_ERROR;

	/* call Electric */
	npp = newportproto(np, ni, pp, argv[4]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)npp, VPORTPROTO), (char *) NULL);
	return TCL_OK;
}

int tcl_portposition(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEINST *ni;
	REGISTER PORTPROTO *pp;
	INTBIG x, y;
	char line[50];

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: portposition NODE PORT", (char *)NULL);
		return TCL_ERROR;
	}
	ni = (NODEINST *)tcl_converttowarnedelectric(argv[1], VNODEINST, "first");
	pp = (PORTPROTO *)tcl_converttowarnedelectric(argv[2], VPORTPROTO, "second");
	if (ni == NONODEINST || pp == NOPORTPROTO) return TCL_ERROR;

	/* call Electric */
	portposition(ni, pp, &x, &y);

	/* convert result */
	(void)sprintf(line, "%ld %ld", x, y);
	Tcl_AppendResult(interp, line, (char *) NULL);
	return TCL_OK;
}

int tcl_getportproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEPROTO *np;
	REGISTER PORTPROTO *pp;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: getportproto FACET NAME", (char *)NULL);
		return TCL_ERROR;
	}
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[1], VNODEPROTO, "first");
	if (np == NONODEPROTO) return TCL_ERROR;

	/* call Electric */
	pp = getportproto(np, argv[2]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)pp, VPORTPROTO), (char *) NULL);
	return TCL_OK;
}

int tcl_killportproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEPROTO *np;
	REGISTER PORTPROTO *pp;
	REGISTER INTBIG ret;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: killportproto FACET PORT", (char *)NULL);
		return TCL_ERROR;
	}
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[1], VNODEPROTO, "first");
	pp = (PORTPROTO *)tcl_converttowarnedelectric(argv[2], VPORTPROTO, "second");
	if (np == NONODEPROTO || pp == NOPORTPROTO) return TCL_ERROR;

	/* call Electric */
	ret = killportproto(np, pp);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_moveportproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEPROTO *np;
	REGISTER PORTPROTO *opp, *npp;
	REGISTER NODEINST *nni;
	REGISTER INTBIG ret;

	/* get input */
	if (argc != 5)
	{
		Tcl_AppendResult(interp, "Usage: moveportproto FACET PORT NEWNODE NEWPORT", (char *)NULL);
		return TCL_ERROR;
	}
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[1], VNODEPROTO, "first");
	opp = (PORTPROTO *)tcl_converttowarnedelectric(argv[2], VPORTPROTO, "second");
	nni = (NODEINST *)tcl_converttowarnedelectric(argv[3], VNODEINST, "third");
	npp = (PORTPROTO *)tcl_converttowarnedelectric(argv[4], VPORTPROTO, "fourth");
	if (np == NONODEPROTO || opp == NOPORTPROTO || nni == NONODEINST || npp == NOPORTPROTO) return TCL_ERROR;

	/* call Electric */
	ret = moveportproto(np, opp, nni, npp);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

/*************************** CHANGE CONTROL ROUTINES ***************************/

int tcl_undoabatch(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	AIDENTRY *aid;

	if (argc != 1)
	{
		Tcl_AppendResult(interp, "Usage: undoabatch", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	if (undoabatch(&aid) == 0)
	{
		Tcl_AppendResult(interp, _("Error during undoabatch"), (char *)NULL);
		return TCL_ERROR;
	}

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)aid, VAID), (char *) NULL);
	return TCL_OK;
}

int tcl_noundoallowed(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	if (argc != 1)
	{
		Tcl_AppendResult(interp, "Usage: noundoallowed", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	noundoallowed();
	return TCL_OK;
}

/****************************** VIEW ROUTINES ******************************/

int tcl_getview(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER VIEW *v;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: getview NAME", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	v = getview(argv[1]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)v, VVIEW), (char *) NULL);
	return TCL_OK;
}

int tcl_newview(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER VIEW *v;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: newview NAME SNAME", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	v = newview(argv[1], argv[2]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)v, VVIEW), (char *) NULL);
	return TCL_OK;
}

int tcl_killview(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER VIEW *v;
	REGISTER INTBIG ret;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: killview VIEW", (char *)NULL);
		return TCL_ERROR;
	}
	v = (VIEW *)tcl_converttowarnedelectric(argv[1], VVIEW, "first");
	if (v == NOVIEW) return TCL_ERROR;

	/* call Electric */
	ret = killview(v);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

/*************************** MISCELLANEOUS ROUTINES ***************************/

int tcl_tellaid(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER AIDENTRY *aid;
	int aidArgc, ret;
	char **aidArgv;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: tellaid AID {PARAMETERS}", (char *)NULL);
		return TCL_ERROR;
	}
	aid = (AIDENTRY *)tcl_converttowarnedelectric(argv[1], VAID, "first");
	ret = Tcl_SplitList(interp, argv[2], &aidArgc, &aidArgv);
	if (aid == NOAID || ret != TCL_OK) return TCL_ERROR;

	/* call Electric */
	ret = tellaid(aid, (INTSML)aidArgc, aidArgv);
	ckfree((char *)aidArgv);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_askaid(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER AIDENTRY *aid;
	INTBIG addr, addr0, addr1, addr2, addr3, type;
	int aidArgc, ret, i, argcS, first;
	char **aidArgv, **argvS, *command;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: askaid AID {PARAMETERS}", (char *)NULL);
		return TCL_ERROR;
	}
	aid = (AIDENTRY *)tcl_converttowarnedelectric(argv[1], VAID, "first");
	ret = Tcl_SplitList(interp, argv[2], &aidArgc, &aidArgv);
	if (aid == NOAID || ret != TCL_OK) return TCL_ERROR;

	command = aidArgv[0];
	for (i=0; i<aidArgc; i++) aidArgv[i] = aidArgv[i+1];
	aidArgv[aidArgc] = NULL;
	aidArgc--;

	/* call Electric */
	if (aidArgc == 0)
	{
		ret = askaid(aid, command);
	} else if (aidArgc == 1)
	{
		/* special case for user_aid: show-multiple command */
		if (strcmp(command, "show-multiple") == 0)
		{
			ret = Tcl_SplitList(interp, aidArgv[0], &argcS, &argvS);
			if (ret == TCL_OK)
			{
				(void)initinfstr();
				first = 0;
				for (i=0; i<argcS; i++)
				{
					if (first != 0) (void)addtoinfstr('\n');
					first++;
					tcl_converttoanyelectric(argvS[i], &addr, &type);
					(void)formatinfstr("FACET=%s FROM=0%lo;-1;0",
						describenodeproto(((GEOM *)addr)->entryaddr.ni->parent), addr);
				}
				ret = askaid(aid, command, (INTBIG)returninfstr());
			}
		} else
		{
			tcl_converttoanyelectric(aidArgv[0], &addr0, &type);
			ret = askaid(aid, command, addr0);
		}
	} else if (aidArgc == 2)
	{
		tcl_converttoanyelectric(aidArgv[0], &addr0, &type);
		tcl_converttoanyelectric(aidArgv[1], &addr1, &type);
		ret = askaid(aid, command, addr0, addr1);
	} else if (aidArgc == 3)
	{
		tcl_converttoanyelectric(aidArgv[0], &addr0, &type);
		tcl_converttoanyelectric(aidArgv[1], &addr1, &type);
		tcl_converttoanyelectric(aidArgv[2], &addr2, &type);
		ret = askaid(aid, command, addr0, addr1, addr2);
	} else if (aidArgc == 4)
	{
		tcl_converttoanyelectric(aidArgv[0], &addr0, &type);
		tcl_converttoanyelectric(aidArgv[1], &addr1, &type);
		tcl_converttoanyelectric(aidArgv[2], &addr2, &type);
		tcl_converttoanyelectric(aidArgv[3], &addr3, &type);
		ret = askaid(aid, command, addr0, addr1, addr2, addr3);
	}
	ckfree((char *)aidArgv);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl(ret, VINTEGER), (char *) NULL);
	return TCL_OK;
}

int tcl_getarcproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER ARCPROTO *ap;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: getarcproto NAME", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	ap = getarcproto(argv[1]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)ap, VARCPROTO), (char *) NULL);
	return TCL_OK;
}

int tcl_getcell(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER CELL *cell;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: getcell NAME", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	cell = getcell(argv[1]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)cell, VCELL), (char *) NULL);
	return TCL_OK;
}

int tcl_gettechnology(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER TECHNOLOGY *tech;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: gettechnology NAME", (char *)NULL);
		return TCL_ERROR;
	}

	/* call Electric */
	tech = gettechnology(argv[1]);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)tech, VTECHNOLOGY), (char *) NULL);
	return TCL_OK;
}

int tcl_getpinproto(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NODEPROTO *np;
	REGISTER ARCPROTO *ap;

	/* get input */
	if (argc != 2)
	{
		Tcl_AppendResult(interp, "Usage: getpinproto ARCPROTO", (char *)NULL);
		return TCL_ERROR;
	}
	ap = (ARCPROTO *)tcl_converttowarnedelectric(argv[1], VARCPROTO, "first");
	if (ap == NOARCPROTO) return TCL_ERROR;

	/* call Electric */
	np = getpinproto(ap);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)np, VNODEPROTO), (char *) NULL);
	return TCL_OK;
}

int tcl_getnetwork(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER NETWORK *net;
	REGISTER NODEPROTO *np;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: getnetwork NAME FACET", (char *)NULL);
		return TCL_ERROR;
	}
	np = (NODEPROTO *)tcl_converttowarnedelectric(argv[2], VNODEPROTO, "second");
	if (np == NONODEPROTO) return TCL_ERROR;

	/* call Electric */
	net = getnetwork(argv[1], np);

	/* convert result */
	Tcl_AppendResult(interp, tcl_converttotcl((INTBIG)net, VNETWORK), (char *) NULL);
	return TCL_OK;
}

/*************************** MENU CONTROL ROUTINES ***************************/

#ifdef USETK

static INTSML              tcl_pulldownmenucount;	/* number of pulldown menus */
static TkMenuReferences **tcl_pulldownmenus;		/* the current pulldown menu handles */
static POPUPMENU        **tcl_pulldowns;			/* the current pulldown menus */

#ifdef MACOS
#  include <Menus.h>
   MenuHandle tkAppleMenu = 0;						/* Handle to Apple menu; needed by menubar code */
   MenuHandle tkFileMenu  = 0;
   MenuHandle tkEditMenu  = 0;
#endif

INTSML tcl_loadtclmsg(char*, ...);
INTSML tcl_pulldownindex(POPUPMENU *pm, char *prevmenu);
TkMenuReferences *tcl_makepdmenu(POPUPMENU *pm, INTSML value, char *prevmenu);
void tcl_nativemenurename(POPUPMENU*, INTSML);

/*
 * Routine to initialize TK menus.
 */
void tcl_nativemenuinitialize(void)
{
	tcl_pulldownmenucount = 0;
}

/*
 * Routine to redraw entry "aindex" of popupmenu "pm" because it changed.
 */
void tcl_nativemenurename(POPUPMENU *pm, INTSML aindex)
{
	INTSML i;
	char line[100], *menuName, *slashpt, *anglept, *command, *pt,
		*acceleratorstring, *acceleratorprefix;
	USERCOM *uc;

	for(i=0; i<tcl_pulldownmenucount; i++)
		if (tcl_pulldowns[i] == pm) break;
	if (i >= tcl_pulldownmenucount) return;
	uc = pm->list[aindex].response;
	menuName = Tk_PathName(tcl_pulldownmenus[i]->menuPtr->tkwin);
	(void)strcpy(line, pm->list[aindex].attribute);

	/* if this command is another menu, just change the label */
	if (uc->menu != NOPOPUPMENU)
	{
		(void)tcl_loadtclmsg("%s entryconfigure %d -label \"%s\"", menuName, aindex, line);
		return;
	}

	/* rewrite the entry */
	slashpt = anglept = 0;
	command = line;
	for(pt = command; *pt != 0; pt++)
	{
		if (*pt == '/') slashpt = pt;
		if (*pt == '<') anglept = pt;
	}

	/* change the accelerator key */
	if (slashpt == 0)
	{
		if (tcl_loadtclmsg("%s entryconfigure %d -accelerator \"\"",
			menuName, aindex) != 0) return;
	} else
	{
		getacceleratorstrings(&acceleratorstring, &acceleratorprefix);
		*slashpt++ = 0;
		if (tcl_loadtclmsg("%s entryconfigure %d -accelerator \"%s+%c\"",
			menuName, aindex, acceleratorstring, *slashpt) != 0) return;
#ifdef WIN32
		if (tcl_loadtclmsg("bind .graphics <%s-KeyPress-%c> \"domenu %d %d\"",
			acceleratorstring, tolower(*slashpt), i, aindex+1) != 0) return;
#endif
	}

	/* change the check box */
	if (anglept != 0)
	{
		*anglept = 0;
		if (*command == '>')
		{
			command++;
			if (tcl_loadtclmsg("%s entryconfigure %d -variable etrue",
				menuName, aindex) != 0) return;
		} else
		{
			if (tcl_loadtclmsg("%s entryconfigure %d -variable efalse",
				menuName, aindex) != 0) return;
		}
		if (tcl_loadtclmsg("set etrue 1") != 0) return;
		if (tcl_loadtclmsg("set efalse 0") != 0) return;
	}

	/* change the menu label */
	if (tcl_loadtclmsg("%s entryconfigure %d -label \"%s\"",
		menuName, aindex, command) != 0) return;

	/* change the menu command */
	if (tcl_loadtclmsg("%s entryconfigure %d -command \"domenu %d %d\"",
		menuName, aindex, i, aindex+1) != 0) return;
}

/*
 * Routine to establish the "count" pulldown menu names in "par" as the pulldown menu bar.
 * Returns nonzero on error.
 */
INTSML tcl_nativemenuload(INTSML count, char *par[])
{
	REGISTER INTSML i, menuindex;
	REGISTER POPUPMENU *pm;
	POPUPMENU *pulls[25];

	if (tcl_loadtclmsg("menu .menus -type menubar") != 0) return(1);
	if (tcl_loadtclmsg("set etrue 1") != 0) return(1);
	if (tcl_loadtclmsg("set efalse 0") != 0) return(1);

#ifdef MACOS
	if (tcl_loadtclmsg("menu .menus.apple -tearoff 0") != 0) return(1);
	if (tcl_loadtclmsg(".menus.apple add command -label \"About Electric...\" -command \"domenu -1 -1\"") != 0) return(1);
	if (tcl_loadtclmsg(".menus add cascade -menu .menus.apple") != 0) return(1);
#endif

	/* build the pulldown menu bar */
	for(i=0; i<count; i++)
	{
		pm = us_getpopupmenu(par[i]);
		if (pm == NOPOPUPMENU) continue;
		pulls[i] = pm;
		menuindex = tcl_pulldownindex(pm, "");
		if (menuindex < 0) return(1);
	}
	return(0);
}

/*
 * Routine to create a pulldown menu from popup menu "pm".  This is a
 * toplevel menu if "prevmenu" is empty, otherwise it is the name of
 * the higher-level menu.  Returns an index to the table of pulldown menus (-1 on error).
 */
INTSML tcl_pulldownindex(POPUPMENU *pm, char *prevmenu)
{
	REGISTER INTSML i, aindex;
	TkMenuReferences **newpulldownmenus;
	POPUPMENU **newpulldowns;

	/* see if it is in the list already */
	for(i=0; i<tcl_pulldownmenucount; i++)
		if (tcl_pulldowns[i] == pm) return(i);

	/* allocate new space with one more */
	newpulldownmenus = (TkMenuReferences **)emalloc((tcl_pulldownmenucount+1) *
		(sizeof (TkMenuReferences *)), us_aid->cluster);
	if (newpulldownmenus == 0) return(-1);
	newpulldowns = (POPUPMENU **)emalloc((tcl_pulldownmenucount+1) *
		(sizeof (POPUPMENU *)), us_aid->cluster);
	if (newpulldowns == 0) return(-1);

	/* copy former arrays then delete them */
	for(i=0; i<tcl_pulldownmenucount; i++)
	{
		newpulldownmenus[i] = tcl_pulldownmenus[i];
		newpulldowns[i] = tcl_pulldowns[i];
	}
	if (tcl_pulldownmenucount != 0)
	{
		efree((char *)tcl_pulldownmenus);
		efree((char *)tcl_pulldowns);
	}
	tcl_pulldownmenus = newpulldownmenus;
	tcl_pulldowns = newpulldowns;

	aindex = tcl_pulldownmenucount++;
	tcl_pulldownmenus[aindex] = tcl_makepdmenu(pm, aindex, prevmenu);
	if (tcl_pulldownmenus[aindex] == 0) return(-1);
	tcl_pulldowns[aindex] = pm;
	return(aindex);
}

/*
 * Routine to create pulldown menu number "value" from the popup menu in "pm" and return
 * the menu handle.  If "prevmenu" is not null, this is a submenu of that one.
 */
TkMenuReferences *tcl_makepdmenu(POPUPMENU *pm, INTSML value, char *prevmenu)
{
	REGISTER INTSML i, submenuindex;
	char myline[256], *pt, *anglept, *command, cmdkey;
	REGISTER USERCOM *uc;
	REGISTER POPUPMENUITEM *mi;
	TkMenuReferences *thismenu;
	char thismenuname[100], *acceleratorstring, *acceleratorprefix;

	strcpy(&myline[1], pm->header);
	myline[0] = strlen(pm->header);
	sprintf(thismenuname, "%s.%d", prevmenu, value);
	if (tcl_loadtclmsg("menu .menus%s -tearoff 0", thismenuname) != 0) return(0);
	sprintf(myline, ".menus%s", thismenuname);
	thismenu = TkFindMenuReferences(tcl_interp, myline);
	if (*prevmenu == 0)
	{
		if (tcl_loadtclmsg(".menus add cascade -label \"%s\" -menu .menus%s",	/*  -underline 0 */
			pm->header, thismenuname) != 0) return(0);
	}

	/* build the actual menu */
	for(i=0; i<pm->total; i++)
	{
		mi = &pm->list[i];
		uc = mi->response;
		if (uc->active < 0)
		{
			if (tcl_loadtclmsg(".menus%s add separator", thismenuname) != 0) return(0);
			continue;
		}

		/* see if this command is another menu */
		if (uc->menu != NOPOPUPMENU)
		{
			sprintf(myline, "%s.%d", prevmenu, value);
			submenuindex = tcl_pulldownindex(uc->menu, myline);
			if (submenuindex < 0) continue;
			if (tcl_loadtclmsg(".menus%s add cascade -label \"%s\" -menu .menus%s.%d",
				thismenuname, mi->attribute, thismenuname, submenuindex) != 0) return(0);
			continue;
		}

		/* insert command title */
		anglept = 0;
		cmdkey = 0;
		strcpy(myline, mi->attribute);
		command = myline;
		for(pt = command; *pt != 0; pt++)
		{
			if (*pt == '/') { *pt++ = 0;   cmdkey = *pt; }
			if (*pt == '<') anglept = pt;
		}
		(void)initinfstr();
		(void)addstringtoinfstr(".menus");
		(void)addstringtoinfstr(thismenuname);
		(void)addstringtoinfstr(" add");
		if (anglept == 0) (void)addstringtoinfstr(" command"); else
		{
			*anglept = 0;
			(void)addstringtoinfstr(" check");
			if (*command != '>') (void)addstringtoinfstr(" -variable efalse"); else
			{
				command++;
				(void)addstringtoinfstr(" -variable etrue");
			}
		}
		(void)addstringtoinfstr(" -label \"");
		(void)addstringtoinfstr(command);
		(void)addtoinfstr('"');
		sprintf(myline, " -command \"domenu %d %d\"", value, i+1);
		(void)addstringtoinfstr(myline);
		if (cmdkey != 0)
		{
			getacceleratorstrings(&acceleratorstring, &acceleratorprefix);
			sprintf(myline, " -accelerator \"%s+%c\"", acceleratorstring, cmdkey);
			(void)addstringtoinfstr(myline);
#ifdef WIN32
			if (tcl_loadtclmsg("bind .graphics <%s-KeyPress-%c> \"domenu %d %d\"",
				acceleratorstring, tolower(cmdkey), value, i+1) != 0) return(0);
#endif
		}
		if (tcl_loadtclmsg("%s", returninfstr()) != 0) return(0);
	}
	return(thismenu);
}

/*
 * Routine to load a string into the interpreter.  Returns nonzero on error.
 */
INTSML tcl_loadtclmsg(char *s, ...)
{
	va_list ap;
	char line[256];

	var_start(ap, s);
	(void)vsprintf(line, s, ap);
	va_end(ap);

	if (Tcl_VarEval(tcl_interp, line, 0) == TCL_ERROR)
	{
		ttyputerr(_("'%s' gave error '%s'"), line, tcl_interp->result);
		return(1);
	}
	return(0);
}

/*
 * Routine called for the TCL command "domenu" to execute the menu entry.
 */
int tcl_domenu(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
{
	REGISTER INTBIG menu, entry;
	INTSML j;
	POPUPMENU *pm;

	/* get input */
	if (argc != 3)
	{
		Tcl_AppendResult(interp, "Usage: domenu COLUMN ROW", (char *)NULL);
		return TCL_ERROR;
	}
	menu = atoi(argv[1]);
	entry = atoi(argv[2]);

#ifdef MACOS
	if (menu == -1 && entry == -1)
	{
		void gra_applemenu(INTSML);

		gra_applemenu(1);
		return(TCL_OK);
	}
#endif

	/* execute the menu entry */
	if (menu >= 0 && menu < tcl_pulldownmenucount)
	{
		pm = tcl_pulldowns[menu];
		j = abs(entry) - 1;
		if (j >= 0 && j < pm->total)
		{
			us_state |= DIDINPUT;
			us_state &= ~GOTXY;
#ifdef MACOS
			setdefaultcursortype(NULLCURSOR);
#endif
			us_forceeditchanges();
			us_execute(pm->list[j].response, us_aid->aidstate&ECHOBIND, 1, 1);
			setactivity(pm->list[j].attribute);
		}
	}
	return TCL_OK;
}

#endif  /* USETK */

#endif  /* LANGTCL - at top */
