/*
 * CmdRS.c --
 *
 * Commands with names beginning with the letters R through S.
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1985, 1990 Regents of the University of California. * 
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] = "$Header: /ufs/repository/magic/commands/CmdRS.c,v 1.9 2006/02/08 03:45:53 fang Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include "misc/magic.h"
#include "utils/stack.h"
#include "utils/geometry.h"
#include "utils/utils.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "cif/cif.h"
#include "database/database.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "main/main.h"
#include "commands/commands.h"
#include "commands/CmdInt.h"
#include "textio/textio.h"
#include "graphics/graphics.h"
#include "tech/tech.h"
#include "drc/drc.h"
#include "textio/txcommands.h"
#include "utils/malloc.h"
#include "router/router.h"
#include "gcr/gcr.h"
#include "utils/doubleint.h"
#include "utils/heap.h"
#include "grouter/grouter.h"
#include "netlist/netlist.h"
#include "netmenu/netmenu.h"
#include "select/select.h"
#include "sim/sim.h"
#include "utils/strdup.h"
#include "plow/plow.h"

extern void DisplayWindow();
extern double atof();
extern char *strchr();


#ifndef NO_SIM
/*
 * ----------------------------------------------------------------------------
 *
 * CmdRsim
 *
 * 	Starts Rsim under Magic.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Rsim is forked.
 *
 * ----------------------------------------------------------------------------
 */
  
void
CmdRsim(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{

    if ((cmd->tx_argc == 1) && (!SimRsimRunning)) {
	TxPrintf("usage: rsim [options] file\n");
	return;
    }
    if ((cmd->tx_argc != 1) && (SimRsimRunning)) {
	TxPrintf("Simulator already running.  You cannot start another.\n");
	return;
    }
    windCheckOnlyWindow(&w);
    if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) {
	TxError("Put the cursor in a layout window.\n");
	return;
    }
    if (cmd->tx_argc != 1) {
	cmd->tx_argv[cmd->tx_argc] = (char *) 0;
	SimStartRsim(cmd->tx_argv);
    }
    SimConnectRsim(FALSE);

}
#endif
#ifndef NO_ROUTE

/*
 * ----------------------------------------------------------------------------
 *
 * CmdRoute --
 *
 * 	Route the nets in the current netlist.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the edit cell.
 *
 * ----------------------------------------------------------------------------
 */

#define ROUTERCHECKS	0
#define ROUTERDEBUG	1
#define ROUTERFILE	2
#define ROUTERSHOWMAP	3
#define ROUTERSHOWRES	4
#define ROUTERSHOWEND	5
#define ROUTEREND	6
#define ROUTERHELP	7
#define ROUTERJOG	8
#define ROUTERMMAX	9
#define ROUTERNETLIST	10
#define ROUTEROBST	11
#define	ROUTERORIGIN	12
#define ROUTERSTATS     13
#define ROUTERSETTINGS	14
#define ROUTERSTEADY	15
#define ROUTERTECHINFO	16
#define ROUTERVIAS	17
#define	ROUTERVIAMIN	18


    /* ARGSUSED */
