/*
 * Electric(tm) VLSI Design System
 *
 * File: usrstatus.c
 * User interface tool: status display routines
 * 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 "global.h"
#include "egraphics.h"
#include "usr.h"
#include "drc.h"
#include "network.h"
#include "efunction.h"
#include "tecgen.h"
#include "tecschem.h"
#include "usrdiacom.h"

static INTBIG *us_layer_letter_data = 0;	/* cache for "us_layerletters" */

/* working memory for "us_uniqueportname()" */
static char *us_uniqueretstr = NOSTRING;

/* structure for "usrbits" information parsing */
struct explanation
{
	INTBIG bit;			/* bit mask in word */
	INTBIG shift;		/* shift of bit mask (-1 if a flag) */
	char  *name;		/* name of the field */
};

/* prototypes for local routines */
static void us_addobjects(NODEPROTO*);
static BOOLEAN us_wildmatch(char*, char*);
static void us_explaintool(INTBIG, struct explanation[]);
static void us_drawarcmenu(ARCPROTO *ap);
static BOOLEAN us_isuniquename(char *name, NODEPROTO *facet, INTBIG type, void *exclude);

static STATUSFIELD *us_clearstatuslines[3] = {0, 0, 0};

/*
 * Routine to free all memory associated with this module.
 */
void us_freestatusmemory(void)
{
	REGISTER INTBIG i, lines;

	if (us_layer_letter_data != 0) efree((char *)us_layer_letter_data);
	if (us_statusalign != 0) ttyfreestatusfield(us_statusalign);
	if (us_statusangle != 0) ttyfreestatusfield(us_statusangle);
	if (us_statusarc != 0) ttyfreestatusfield(us_statusarc);
	if (us_statusfacet != 0) ttyfreestatusfield(us_statusfacet);
	if (us_statusfacetsize != 0) ttyfreestatusfield(us_statusfacetsize);
	if (us_statusgridsize != 0) ttyfreestatusfield(us_statusgridsize);
	if (us_statuslambda != 0) ttyfreestatusfield(us_statuslambda);
	if (us_statusnetwork != 0) ttyfreestatusfield(us_statusnetwork);
	if (us_statusnode != 0) ttyfreestatusfield(us_statusnode);
	if (us_statustechnology != 0) ttyfreestatusfield(us_statustechnology);
	if (us_statusxpos != 0) ttyfreestatusfield(us_statusxpos);
	if (us_statusypos != 0) ttyfreestatusfield(us_statusypos);
	if (us_statusproject != 0) ttyfreestatusfield(us_statusproject);
	if (us_statusroot != 0) ttyfreestatusfield(us_statusroot);
	if (us_statuspart != 0) ttyfreestatusfield(us_statuspart);
	if (us_statuspackage != 0) ttyfreestatusfield(us_statuspackage);
	if (us_statusselection != 0) ttyfreestatusfield(us_statusselection);

	lines = ttynumstatuslines();
	for(i=0; i<lines; i++) ttyfreestatusfield(us_clearstatuslines[i]);

	if (us_uniqueretstr != NOSTRING) efree(us_uniqueretstr);
}

/******************** STATUS DISPLAY MANIPULATION ********************/

/* routine to determine the state of the status fields */
void us_initstatus(void)
{
	REGISTER INTBIG lines, i;
	static BOOLEAN first = TRUE;

	if (first)
	{
		first = FALSE;
		lines = ttynumstatuslines();
		for(i=0; i<lines; i++) us_clearstatuslines[i] = ttydeclarestatusfield(i+1, 0, 100, "");
		us_statusfacet = us_statusnode = us_statusarc = us_statusnetwork = 0;
		us_statuslambda = us_statustechnology = us_statusxpos = us_statusypos = 0;
		us_statusangle = us_statusalign = us_statusfacetsize = us_statusgridsize = 0;
		us_statusproject = us_statusroot = us_statuspart = us_statuspackage = 0;
		us_statusselection = 0;
		us_statusfacet      = ttydeclarestatusfield(0,  0,   0, "");
		us_statusnode       = ttydeclarestatusfield(1,  0,  22, _("NODE: "));
		us_statusarc        = ttydeclarestatusfield(1, 22,  44, _("ARC: "));
		us_statusfacetsize  = ttydeclarestatusfield(1, 44,  65, _("SIZE: "));
		us_statuslambda     = ttydeclarestatusfield(1, 65,  80, _("LAMBDA: "));
		us_statustechnology = ttydeclarestatusfield(1, 80, 100, _("TECHNOLOGY: "));
		us_statusxpos       = ttydeclarestatusfield(1, 65,  80, _("X: "));
		us_statusypos       = ttydeclarestatusfield(1, 80, 100, _("Y: "));
		if (lines > 1)
		{
			us_statusangle     = ttydeclarestatusfield(2,  0,  25, _("ANGLE: "));
			us_statusalign     = ttydeclarestatusfield(2, 25,  50, _("ALIGN: "));
			us_statusgridsize  = ttydeclarestatusfield(2, 75, 100, _("GRID: "));
		}
	}
}

/*
 * Routine to create a field in the status display.  "line" is the status line number (line 0
 * is the window header).  "startper" and "endper" are the starting and ending percentages
 * of the line to use (ignored for the window header).  "label" is the prefix to use.
 * Returns an object to use with subsequent calls to "ttysetstatusfield" (zero on error).
 */
STATUSFIELD *ttydeclarestatusfield(INTBIG line, INTBIG startper, INTBIG endper, char *label)
{
	STATUSFIELD *sf;

	sf = (STATUSFIELD *)emalloc((sizeof (STATUSFIELD)), us_tool->cluster);
	if (sf == 0) return(0);
	if (line < 0 || line > ttynumstatuslines()) return(0);
	if (line > 0 && (startper < 0 || endper > 100 || startper >= endper)) return(0);
	sf->line = line;
	sf->startper = startper;
	sf->endper = endper;
	(void)allocstring(&sf->label, label, us_tool->cluster);
	return(sf);
}

/* write the current grid alignment from "us_alignment_ratio" */
void us_setalignment(WINDOWFRAME *frame)
{
	char valstring[60];

	(void)strcpy(valstring, frtoa(us_alignment_ratio));
	if (us_edgealignment_ratio != 0)
	{
		(void)strcat(valstring, "/");
		(void)strcat(valstring, frtoa(us_edgealignment_ratio));
	}
	ttysetstatusfield(frame, us_statusalign, valstring, FALSE);
}

/* write the current lambda value from the current library/technology in microns */
void us_setlambda(WINDOWFRAME *frame)
{
	REGISTER INTBIG len;
	float lambdainmicrons;
	char hold[50];

	/* show nothing in X/Y mode */
	if ((us_tool->toolstate&SHOWXY) != 0) return;

	lambdainmicrons = scaletodispunit(el_curlib->lambda[el_curtech->techindex], DISPUNITMIC);
	(void)sprintf(hold, "%g", lambdainmicrons);
	for(;;)
	{
		len = strlen(hold)-1;
		if (hold[len] != '0') break;
		hold[len] = 0;
	}
	if (hold[len] == '.') hold[len] = 0;
	(void)strcat(hold, "u");
	ttysetstatusfield(NOWINDOWFRAME, us_statuslambda, hold, FALSE);
}

/* write the name of the current technology from "el_curtech->techname" */
void us_settechname(WINDOWFRAME *frame)
{
	/* show nothing in XY mode */
	if ((us_tool->toolstate&SHOWXY) != 0) return;

	ttysetstatusfield(NOWINDOWFRAME, us_statustechnology, el_curtech->techname, TRUE);
}

/* write the cursor coordinates */
void us_setcursorpos(WINDOWFRAME *frame, INTBIG x, INTBIG y)
{
	static INTBIG lastx=0, lasty=0;

	/* show nothing in non XY mode */
	if ((us_tool->toolstate&SHOWXY) == 0) return;

	if (lastx != x)
	{
		ttysetstatusfield(frame, us_statusxpos, latoa(x), FALSE);
		lastx = x;
	}

	if (lasty != y)
	{
		ttysetstatusfield(frame, us_statusypos, latoa(y), FALSE);
		lasty = y;
	}
}

/* set the current nodeproto */
void us_setnodeproto(NODEPROTO *np)
{
	(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_node_key, (INTBIG)np, VNODEPROTO|VDONTSAVE);
}

/*
 * set the current arcproto to "ap".  If "always" is false, do this only if sensible.
 */
void us_setarcproto(ARCPROTO *ap, BOOLEAN always)
{
	/* don't do it if setting universal arc when not in generic technology */
	if (!always && ap != NOARCPROTO && ap->tech != el_curtech)
	{
		if (ap == gen_universalarc) return;
	}
	(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_arc_key, (INTBIG)ap, VARCPROTO|VDONTSAVE);
}

