/*
 * 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[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic/commands/CmdRS.c,v 1.1.1.1 2005/05/09 23:29:24 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef SYSV
#include <string.h>
#endif

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/stack.h"
#include "utils/geometry.h"
#include "utils/utils.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "utils/main.h"
#include "commands/commands.h"
#include "textio/textio.h"
#include "graphics/graphics.h"
#include "utils/tech.h"
#include "drc/drc.h"
#include "textio/txcommands.h"
#include "utils/malloc.h"
#include "utils/netlist.h"
#include "netmenu/netmenu.h"
#include "select/select.h"
#include "utils/signals.h"
#include "sim/sim.h"

extern void DisplayWindow();


#if !defined(NO_SIM_MODULE) && defined(RSIM_MODULE)
/*
 * ----------------------------------------------------------------------------
 *
 * 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, DBWclientID);
    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


/*
 * ----------------------------------------------------------------------------
 *
 * 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.
 *
 * ----------------------------------------------------------------------------
 */

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 2 to 1, 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.
 *	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.
 *	Geometry alterations are not undoable!
 *
 * ----------------------------------------------------------------------------
 */

void
CmdScaleGrid(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    extern void DBScalePoint();
    int scalen, scaled;
    char *argsep;
    Rect rootBox;
    CellDef *rootBoxDef;

    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]);

	if (scalen <= 0 || scaled <= 0) goto scalegridusage;
	else if (scalen == scaled) goto scalegridreport;

	/* Reduce fraction by the greatest common factor */

	ReduceFraction(&scalen, &scaled);

	/* Scale cifinput and cifoutput */

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

	/* Scale drc rules */

	DRCTechScale(scalen, scaled);

	/* Scale extract parameters */

	ExtTechScale(scalen, scaled);

	/* Scale wiring parameters */

	WireTechScale(scalen, scaled);

	/* Scale LEF parameters */

#ifdef LEF_MODULE
	LefTechScale(scalen, scaled);
#endif

	/* Scale router parameters */

#ifdef ROUTE_MODULE
	RtrTechScale(scalen, scaled);
#endif

	/* Scale all tiles */

	DBScaleEverything(scaled, scalen);

	/* Save the current scale factor */

	DBLambda[0] *= scalen;
	DBLambda[1] *= scaled;
	ReduceFraction(&DBLambda[0], &DBLambda[1]);

	/* Rescale cursor box */

	if (ToolGetBox(&rootBoxDef, &rootBox))
	{
	    DBScalePoint(&rootBox.r_ll, scaled, scalen);
	    DBScalePoint(&rootBox.r_ur, scaled, scalen);
	    ToolMoveBox(TOOL_BL, &rootBox.r_ll, FALSE, rootBoxDef);
	    ToolMoveCorner(TOOL_TR, &rootBox.r_ur, FALSE, rootBoxDef);
	}

	/* Adjust all window viewing scales and positions and redraw */

	WindScale(scaled, scalen);

	/* This is harsh.  Might work to scale all distance measures in */
	/* the undo record, but this is simple and direct.		*/

	UndoFlush();

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

scalegridreport:
	TxPrintf("%d Magic internal unit%s = %d Lambda\n",
		DBLambda[1], (DBLambda[1] == 1) ? "" : "s", DBLambda[0]);

	return;
    }

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

#ifdef MAGIC_WRAPPER
/*
 * ----------------------------------------------------------------------------
 *
 * CmdSearch --
 *
 *	This is a low-level search command that allows a hierarchical tile
 *	search to execute a TCL procedure on each tile found.
 *
 *	Use: search [layers] <tcl_procedure>
 *
 *	<tcl_procedure> takes a list containing 4 integer values representing
 *	the bounding box (llx, lly, urx, ury) of the tile, and one value
 *	which is either a string containing the type name of the tile, or a
 *	list of two layers in the case of a split tile, representing LEFT
 *	and RIGHT types, respectively.
 *
 * ----------------------------------------------------------------------------
 */