void
CmdRoute(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    int option;
    GCRChannel *ch;
    Rect area;
    NLNetList netList;
    char *netListName;

    static char *cmdRouteOption[] =
    {	
	"*checks		toggle column checking",
	"*debug		toggle router trace messages",
	"*file file		route from a channel data file",
	"*map			toggle channel obstacle map display",
	"*showcolumns		toggle channel column text display",
	"*showfinal		toggle text display of final channels",

	"end real		set channel router end constant",
	"help			print this help information",
	"jog int		set minimum jog length (grid units)",
	"metal			toggle metal maximization",
	"netlist file		set current netlist",
	"obstacle real		set obstacle constant",
	"origin [x y]		print or set routing grid origin",
	"stats			print and clear previous route statistics",
	"settings		show router parameters", 
	"steady int		set steady net constant",
	"tech			show router technology information",
	"vias int		set metal maximization via limit (grid units)",
	"viamin			via minimization",
	NULL
    };

    if (cmd->tx_argc == 1)
    {
	if (!ToolGetEditBox(&area))
	    return;
	Route(EditCellUse, &area);
	return;
    }

    option = Lookup(cmd->tx_argv[1], cmdRouteOption);
    if (option == -1)
    {
	TxError("Ambiguous routing option: \"%s\"\n", cmd->tx_argv[1]);
	goto usage2;
    }
    if (option < 0)
	goto usage;

    switch (option)
    {
	/* These first few options are for wizards only.
	 */
	case ROUTERDEBUG:
	    GcrDebug= !GcrDebug;
	    TxPrintf("Router debug tracing: %s\n", GcrDebug ? "on" : "off");
	    break;
	case ROUTERCHECKS:
	    GcrNoCheck= !GcrNoCheck;
	    TxPrintf("Router column checking: %s\n", GcrNoCheck ? "on" : "off");
	    break;
	case ROUTERFILE:
	    /*  Display routing problems from file directly on the screen.
	     */
	    if(cmd->tx_argc!=3)
		goto wrongNumArgs;
	    if((ch = GCRRouteFromFile(cmd->tx_argv[2])) == (GCRChannel *) NULL)
		 TxError("Bad channel from file %s\n", cmd->tx_argv[2]);
	    else RtrPaintBack(ch, EditCellUse->cu_def);
	    break;
	case ROUTERSHOWRES:
	    GcrShowResult= !GcrShowResult;
	    TxPrintf("Show channel columns: %s\n", GcrShowResult ? "on":"off");
	    break;
	case ROUTERSHOWEND:
	    GcrShowEnd= !GcrShowEnd;
	    TxPrintf("Show finished channels: %s\n", GcrShowEnd ? "on" : "off");
	    break;
	case ROUTERSHOWMAP:
	    GcrShowMap= !GcrShowMap;
	    TxPrintf("Show channel maps: %s\n", GcrShowMap ? "on" : "off");
	    break;

	case ROUTERHELP:
	    TxPrintf("Router commands have the form \"route option\",\n");
	    TxPrintf("where option is one of:\n\n");
	    if(cmd->tx_argc==2)
		for (option=ROUTEREND; option<= ROUTERVIAMIN; option++)
		    TxPrintf("  %s\n", cmdRouteOption[option]);
	    else
	    if((cmd->tx_argc==3) && !strcmp(cmd->tx_argv[2], "*wizard"))
		for (option=0; option !=ROUTEREND; option++)
			TxPrintf("  %s\n", cmdRouteOption[option]);
	    TxPrintf("\n");
	    break;
	case ROUTERSTATS:
	    RtrPaintStats(TT_SPACE, 0);
	    break;
	case ROUTERSETTINGS:
	    TxPrintf("Router parameter settings:\n");
	    TxPrintf("   Channel end constant... %f\n", RtrEndConst);
	    TxPrintf("   Metal maximization..... %s\n", RtrDoMMax ? "on":"off");
	    TxPrintf("   Minimum jog length..... %d\n", GCRMinJog);
	    TxPrintf("   Net list............... \"%s\"\n", NMNetlistName());
	    TxPrintf("   Obstacle constant...... %f\n", GCRObstDist);
	    TxPrintf("   Steady net constant.... %d\n", GCRSteadyNet);
	    TxPrintf("   Via limit.............. %d\n", RtrViaLimit);
	    if((cmd->tx_argc!=3) || strcmp(cmd->tx_argv[2], "*wizard"))
		break;
	    TxPrintf("\n");
	    TxPrintf("   Debug tracing.......... %s\n", GcrDebug ? "on":"off");
	    TxPrintf("   Column checking........ %s\n", GcrNoCheck?"on":"off");
	    TxPrintf("   Show channel columns... %s\n",
		    GcrShowResult ? "on" : "off");
	    TxPrintf("   Show finished channels  %s\n", GcrShowEnd?"on":"off");
	    TxPrintf("   Show channel maps...... %s\n", GcrShowMap?"on":"off");
	    break;
	case ROUTERTECHINFO:
	    TxPrintf("Router Technology Information:\n");
	    TxPrintf("   Preferred layer..... %s; width %d\n",
		    DBTypeLongName(RtrMetalType), RtrMetalWidth);
	    TxPrintf("   Alternate layer..... %s; width %d\n",
		    DBTypeLongName(RtrPolyType), RtrPolyWidth);
	    TxPrintf("   Contacts............ %s; ",
		    DBTypeLongName(RtrContactType));
	    TxPrintf("width %d; offset %d; surrounds %d, %d\n", RtrContactWidth,
		    RtrContactOffset, RtrMetalSurround, RtrPolySurround);
	    TxPrintf("   Subcell separations: %d up; %d down\n",
		    RtrSubcellSepUp, RtrSubcellSepDown);
	    TxPrintf("   Router grid spacing: %d\n", RtrGridSpacing);
	    break;
	case ROUTERMMAX:
	    RtrDoMMax= !RtrDoMMax;
	    TxPrintf("Metal maximization: %s\n", RtrDoMMax ? "on" : "off");
	    break;
	case ROUTERVIAS:
	    if(cmd->tx_argc!=2)
	    {
		if(cmd->tx_argc!=3)
		    goto wrongNumArgs;
		if(!sscanf(cmd->tx_argv[2], "%d", &RtrViaLimit))
		    TxError("Bad value for via limit\n");
	    }
	    TxPrintf("Via limit is %d\n", RtrViaLimit);
	    break;
	case ROUTEREND:
	    if(cmd->tx_argc!=2)
	    {
		if(cmd->tx_argc!=3)
		    goto wrongNumArgs;
		if(!sscanf(cmd->tx_argv[2], "%f", &RtrEndConst))
		    TxError("Bad value for channel end distance\n");
	    }
	    TxPrintf("Channel end constant is %f\n", RtrEndConst);
	    break;
	case ROUTERJOG:
	    if(cmd->tx_argc!=2)
	    {
		if(cmd->tx_argc!=3)
		    goto wrongNumArgs;
		if(!sscanf(cmd->tx_argv[2], "%d", &GCRMinJog))
		    TxError("Bad value for minimum jog length\n");
	    }
	    TxPrintf("Minimum jog length is %d\n", GCRMinJog);
	    break;
	case ROUTEROBST:
	    if(cmd->tx_argc!=2)
	    {
		if(cmd->tx_argc!=3)
		    goto wrongNumArgs;
		if(!sscanf(cmd->tx_argv[2], "%f", &GCRObstDist))
		    TxError("Bad value for obstacle constant\n");
	    }
	    TxPrintf("Obstacle constant is %f\n", GCRObstDist);
	    break;
	case ROUTERSTEADY:
	    if(cmd->tx_argc!=2)
	    {
		if(cmd->tx_argc!=3)
		    goto wrongNumArgs;
		if(!sscanf(cmd->tx_argv[2], "%d", &GCRSteadyNet))
		    TxError("Bad value for steady net constant\n");
	    }
	    TxPrintf("Steady net constant is %d\n", GCRSteadyNet);
	    break;
	case ROUTERNETLIST:
	    if(cmd->tx_argc!=2)
	    {
		if(cmd->tx_argc!=3)
		    goto wrongNumArgs;
		NMNewNetlist(cmd->tx_argv[2]);
	    }
	    TxPrintf("Current list is \"%s\"\n", NMNetlistName());
	    break;
	case ROUTERORIGIN:
	    if (cmd->tx_argc != 2)
	    {
		if (cmd->tx_argc != 4)
		    goto wrongNumArgs;
		if (!StrIsInt(cmd->tx_argv[2]) || !StrIsInt(cmd->tx_argv[3]))
		{
		    TxError("Origin coordinates must be integers\n");
		    break;
		}
		RtrOrigin.p_x = atoi(cmd->tx_argv[2]);
		RtrOrigin.p_y = atoi(cmd->tx_argv[3]);
		break;
	    }
	    TxPrintf("Routing grid origin = (%d,%d)\n",
			RtrOrigin.p_x, RtrOrigin.p_y);
	    break;
	case ROUTERVIAMIN:
	    if (!ToolGetEditBox(&area))
		return;
	    if (!NMHasList())
	    {
		netListName = EditCellUse->cu_def->cd_name;
		TxPrintf("No netlist selected yet;  using \"%s\".\n", netListName);
		NMNewNetlist(netListName);
	    }
	    else
		netListName = NMNetlistName();

	    if ( NLBuild(EditCellUse, &netList))
	    {
		int nvia;

		nvia = RtrViaMinimize(EditCellUse->cu_def);
		DBWAreaChanged(EditCellUse->cu_def, &area,
		    DBW_ALLWINDOWS, &DBAllButSpaceBits);
		WindUpdate();
		TxPrintf("\n%d vias removed\n",nvia);
		NLFree(&netList);
	    }
	    break;
    } /* switch*/
    return;

wrongNumArgs:
    TxError("Wrong number of arguments to %s option.\n", cmd->tx_argv[1]);
    TxError("Type \":route help\" for help.\n");
	    return;
usage:
    TxError("\"%s\" isn't a valid router option.", cmd->tx_argv[1]);

usage2:
    TxError("  Type \"route help\" for help.\n");
    return;
}
#else
     /* these guys are used in a couple of places */
     int RtrMetalWidth=2, RtrPolyWidth=2, RtrContactWidth=2;
#endif

/*
 * ----------------------------------------------------------------------------
 *
 * CmdSave --
 *
 * Implement the "save" command.
 * Writes the EditCell out to a disk file.
 *
 * Usage:
 *	save [file]
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes the cell out to file, if specified, or the file
 *	associated with the cell otherwise.
 *	Updates the caption in the window if the name of the edit
 *	cell has changed.
 *	Clears the modified bit in the cd_flags.
 *
 * ----------------------------------------------------------------------------
 */

    /* ARGSUSED */
void
CmdSave(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    if (cmd->tx_argc > 2)
    {
	TxError("Usage: %s [file]\n", cmd->tx_argv[0]);
	return;
    }

    ASSERT(EditCellUse != (CellUse *) NULL, "CmdSave");
    DBUpdateStamps();
    if (cmd->tx_argc == 2)
    {
	if (CmdIllegalChars(cmd->tx_argv[1], "[],", "Cell name"))
	    return;
	cmdSaveCell(EditCellUse->cu_def, cmd->tx_argv[1], FALSE, TRUE);
    }
    else cmdSaveCell(EditCellUse->cu_def, (char *) NULL, FALSE, TRUE);
}