/* shadow changes to the current nodeproto */
void us_shadownodeproto(WINDOWFRAME *frame, NODEPROTO *np)
{
	REGISTER char *pt;
	HIGHLIGHT high;
	REGISTER VARIABLE *var;
	REGISTER INTBIG len;
	REGISTER NODEINST *ni;

	if (us_curnodeproto == np) return;
	us_curnodeproto = np;

	if (np == NONODEPROTO) pt = ""; else
	{
		pt = describenodeproto(np);

		/* see if this type of node is selected */
		var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
		if (var != NOVARIABLE)
		{
			len = getlength(var);
			if (len == 1)
			{
				if (!us_makehighlight(((char **)var->addr)[0], &high))
				{
					if ((high.status&HIGHTEXT) != 0)
					{
						(void)initinfstr();
						var = high.fromvar;
						if (var == NOVARIABLE)
						{
							if (high.fromport == NOPORTPROTO)
							{
								(void)formatinfstr(_("%s (facet name)"),
									describenodeinst(high.fromgeom->entryaddr.ni));
							} else
							{
								(void)formatinfstr(_("%s (export)"), high.fromport->protoname);
							}
						} else
						{
							if (high.fromgeom == NOGEOM)
							{
								(void)formatinfstr(_("%s on instance"),
									us_trueattributename(var));
							} else if (high.fromport != NOPORTPROTO)
							{
								(void)formatinfstr(_("%s on export"),
									us_trueattributename(var));
							} else
							{
								if (high.fromgeom->entryisnode)
								{
									ni = high.fromgeom->entryaddr.ni;
									if (ni->proto == gen_invispinprim)
									{
										(void)addstringtoinfstr(us_invisiblepintextname(var));
									} else
									{
										(void)formatinfstr(_("%s on node"), us_trueattributename(var));
									}
								} else
								{
									(void)formatinfstr(_("%s on arc"), us_trueattributename(var));
								}
							}
						}
						pt = returninfstr();
					}
					if ((high.status&HIGHFROM) != 0 && high.fromgeom->entryisnode)
					{
						ni = high.fromgeom->entryaddr.ni;
						if (ni->proto == np)
						{
							/* one node selected that is the same as the current proto: qualify proto */
							pt = us_describenodeinsttype(np, ni->userbits&NTECHBITS);
						}
					}
				}
			}
		}
	}
	ttysetstatusfield(frame, us_statusnode, pt, TRUE);

	/* highlight the menu entry */
	us_showcurrentnodeproto();
}

/*
 * routine to draw highlighting around the menu entry with the current
 * node prototype
 */
void us_showcurrentnodeproto(void)
{
	REGISTER char *str;
	REGISTER INTBIG x, y;
	REGISTER VARIABLE *var;
	REGISTER NODEPROTO *np;
	COMMANDBINDING commandbinding;

	if (us_curnodeproto == NONODEPROTO) return;
	if ((us_tool->toolstate&MENUON) == 0) return;
	if ((us_tool->toolstate&ONESHOTMENU) != 0) return;
	if (us_menuhnx >= 0) us_highlightmenu(us_menuhnx, us_menuhny, el_colmenbor);
	us_menuhnx = -1;
	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
	if (var != NOVARIABLE) for(x=0; x<us_menux; x++) for(y=0; y<us_menuy; y++)
	{
		if (us_menupos <= 1) str = ((char **)var->addr)[y * us_menux + x]; else
			str = ((char **)var->addr)[x * us_menuy + y];
		us_parsebinding(str, &commandbinding);
		np = commandbinding.nodeglyph;
		us_freebindingparse(&commandbinding);
		if (np == us_curnodeproto)
		{
			us_menuhnx = x;   us_menuhny = y;
			us_highlightmenu(us_menuhnx, us_menuhny, el_colhmenbor);
			break;
		}
	}
}

/* shadow changes to the current arcproto */
void us_shadowarcproto(WINDOWFRAME *frame, ARCPROTO *ap)
{
	REGISTER ARCPROTO *oldap;

	if (us_curarcproto == ap) return;

	/* undraw the menu entry with the last arc proto in it */
	oldap = us_curarcproto;
	us_curarcproto = ap;

	/* adjust the component menu entries */
	if (oldap != NOARCPROTO) us_drawarcmenu(oldap);
	if (us_curarcproto != NOARCPROTO) us_drawarcmenu(us_curarcproto);

	ttysetstatusfield(frame, us_statusarc, describearcproto(ap), TRUE);
	us_showcurrentarcproto();
}

/*
 * Routine to find arcproto "ap" in the component menu and redraw that entry.
 */
void us_drawarcmenu(ARCPROTO *ap)
{
	REGISTER char *str;
	REGISTER INTBIG x, y;
	REGISTER VARIABLE *var;
	REGISTER ARCPROTO *pap;
	COMMANDBINDING commandbinding;

	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
	if (var == NOVARIABLE) return;
	for(x=0; x<us_menux; x++)
		for(y=0; y<us_menuy; y++)
	{
		if (us_menupos <= 1) str = ((char **)var->addr)[y * us_menux + x]; else
			str = ((char **)var->addr)[x * us_menuy + y];
		us_parsebinding(str, &commandbinding);
		pap = commandbinding.arcglyph;
		us_freebindingparse(&commandbinding);
		if (pap == ap)
		{
			us_drawmenuentry(x, y, str);
			return;
		}
	}
}

/*
 * routine to draw highlighting around the menu entry with the current
 * arc prototype
 */
void us_showcurrentarcproto(void)
{
	REGISTER char *str;
	REGISTER INTBIG x, y;
	REGISTER VARIABLE *var;
	REGISTER ARCPROTO *ap;
	COMMANDBINDING commandbinding;

	/* highlight the menu entry */
	if (us_curarcproto == NOARCPROTO) return;
	if ((us_tool->toolstate&MENUON) == 0) return;
	if ((us_tool->toolstate&ONESHOTMENU) != 0) return;
	if (us_menuhax >= 0) us_highlightmenu(us_menuhax, us_menuhay, el_colmenbor);
	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
	if (var != NOVARIABLE) for(x=0; x<us_menux; x++) for(y=0; y<us_menuy; y++)
	{
		if (us_menupos <= 1) str = ((char **)var->addr)[y * us_menux + x]; else
			str = ((char **)var->addr)[x * us_menuy + y];
		us_parsebinding(str, &commandbinding);
		ap = commandbinding.arcglyph;
		us_freebindingparse(&commandbinding);
		if (ap == us_curarcproto)
		{
			us_menuhax = x;   us_menuhay = y;
			us_highlightmenu(us_menuhax, us_menuhay, el_colhmenbor);
			break;
		}
	}
}

/* show the current network name */
void us_shownetworkname(WINDOWFRAME *frame)
{
	REGISTER NETWORK *net;
	REGISTER char *hold;

	net = net_gethighlightednet(FALSE, FALSE);
	if (net == NONETWORK) hold = ""; else
		hold = describenetwork(net);
	ttysetstatusfield(frame, us_statusnetwork, hold, FALSE);
}

/* write the default placement angle from "tool:user.USER_placement_angle" */
void us_setnodeangle(WINDOWFRAME *frame)
{
	char hold[5];
	REGISTER VARIABLE *var;
	REGISTER INTBIG pangle;

	var = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER, us_placement_angle_key);
	if (var == NOVARIABLE) pangle = 0; else pangle = var->addr;
	(void)strcpy(hold, frtoa(pangle%3600*WHOLE/10));
	if (pangle >= 3600) (void)strcat(hold, "T");
	ttysetstatusfield(frame, us_statusangle, hold, FALSE);
}

/* write the name of the facet in window "w" */
void us_setfacetname(WINDOWPART *w)
{
	REGISTER EDITOR *ed;

	/* do not show name of explorer window when it is split */
	if ((w->state&WINDOWTYPE) == EXPLORERWINDOW &&
		strcmp(w->location, "entire") != 0) return;

	/* if this is part of a frame and the current window is in another part, don't write name */
	if (el_curwindowpart != NOWINDOWPART && w != el_curwindowpart &&
		w->frame == el_curwindowpart->frame) return;

	/* write the facet name */
	(void)initinfstr();
	switch (w->state&WINDOWTYPE)
	{
		case TEXTWINDOW:
		case POPTEXTWINDOW:
			if (w->curnodeproto == NONODEPROTO)
			{
				ed = w->editor;
				if (ed != NOEDITOR)
				{
					(void)addstringtoinfstr(ed->header);
					break;
				}
			}
			/* FALLTHROUGH */ 
		case DISPWINDOW:
		case DISP3DWINDOW:
			if (w->curnodeproto != NONODEPROTO)
			{
				(void)formatinfstr("%s:%s", w->curnodeproto->cell->lib->libname,
					nldescribenodeproto(w->curnodeproto));
				if (w->curnodeproto->cell->lib != el_curlib)
					(void)formatinfstr(_(" - Current library: %s"), el_curlib->libname);
			}
			break;
		case EXPLORERWINDOW:
			(void)addstringtoinfstr(_("Facet Explorer"));
			break;
		case WAVEFORMWINDOW:
			if (w->curnodeproto != NONODEPROTO)
			{
				(void)formatinfstr(_("Waveform of %s"), describenodeproto(w->curnodeproto));
			} else
			{
				(void)addstringtoinfstr(_("Waveform"));
			}
			break;
	}

	ttysetstatusfield(w->frame, us_statusfacet, returninfstr(), TRUE);
}