void
CmdSearch(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    SearchContext scx;
    TileTypeBitMask visitMask;
    int xMask, windowMask;
    char *tclproc, *pptr;
    int cmdTclProcSrFunc();     /* Fwd declaration */
    CellDef *rootBoxDef;
    MagWindow *window;

    bzero(&scx, sizeof(SearchContext));
    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");
	return;
    }
    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;
	}
    }
    scx.scx_use = (CellUse *) window->w_surfaceID;
    scx.scx_trans = GeoIdentityTransform;

    if ((cmd->tx_argc == 1) || (cmd->tx_argc > 3))
    {
	TxError("Usage: search [layers] <tcl_procedure>\n");
	return;
    }

    if (cmd->tx_argc == 3)
    {
	if (!CmdParseLayers(cmd->tx_argv[1], &visitMask))
	    return;
    }
    else visitMask = DBAllButSpaceAndDRCBits;
    pptr = cmd->tx_argv[cmd->tx_argc - 1];
    if (*pptr == '{')           /* in-line procedure */
    {
	tclproc = StrDup(NULL, pptr + 1);
	pptr = tclproc + strlen(tclproc) - 1;
	if (*pptr == '}') *pptr = '\0';
    }
    else
	tclproc = StrDup(NULL, cmd->tx_argv[cmd->tx_argc - 1]);

    (void) DBTreeSrTiles(&scx, &visitMask, xMask,
		cmdTclProcSrFunc, (ClientData) tclproc);
   
    freeMagic(tclproc);
}   

/*
 * cmdTclProcSrFunc --
 *  
 * Filter procedure applied to tiles by cmdSearch() above.
 */ 
  
int
cmdTclProcSrFunc(tile, cxp)
    Tile *tile;
    TreeContext *cxp;
{
    Rect tileRect, rootRect;
    SearchContext *scx = cxp->tc_scx;
    char *tclproc = (char *)cxp->tc_filter->tf_arg;
    char evalstr[512], *eptr;
    int rval;

    TiToRect(tile, &tileRect);
    GeoTransRect(&scx->scx_trans, &tileRect, &rootRect);
    if (GEO_OVERLAP((&tileRect), &cxp->tc_scx->scx_area))
    {
	sprintf(evalstr, "%s %d %d %d %d ", tclproc, rootRect.r_xbot,
		rootRect.r_ybot, rootRect.r_xtop, rootRect.r_ytop);
	eptr = evalstr + strlen(evalstr);
#ifdef NONMANHATTAN
	if (IsSplit(tile))
	    sprintf(eptr, "{%s %s}\n", DBTypeLongNameTbl[SplitLeftType(tile)],
			DBTypeLongNameTbl[SplitRightType(tile)]);
	else
#endif
	    sprintf(eptr, "%s\n", DBTypeLongNameTbl[TiGetType(tile)]);

	rval = Tcl_EvalEx(magicinterp, evalstr, -1, 0);
	if (rval != TCL_OK) return 1;
    }
    /* Allow interrupts in case something goes wrong with the Tcl procedure */
    if (SigInterruptPending) return 1;
    return 0;
}   

#endif

/*
 * ----------------------------------------------------------------------------
 *
 * 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.
 *
 * ----------------------------------------------------------------------------
 */

void
CmdSee(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    int flags;
    bool off;
    char *arg;
    TileType i, j;
    TileTypeBitMask mask, *rmask;
    DBWclientRec *crec;

    windCheckOnlyWindow(&w, DBWclientID);
    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)
    {
	for (i = 0; i < DBNumUserLayers; i++)
	{
	    if (TTMaskHasType(&mask, i))
		TTMaskClearMask(&crec->dbw_visibleLayers,
			&DBLayerTypeMaskTbl[i]);
	}
	for (; i < DBNumTypes; i++)
	{
	    /* This part handles stacked contact types.  The display	*/
	    /* routine calls DBTreeSrUniqueTiles(), which displays	*/
	    /* stacked contact types only on their home plane.  Thus	*/
	    /* if we select a contact type, we should also select all	*/
	    /* stacking types for that contact that are on the same	*/
	    /* plane.							*/

	    rmask = DBResidueMask(i);
	    for (j = 0; j < DBNumUserLayers; j++)
		if (TTMaskHasType(rmask, j))
		    if (TTMaskHasType(&mask, j))
			if (DBPlane(i) == DBPlane(j))
			    TTMaskClearMask(&crec->dbw_visibleLayers,
					&DBLayerTypeMaskTbl[i]);
	}
	crec->dbw_flags &= ~flags;
    }
    else
    {
	for (i = 0; i < DBNumUserLayers; i++)
	{
	    if (TTMaskHasType(&mask, i))
		TTMaskSetMask(&crec->dbw_visibleLayers,
			&DBLayerTypeMaskTbl[i]);
	}
	for (; i < DBNumTypes; i++)
	{
	    rmask = DBResidueMask(i);
	    for (j = 0; j < DBNumUserLayers; j++)
		if (TTMaskHasType(rmask, j))
		    if (TTMaskHasType(&mask, j))
			if (DBPlane(i) == DBPlane(j))
			    TTMaskSetMask(&crec->dbw_visibleLayers,
					&DBLayerTypeMaskTbl[i]);
	}
	crec->dbw_flags |= flags;
    }
    WindAreaChanged(w, &w->w_screenArea);
    return;
}