/*
 * ----------------------------------------------------------------------------
 *
 * CmdScaleGrid --
 *
 *	This procedure scales magic units with respect to lambda.  A scaling
 *	of 1 to 2, for example, makes the magic units half their former size,
 *	allowing magic to read CIF files with components on the half-lambda
 *	grid without the necessity of altering the technology file for the
 *	process.
 *
 * Usage:
 *	scalegrid a b	(or)	scalegrid a/b	(or) scalegrid a:b
 *
 *	"a" and "b" are integers
 *
 * Results:
 *	magic internal units are scaled by factor a/b.
 *
 * Side Effects:
 *	cifinput and cifoutput scale factors are multiplied by a/b.
 *	All drc widths and distances are multiplied by b/a.
 * To be done:
 *	All layout tile coordinates are multiplied by b/a.
 *	The current drawing scale and position are altered to maintain
 *	   the current view.
 *	All windows are redrawn in case scaling has caused round-off
 *	   truncation of tile coordinates.
 *
 * ----------------------------------------------------------------------------
 */
void
CmdScaleGrid(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    int scalen, scaled;
    char *argsep;

    if ((cmd->tx_argc == 2) || (cmd->tx_argc == 3))
    {
	if (cmd->tx_argc == 2)
	{
	    if (((argsep = strchr(cmd->tx_argv[1], ':')) != NULL) ||
			((argsep = strchr(cmd->tx_argv[1], '/')) != NULL))
	    {
		*argsep++ = '\0';
	        if (!StrIsInt(argsep))
		    goto scalegridusage;
		else
		    scaled = atoi(argsep);
	    }
	    else
		goto scalegridusage;
	}
	else	
	{
	    if (!StrIsInt(cmd->tx_argv[2]))
		goto scalegridusage;
	    else
		scaled = atoi(cmd->tx_argv[2]);
	}

	if (!StrIsInt(cmd->tx_argv[1]))
	    goto scalegridusage;
	else
	    scalen = atoi(cmd->tx_argv[1]);

	/* Scale cifinput and cifoutput */

	CIFTechInputScale(scalen, scaled);
	CIFTechOutputScale(scalen, scaled);

	/* Scale drc rules */

	DRCTechScale(scalen, scaled);

	/* Scale all tiles */

	/* Adjust all window viewing scales and positions */

	/* Redraw all windows */

	TxPrintf("Magic internal unit scaled by %.3f\n",
		(float)scalen / (float)scaled);
	return;
    }

scalegridusage:
    TxError("Usage:  scalegrid a b, where a and b are integers\n");
    return;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdSee --
 *
 * 	This procedure is used to enable or disable display of certain
 *	things on the screen.
 *
 * Usage:
 *	see [no] stuff
 *
 *	Stuff consists of mask layers or the keyword "allSame"
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The indicated mask layers are enabled or disabled from being
 *	displayed in the current window.
 *
 * ----------------------------------------------------------------------------
 */

    /* ARGSUSED */
void
CmdSee(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    int flags;
    bool off;
    char *arg;
    TileTypeBitMask mask;
    DBWclientRec *crec;

    windCheckOnlyWindow(&w);
    if ((w == NULL) || (w->w_client != DBWclientID))
    {
	TxError("Point to a layout window first.\n");
	return;
    }
    crec = (DBWclientRec *) w->w_clientData;

    arg = (char *) NULL;
    off = FALSE;
    flags = 0;
    if (cmd->tx_argc > 1)
    {
	if (strcmp(cmd->tx_argv[1], "no") == 0)
	{
	    off = TRUE;
	    if (cmd->tx_argc > 2) arg = cmd->tx_argv[2];
	}
	else arg = cmd->tx_argv[1];
	if ((cmd->tx_argc > 3) || ((cmd->tx_argc == 3) && !off))
	{
	    TxError("Usage: see [no] layers|allSame\n");
	    return;
	}
    }

    /* Figure out which things to set or clear.  Don't ever make space
     * invisible:  that doesn't make any sense.
     */

    if (arg != NULL)
    {
	if (strcmp(arg, "allSame") == 0)
	{
	    mask = DBZeroTypeBits;
	    flags = DBW_ALLSAME;
	}
	else
	{
	    if (!CmdParseLayers(arg, &mask))
		return;
	}
    }
    else mask = DBAllTypeBits;

    if (TTMaskHasType(&mask, L_LABEL))
	flags |= DBW_SEELABELS;
    TTMaskClearType(&mask, L_LABEL);
    TTMaskClearType(&mask, L_CELL);
    TTMaskClearType(&mask, TT_SPACE);

    if (off)
    {
	int i;
	for (i = 0; i < DBNumUserLayers; i++)
	{
	    if (TTMaskHasType(&mask, i))
		TTMaskClearMask(&crec->dbw_visibleLayers,
			&DBLayerTypeMaskTbl[i]);
	}
	crec->dbw_flags &= ~flags;
    }
    else
    {
	int i;
	for (i = 0; i < DBNumUserLayers; i++)
	{
	    if (TTMaskHasType(&mask, i))
		TTMaskSetMask(&crec->dbw_visibleLayers,
			&DBLayerTypeMaskTbl[i]);
	}
	crec->dbw_flags |= flags;
    }
    WindAreaChanged(w, &w->w_screenArea);
    return;
}
#ifndef NO_ROUTE

/*
 * ----------------------------------------------------------------------------
 * CmdSeeFlags --
 *
 * 	Display router-generated flags on the highlight layer.  User points
 *	to a channel and invokes the command with an argument naming the
 *	flag to be displayed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Paints highlights on the screen.  Allocates a special cell def and
 *	initializes a hash table glTileToChannel the first time it is called.
 * ----------------------------------------------------------------------------
 */

    /* ARGSUSED */
void
CmdSeeFlags(w, cmd)
    MagWindow * w;
    TxCommand *cmd;
{
    Rect      rootRect;
    Point     point;
    MagWindow * window;

    window = CmdGetRootPoint(&point, &rootRect);
    if (window == (MagWindow *) NULL)
	return;

    if(cmd->tx_argc > 2)
    {
	TxError("Useage:  %s [flag name]\n", cmd->tx_argv[0]);
	return;
    }
    if(cmd->tx_argc == 2)
    {
	GCRShow(&point, cmd->tx_argv[1]);
	TxError("%s:  flag highlights turned on.\n", cmd->tx_argv[0]);
    }
    else
    {
	NMUnsetCell();
	TxError("%s:  flag highlights turned off.\n", cmd->tx_argv[0]);
    }
}
#endif

/*
 * ----------------------------------------------------------------------------
 *
 * cmdSelectArea --
 *
 * 	This is a utility procedure used by CmdSelect to do area
 *	selection.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The selection is augmented to contain all the information on
 *	layers that is visible under the box, including paint, labels,
 *	and unexpanded subcells.
 *
 * ----------------------------------------------------------------------------
 */

	/* ARGSUSED */
void
cmdSelectArea(layers, less)
    char *layers;			/* Which layers are to be selected. */
    bool less;
{
    SearchContext scx;
    TileTypeBitMask mask;
    int windowMask, xMask;
    DBWclientRec *crec;
    MagWindow *window;

    bzero(&scx, sizeof(SearchContext));
    window = ToolGetBoxWindow(&scx.scx_area, &windowMask);
    if (window == NULL)
    {
	TxPrintf("The box isn't in a window.\n");
	return;
    }

    /* Since the box may actually be in multiple windows, we have to
     * be a bit careful.  If the box is only in one window, then there's
     * no problem.  If it's in more than window, the cursor must
     * disambiguate the windows.
     */
    
    xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
    if ((windowMask & ~xMask) != 0)
    {
	window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL);
        xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
	if ((windowMask & xMask) == 0)
	{
	    TxPrintf("The box is in more than one window;  use the cursor\n");
	    TxPrintf("to select the one you want to select from.\n");
	    return;
	}
    }
    if (CmdParseLayers(layers, &mask))
    {
	if (TTMaskEqual(&mask, &DBSpaceBits))
	    (void) CmdParseLayers("*,label", &mask);
	TTMaskClearType(&mask, TT_SPACE);
    }
    else return;
    
    if (less)
      {
	(void) SelRemoveArea(&scx.scx_area, &mask);
	return;
      }

    scx.scx_use = (CellUse *) window->w_surfaceID;
    scx.scx_trans = GeoIdentityTransform;
    crec = (DBWclientRec *) window->w_clientData;
    SelectArea(&scx, &mask, crec->dbw_bitmask);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cmdSelectVisible --
 *
 * 	This is a utility procedure used by CmdSelect to do area
 *	selection of visible paint.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The selection is augmented to contain all the information on
 *	layers that is visible under the box, including paint, labels,
 *	and expanded subcells.
 *
 * ----------------------------------------------------------------------------
 */

	/* ARGSUSED */
void
cmdSelectVisible(layers, less)
    char *layers;			/* Which layers are to be selected. */
    bool less;
{
    SearchContext scx;
    TileTypeBitMask mask;
    int windowMask, xMask;
    DBWclientRec *crec;
    MagWindow *window;

    bzero(&scx, sizeof(SearchContext));
    window = ToolGetBoxWindow(&scx.scx_area, &windowMask);
    if (window == NULL)
    {
	TxPrintf("The box isn't in a window.\n");
	return;
    }

    /* Since the box may actually be in multiple windows, we have to
     * be a bit careful.  If the box is only in one window, then there's
     * no problem.  If it's in more than window, the cursor must
     * disambiguate the windows.
     */
    
    xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
    if ((windowMask & ~xMask) != 0)
    {
	window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL);
        xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
	if ((windowMask & xMask) == 0)
	{
	    TxPrintf("The box is in more than one window;  use the cursor\n");
	    TxPrintf("to select the one you want to select from.\n");
	    return;
	}
    }
    if (CmdParseLayers(layers, &mask))
    {
	if (TTMaskEqual(&mask, &DBSpaceBits))
	    (void) CmdParseLayers("*,label", &mask);
	TTMaskClearType(&mask, TT_SPACE);
    }
    else return;
    
    if (less)
      {
	(void) SelRemoveArea(&scx.scx_area, &mask);
	return;
      }

    scx.scx_use = (CellUse *) window->w_surfaceID;
    scx.scx_trans = GeoIdentityTransform;
    crec = (DBWclientRec *) window->w_clientData;
    {
	int i;
	for (i = 0; i < DBNumUserLayers; i++)
	{
	    if((TTMaskHasType(&mask, i)) && !(TTMaskHasType(&crec->dbw_visibleLayers, i)))
		TTMaskClearType(&mask, i);
	}
    }
    SelectArea(&scx, &mask, crec->dbw_bitmask);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdSelect --
 *
 * Implement the "select" command.
 *
 * Usage:
 *	select [option args]
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The current selection is modified.  See the user documentation
 *	for all the possible things this command can do.
 *
 * ----------------------------------------------------------------------------
 */

    /* ARGSUSED */
void
CmdSelect(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    TileTypeBitMask mask;
    SearchContext scx;
    DBWclientRec *crec;
    MagWindow *window;
    bool samePlace;

    /* The two tables below define the allowable selection options, and
     * also the message printed out by ":select help" to describe the
     * options (can't use the same table for both because of the presence
     * of the "more" option).  Note that there's one more entry in the
     * second table, due to a help message for ":select" with no arguments.
     */

#define AREA	0
#define VISIBLE	1
#define CELL	2
#define SCLEAR	3
#define HELP	4
#define SAVE	5
#define SBOX	6
#define DEFAULT	7

    static char *cmdSelectOption[] =
    {
	"area",
	"visible",
	"cell",
	"clear",
	"help",
	"save",
	"box",
	NULL
    };
    static char *cmdSelectMsg[] =
    {
	"[more | less] [layers]          [de]select paint chunk/region/net under\n\
                                           cursor, or [de]select subcell if cursor\n\
		                           over space",
	"[more | less] area [layers]     [de]select all info under box in layers",
	"[more | less] visible [layers]  [de]select all visible info under box in layers",
	"[more | less] cell [name]       [de]select cell under cursor, or \"name\"",
	"clear                           clear selection",
	"help                            print this message",
	"save file                       save selection on disk in file.mag",
	"[more | less] box [layers]      [de]select chunk/region/net \n\
					 specified by lower left corner\n\
					 of the current box",
	NULL
    };

    static TileType type = TT_SELECTBASE-1;
				/* Type of material being pointed at.
				 * Remembered across commands so that when
				 * multiples types are pointed to, consecutive
				 * selections will cycle through them.
				 */
    static Rect lastArea = {{-100, -100}, {-200, -200}};
				/* Used to remember region around what was
				 * pointed at in the last select command:  a
				 * new selection in this area causes the next
				 * bigger thing to be selected.
				 */
    static int lastCommand;	/* Serial number of last command:  the next
				 * bigger thing is only selected when there
				 * are several select commands in a row.
				 */
    static Rect chunkSelection;	/* Used to remember the size of the last chunk
				 * selected.
				 */
    static int level;		/* How big a piece to select.  See definitions
				 * below.
				 */
#define CHUNK 0
#define REGION 1
#define NET 2
    static CellUse *lastUse;	/* The last cellUse selected.  Used to step
				 * through multiple uses underneath the cursor.
				 */
    static Point lastIndices;	/* The array indices of the last cell selected.
				 * also used to step through multiple uses.
				 */
    static bool lessCycle = FALSE, lessCellCycle = FALSE;
    char path[200], *printPath, **msg, **optionArgs;
    TerminalPath tpath;
    CellUse *use;
    Transform trans, rootTrans, tmp1;
    Point p;
    Rect r;
    bool more, less, layerspec;
    int option;
    
/* How close two clicks must be to be considered the same point: */

#define MARGIN 2

    bzero(&scx, sizeof(SearchContext));
    windCheckOnlyWindow(&w);
    if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
    {
	TxError("Put the cursor in a layout window\n");
	return;
    }

    /* See if "more" was given.  If so, just strip off the "more" from
     * the argument list and set the "more" flag.
     */
    
    if ((cmd->tx_argc >= 2)
	    && (strncmp(cmd->tx_argv[1], "more", strlen(cmd->tx_argv[1]))
	    == 0))
    {
	more = TRUE;
	less = FALSE;
	optionArgs = &cmd->tx_argv[2];
	cmd->tx_argc--;
    }
    else if ((cmd->tx_argc >= 2)
	     && (strncmp(cmd->tx_argv[1], "less", strlen(cmd->tx_argv[1])) == 0))
      {
	more = FALSE;
	less = TRUE;
	optionArgs = &cmd->tx_argv[2];
	cmd->tx_argc--;
      }
    else
    {
	more = FALSE;
	less = FALSE;
	optionArgs = &cmd->tx_argv[1];
    }

    /* Check the option for validity. */

    if (cmd->tx_argc == 1)
	option = DEFAULT;
    else
    {
	option = Lookup(optionArgs[0], cmdSelectOption);
	if (option < 0 && cmd->tx_argc != 2)
	{
	    TxError("\"%s\" isn't a valid select option.\n", cmd->tx_argv[1]);
	    option = HELP;
	    cmd->tx_argc = 2;
	}
	else if (option < 0)
	{
	    option = DEFAULT;
	    if (more || less)
		optionArgs = &cmd->tx_argv[1];
	    else
		optionArgs = &cmd->tx_argv[0];
	}
    }

#ifndef NO_SIM
    SimRecomputeSel = TRUE;
#endif

    switch (option)
    {
	/*--------------------------------------------------------------------
	 * Select everything under the box, perhaps looking only at
	 * particular layers.
	 *--------------------------------------------------------------------
	 */

	case AREA:
	    if (cmd->tx_argc > 3)
	    {
		usageError:
		TxError("Bad arguments:\n    select %s\n",
			cmdSelectMsg[option+1]);
		return;
	    }
	    if (!(more || less)) SelectClear();
	    if (cmd->tx_argc == 3)
		cmdSelectArea(optionArgs[1], less);
	    else cmdSelectArea("*,label,subcell", less);
	    return;
	
	/*--------------------------------------------------------------------
	 * Select everything under the box, perhaps looking only at
	 * particular layers, but only if its visible.
	 *--------------------------------------------------------------------
	 */

	case VISIBLE:
	    if (cmd->tx_argc > 3) goto usageError;
	    if (!(more || less)) SelectClear();
	    if (cmd->tx_argc == 3)
		cmdSelectVisible(optionArgs[1], less);
	    else cmdSelectVisible("*,label,subcell", less);
	    return;
	
	/*--------------------------------------------------------------------
	 * Clear out all of the material in the selection.
	 *--------------------------------------------------------------------
	 */

	case SCLEAR:
	    if ((more) || (less) || (cmd->tx_argc > 2)) goto usageError;
	    SelectClear();
	    return;
	
	/*--------------------------------------------------------------------
	 * Print out help information.
	 *--------------------------------------------------------------------
	 */

	case HELP:
	    TxPrintf("Selection commands are:\n");
	    for (msg = &(cmdSelectMsg[0]); *msg != NULL; msg++)
		TxPrintf("    select %s\n", *msg);
	    return;

	/*--------------------------------------------------------------------
	 * Save the selection as a new Magic cell on disk.
	 *--------------------------------------------------------------------
	 */

	 case SAVE:
	    if (cmd->tx_argc != 3) goto usageError;

	    /* Be sure to paint DRC check information into the cell before
	     * saving it!  Otherwise DRC problems may not be detected.  Also
	     * be sure to adjust labels in the cell.
	     */

	    DBAdjustLabels(SelectDef, &TiPlaneRect);
	    DBPaintPlane(SelectDef->cd_planes[PL_DRC_CHECK],
		    &SelectDef->cd_bbox,
		    DBStdPaintTbl(TT_CHECKPAINT, PL_DRC_CHECK),
		    (PaintUndoInfo *) NULL);

	    DBUpdateStamps();
	    cmdSaveCell(SelectDef, cmd->tx_argv[2], FALSE, FALSE);
	    return;
	
	case SBOX:
	    if (cmd->tx_argc > 3) goto usageError;
	    if (cmd->tx_argc == 3)
		layerspec = TRUE;
	    else
		layerspec = FALSE;
	    goto Okay;

	/*--------------------------------------------------------------------
	 * The default case (no args):  see what's under the cursor.  Select
	 * paint if there is any, else select a cell.  In both cases,
	 * multiple clicks cycle through larger and larger selections.  The
	 * CELL option also comes here (to share initialization code) but
	 * quickly branches away.
	 *--------------------------------------------------------------------
	 */

	case DEFAULT:
	    if (cmd->tx_argc > 2) goto usageError;
	    if (cmd->tx_argc == 2)
		layerspec = TRUE;
	    else
		layerspec = FALSE;
	    goto Okay;
	case CELL:
	    layerspec = FALSE;
Okay:
	    if (!(more || less)) SelectClear();
	    if (option == SBOX)
	    {
		CellDef *rootBoxDef;
		int windowMask, xMask;

		if (!ToolGetBox (&rootBoxDef, NULL)) {
		    TxError ("Box tool must be present\n");
		    return;
		}
		window = ToolGetBoxWindow (&scx.scx_area, &windowMask);
		if (!window)
		    TxError ("Box tool does not exist in any window\n");
		xMask = ((DBWclientRec *)window->w_clientData)->dbw_bitmask;
		if ((windowMask & ~xMask) != 0) {
		    window = CmdGetRootPoint ((Point *) NULL, (Rect *) NULL);
		    xMask = ((DBWclientRec *)window->w_clientData)->dbw_bitmask;
		    if ((windowMask & xMask) == 0) {
			TxError("Box present in multiple windows; use the cursor\nto select the one you want\n");
			return;
		    }
		}
		scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1;
		scx.scx_area.r_ytop = scx.scx_area.r_ybot + 1;
	    }
	    else
	    {
	        window = CmdGetRootPoint((Point *) NULL, &scx.scx_area);
	    }
	    if (window == NULL) return;
	    scx.scx_use = (CellUse *) window->w_surfaceID;
	    scx.scx_trans = GeoIdentityTransform;
	    crec = (DBWclientRec *) window->w_clientData;
	    DBSeeTypesAll(scx.scx_use, &scx.scx_area, crec->dbw_bitmask, &mask);
	    TTMaskAndMask(&mask, &crec->dbw_visibleLayers);
	    TTMaskAndMask(&mask, &DBAllButSpaceAndDRCBits);

	    /* See if we're pointing at the same place as we were the last time
	     * this command was invoked, and if this command immediately follows
	     * another selection comand.
	     */
	
	    if (GEO_ENCLOSE(&cmd->tx_p, &lastArea)
		    && (lastCommand+1 == TxCommandNumber))
		samePlace = TRUE;
	    else samePlace = FALSE;
	    lastArea.r_xbot = cmd->tx_p.p_x - MARGIN;
	    lastArea.r_ybot = cmd->tx_p.p_y - MARGIN;
	    lastArea.r_xtop = cmd->tx_p.p_x + MARGIN;
	    lastArea.r_ytop = cmd->tx_p.p_y + MARGIN;
	    lastCommand = TxCommandNumber;

	    /* If there's material under the cursor, select some paint.
	     * Repeated selections at the same place result in first a
	     * chunk being selected, then a region of a particular type,
	     * then a whole net.
	     */

	    if (!TTMaskIsZero(&mask) && (option != CELL))
	    {
		if (layerspec == TRUE)
		{
		    /* User specified a layer.  Use the smallest type
		     * specified by the user and present under the
		     * box/cursor to begin the selection
		     */
		    TileTypeBitMask uMask;
	
		    if (CmdParseLayers (optionArgs[1], &uMask))
		    {
			if (TTMaskEqual (&uMask, &DBSpaceBits))
			    CmdParseLayers ("*,label", &uMask);
		    }
		    else
		    {
			TxError ("Invalid layer specification\n");
			return;
		    }

		    TTMaskAndMask (&mask, &uMask);

		    if (TTMaskIsZero (&mask))
		    {
			TxError ("No paint of this type under the cursor/box\n");
			return;
		    }
		}
		if (samePlace && lessCycle == less)
		{
		    level += 1;
		    if (level > NET) level = CHUNK;
		}
		else level = CHUNK;

		if (level != CHUNK && !TTMaskHasType (&mask, type)) {
		    /* User specified a new mask, and the current tile
		     * type being expanded is not in the set of types
		     * which the user wants us to use => reset level
		     */
		    level = CHUNK;
		}

		lessCycle = less;

		if (level == CHUNK)
		{
		    /* Pick a tile type to use for selection.  If there are
		     * several different types under the cursor, pick one of
		     * them.  This code remembers which type was used to
		     * choose last time, so that consecutive selections will
		     * use different types.
		     */

		    for (type += 1; ; type += 1)
		    {
			if (type >= DBNumTypes)
			    type = TT_SELECTBASE;
			if (TTMaskHasType(&mask, type)) break;
		    }

		    SelectChunk(&scx, type, crec->dbw_bitmask, &chunkSelection, less);
		    if (!less)
		      DBWSetBox(scx.scx_use->cu_def, &chunkSelection);
		}
		if (level == REGION)
		{
		    /* If a region has the same size as the preceding chunk,
		     * then we haven't added anything to the selection, so
		     * go on immediately and select the whole net.
		     */

		    Rect area;

		    SelectRegion(&scx, type, crec->dbw_bitmask, &area, less);
		    if (GEO_SURROUND(&chunkSelection, &area))
			level = NET;
		}
		if (level == NET)
		    SelectNet(&scx, type, crec->dbw_bitmask, (Rect *) NULL, less);
		return;
	    }

	/*--------------------------------------------------------------------
	 * We get here either if the CELL option is requested, or under
	 * the DEFAULT case where there's no paint under the mouse.  In
	 * this case, select a subcell.
	 *--------------------------------------------------------------------
	 */

	    if (layerspec == TRUE)
	    {
		TileTypeBitMask uMask;

		if (CmdParseLayers (optionArgs[1], &uMask))
		{
		    TxError ("No paint of this type under the cursor/box\n");
		}
		else
		{
		    TxError ("Invalid layer specification\n");
		}
		return;
	    }

	    if (cmd->tx_argc > 3) goto usageError;

	    /* If an explicit cell use id is provided, look for that cell
	     * and select it.  In this case, defeat all of the "multiple
	     * click" code.
	     */
	    
	    if (cmd->tx_argc == 3)
	    {
		SearchContext scx2;

		bzero(&scx2, sizeof(SearchContext));
		DBTreeFindUse(optionArgs[1], scx.scx_use, &scx2);
		use = scx2.scx_use;
		if (use == NULL)
		{
		    TxError("Couldn't find a cell use named \"%s\"\n",
			    optionArgs[1]);
		    return;
		}
		trans = scx2.scx_trans;
		p.p_x = scx2.scx_x;
		p.p_y = scx2.scx_y;
		printPath = optionArgs[1];
		samePlace = FALSE;
		lastArea.r_xbot = lastArea.r_ybot = -1000;
		lastArea.r_xtop = lastArea.r_ytop = -1000;
	    }
	    else
	    {
		/* Find the cell underneath the cursor.  If this is a
		 * second or later click at the same position, select
		 * the "next" cell underneath the point (see comments
		 * in DBSelectCell() for what "next" means).
		 */

		tpath.tp_first = tpath.tp_next = path;
		tpath.tp_last = &path[(sizeof path) - 2];
		if ((lastUse == scx.scx_use) || !samePlace || (lessCellCycle != less))
		    lastUse = NULL;
		lessCellCycle = less;
		use = DBSelectCell(scx.scx_use, lastUse, &lastIndices,
			&scx.scx_area, crec->dbw_bitmask, &trans, &p, &tpath);
    
		/* Use the window's root cell if nothing else is found. */

		if (use == NULL)
		{
		    use = lastUse = scx.scx_use;
		    p.p_x = scx.scx_use->cu_xlo;
		    p.p_y = scx.scx_use->cu_ylo;
		    trans = GeoIdentityTransform;
		    printPath = scx.scx_use->cu_id;
		}
		else
		{
		    printPath = index(path, '/');
		    if (printPath == NULL)
			printPath = path;
		    else printPath++;
		}
	    }

	    lastUse = use;
	    lastIndices = p;

	    /* The translation stuff is funny, since we got one
	     * element of the array, but not necessarily the
	     * lower-left element.  To get the transform for the
	     * array as a whole, subtract off for the indx of
	     * the element.
	     */

	    GeoInvertTrans(DBGetArrayTransform(use, p.p_x, p.p_y), &tmp1);
	    GeoTransTrans(&tmp1, &trans, &rootTrans);

	    if (less)
	      SelectRemoveCellUse(use, &rootTrans);
	    else
	      SelectCell(use, scx.scx_use->cu_def, &rootTrans, samePlace);
	    GeoTransRect(&trans, &use->cu_def->cd_bbox, &r);
	    DBWSetBox(scx.scx_use->cu_def, &r);

	    TxPrintf("Selected cell is %s (%s)\n", use->cu_def->cd_name,
		    printPath);
	    return;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdSideways --
 *
 * Implement the "sideways" command.
 *
 * Usage:
 *	sideways
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The selection and box are flipped left-to-right, using the
 *	center of the selection as the axis for flipping.
 *
 * ----------------------------------------------------------------------------
 */

    /* ARGSUSED */
void
CmdSideways(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    Transform trans;
    Rect rootBox, bbox;
    CellDef *rootDef;

    if (cmd->tx_argc != 1)
    {
	TxError("Usage: %s\n", cmd->tx_argv[0]);
	return;
    }

    /* To flip the selection sideways, first flip it around the
     * y-axis, then move it back so its lower-left corner is in
     * the same place that it used to be.
     */
    
    GeoTransRect(&GeoSidewaysTransform, &SelectDef->cd_bbox, &bbox);
    GeoTranslateTrans(&GeoSidewaysTransform,
	SelectDef->cd_bbox.r_xbot - bbox.r_xbot,
	SelectDef->cd_bbox.r_ybot - bbox.r_ybot, &trans);

    SelectTransform(&trans);

    /* Flip the box, if it exists and is in the same window as the
     * selection.
     */
    
    if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef))
    {
	Rect newBox;

	GeoTransRect(&trans, &rootBox, &newBox);
	DBWSetBox(rootDef, &newBox);
    }

    return;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdShell
 *
 * Implement the "shell" or "!" command.
 *
 * Usage:
 *	shell [command]
 *
 * Results:
 *	Executes the command in a unix shell
 *
 * Side effects:
 *	May alter unix files
 *
 * ----------------------------------------------------------------------------
 */
void
CmdShell(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    int i, cmdlength;
    char *command;

    if (cmd->tx_argc != 1) {
	cmdlength = 1;
	for (i = 1; i < cmd->tx_argc; i++) {
	    cmdlength = cmdlength + strlen(cmd->tx_argv[i]) + 1;
	}
	command = mallocMagic((unsigned) (cmdlength));
	(void) strcpy(command, cmd->tx_argv[1]);
	for (i = 2; i < cmd->tx_argc; i++) {
	    strcat(command, " ");
	    strcat(command, cmd->tx_argv[i]);
	}
	system(command);
	freeMagic(command);
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdSgraph
 *
 * Implement the "sgraph" command.
 *
 * Usage:
 *	sgraph [off|add|delete|debug]
 *	sgraph [show|auto] [vertical|horizontal]
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Calls the 'stretchgraph' module, if present.
 *
 * ----------------------------------------------------------------------------
 */

#ifdef	LLNL
/* Void (*CmdStretchCmd)() = NULL; */
void (*CmdStretchCmd)() = NULL;
    /* ARGSUSED */

CmdSgraph(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    if (CmdStretchCmd != NULL)
    {
	(*CmdStretchCmd)(w, cmd);
    }
    else
    {
	TxError("Sorry, the sgraph command doesn't work in this version.\n");
	TxError("(Magic was not linked with stretchgraph module.)\n");
    }
}
#endif	/* LLNL */
#ifndef NO_SIM
/*
 * ----------------------------------------------------------------------------
 *
 * CmdStartRsim
 *
 * 	This command starts Rsim under Magic, escapes Rsim, and returns
 *	back to Magic.
 *
 * Results:
 *	Rsim is forked from Magic.
 * 
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */
void
CmdStartRsim(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    static char rsimstr[] = "rsim";

    if ((cmd->tx_argc == 1) && (!SimRsimRunning)) {
	TxPrintf("usage: startrsim [options] file\n");
	return;
    }
    if ((cmd->tx_argc != 1) && (SimRsimRunning)) {
	TxPrintf("Simulator already running.  You cannont start another.\n");
	return;
    }
    windCheckOnlyWindow(&w);
    if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) {
	TxError("Put the cursor in a layout window.\n");
	return;
    }

    /* change argv[0] to be "rsim" and send it to Rsim_start */

    cmd->tx_argv[0] = rsimstr;
    if (cmd->tx_argc != 1) {
	cmd->tx_argv[cmd->tx_argc] = (char *) 0;
	SimStartRsim(cmd->tx_argv);
    }
    SimConnectRsim(TRUE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdSimCmd
 *
 *	Applies the given rsim command to the currently selected nodes.
 *
 * Results:
 *	Whatever rsim replys to the commands input.
 *
 * Side effects:
 *	None.
 *
 * ---------------------------------------------------------------------------- 
 */
void
CmdSimCmd(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    static char cmdbuf[200];
    char 	*strptr;
    char 	*nodeCmd;
    int 	i;

    if (!SimRsimRunning) {
	TxPrintf("You must first start the simulator by using the rsim command.\n");
	return;
    }
    if (cmd->tx_argc == 1) {
	TxPrintf("usage: simcmd command [options]\n");
	return;
    }
    windCheckOnlyWindow(&w);
    if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) {
	TxError("Put the cursor in a layout window.\n");
	return;
    }

    /* check to see whether to apply the command to each node selected,
     * or whether to just ship the command to rsim without any node
     * names.
     */
    nodeCmd = SimGetNodeCommand( cmd->tx_argv[1] );

    strcpy( cmdbuf, (nodeCmd != NULL) ? nodeCmd : cmd->tx_argv[1] );
    strptr = cmdbuf + strlen(cmdbuf);
    *strptr++ = ' ';
    *strptr = '\0';

    for (i = 2; i <= cmd->tx_argc - 1; i++) {
	strcpy(strptr, cmd->tx_argv[i]);
	strcat(strptr, " ");
	strptr += strlen(strptr) + 1;
    }

    if (nodeCmd != NULL) {
	SimSelection(cmdbuf);
    }
    else {
	SimRsimIt(cmdbuf, "");

        while (TRUE) {
	    if (!SimGetReplyLine(&strptr)) {
		break;
	    }
	    if (!strptr) {
		break;
	    }
	    TxPrintf("%s\n", strptr);
	}
    }
}
#endif


/*
 * ----------------------------------------------------------------------------
 *
 * CmdSnap --
 *
 * Enable/disable the box snapping to the nearest user-defined grid.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */
void
CmdSnap(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    static char *names[] = { "off", "on", 0 };
    int n;

    if (cmd->tx_argc != 2) goto printit;

    n = Lookup(cmd->tx_argv[1], names);
    if (n < 0)
    {
	TxPrintf("Usage: snap on   or   snap off\n");
	return;
    }
    DBWSnapToGrid = n;

printit:
    TxPrintf("Snapping to grid is %sabled\n", DBWSnapToGrid ? "en" : "dis");
}
#ifndef NO_PLOW


#ifdef NONMANHATTAN

/*
 * ----------------------------------------------------------------------------
 *
 * CmdSplit --
 *
 * Split a tile with a diagonal (nonmanhattan geometry)
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the geometry of the edit cell.
 *
 * ----------------------------------------------------------------------------
 */
void
CmdSplit(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    Rect editRect;
    TileTypeBitMask mask1, mask2, *cmask;
    TileType t, dir, side;
    int pNum, direction;
    PaintUndoInfo ui;

    windCheckOnlyWindow(&w);
    if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
    {
	TxError("Put the cursor in a layout window\n");
	return;
    }

    if (cmd->tx_argc != 3 && cmd->tx_argc != 4)
    {
	TxError("Usage: %s dir layer [layer2]\n", cmd->tx_argv[0]);
	return;
    }

    if (!ToolGetEditBox(&editRect)) return;

    if (!CmdParseLayers(cmd->tx_argv[2], &mask1))
        return;

    if ((direction = GeoNameToPos(cmd->tx_argv[1], FALSE, TRUE)) < 0)
	return;

    if (cmd->tx_argc == 4)
    {
	if (!CmdParseLayers(cmd->tx_argv[3], &mask2))
	    return;
	TTMaskClearType(&mask2, TT_SPACE);
    }
    else
	TTMaskZero(&mask2);

    TTMaskClearType(&mask1, TT_SPACE);

    direction = (direction >> 1) - 1;
    dir = (direction & 0x1) ? 0 : TT_DIRECTION;

    for (t = TT_SPACE + 1; t < DBNumTypes; t++)
    {
	side = (direction & 0x2) ? 0 : TT_SIDE;
	for (cmask = &mask1; cmask != NULL; cmask = ((cmask == &mask1) ? &mask2 : NULL))
	{
	    if (cmask == &mask2) side = (side) ? 0 : TT_SIDE;

	    if (TTMaskHasType(cmask, t))
	    {
		EditCellUse->cu_def->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
		ui.pu_def = EditCellUse->cu_def;
		for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
		    if (DBPaintOnPlane(t, pNum))
		    {
			ui.pu_pNum = pNum;
			DBNMPaintPlane(EditCellUse->cu_def->cd_planes[pNum],
				TT_DIAGONAL | dir | side,
				&editRect, DBStdPaintTbl(t, pNum), &ui);
		    }
	    }
	}
    }

    SelectClear();
    DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask1);
    DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask2);
    DBReComputeBbox(EditCellUse->cu_def);
    DRCCheckThis (EditCellUse->cu_def, TT_CHECKPAINT, &editRect);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdSplitErase --
 *
 * Erase a diagonal section from a tile (nonmanhattan geometry)
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the geometry of the edit cell.
 *
 * ----------------------------------------------------------------------------
 */
void
CmdSplitErase(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    Rect editRect;
    TileTypeBitMask mask;
    TileType t, dir, side;
    int pNum, direction;
    PaintUndoInfo ui;

    windCheckOnlyWindow(&w);
    if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
    {
	TxError("Put the cursor in a layout window\n");
	return;
    }

    if (cmd->tx_argc != 2 && cmd->tx_argc != 3)
    {
	TxError("Usage: %s dir [layer]\n", cmd->tx_argv[0]);
	return;
    }

    if (!ToolGetEditBox(&editRect)) return;

    if ((direction = GeoNameToPos(cmd->tx_argv[1], FALSE, TRUE)) < 0)
	return;

    if (cmd->tx_argc == 2)
	(void) CmdParseLayers("*", &mask);
    else if (!CmdParseLayers(cmd->tx_argv[2], &mask))
	return;

    if (TTMaskEqual(&mask, &DBSpaceBits))
	(void) CmdParseLayers("*,label", &mask);
    TTMaskClearType(&mask, TT_SPACE);
    if (TTMaskIsZero(&mask))
	return;

    direction = (direction >> 1) - 1;
    dir = (direction & 0x1) ? 0 : TT_DIRECTION;

    for (t = TT_SPACE + 1; t < DBNumTypes; t++)
    {
	side = (direction & 0x2) ? 0 : TT_SIDE;
	if (TTMaskHasType(&mask, t))
	{
	    EditCellUse->cu_def->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
	    ui.pu_def = EditCellUse->cu_def;
	    for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
		if (DBPaintOnPlane(t, pNum))
		{
		    ui.pu_pNum = pNum;
		    DBNMPaintPlane(EditCellUse->cu_def->cd_planes[pNum],
				TT_DIAGONAL | dir | side,
				&editRect, DBStdEraseTbl(t, pNum), &ui);
		}
	}
    }

    SelectClear();
    DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask);
    DBReComputeBbox(EditCellUse->cu_def);
    DRCCheckThis (EditCellUse->cu_def, TT_CHECKPAINT, &editRect);
}

#endif

/*
 * ----------------------------------------------------------------------------
 *
 * CmdStraighten --
 *
 * Straighten jogs in an area by pulling in a particular direction.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the geometry of the edit cell.
 *
 * ----------------------------------------------------------------------------
 */
void
CmdStraighten(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    Rect editBox;
    int dir;

    windCheckOnlyWindow(&w);
    if (w == (MagWindow *) NULL)
    {
	TxError("Point to a window first\n");
	return;
    }

    if (cmd->tx_argc != 2)
	goto usage;

    dir = GeoNameToPos(cmd->tx_argv[1], TRUE, FALSE);
    if (dir < 0)
	goto usage;
    dir = GeoTransPos(&RootToEditTransform, dir);

    if (EditCellUse == (CellUse *) NULL)
    {
	TxError("There is no edit cell!\n");
	return;
    }
    if (!ToolGetEditBox(&editBox))
    {
	TxError("The box is not in a window over the edit cell.\n");
	return;
    }

    PlowStraighten(EditCellUse->cu_def, &editBox, dir);
    return;

usage:
    TxError("Usage: straighten manhattan-direction\n");
}
#endif

/*
 * ----------------------------------------------------------------------------
 *
 * CmdStretch --
 *
 * Implement the "stretch" command.
 *
 * Usage:
 *	stretch [direction [distance]]
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Moves everything that's currently selected, erases material that
 *	the selection would sweep over, and fills in material behind the
 *	selection.
 *
 * ----------------------------------------------------------------------------
 */

    /* ARGSUSED */
/* Void */
void
CmdStretch(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    Transform t;
    Rect rootBox, newBox;
    CellDef *rootDef;
    int xdelta, ydelta;

    if (cmd->tx_argc > 3)
    {
	badUsage:
	TxError("Usage: %s [direction [amount]]\n", cmd->tx_argv[0]);
	return;
    }

    if (cmd->tx_argc > 1)
    {
	int indx, amount;

	indx = GeoNameToPos(cmd->tx_argv[1], TRUE, TRUE);
	if (indx < 0)
	    return;
	if (cmd->tx_argc == 3)
	{
	    if (!StrIsInt(cmd->tx_argv[2])) goto badUsage;
	    amount = atoi(cmd->tx_argv[2]);
	}
	else amount = 1;

	switch (indx)
	{
	    case GEO_NORTH:
		xdelta = 0;
		ydelta = amount;
		break;
	    case GEO_SOUTH:
		xdelta = 0;
		ydelta = -amount;
		break;
	    case GEO_EAST:
		xdelta = amount;
		ydelta = 0;
		break;
	    case GEO_WEST:
		xdelta = -amount;
		ydelta = 0;
		break;
	    default:
		ASSERT(FALSE, "Bad direction in CmdStretch");
		return;
	}
	GeoTransTranslate(xdelta, ydelta, &GeoIdentityTransform, &t);

	/* Move the box by the same amount as the selection, if the
	 * box exists.
	 */

	if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef))
	{
	    GeoTransRect(&t, &rootBox, &newBox);
	    DBWSetBox(rootDef, &newBox);
	}
    }
    else
    {
	/* Use the displacement between the box lower-left corner and
	 * the point as the transform.  Round off to a Manhattan distance.
	 */
	
	Point rootPoint;
	MagWindow *window;
	int absX, absY;

	if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef))
	{
	    TxError("\"Stretch\" uses the box lower-left corner as a place\n");
	    TxError("    to pick up the selection for stretching, but the\n");
	    TxError("    box isn't in a window containing the selection.\n");
	    return;
	}
	window = ToolGetPoint(&rootPoint, (Rect *) NULL);
	if ((window == NULL) ||
	    (EditRootDef != ((CellUse *) window->w_surfaceID)->cu_def))
	{
	    TxError("\"Stretch\" uses the point as the place to put down a\n");
	    TxError("    the selection, but the point doesn't point to the\n");
	    TxError("    edit cell.\n");
	    return;
	}
	xdelta = rootPoint.p_x - rootBox.r_xbot;
	ydelta = rootPoint.p_y - rootBox.r_ybot;
	if (xdelta < 0) absX = -xdelta;
	else absX = xdelta;
	if (ydelta < 0) absY = -ydelta;
	else absY = ydelta;
	if (absY <= absX) ydelta = 0;
	else xdelta = 0;
	GeoTransTranslate(xdelta, ydelta, &GeoIdentityTransform, &t);
	GeoTransRect(&t, &rootBox, &newBox);
	DBWSetBox(rootDef, &newBox);
    }
    
    SelectStretch(xdelta, ydelta);
}