/*
 * write the size of the facet in window "w" from the size of "w->curnodeproto"
 */
void us_setfacetsize(WINDOWPART *w)
{
	REGISTER NODEPROTO *np;
	char valstring[50];
	REGISTER INTBIG lines;

	np = w->curnodeproto;
	valstring[0] = 0;
	if (np != NONODEPROTO)
	{
		switch (w->state&WINDOWTYPE)
		{
			case TEXTWINDOW:
			case POPTEXTWINDOW:
				lines = us_totallines(w);
				sprintf(valstring, _("%ld lines"), lines);
				break;
			case DISPWINDOW:
			case DISP3DWINDOW:
				(void)strcpy(valstring, latoa(np->highx - np->lowx));
				(void)strcat(valstring, "x");
				(void)strcat(valstring, latoa(np->highy - np->lowy));
				break;
		}
	}
	ttysetstatusfield(w->frame, us_statusfacetsize, valstring, FALSE);
}

/* write the grid size of window "w" from "w->gridx" and "w->gridy" */
void us_setgridsize(WINDOWPART *w)
{
	REGISTER NODEPROTO *np;
	char valstring[50];

	np = w->curnodeproto;
	valstring[0] = 0;
	if (np != NONODEPROTO)
	{
		switch (w->state&WINDOWTYPE)
		{
			case DISPWINDOW:
			case DISP3DWINDOW:
				(void)strcpy(valstring, frtoa(w->gridx));
				(void)strcat(valstring, "x");
				(void)strcat(valstring, frtoa(w->gridy));
				break;
		}
	}
	ttysetstatusfield(NOWINDOWFRAME, us_statusgridsize, valstring, FALSE);
}

/* routine to re-write the status information (on all frames if frame is 0) */
void us_redostatus(WINDOWFRAME *frame)
{
	REGISTER WINDOWPART *w;
	REGISTER ARCPROTO *ap;
	REGISTER NODEPROTO *np;
	REGISTER INTBIG lines, i;

	/* clear the status bar */
	lines = ttynumstatuslines();
	for(i=0; i<lines; i++) ttysetstatusfield(NOWINDOWFRAME, us_clearstatuslines[i], "", FALSE);

	/* node and arc */
	ap = us_curarcproto;   np = us_curnodeproto;
	us_curarcproto = (ARCPROTO *)0;   us_curnodeproto = (NODEPROTO *)0;
	us_shadownodeproto(NOWINDOWFRAME, np);
	us_shadowarcproto(NOWINDOWFRAME, ap);

#ifdef USEQT
	/* write the window information on the window lines */
	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
	{
		us_setfacetname(w);
		if (graphicshas(CANSTATUSPERFRAME) || w->frame == frame)
			us_setfacetsize(w);
		us_setgridsize(w);
	}

	/* other fields */
	us_shownetworkname(frame);
	us_setnodeangle(frame);
	us_setalignment(frame);
	us_setlambda(frame);
	us_settechname(frame);
#else
	/* other fields */
	us_shownetworkname(frame);
	us_setnodeangle(frame);
	us_settechname(frame);
	us_setalignment(frame);
	us_setlambda(frame);

	/* write the window information on the window lines */
	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
	{
		us_setfacetname(w);
		if (graphicshas(CANSTATUSPERFRAME) || w->frame == frame)
			us_setfacetsize(w);
		us_setgridsize(w);
	}
#endif
}

/******************** OUTPUT PREPARATION ********************/

/*
 * routine to list macros by the package of origin
 */
void us_printmacros(void)
{
	INTBIG len, coms, keyboard, gotany, i, j, count;
	char line[150], *build[MAXPARS];
	REGISTER char *name, *comname, *inter;
	REGISTER MACROPACK *pack, *thispack;
	REGISTER VARIABLE *var;

	/* count the number of macros in each package */
	for(pack=us_macropacktop; pack!=NOMACROPACK; pack=pack->nextmacropack)
		pack->total = 0;
	keyboard = gotany = 0;
	for(i=0; i<us_tool->numvar; i++)
	{
		if (namesamen(makename(us_tool->firstvar[i].key), "USER_macro_", 11) == 0)
		{
			var = &us_tool->firstvar[i];
			if (getlength(var) <= 3) continue;
			count = us_parsecommand(((char **)var->addr)[0], build);
			for(j=0; j<count; j++)
				if (namesamen(build[j], "macropack=", 10) == 0)
			{
				pack = us_getmacropack(&build[j][10]);
				if (pack != NOMACROPACK) pack->total++;
				break;
			}
			if (j >= count) keyboard++;
			gotany++;
		}
	}
	if (gotany == 0)
	{
		ttyputmsg(M_("No macros defined"));
		return;
	}

	ttyputmsg(M_("---------- Macros by Package ----------"));

	/* now print the macros by package */
	for(pack = us_macropacktop; ; pack = pack->nextmacropack)
	{
		if (pack == NOMACROPACK)
		{
			if (keyboard == 0) break;
			name = M_("Keyboard-defined");
		} else
		{
			if (pack->total == 0) continue;
			name = pack->packname;
		}
		(void)strcpy(line, name);
		(void)strcat(line, M_(" macros:"));
		len = strlen(line);
		inter = "";
		for(i=0; i<us_tool->numvar; i++)
			if (namesamen(makename(us_tool->firstvar[i].key), "USER_macro_", 11) == 0)
		{
			var = &us_tool->firstvar[i];
			if (getlength(var) <= 3) continue;
			count = us_parsecommand(((char **)var->addr)[0], build);
			thispack = NOMACROPACK;
			for(j=0; j<count; j++)
				if (namesamen(build[j], "macropack=", 10) == 0)
			{
				thispack = us_getmacropack(&build[j][10]);
				break;
			}
			if (thispack != pack) continue;
			(void)strcat(line, inter);  len += strlen(inter);
			inter = ",";
			comname = &makename(var->key)[11];
			coms = strlen(comname) + 2;
			if (len + coms >= MESSAGESWIDTH)
			{
				ttyputmsg(line);  (void)strcpy(line, "  ");  len = 2;
			}
			(void)strcat(line, " ");
			(void)strcat(line, comname);  len += coms;

		}
		if (len > 2) ttyputmsg(line);
		if (pack == NOMACROPACK) break;
	}
}

/*
 * print macro "mac".  Show every command in the macro.
 */
void us_printmacro(VARIABLE *mac)
{
	REGISTER INTBIG j, len;

	if (mac == NOVARIABLE) return;
	len = getlength(mac);
	if (len <= 3)
	{
		ttyputmsg(M_("Macro '%s' is not defined"), &makename(mac->key)[11]);
		return;
	}

	ttyputmsg(M_("Macro '%s' has %ld %s"), &makename(mac->key)[11], len-3,
		makeplural(M_("command"), len-3));
	for(j=3; j<len; j++) ttyputmsg("   %s", ((char **)mac->addr)[j]);
}

static struct
{
	char *typestring;
	INTBIG typecode;
} us_vartypename[] =
{
	{"Unknown",      VUNKNOWN},
	{"Integer",      VINTEGER},
	{"Address",      VADDRESS},
	{"Character",    VCHAR},
	{"String",       VSTRING},
	{"Float",        VFLOAT},
	{"Double",       VDOUBLE},
	{"Nodeinst",     VNODEINST},
	{"Nodeproto",    VNODEPROTO},
	{"Portarcinst",  VPORTARCINST},
	{"Portexpinst",  VPORTEXPINST},
	{"Portproto",    VPORTPROTO},
	{"Arcinst",      VARCINST},
	{"Arcproto",     VARCPROTO},
	{"Geometry",     VGEOM},
	{"Library",      VLIBRARY},
	{"Technology",   VTECHNOLOGY},
	{"Tool",         VTOOL},
	{"R-tree",       VRTNODE},
	{"Fixed-Point",  VFRACT},
	{"Network",      VNETWORK},
	{"Cell",         VCELL},
	{"View",         VVIEW},
	{"WindowPart",   VWINDOWPART},
	{"Graphics",     VGRAPHICS},
	{"Short",        VSHORT},
	{"Constraint",   VCONSTRAINT},
	{"General",      VGENERAL},
	{"Window-Frame", VWINDOWFRAME},
	{"Polygon",      VPOLYGON},
	{NULL, 0}
};

char *us_variabletypename(INTBIG type)
{
	INTBIG i;

	for(i=0; us_vartypename[i].typestring != 0; i++)
		if (us_vartypename[i].typecode == (type&VTYPE))
			return(us_vartypename[i].typestring);
	return("unknown");
}

INTBIG us_variabletypevalue(char *name)
{
	INTBIG i;

	for(i=0; us_vartypename[i].typestring != 0; i++)
		if (namesame(name, us_vartypename[i].typestring) == 0)
			return(us_vartypename[i].typecode);
	return(0);
}