/*
 * ----------------------------------------------------------------------------
 *
 * 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.
 *
 * ----------------------------------------------------------------------------
 */

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

    /* 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 SEL_AREA	 0
#define SEL_VISIBLE	 1
#define SEL_CELL	 2
#define SEL_CLEAR	 3
#define SEL_HELP	 4
#define SEL_KEEP	 5
#define SEL_MOVE	 6
#define SEL_PICK	 7
#define SEL_SAVE	 8
#define SEL_BOX		 9
#define SEL_CHUNK	10
#define SEL_REGION	11
#define SEL_NET		12
#define SEL_DEFAULT	13

    static char *cmdSelectOption[] =
    {
	"area",
	"visible",
	"cell",
	"clear",
	"help",
	"keep",
	"move",
	"pick",
	"save",
	"box",
	"chunk",
	"region",
	"net",
	NULL
    };
    static char *cmdSelectMsg[] =
    {
	"[more | less | nocycle] [layers]\n"
        "                                [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 | top] cell [name] [de]select cell under cursor, or \"name\"",
	"clear                           clear selection",
	"help                            print this message",
	"keep                            copy the selection to the layout",
	"move                            move selection to a location in the layout",
	"pick                            delete selection from layout",
	"save file                       save selection on disk in file.mag",
	"[more | less] box | chunk | region | net [layers]\n"
	"				 [de]select chunk/region/net specified by\n"
	"				 the lower left corner 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.
				 */
    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;
    CellDef *rootBoxDef;
    Transform trans, rootTrans, tmp1;
    Point p, rootPoint;
    Rect r, selarea;
    int option;
    bool layerspec;
    bool more = FALSE, less = FALSE, samePlace = TRUE;
#ifdef MAGIC_WRAPPER
    char *tclstr;
#endif
    
/* How close two clicks must be to be considered the same point: */

#define MARGIN 2

    bzero(&scx, sizeof(SearchContext));
    windCheckOnlyWindow(&w, DBWclientID);
    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.  Similarly for options
     * "less", "nocycle", "top", and "cell".
     */
    
    if (cmd->tx_argc >= 2)
    {
	int arg1len = strlen(cmd->tx_argv[1]);

	optionArgs = &cmd->tx_argv[1];
	if (!strncmp(cmd->tx_argv[1], "more", arg1len))
	{
	    more = TRUE;
	    less = FALSE;
	    optionArgs = &cmd->tx_argv[2];
	    cmd->tx_argc--;
	}
	else if (!strncmp(cmd->tx_argv[1], "less", arg1len))
	{
	    more = FALSE;
	    less = TRUE;
	    optionArgs = &cmd->tx_argv[2];
	    cmd->tx_argc--;
	}
	else if (!strncmp(cmd->tx_argv[1], "nocycle", arg1len))
	{
	    samePlace = FALSE;
	    more = FALSE;
	    less = FALSE;
	    type = TT_SELECTBASE - 1;	   /* avoid cycling between types */
	    optionArgs = &cmd->tx_argv[2];
	    cmd->tx_argc--;
	}

	else if (!strncmp(cmd->tx_argv[1], "top", arg1len)) 
	{
	    if ((cmd->tx_argc >= 3) && !strncmp(cmd->tx_argv[2],
			"cell", strlen(cmd->tx_argv[2])))
		optionArgs = &cmd->tx_argv[2];
	}
    }

    /* Check the option for validity. */

    if (cmd->tx_argc == 1)
	option = SEL_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 = SEL_HELP;
	    cmd->tx_argc = 2;
	}
	else if (option < 0)
	{
	    option = SEL_DEFAULT;
	    if (more || less)
		optionArgs = &cmd->tx_argv[1];
	    else
		optionArgs = &cmd->tx_argv[0];
	}
    }