/*
 * routine to describe variable "var".  If "index" is nonnegative then describe
 * entry "index" of an array variable.  The description is returned.
 */
char *us_variableattributes(VARIABLE *var, INTBIG aindex)
{
	char *arr, line[30];
	REGISTER INTBIG len;

	if ((var->type & VISARRAY) == 0 || aindex >= 0) arr = ""; else
	{
		len = getlength(var);
		if (len == 0) arr = " array[]"; else
		{
			if ((var->type&VTYPE) == VGENERAL) len /= 2;
			(void)sprintf(line, " array[%ld]", len);
			arr = line;
		}
	}
	(void)initinfstr();
	if ((var->type&VDONTSAVE) != 0) (void)addstringtoinfstr(_("Temporary "));
	if ((var->type&VDISPLAY) != 0)
	{
		(void)addstringtoinfstr(_("Displayable("));
		(void)addstringtoinfstr(us_describetextdescript(var->textdescript));
		(void)addstringtoinfstr(") ");
	}
	if ((var->type&(VCODE1|VCODE2)) != 0)
	{
		switch (var->type&(VCODE1|VCODE2))
		{
			case VLISP: (void)addstringtoinfstr("LISP ");  break;
			case VTCL:  (void)addstringtoinfstr("TCL ");   break;
			case VJAVA: (void)addstringtoinfstr("Java ");  break;
		}
	}
	(void)addstringtoinfstr(us_variabletypename(var->type));
	(void)addstringtoinfstr(arr);
	return(returninfstr());
}

/* routine to recursively count and print the nodes in facet "facet" */
void us_describecontents(NODEPROTO *facet)
{
	REGISTER INTBIG i;
	REGISTER NODEPROTO *np;
	REGISTER LIBRARY *lib, *printlib;
	REGISTER TECHNOLOGY *curtech, *printtech, *tech;
	REGISTER NODEPROTO **sortedfacets;

	/* first zero the count of each nodeproto */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp1 = 0;
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp1 = 0;

	/* now look at every object recursively in this facet */
	us_addobjects(facet);

	/* print the totals */
	ttyputmsg(_("Contents of facet %s:"), describenodeproto(facet));
	printtech = NOTECHNOLOGY;
	for(curtech = el_technologies; curtech != NOTECHNOLOGY; curtech = curtech->nexttechnology)
	{
		for(np = curtech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			if (np->temp1 != 0)
		{
			if (curtech != printtech)
			{
				ttyputmsg(_("%s technology:"), curtech->techname);
				printtech = curtech;
			}
			ttyputmsg(_("%6d %s %s"), np->temp1, describenodeproto(np),
				makeplural(_("node"), np->temp1));
		}
	}
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		sortedfacets = us_sortlib(lib, (char *)0, FALSE);
		if (sortedfacets == 0) ttyputnomemory(); else
		{
			printlib = NOLIBRARY;
			for(i = 0; sortedfacets[i] != NONODEPROTO; i++)
			{
				np = sortedfacets[i];
				if (np->temp1 == 0) continue;
				if (lib != printlib)
				{
					ttyputmsg(_("%s library:"), lib->libname);
					printlib = lib;
				}
				ttyputmsg(_("%6d %s %s"), np->temp1, nldescribenodeproto(np),
					makeplural(_("node"), np->temp1));
			}
			efree((char *)sortedfacets);
		}
	}
}

/*
 * routine to recursively examine facet "np" and update the number of
 * instantiated primitive nodeprotos in the "temp1" field of the nodeprotos.
 */
void us_addobjects(NODEPROTO *np)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *cnp;

	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		ni->proto->temp1++;
		if (ni->proto->primindex != 0) continue;

		/* ignore recursive references (showing icon in contents) */
		if (ni->proto->cell == np->cell) continue;
		cnp = contentsview(ni->proto);
		if (cnp == NONODEPROTO) cnp = ni->proto;
		us_addobjects(cnp);
	}
}

static struct
{
	NODEPROTO **prim;
	INTBIG      bits;
	char       *statusmessage;
} us_specialstatus[] =
{
	{&sch_transistorprim, TRANNMOS,     "nMOS"},
	{&sch_transistorprim, TRANDMOS,     "dMOS"},
	{&sch_transistorprim, TRANPMOS,     "pMOS"},
	{&sch_transistorprim, TRANNPN,      "NPN"},
	{&sch_transistorprim, TRANPNP,      "PNP"},
	{&sch_transistorprim, TRANNJFET,    "NJFET"},
	{&sch_transistorprim, TRANPJFET,    "PJFET"},
	{&sch_transistorprim, TRANDMES,     "DMES"},
	{&sch_transistorprim, TRANEMES,     "EMES"},
	{&sch_transistor4prim,TRANNMOS,     "nMOS"},
	{&sch_transistor4prim,TRANDMOS,     "dMOS"},
	{&sch_transistor4prim,TRANPMOS,     "pMOS"},
	{&sch_transistor4prim,TRANNPN,      "NPN"},
	{&sch_transistor4prim,TRANPNP,      "PNP"},
	{&sch_transistor4prim,TRANNJFET,    "NJFET"},
	{&sch_transistor4prim,TRANPJFET,    "PJFET"},
	{&sch_transistor4prim,TRANDMES,     "DMES"},
	{&sch_transistor4prim,TRANEMES,     "EMES"},
	{&sch_twoportprim,    TWOPVCCS,     "VCCS"},
	{&sch_twoportprim,    TWOPCCVS,     "CCVS"},
	{&sch_twoportprim,    TWOPVCVS,     "VCVS"},
	{&sch_twoportprim,    TWOPCCCS,     "CCCS"},
	{&sch_twoportprim,    TWOPTLINE,    "Transmission"},
	{&sch_diodeprim,      DIODEZENER,   "Zener"},
	{&sch_capacitorprim,  CAPACELEC,    "Electrolytic"},
	{&sch_ffprim,         FFTYPERS,     "RS"},
	{&sch_ffprim,         FFTYPEJK,     "JK"},
	{&sch_ffprim,         FFTYPED,      "D"},
	{&sch_ffprim,         FFTYPET,      "T"},
	{0, 0, 0}
};

/*
 * Routine to describe the node in the commandbinding structure "commandbinding".
 */
char *us_describemenunode(COMMANDBINDING *commandbinding)
{
	REGISTER INTBIG bits;

	if (commandbinding->menumessage == 0) bits = 0; else
		bits = myatoi(commandbinding->menumessage);
	return(us_describenodeinsttype(commandbinding->nodeglyph, bits));
}

/*
 * Routine to return a description of node "np" that is qualified with bits "bits"
 */
char *us_describenodeinsttype(NODEPROTO *np, INTBIG bits)
{
	REGISTER INTBIG i;

	(void)initinfstr();
	(void)addstringtoinfstr(describenodeproto(np));
	for(i=0; us_specialstatus[i].prim != 0; i++)
		if (us_specialstatus[i].bits == bits &&
			*us_specialstatus[i].prim == np) break;
	if (us_specialstatus[i].prim != 0)
	{
		(void)addstringtoinfstr(" (");
		(void)addstringtoinfstr(us_specialstatus[i].statusmessage);
		(void)addstringtoinfstr(")");
	}
	return(returninfstr());
}

/*
 * routine to sort the facets in library "lib" by facet name and return an
 * array of NODEPROTO pointers (terminating with NONODEPROTO).  If "matchspec"
 * is nonzero, it is a string that forces wildcard matching of facet names.
 * If "shortlist" is true, only list one facet of every cell.  Remember
 * to free the returned array when done.  Routine returns zero upon error.
 */
NODEPROTO **us_sortlib(LIBRARY *lib, char *matchspec, BOOLEAN shortlist)
{
	REGISTER NODEPROTO **sortindex, *np;
	REGISTER CELL *c;
	REGISTER INTBIG total, i;
	REGISTER char *pt;

	/* remove library name if it is part of spec */
	if (matchspec != 0)
	{
		for(pt = matchspec; *pt != 0; pt++) if (*pt == ':') break;
		if (*pt == ':') matchspec = pt+1;
	}

	/* count the number of facets in the library */
	total = 0;
	if (shortlist)
	{
		for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
			if (us_wildmatch(c->cellname, matchspec)) total++;
	} else
	{
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			if (us_wildmatch(nldescribenodeproto(np), matchspec)) total++;
	}

	/* allocate array for sorting entries */
	sortindex = (NODEPROTO **)emalloc(((total+1) * (sizeof (NODEPROTO *))), el_tempcluster);
	if (sortindex == 0) return(0);
	if (shortlist)
	{
		for(c = lib->firstcell, i = 0; c != NOCELL; c = c->nextcell)
			if (us_wildmatch(c->cellname, matchspec))
				sortindex[i++] = c->firstincell;
	} else
	{
		for(np = lib->firstnodeproto, i = 0; np != NONODEPROTO; np = np->nextnodeproto)
			if (us_wildmatch(nldescribenodeproto(np), matchspec))
				sortindex[i++] = np;
	}
	sortindex[i] = NONODEPROTO;

	/* sort the facet names */
	esort(sortindex, total, sizeof (NODEPROTO *), sort_facetnameascending);
	return(sortindex);
}

/*
 * routine to match a name in "name" with a wildcard specification in "spec".
 * If "spec" is zero, all names are valid.  Returns true if the name matches.
 */
BOOLEAN us_wildmatch(char *name, char *spec)
{
	REGISTER char *pt, *pt2;
	REGISTER char c1, c2;

	if (spec == 0) return(TRUE);

	pt = name;
	pt2 = spec;
	for(;;)
	{
		if (*pt == 0 && *pt2 == 0) break;
		if (*pt != 0 && *pt2 == '?')
		{
			pt++;
			pt2++;
			continue;
		}
		if (*pt2 == '*')
		{
			for(;;)
			{
				if (us_wildmatch(pt, &pt2[1]) != 0) return(TRUE);
				if (*pt == 0) break;
				pt++;
			}
			return(FALSE);
		}
		if (isupper(*pt)) c1 = tolower(*pt); else c1 = *pt;
		if (isupper(*pt2)) c2 = tolower(*pt2); else c2 = *pt2;
		if (c1 != c2) return(FALSE);
		pt++;
		pt2++;
	}
	return(TRUE);
}

/* routine to print the tool information about nodeinst "ni" */
void us_printnodetoolinfo(NODEINST *ni)
{
	static struct explanation explain[] =
	{
		{DEADN,       -1,      N_("dead")},
		{NEXPAND,     -1,      N_("expand")},
		{WIPED,       -1,      N_("wiped")},
		{NSHORT,      -1,      N_("short")},
		{0, -1, NULL}
	};
	us_explaintool(ni->userbits, explain);
}

/* routine to print the tool information about arcinst "ai" */
void us_printarctoolinfo(ARCINST *ai)
{
	static struct explanation explain[] =
	{
		{FIXED,         -1,                 N_("rigid")},
		{CANTSLIDE,     -1,                 N_("rigid-in-ports")},
		{FIXANG,        -1,                 N_("fixed-angle")},
		{DEADA,         -1,                 N_("dead")},
		{AANGLE,        AANGLESH,           N_("angle")},
		{ASHORT,        -1,                 N_("short")},
		{AFUNCTION,     APBUS<<AFUNCTIONSH, N_("bus")},
		{NOEXTEND,      -1,                 N_("ends-shortened")},
		{ISNEGATED,     -1,                 N_("negated")},
		{ISDIRECTIONAL, -1,                 N_("directional")},
		{NOTEND0,       -1,                 N_("skip-tail")},
		{NOTEND1,       -1,                 N_("skip-head")},
		{REVERSEEND,    -1,                 N_("direction-reversed")},
		{0, -1, NULL}
	};
	us_explaintool(ai->userbits, explain);
}

/* helper routine for "us_printnodetoolinfo" and "us_printarctoolinfo" */
void us_explaintool(INTBIG bits, struct explanation explain[])
{
	REGISTER INTBIG j, k;
	char partial[40];

	/* print the tool bit information */
	(void)initinfstr();
	(void)sprintf(partial, _("User state = %ld"), bits);
	(void)addstringtoinfstr(partial);
	k = 0;
	for(j=0; explain[j].bit != 0; j++)
	{
		if (explain[j].shift != -1)
		{
			if (k == 0) (void)addtoinfstr('('); else
				(void)addstringtoinfstr(", ");
			(void)addstringtoinfstr(_(explain[j].name));
			(void)addtoinfstr('=');
			(void)sprintf(partial, "%ld",(bits&explain[j].bit)>>explain[j].shift);
			(void)addstringtoinfstr(partial);
			k++;
			continue;
		}
		if ((bits & explain[j].bit) == 0) continue;
		if (k == 0) (void)addtoinfstr('('); else (void)addstringtoinfstr(", ");
		(void)addstringtoinfstr(_(explain[j].name));
		k++;
	}
	if (k != 0) (void)addtoinfstr(')');
	ttyputmsg("%s", returninfstr());
}

/*
 * routine to print information about port prototype "pp" on nodeinst "ni".
 * If the port prototype's "temp1" is nonzero, this port has already been
 * mentioned and should not be done again.
 */
void us_chatportproto(NODEINST *ni, PORTPROTO *pp)
{
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER ARCINST *ai;
	REGISTER INTBIG i;
	INTBIG lx, hx, ly, hy;
	REGISTER char *activity;
	char locx[40], locy[40];
	static POLYGON *poly = NOPOLYGON;

	/* get polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_tool->cluster);

	/* if this port has already been discussed, quit now */
	if (pp->temp1 != 0) return;
	pp->temp1++;

	/* talk about the port prototype */
	(void)initinfstr();
	activity = describeportbits(pp);
	if (*activity != 0)
	{
		(void)formatinfstr(_("%s port %s connects to "), activity, pp->protoname);
	} else
	{
		(void)formatinfstr(_("Port %s connects to "), pp->protoname);
	}
	for(i=0; pp->connects[i] != NOARCPROTO; i++)
	{
		if (i != 0) (void)addstringtoinfstr(", ");
		(void)addstringtoinfstr(pp->connects[i]->protoname);
	}
	ttyputmsg("%s", returninfstr());

	if (pp->subnodeinst == NONODEINST)
	{
		shapeportpoly(ni, pp, poly, FALSE);

		/* see if the port is a single point */
		for(i=1; i<poly->count; i++)
			if (poly->xv[i] != poly->xv[i-1] || poly->yv[i] != poly->yv[i-1]) break;
		if (i >= poly->count)
		{
			ttyputmsg(_("  Located at (%s, %s)"), latoa(poly->xv[0]), latoa(poly->yv[0]));
		} else if (isbox(poly, &lx,&hx, &ly,&hy))
		{
			if (lx == hx) (void)sprintf(locx, _("at %s"), latoa(lx)); else
				(void)sprintf(locx, _("from %s to %s"), latoa(lx), latoa(hx));
			if (ly == hy) (void)sprintf(locy, _("at %s"), latoa(ly)); else
				(void)sprintf(locy, _("from %s to %s"), latoa(ly), latoa(hy));
			ttyputmsg(_("  Located %s in X and %s in Y"), locx, locy);
		} else
		{
			(void)initinfstr();
			for(i=0; i<poly->count; i++)
				(void)formatinfstr(" (%s, %s)", latoa(poly->xv[i]), latoa(poly->yv[i]));
			ttyputmsg(_("  Located inside polygon%s"), returninfstr());
		}
	} else ttyputmsg(_("  Located at subnode %s subport %s"),
		describenodeinst(pp->subnodeinst), pp->subportproto->protoname);

	/* talk about any arcs on this prototype */
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
	{
		if (pi->proto != pp) continue;
		ai = pi->conarcinst;
		if (ai->end[0].portarcinst == pi) i = 0; else i = 1;
		ttyputmsg(_("  Connected at (%s,%s) to %s"), latoa(ai->end[i].xpos),
			latoa(ai->end[i].ypos), describearcinst(ai));
	}

	/* talk about any exports of this prototype */
	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		if (pe->proto == pp)
	{
		(void)initinfstr();
		(void)formatinfstr(_("  Available as %s export '%s' (label %s"),
			describeportbits(pe->exportproto), pe->exportproto->protoname,
				us_describetextdescript(pe->exportproto->textdescript));
		if ((pe->exportproto->userbits&PORTDRAWN) != 0) (void)addstringtoinfstr(_(",always-drawn"));
		if ((pe->exportproto->userbits&BODYONLY) != 0) (void)addstringtoinfstr(_(",only-on-body"));
		(void)addstringtoinfstr(") ");
		ttyputmsg("%s", returninfstr());
	}
}

/*
 * routine to construct a string that describes the text descriptor field
 * in "descript".  The string has the form "POSITION+(DX,DY),SIZE" if there
 * is an offset, or "POSITION,SIZE" if there is not.  The string is returned.
 */
char *us_describetextdescript(UINTBIG *descript)
{
	REGISTER INTBIG xdist, ydist;
	char offset[50];

	(void)initinfstr();
	(void)addstringtoinfstr(us_describestyle(TDGETPOS(descript)));
	if (TDGETXOFF(descript) != 0 || TDGETYOFF(descript) != 0)
	{
		xdist = TDGETXOFF(descript);
		ydist = TDGETYOFF(descript);
		(void)sprintf(offset, "+(%s,%s)", frtoa(xdist*WHOLE/4), frtoa(ydist*WHOLE/4));
		(void)addstringtoinfstr(offset);
	}
	(void)addtoinfstr(',');
	(void)addstringtoinfstr(us_describefont(TDGETSIZE(descript)));
	return(returninfstr());
}

/*
 * routine to return a string describing text style "style".
 */