#ifndef NO_SIM_MODULE
    SimRecomputeSel = TRUE;
#endif

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

	case SEL_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 SEL_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 SEL_CLEAR:
	    if ((more) || (less) || (cmd->tx_argc > 2)) goto usageError;
	    SelectClear();
	    return;
	
	/*--------------------------------------------------------------------
	 * Print out help information.
	 *--------------------------------------------------------------------
	 */

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

	/*--------------------------------------------------------------------
	 * Make a copy of the selection at its present loction but do not
	 * clear the selection.
	 *--------------------------------------------------------------------
	 */

	 case SEL_KEEP:
	    if ((more) || (less) || (cmd->tx_argc > 2)) goto usageError;
	    SelectAndCopy1();
	    GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
	    DBWHLRedraw(SelectRootDef, &selarea, FALSE);
	    return;

	/*--------------------------------------------------------------------
	 * Move the selection relative to the cell def
	 *--------------------------------------------------------------------
	 */

	 case SEL_MOVE:
	    if ((more) || (less) || (cmd->tx_argc != 4)) goto usageError;

	    p.p_x = cmdParseCoord(w, cmd->tx_argv[2], FALSE, TRUE);
	    p.p_y = cmdParseCoord(w, cmd->tx_argv[3], FALSE, FALSE);

	    /* Erase first, then recompute the transform */
	    GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
	    DBWHLRedraw(SelectRootDef, &selarea, TRUE);

	    GeoTransTranslate(p.p_x, p.p_y,
			&GeoIdentityTransform, &SelectUse->cu_transform);

	    GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
	    DBWHLRedraw(SelectRootDef, &selarea, FALSE);
	    return;

	/*--------------------------------------------------------------------
	 * Delete the selection from the layout, but don't clear the selection
	 * cell.  This allows the selection to be dragged around independently
	 * of the layout.
	 *--------------------------------------------------------------------
	 */

	 case SEL_PICK:
	    if ((more) || (less) || (cmd->tx_argc > 2)) goto usageError;
	    SelectDelete("picked", FALSE);
	    DBWHLRedraw(SelectRootDef, &selarea, FALSE);
	    return;

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

	 case SEL_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 SEL_BOX: case SEL_CHUNK: case SEL_REGION: case SEL_NET:
	    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
	 * SEL_CELL option also comes here (to share initialization code) but
	 * quickly branches away.
	 *--------------------------------------------------------------------
	 */

	case SEL_DEFAULT:
	    if (cmd->tx_argc > 2) goto usageError;
	    if (cmd->tx_argc == 2)
		layerspec = TRUE;
	    else
		layerspec = FALSE;
	    goto Okay;
	case SEL_CELL:
	    layerspec = FALSE;
Okay:
	    if (!(more || less)) SelectClear();
	    if (option == SEL_BOX || option == SEL_CHUNK || option == SEL_REGION
			|| option == SEL_NET)
	    {
		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 = 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 != SEL_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;
		    }
		}

		/* Set connectivity searching level */

		if (option == SEL_CHUNK || option == SEL_REGION || option == SEL_NET)
		{
		    samePlace = TRUE;
		    level = option;
		}
		else
		{
		    if (samePlace && lessCycle == less)
		    {
			level += 1;
			if (level > SEL_NET) level = SEL_CHUNK;
		    }
		    else level = SEL_CHUNK;

		    if (level != SEL_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 = SEL_CHUNK;
		    }
		}

		lessCycle = less;

		if (level == SEL_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 >= DBNumUserLayers)	/* used to be 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 == SEL_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 = SEL_NET;
		}
		if (level == SEL_NET)
		    SelectNet(&scx, type, crec->dbw_bitmask, (Rect *) NULL, less);
		return;
	    }

	/*--------------------------------------------------------------------
	 * We get here either if the SEL_CELL option is requested, or under
	 * the SEL_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) && (optionArgs == &cmd->tx_argv[2]))
	    {
		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 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);

#ifdef MAGIC_WRAPPER
	    tclstr = Tcl_escape(printPath);
	    Tcl_SetResult(magicinterp, tclstr, TCL_DYNAMIC);
#else
	    TxPrintf("Selected cell is %s (%s)\n", use->cu_def->cd_name,
		    printPath);
#endif
	    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.
 *
 * ----------------------------------------------------------------------------
 */

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
int (*CmdStretchCmd)() = NULL;
    /* ARGSUSED */