char *us_describestyle(INTBIG style)
{
	switch (style)
	{
		case VTPOSCENT:      return(_("centered"));
		case VTPOSBOXED:     return(_("boxed"));
		case VTPOSUP:        return(_("up"));
		case VTPOSDOWN:      return(_("down"));
		case VTPOSLEFT:      return(_("left"));
		case VTPOSRIGHT:     return(_("right"));
		case VTPOSUPLEFT:    return(_("up-left"));
		case VTPOSUPRIGHT:   return(_("up-right"));
		case VTPOSDOWNLEFT:  return(_("down-left"));
		case VTPOSDOWNRIGHT: return(_("down-right"));
	}
	return("?");
}

/*
 * routine to return a string describing text font "font".
 */
char *us_describefont(INTBIG font)
{
	static char description[60];
	if (TXTGETPOINTS(font) != 0)
	{
		sprintf(description, _("%ld-points"), TXTGETPOINTS(font));
		return(description);
	}
	if (TXTGETQLAMBDA(font) != 0)
	{
		sprintf(description, _("%s-lambda"), frtoa(TXTGETQLAMBDA(font) * WHOLE / 4));
		return(description);
	}
	return("?");
}

/*
 * Routine to return the type of nonlayout text that is in variable "var".
 */
char *us_invisiblepintextname(VARIABLE *var)
{
	if (namesame(makename(var->key), "VERILOG_code") == 0)
		return(_("Verilog code"));
	if (namesame(makename(var->key), "VERILOG_declaration") == 0)
		return(_("Verilog declaration"));
	if (namesame(makename(var->key), "SIM_spice_card") == 0)
		return(_("SPICE card"));
	return(_("Nonlayout Text"));
}

typedef struct
{
	char *origvarname;
	char *uservarname;
} USERVARIABLES;

USERVARIABLES us_uservariables[] =
{
	"ARC_name",                 N_("Arc Name"),
	"ARC_radius",               N_("Arc Radius"),
	"ART_color",                N_("Color"),
	"ART_degrees",              N_("Number of Degrees"),
	"ART_message",              N_("Text"),
	"NET_ncc_match",            N_("NCC equivalence"),
	"NODE_name",                N_("Node Name"),
	"SCHEM_capacitance",        N_("Capacitor Size"),
	"SCHEM_diode",              N_("Diode Size"),
	"SCHEM_inductance",         N_("Inductance"),
	"SCHEM_resistance",         N_("Resistance"),
	"SIM_fall_delay",           N_("Fall Delay"),
	"SIM_fasthenry_group_name", N_("FastHenry Group"),
	"SIM_rise_delay",           N_("Rise Delay"),
	"SIM_spice_model",          N_("SPICE card"),
	"transistor_width",         N_("Transistor Width"),
	"SCHEM_global_name",        N_("Global Signal Name"),
	0, 0
};

/*
 * Routine to see if variable name "name" is a known variable.  If
 * so, returns a better name for it.  If not, returns zero.
 */
char *us_bettervariablename(char *name)
{
	REGISTER INTBIG i;

	for(i=0; us_uservariables[i].origvarname != 0; i++)
	{
		if (namesame(name, us_uservariables[i].origvarname) == 0)
			return(_(us_uservariables[i].uservarname));
	}
	return(0);
}

/*
 * Routine to return a user-friendly name of variable "var". 
 */
char *us_trueattributename(VARIABLE *var)
{
	REGISTER char *pt, *bettername;

	pt = makename(var->key);
	if (namesamen(pt, "ATTR_", 5) == 0)
	{
		(void)initinfstr();
		if (TDGETISPARAM(var->textdescript) != 0)
			(void)formatinfstr(_("Parameter '%s'"), &pt[5]); else
				(void)formatinfstr(_("Attribute '%s'"), &pt[5]);
		return(returninfstr());
	}
	bettername = us_bettervariablename(pt);
	if (bettername != 0) return(bettername);

	(void)initinfstr();
	(void)formatinfstr(_("Variable '%s'"), pt);
	return(returninfstr());
}

/*
 * routine to print a description of color map entry "ind"
 */
void us_printcolorvalue(INTBIG ind)
{
	GRAPHICS *desc;
	char line[40];
	REGISTER INTBIG i, j, high;
	REGISTER VARIABLE *varred, *vargreen, *varblue;

	/* get color arrays */
	varred = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
	vargreen = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
	varblue = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
	if (varred == NOVARIABLE || vargreen == NOVARIABLE || varblue == NOVARIABLE)
	{
		ttyputerr(_("Cannot get current color map"));
		return;
	}

	(void)initinfstr();
	if ((ind&(LAYERH|LAYERG)) == 0)
	{
		(void)sprintf(line, _("Entry %ld"), ind);
		(void)addstringtoinfstr(line);
		j = 0;
		high = el_curtech->layercount;
		for(i=0; i<high; i++)
		{
			desc = el_curtech->layers[i];
			if (desc->bits == LAYERO && desc->col != ind) continue;
			if (desc->bits != LAYERO && ((desc->col&ind) == 0 || (ind&LAYEROE) != 0)) continue;
			if (j == 0) (void)addstringtoinfstr(" ("); else
				(void)addtoinfstr(',');
			j++;
			(void)addstringtoinfstr(layername(el_curtech, i));
		}
		if (j != 0) (void)addtoinfstr(')');
		(void)addstringtoinfstr(_(" is"));
	} else
	{
		if ((ind&LAYERG) != 0) (void)addstringtoinfstr(_("Grid entry is")); else
			if ((ind&LAYERH) != 0) (void)addstringtoinfstr(_("Highlight entry is"));
	}
	(void)sprintf(line, _(" color (%ld,%ld,%ld)"), ((INTBIG *)varred->addr)[ind],
		((INTBIG *)vargreen->addr)[ind], ((INTBIG *)varblue->addr)[ind]);
	(void)addstringtoinfstr(line);
	ttyputmsg("%s", returninfstr());
}

/*
 * routine to make a line of text that describes facet "np".  The text
 * includes the facet name, version, creation, and revision dates.  The
 * amount of space to leave for the facet name is "maxlen" characters
 * (which is negative if dumping to a file).
 */
char *us_makefacetline(NODEPROTO *np, INTBIG maxlen)
{
	char line[40];
	REGISTER INTBIG l, i, gooddrc, len, total;
	INTBIG year, month, mday, hour, minute, second;
	REGISTER char *pt;
	REGISTER VARIABLE *var;
	REGISTER NODEINST *ni;
	UINTBIG lastgooddate;

	(void)initinfstr();
	pt = describenodeproto(np);
	(void)addstringtoinfstr(pt);
	if (maxlen < 0) (void)addtoinfstr('\t'); else
	{
		l = strlen(pt);
		for(i=l; i<maxlen; i++) (void)addtoinfstr(' ');
	}

	/* add the version number */
	(void)sprintf(line, "%5d", np->version);
	(void)addstringtoinfstr(line);
	if (maxlen < 0) (void)addtoinfstr('\t'); else
		(void)addstringtoinfstr("   ");

	/* add the creation date */
	if (np->creationdate == 0)
	{
		if (maxlen < 0) (void)addstringtoinfstr(_("UNRECORDED")); else
			(void)addstringtoinfstr(_("     UNRECORDED     "));
	} else
	{
		if (maxlen < 0)
		{
			parsetime(np->creationdate, &year, &month, &mday, &hour, &minute, &second);
			sprintf(line, "%4d-%02d-%02d %02d:%02d:%02d", year, month, mday, hour,
				minute, second);
			(void)addstringtoinfstr(line);
		} else
		{
			pt = timetostring(np->creationdate);
			if (pt == NULL)
			{
				(void)addstringtoinfstr(_("     UNAVAILABLE     "));
			} else
			{
				for(i=4; i<=9; i++) (void)addtoinfstr(pt[i]);
				for(i=19; i<=23; i++) (void)addtoinfstr(pt[i]);
				for(i=10; i<=18; i++) (void)addtoinfstr(pt[i]);
			}
		}
	}

	/* add the revision date */
	if (maxlen < 0) (void)addtoinfstr('\t'); else
		(void)addstringtoinfstr("  ");
	if (np->revisiondate == 0)
	{
		if (maxlen < 0) (void)addstringtoinfstr(_("UNRECORDED")); else
			(void)addstringtoinfstr(_("     UNRECORDED     "));
	} else
	{
		if (maxlen < 0)
		{
			parsetime(np->revisiondate, &year, &month, &mday, &hour, &minute, &second);
			sprintf(line, "%4d-%02d-%02d %02d:%02d:%02d", year, month, mday, hour,
				minute, second);
			(void)addstringtoinfstr(line);
		} else
		{
			pt = timetostring(np->revisiondate);
			if (pt == NULL)
			{
				(void)addstringtoinfstr(_("     UNAVAILABLE     "));
			} else
			{
				for(i=4; i<=9; i++) (void)addtoinfstr(pt[i]);
				for(i=19; i<=23; i++) (void)addtoinfstr(pt[i]);
				for(i=10; i<=18; i++) (void)addtoinfstr(pt[i]);
			}
		}
	}

	/* add the size */
	if (maxlen < 0) (void)addtoinfstr('\t'); else
		(void)addstringtoinfstr("  ");
	if ((np->cellview->viewstate&TEXTVIEW) != 0)
	{
		var = getvalkey((INTBIG)np, VNODEPROTO, VSTRING|VISARRAY, el_facet_message_key);
		if (var == NOVARIABLE) len = 0; else
			len = getlength(var);
		if (maxlen < 0) sprintf(line, _("%ld lines"), len); else
			sprintf(line, _("%8ld lines   "), len);
		(void)addstringtoinfstr(line);
	} else
	{
		strcpy(line, latoa(np->highx-np->lowx));
		if (maxlen >= 0)
		{
			for(i = strlen(line); i<8; i++) (void)addtoinfstr(' ');
		}
		(void)addstringtoinfstr(line);
		(void)addtoinfstr('x');
		strcpy(line, latoa(np->highy-np->lowy));
		(void)addstringtoinfstr(line);
		if (maxlen >= 0)
		{
			for(i = strlen(line); i<8; i++) (void)addtoinfstr(' ');
		}
	}
	if (maxlen < 0) (void)addtoinfstr('\t');

	/* count the number of instances */
	total = 0;
	for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst) total++;
	if (maxlen < 0) sprintf(line, "%ld", total); else
		sprintf(line, "%4ld", total);
	(void)addstringtoinfstr(line);

	/* show other factors about the facet */
	if (maxlen < 0) (void)addtoinfstr('\t'); else
		(void)addstringtoinfstr("   ");
	if ((np->userbits&NLOCKED) != 0) (void)addstringtoinfstr("L"); else
		(void)addstringtoinfstr(" ");
	if (maxlen < 0) (void)addtoinfstr('\t'); else
		(void)addstringtoinfstr(" ");
	if ((np->userbits&NILOCKED) != 0) (void)addstringtoinfstr("I"); else
		(void)addstringtoinfstr(" ");
	if (maxlen < 0) (void)addtoinfstr('\t'); else
		(void)addstringtoinfstr(" ");
	if ((np->userbits&INCELLLIBRARY) != 0) (void)addstringtoinfstr("C"); else
		(void)addstringtoinfstr(" ");
	if (maxlen < 0) (void)addtoinfstr('\t'); else
		(void)addstringtoinfstr(" ");
	gooddrc = 0;
	var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER, dr_lastgooddrckey);
	if (var != NOVARIABLE)
	{
		lastgooddate = (UINTBIG)var->addr;
		if (np->revisiondate <= lastgooddate) gooddrc = 1;
	}
	if (gooddrc != 0) (void)addstringtoinfstr("D"); else
		(void)addstringtoinfstr(" ");
	if (maxlen < 0) (void)addtoinfstr('\t'); else
		(void)addstringtoinfstr(" ");
	if (net_ncchasmatch(np) != 0) (void)addstringtoinfstr("N"); else
		(void)addstringtoinfstr(" ");
	return(returninfstr());
}

/******************** KEYBOARD SUPPORT ********************/

/*
 * routine to check to see if library "lib" has changed and warn the
 * user prior to destroying changes to that library.  If "lib" is NOLIBRARY, all
 * libraries are checked.  "action" is the type of action to be performed:
 * 0=quit, 1=close library, 2=replace library
 * If the operation can be cancelled, "cancancel" is true.  Returns true
 * if the operation is to be aborted.
 */
BOOLEAN us_preventloss(LIBRARY *lib, INTBIG action, BOOLEAN cancancel)
{
	REGISTER INTBIG result, count;
	char *par[10];
	REGISTER LIBRARY *l;
	extern COMCOMP us_yesnop;

	for(l = el_curlib; l != NOLIBRARY; l = l->nextlibrary)
	{
		if ((l->userbits&HIDDENLIBRARY) != 0) continue;
		if (lib != NOLIBRARY && lib != l) continue;
		if ((l->userbits&(LIBCHANGEDMAJOR|LIBCHANGEDMINOR)) == 0) continue;

		(void)initinfstr();
		if ((l->userbits&LIBCHANGEDMAJOR) != 0)
			(void)formatinfstr(_("Library %s has changed significantly.  "), l->libname); else
				(void)formatinfstr(_("Library %s has changed insignificantly.  "), l->libname);
		switch (action)
		{
			case 0: (void)addstringtoinfstr(_("Save before quitting?"));   break;
			case 1: (void)addstringtoinfstr(_("Save before closing?"));    break;
			case 2: (void)addstringtoinfstr(_("Save before replacing?"));  break;
		}
		if (cancancel)
		{
			result = us_quitdlog(returninfstr(), (action==0 ? 1 : 0));
			if (result == 0)
			{
				ttyputverbose(M_("Keep working"));
				return(TRUE);
			}
			if (result == 1) continue;
			if (result == 2) break;
			if (result == 3)
			{
				/* save the library */
				(void)asktool(io_tool, "write", (INTBIG)l, (INTBIG)"binary");
			}
		} else
		{
			count = ttygetparam(returninfstr(), &us_yesnop, MAXPARS, par);
			if (count > 0 && namesamen(par[0], "yes", strlen(par[0])) == 0)
			{
				/* save the library */
				(void)asktool(io_tool, "write", (INTBIG)l, (INTBIG)"binary");
			}
		}
	}

	/* also check for option changes on quit */
	if (us_optionschanged && action == 0)
	{
		if (optionshavechanged())
		{
			if (us_saveoptdlog())
			{
				ttyputverbose(M_("Keep working"));
				return(TRUE);
			}
		}
	}
	return(FALSE);
}

/*
 * Routine to save all options by creating a dummy library and saving it (without
 * making options temporary first).
 */
void us_saveoptions(void)
{
	REGISTER INTBIG retval;
	REGISTER LIBRARY *l;
	REGISTER char *libname, *libfile;

	libname = us_tempoptionslibraryname();
	libfile = optionsfilepath();
	l = newlibrary(libname, libfile);
	if (l == NOLIBRARY)
	{
		ttyputerr(_("Cannot create options library %s"), libfile);
		return;
	}
	l->userbits |= READFROMDISK | HIDDENLIBRARY;
	retval = asktool(io_tool, "write", (INTBIG)l, (INTBIG)"nobackupbinary");
	killlibrary(l);
	if (retval != 0)
		ttyputerr(_("Could not save options library"));
	us_optionschanged = FALSE;
}

/*
 * Routine to construct a temporary library name that doesn't already exist.
 */
char *us_tempoptionslibraryname(void)
{
	static char libname[256];
	REGISTER LIBRARY *lib;
	REGISTER INTBIG i;

	for(i=1; ; i++)
	{
		sprintf(libname, "options%ld", i);
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			if (namesame(libname, lib->libname) == 0) break;
		if (lib == NOLIBRARY) break;
	}
	return(libname);
}

/*
 * routine to determine the color map entry that corresponds to the string
 * "pp".  If the string is a number then return that number.  If the string
 * contains layer letters from the current technology, return that index.
 * The value of "purpose" determines how to interpret the layer letters: 0
 * means that the letters indicate entries in the color map and 1 means that
 * only one letter is allowed and its layer number should be returned.  If no
 * entry can be determined, the routine issues an error and returns -1.
 */
INTBIG us_getcolormapentry(char *pp, BOOLEAN layerletter)
{
	char *ch, *pt1;
	REGISTER INTBIG color, i, high;
	GRAPHICS *desc;

	/* first see if the string is all digits and return that value if so */
	if (isanumber(pp)) return(myatoi(pp));

	/* for layer conversion, the job is simple */
	high = el_curtech->layercount;
	if (layerletter)
	{
		if (pp[0] == 0 || pp[1] != 0)
		{
			us_abortcommand(_("Can only give one layer letter"));
			return(-1);
		}
		for(i=0; i<high; i++)
		{
			for(ch = us_layerletters(el_curtech, i); *ch != 0; ch++)
				if (*ch == pp[0]) return(i);
		}
		us_abortcommand(_("Letter '%s' is not a valid layer"), pp);
		return(-1);
	}

	/* accumulate the desired color */
	color = 0;
	for(pt1 = pp; *pt1 != 0; pt1++)
	{
		/* find the layer that corresponds to letter "*pt1" */
		for(i=0; i<high; i++)
		{
			/* see if this layer has the right letter */
			for(ch = us_layerletters(el_curtech, i); *ch != 0; ch++)
				if (*ch == *pt1) break;
			if (*ch == 0) continue;

			/* get the color characteristics of this layer */
			desc = el_curtech->layers[i];
			if ((desc->bits & color) != 0)
			{
				us_abortcommand(_("No single color for the letters '%s'"), pp);
				return(-1);
			}
			color |= desc->col;
			break;
		}
		if (i == high)
		{
			us_abortcommand(_("Letter '%c' is not a valid layer"), *pt1);
			return(-1);
		}
	}
	return(color);
}

/*
 * routine to return a unique port prototype name in facet "facet" given that
 * a new prototype wants to be named "name".  The routine allocates space
 * for the string that is returned so this must be freed when done.
 */