void
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 */

#if !defined(NO_SIM_MODULE) && defined(RSIM_MODULE)
/*
 * ----------------------------------------------------------------------------
 *
 * 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, DBWclientID);
    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, DBWclientID);
    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 --
 *
 * Set the box snapping to align either with the nearest user-defined grid,
 * the nearest integer lambda value, or turn snapping off (aligns to internal
 * units).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

void
CmdSnap(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    static char *names[] = { "off", "internal", "lambda", "grid", "user", "on", 0 };
    int n, mingrid;
    DBWclientRec *crec;

    if (cmd->tx_argc < 2) goto printit;

    n = Lookup(cmd->tx_argv[1], names);
    if (n < 0)
    {
	TxPrintf("Usage: snap [internal | lambda | user]\n");
	return;
    }
    switch (n)
    {
	case 0: case 1:
	    DBWSnapToGrid = DBW_SNAP_INTERNAL;
	    break;
	case 2:
	    DBWSnapToGrid = DBW_SNAP_LAMBDA;
	    break;
	case 3: case 4: case 5:
	    DBWSnapToGrid = DBW_SNAP_USER;
	    break;
    }

printit:
    TxPrintf("Box is aligned to %s grid\n", (DBWSnapToGrid == DBW_SNAP_INTERNAL)
	? "internal" : ((DBWSnapToGrid == DBW_SNAP_LAMBDA) ? "lambda" : "user"));
}


#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, diag;
    int pNum, direction;
    PaintUndoInfo ui;

    windCheckOnlyWindow(&w, DBWclientID);
    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;
	    diag = DBTransformDiagonal(TT_DIAGONAL | dir | side, &RootToEditTransform);

	    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],
				diag, &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, diag;
    int pNum, direction;
    PaintUndoInfo ui;

    windCheckOnlyWindow(&w, DBWclientID);
    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;
	diag = DBTransformDiagonal(TT_DIAGONAL | dir | side, &RootToEditTransform);

	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],
				diag, &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


/*
 * ----------------------------------------------------------------------------
 *
 * 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.
 *
 * ----------------------------------------------------------------------------
 */

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

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

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

	indx = GeoNameToPos(cmd->tx_argv[1], FALSE, TRUE);
	if (indx < 0)
	    return;
	if (cmd->tx_argc == 3)
	{
	    if (DBWSnapToGrid != DBW_SNAP_USER)
	    {
		amountx = amounty = cmdParseCoord(w, cmd->tx_argv[2], TRUE, FALSE);
	    }
	    else
	    {
		switch (indx)
		{
		    case GEO_EAST: case GEO_WEST:
			amountx = cmdParseCoord(w, cmd->tx_argv[2], TRUE, TRUE);
			amounty = amountx;
			break;
		    case GEO_NORTH: case GEO_SOUTH:
			amounty = cmdParseCoord(w, cmd->tx_argv[2], TRUE, FALSE);
			amountx = amounty;
			break;
		    default:
			amountx = cmdParseCoord(w, cmd->tx_argv[2], TRUE, TRUE);
			amounty = cmdParseCoord(w, cmd->tx_argv[2], TRUE, FALSE);
			break;
		}
	    }
	}
	else
	{
	    amountx = cmdParseCoord(w, "1l", TRUE, TRUE);
	    amounty = cmdParseCoord(w, "1l", TRUE, FALSE);
	}

	switch (indx)
	{
	    case GEO_NORTH:
		xdelta = 0;
		ydelta = amounty;
		break;
	    case GEO_SOUTH:
		xdelta = 0;
		ydelta = -amounty;
		break;
	    case GEO_EAST:
		xdelta = amountx;
		ydelta = 0;
		break;
	    case GEO_WEST:
		xdelta = -amountx;
		ydelta = 0;
		break;
	    case GEO_NORTHEAST:
		xdelta = amountx;
		ydelta = amounty;
		break;
	    case GEO_NORTHWEST:
		xdelta = -amountx;
		ydelta = amounty;
		break;
	    case GEO_SOUTHEAST:
		xdelta = amountx;
		ydelta = -amounty;
		break;
	    case GEO_SOUTHWEST:
		xdelta = -amountx;
		ydelta = -amounty;
		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);
}