char *us_uniqueportname(char *name, NODEPROTO *facet)
{
	char *str;

	str = us_uniqueobjectname(name, facet, VPORTPROTO, 0);

	if (us_uniqueretstr != NOSTRING) efree(us_uniqueretstr);
	(void)allocstring(&us_uniqueretstr, str, us_tool->cluster);
	return(us_uniqueretstr);
}

/*
 * Routine to determine whether the name "name" is unique in facet "facet"
 * (given that it is on objects of type "type").  Does not consider object
 * "exclude" (if nonzero).
 */
BOOLEAN us_isuniquename(char *name, NODEPROTO *facet, INTBIG type, void *exclude)
{
	REGISTER PORTPROTO *pp;
	REGISTER ARCINST *ai;
	REGISTER NODEINST *ni;
	REGISTER VARIABLE *var;

	switch (type)
	{
		case VPORTPROTO:
			for(pp = facet->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			{
				if (exclude != 0 && (PORTPROTO *)exclude == pp) continue;
				if (namesame(name, pp->protoname) == 0) return(FALSE);
			}
			break;
		case VNODEINST:
			for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				if (exclude != 0 && (NODEINST *)exclude == ni) continue;
				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
				if (var == NOVARIABLE) continue;
				if (namesame(name, (char *)var->addr) == 0) return(FALSE);
			}
			break;
		case VARCINST:
			for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			{
				if (exclude != 0 && (ARCINST *)exclude == ai) continue;
				var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
				if (var == NOVARIABLE) continue;
				if (namesame(name, (char *)var->addr) == 0) return(FALSE);
			}
			break;
	}
	return(TRUE);
}

/*
 * routine to return a unique object name in facet "facet" starting with the
 * name "name".
 */
char *us_uniqueobjectname(char *name, NODEPROTO *facet, INTBIG type, void *exclude)
{
	char *newname, *trailingpart, save;
	REGISTER INTBIG baselen, nextindex, i, adddash, startindex, endindex, spacing;
	BOOLEAN foundnumber;

	/* first see if the name is unique */
	if (us_isuniquename(name, facet, type, exclude)) return(name);

	/* see if there is a number in the name which can be incremented */
	baselen = strlen(name);
	nextindex = 1;
	trailingpart = 0;
	adddash = 1;
	for(;;)
	{
		if (name[baselen-1] == ']')
		{
			/* see if there is a single number inside square brackets */
			baselen--;
			trailingpart = &name[baselen];
			while (baselen > 0 && isdigit(name[baselen-1]) != 0) baselen--;
			if (baselen > 0 && name[baselen-1] == ':')
			{
				/* special case for range of numbers */
				endindex = myatoi(&name[baselen]);
				i = baselen - 1;
				while (i > 0 && isdigit(name[i-1]) != 0) i--;
				if (i > 0 && name[i-1] == '[')
				{
					startindex = myatoi(&name[i]);
					spacing = abs(endindex - startindex) + 1;
					baselen = i-1;

					for(nextindex = 1; ; nextindex++)
					{
						(void)initinfstr();
						for(i=0; i<baselen; i++) (void)addtoinfstr(name[i]);
						(void)formatinfstr("[%ld:%ld%s", startindex+spacing*nextindex,
							endindex+spacing*nextindex, trailingpart);
						newname = returninfstr();
						if (us_isuniquename(newname, facet, type, 0)) break;
					}
					return(newname);
				}
			}
#if 0
			for(i=baselen-1; i>0; i--)
				if (name[i] == '[' || name[i] == ':' || name[i] == ',') break;
			if (name[i] != '[')
			{
				/* did not find a single entry inside square brackets */
				while (baselen > 0 && name[baselen] != '[') baselen--;
				if (baselen == 0) baselen = strlen(name); else
				{
					trailingpart = &name[baselen];
					while (baselen > 0 && isdigit(name[baselen-1]) != 0) baselen--;
					if (isdigit(name[baselen]) != 0)
					{
						nextindex = atoi(&name[baselen]) + 1;
						if (baselen > 0 && ispunct(name[baselen-1])) baselen--;
					}
				}
			} else
#else
			for(i=baselen-1; i>0; i--)
				if (name[i] == '[') break;
#endif
			{
				/* if this index is not numeric and there is another one, try it */
				save = *trailingpart;   *trailingpart = 0;
				foundnumber = isanumber(&name[baselen]);
				*trailingpart = save;
				if (!foundnumber && i > 1 && name[i-1] == ']' &&
					isdigit(name[i-2]) != 0)
				{
					/* multidimensional array: look for numeric part in next section */
					baselen = i;
					continue;
				}

				/* found a valid index inside square brackets */
				nextindex = atoi(&name[baselen]) + 1;
				adddash = 0;
			}
		} else
		{
			/* not an arrayed name: look for a number at the end */
			while (baselen > 0 && isdigit(name[baselen-1]) != 0)
			{
				baselen--;
				adddash = 0;
			}
			nextindex = atoi(&name[baselen]) + 1;
		}
		break;
	}

	for(; ; nextindex++)
	{
		(void)initinfstr();
		for(i=0; i<baselen; i++) (void)addtoinfstr(name[i]);
		if (adddash != 0) (void)addtoinfstr((char)us_separatechar);
		(void)formatinfstr("%ld", nextindex);
		if (trailingpart != 0) (void)addstringtoinfstr(trailingpart);
		newname = returninfstr();
		if (us_isuniquename(newname, facet, type, 0)) break;
	}
	return(newname);
}

/*
 * routine to initialize the database variable "USER_layer_letters".  This
 * is called once at initialization and again whenever the array is changed.
 */
void us_initlayerletters(void)
{
	REGISTER VARIABLE *var;
	REGISTER TECHNOLOGY *t;

	if (us_layer_letter_data != 0) efree((char *)us_layer_letter_data);
	us_layer_letter_data = emalloc((el_maxtech * SIZEOFINTBIG), us_tool->cluster);
	if (us_layer_letter_data == 0) return;
	for(t = el_technologies; t != NOTECHNOLOGY; t = t->nexttechnology)
	{
		var = getvalkey((INTBIG)t, VTECHNOLOGY, VSTRING|VISARRAY, us_layer_letters_key);
		us_layer_letter_data[t->techindex] = (var == NOVARIABLE ? 0 : var->addr);
	}
}

/*
 * routine to return a string of unique letters describing layer "layer"
 * in technology "tech".  The letters for all layers of a given technology
 * must not intersect.  This routine accesses the "USER_layer_letters"
 * variable on the technology objects.
 */
char *us_layerletters(TECHNOLOGY *tech, INTBIG layer)
{
	REGISTER INTBIG addr;

	if (us_layer_letter_data == 0)
	{
		us_initlayerletters();
		if (us_layer_letter_data == 0) return("");
	}

	addr = us_layer_letter_data[tech->techindex];
	if (addr == 0) return("");
	return(((char **)addr)[layer]);
}

/*
 * routine to change an tool state.  The name of the tool is in "pt" (if "pt" is
 * null then an tool name is prompted).  The state of the tool is set to "state"
 * (0 for off, 1 for permanently off, 2 for on, 3 for on without catchup).
 */
void us_settool(INTBIG count, char *par[], INTBIG state)
{
	REGISTER INTBIG i;
	BOOLEAN toolstate;
	REGISTER char *pt;
	extern COMCOMP us_onofftoolp;
	REGISTER TOOL *tool;

	if (count == 0)
	{
		count = ttygetparam(M_("Which tool: "), &us_onofftoolp, MAXPARS, par);
		if (count == 0)
		{
			us_abortcommand(M_("Specify an tool to control"));
			return;
		}
	}
	pt = par[0];

	for(i=0; i<el_maxtools; i++)
		if (namesame(el_tools[i].toolname, pt) == 0) break;
	if (i >= el_maxtools)
	{
		us_abortcommand(_("No tool called %s"), pt);
		return;
	}
	tool = &el_tools[i];

	if (tool == us_tool && state <= 1)
	{
		us_abortcommand(M_("No! I won't go!"));
		return;
	}
	if ((tool->toolstate&TOOLON) == 0 && state <= 1)
	{
		ttyputverbose(M_("%s already off"), pt);
		return;
	}
	if ((tool->toolstate&TOOLON) != 0 && state > 1)
	{
		ttyputverbose(M_("%s already on"), pt);
		return;
	}
	if (state <= 1) ttyputverbose(M_("%s turned off"), tool->toolname); else
	{
		if ((tool->toolstate&TOOLINCREMENTAL) == 0 || state == 3)
			ttyputverbose(M_("%s turned on"), tool->toolname); else
				ttyputmsg(_("%s turned on, catching up on changes..."), tool->toolname);
	}

	if (state <= 1)
	{
		if (state != 0) toolstate = TRUE; else
			toolstate = FALSE;
		toolturnoff(tool, toolstate);
	} else
	{
		if (state == 2) toolturnon(tool, FALSE); else toolturnon(tool, TRUE);
		if ((tool->toolstate&TOOLINCREMENTAL) != 0 && state != 3)
			ttyputmsg(_("...%s caught up"), tool->toolname);
	}
}
