magic/commands/CmdCD.c

4157 lines
104 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* CmdCD.c --
*
* Commands with names beginning with the letters C through D.
*
* *********************************************************************
* * 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-8.0/commands/CmdCD.c,v 1.7 2010/06/24 12:37:15 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.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 "utils/utils.h"
#include "textio/textio.h"
#include "drc/drc.h"
#include "graphics/graphics.h"
#include "textio/txcommands.h"
#include "textio/textio.h"
#include "cif/cif.h"
#include "calma/calma.h"
#include "utils/styles.h"
#include "router/rtrDcmpose.h"
#include "select/select.h"
#include "utils/signals.h"
#include "utils/malloc.h"
#include "cif/CIFint.h"
#include "cif/CIFread.h"
/* The following structure is used by CmdCorner to keep track of
* areas to be filled.
*/
struct cmdCornerArea
{
Rect cca_area; /* Area to paint. */
TileType cca_type; /* Type of material. */
struct cmdCornerArea *cca_next; /* Next in list of areas to paint. */
};
/* Forward declarations */
int cmdDumpFunc();
bool cmdDumpParseArgs();
#ifdef CALMA_MODULE
/*
* ----------------------------------------------------------------------------
*
* CmdCalma --
*
* Implement the "gds" or "calma" command.
*
* Usage:
* gds option args
* calma option args
*
* Results:
* None.
*
* Side effects:
* There are no side effects on the circuit. Currently, there
* is only a single option, "write", to write a CALMA stream
* file.
*
* ----------------------------------------------------------------------------
*/
#define CALMA_HELP 0
#define CALMA_ARRAYS 1
#define CALMA_CONTACTS 2
#define CALMA_DRCCHECK 3
#define CALMA_FLATTEN 4
#define CALMA_ORDERING 5
#define CALMA_LABELS 6
#define CALMA_LOWER 7
#define CALMA_MERGE 8
#define CALMA_READ 9
#define CALMA_READONLY 10
#define CALMA_RESCALE 11
#define CALMA_WARNING 12
#define CALMA_WRITE 13
#define CALMA_POLYS 14
#define CALMA_PATHS 15
#define CALMA_WARN_HELP CIF_WARN_END /* undefined by CIF module */
void
CmdCalma(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int option, ext;
char **msg, *namep, *dotptr;
CellDef *rootDef;
FILE *f;
static char *gdsExts[] = {".gds", ".gds2", ".strm", "", NULL};
static char *cmdCalmaYesNo[] = { "no", "false", "off", "yes", "true", "on", 0 };
static char *cmdCalmaWarnOptions[] = { "default", "none", "align",
"limit", "redirect", "help", 0 };
static char *cmdCalmaOption[] =
{
"help print this help information",
"arrays [yes|no] output arrays as individual subuses (like in CIF)",
"contacts [yes|no] optimize output by arraying contacts as subcells",
"drccheck [yes|no] mark all cells as needing DRC checking",
"flatten [yes|no] flatten simple cells (e.g., contacts) on input",
"ordering [on|off] cause cells to be read in post-order",
"labels [yes|no] cause labels to be output when writing GDS-II",
"lower [yes|no] allow both upper and lower case in labels",
"merge [yes|no] merge tiles into polygons in the output",
"read file read Calma GDS-II format from \"file\"\n"
" into edit cell",
"readonly [yes|no] set cell as read-only and generate output from GDS file",
"rescale [yes|no] allow or disallow internal grid subdivision",
"warning [option] set warning information level",
"write file output Calma GDS-II format to \"file\"\n"
" for the window's root cell",
"polygon subcells [yes|no]\n"
" put non-Manhattan polygons into subcells",
"path subcells [yes|no]\n"
" put wire paths into individual subcells",
NULL
};
if (cmd->tx_argc == 1)
option = CALMA_WRITE;
else
{
option = Lookup(cmd->tx_argv[1], cmdCalmaOption);
if (option < 0)
{
TxError("\"%s\" isn't a valid gds option.\n", cmd->tx_argv[1]);
option = CALMA_HELP;
cmd->tx_argc = 2;
}
}
/* Only check for a window on options requiring one */
switch (option)
{
case CALMA_READ: case CALMA_WRITE:
windCheckOnlyWindow(&w, DBWclientID);
if (w == (MagWindow *) NULL)
{
TxError("Point to a window first\n");
return;
}
rootDef = ((CellUse *) w->w_surfaceID)->cu_def;
if (cmd->tx_argc == 1)
{
namep = strrchr(rootDef->cd_name, '/');
if (namep == (char *) NULL)
namep = rootDef->cd_name;
goto outputCalma;
}
break;
}
switch (option)
{
case CALMA_HELP:
TxPrintf("GDS commands have the form \":gds option\",");
TxPrintf(" where option is one of:\n");
for (msg = &(cmdCalmaOption[0]); *msg != NULL; msg++)
{
if (**msg == '*') continue;
TxPrintf(" %s\n", *msg);
}
TxPrintf("If no option is given, a CALMA GDS-II stream file is\n");
TxPrintf(" produced for the root cell.\n");
TxPrintf("The current CIF output style (\"cif ostyle\") is used\n");
TxPrintf(" to select the mask layers output by :gds write.\n");
TxPrintf("The current CIF input style (\"cif istyle\") is used\n");
TxPrintf(" to select the mask layers read by :gds read.\n");
return;
case CALMA_LABELS:
if (cmd->tx_argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaDoLabels));
#else
TxPrintf("Labels will %sbe output to the GDS file.\n",
(CalmaDoLabels) ? "" : "not ");
#endif
return;
}
else if (cmd->tx_argc != 3)
{
wrongNumArgs:
TxError("Wrong number of arguments in \"gds\" command.");
TxError(" Try \":gds help\" for help.\n");
return;
}
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaDoLabels = (option < 3) ? FALSE : TRUE;
return;
case CALMA_CONTACTS:
if (cmd->tx_argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaContactArrays));
#else
if (CalmaContactArrays)
TxPrintf("Contact areas are arrayed as subcells.\n");
else
TxPrintf("Contact areas are output as individual cuts.\n");
#endif
return;
}
else if (cmd->tx_argc != 3)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaContactArrays = (option < 3) ? FALSE : TRUE;
return;
case CALMA_DRCCHECK:
if (cmd->tx_argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(!CalmaNoDRCCheck));
#else
TxPrintf("GDS cells read from input file are%s checked for DRC.\n",
(CalmaNoDRCCheck) ? " not" : " ");
#endif
return;
}
else if (cmd->tx_argc != 3)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaNoDRCCheck = (option < 3) ? TRUE : FALSE;
return;
case CALMA_FLATTEN:
if (cmd->tx_argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaFlattenUses));
#else
if (CalmaFlattenUses)
TxPrintf("Small cells in input are flattened.\n");
else
TxPrintf("Cells are never flattened on input.\n");
#endif
return;
}
else if (cmd->tx_argc != 3)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaFlattenUses = (option < 3) ? FALSE : TRUE;
return;
case CALMA_ORDERING:
if (cmd->tx_argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaPostOrder));
#else
if (CalmaPostOrder)
TxPrintf("GDS parser reads cells in post-order.\n");
else
TxPrintf("GDS parser reads cells in the order "
"encountered in the stream file.\n");
return;
#endif
}
else if (cmd->tx_argc != 3)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaPostOrder = (option < 3) ? FALSE : TRUE;
return;
case CALMA_ARRAYS:
if (cmd->tx_argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaFlattenArrays));
#else
TxPrintf("GDS are %s.\n",
(CalmaFlattenArrays) ? "flattened" : "hierarchical");
#endif
return;
}
else if (cmd->tx_argc != 3)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaFlattenArrays = (option < 3) ? FALSE : TRUE;
return;
case CALMA_LOWER:
if (cmd->tx_argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaDoLower));
#else
TxPrintf("GDS labels are %s.\n",
(CalmaDoLower) ? "lowercase only" : "mixed-case");
#endif
return;
}
else if (cmd->tx_argc != 3)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaDoLower = (option < 3) ? FALSE : TRUE;
return;
case CALMA_MERGE:
if (cmd->tx_argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaMergeTiles));
#else
if (CalmaMergeTiles)
TxPrintf("Merge connected tiles into polygons on output\n");
else
TxPrintf("Output individual tiles\n");
#endif
return;
}
else if (cmd->tx_argc != 3)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaMergeTiles = (option < 3) ? FALSE : TRUE;
return;
case CALMA_POLYS:
if (cmd->tx_argc == 3)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaSubcellPolygons));
#else
if (CalmaSubcellPolygons)
TxPrintf("Non-manhattan polygons placed in subcells.\n");
else
TxPrintf("Non-manhattan polygons read as-is.\n");
#endif
return;
}
else if (cmd->tx_argc != 4)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[3], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaSubcellPolygons = (option < 3) ? FALSE : TRUE;
return;
case CALMA_PATHS:
if (cmd->tx_argc == 3)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaSubcellPaths));
#else
if (CalmaSubcellPaths)
TxPrintf("Wire paths placed in subcells.\n");
else
TxPrintf("Wire paths read as-is.\n");
#endif
return;
}
else if (cmd->tx_argc != 4)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[3], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaSubcellPaths = (option < 3) ? FALSE : TRUE;
return;
case CALMA_READONLY:
if (cmd->tx_argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaReadOnly));
#else
TxPrintf("GDS cells read from input file are set read-%s.\n",
(CalmaReadOnly) ? "only" : "write");
#endif
return;
}
else if (cmd->tx_argc != 3)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CalmaReadOnly = (option < 3) ? FALSE : TRUE;
return;
case CALMA_RESCALE:
if (cmd->tx_argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CIFRescaleAllow));
#else
TxPrintf("Internal grid rescaling %sallowed\n",
(CIFRescaleAllow) ? "" : "dis");
#endif
return;
}
else if (cmd->tx_argc != 3)
goto wrongNumArgs;
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
if (option < 0)
goto wrongNumArgs;
CIFRescaleAllow = (option < 3) ? FALSE : TRUE;
if (!CIFRescaleAllow)
CIFWarningLevel = CIF_WARN_LIMIT;
return;
case CALMA_WARNING:
if (cmd->tx_argc == 2)
{
TxPrintf("Warning display options: %s\n",
cmdCalmaWarnOptions[CIFWarningLevel]);
}
else
{
int suboption = Lookup(cmd->tx_argv[2], cmdCalmaWarnOptions);
if (suboption < 0)
{
TxError("\"%s\" isn't a valid gds warning option.\n",
cmd->tx_argv[2]);
suboption = CALMA_WARN_HELP;
}
if (suboption == CALMA_WARN_HELP)
TxError("Valid options are: default, align, limit, "
"redirect, and none.\n");
else
CIFWarningLevel = suboption;
if (suboption == CIF_WARN_REDIRECT)
{
if (cmd->tx_argc == 4)
StrDup(&CIFErrorFilename, cmd->tx_argv[3]);
else
StrDup(&CIFErrorFilename, NULL);
}
}
return;
case CALMA_WRITE:
if (cmd->tx_argc != 3) goto wrongNumArgs;
namep = cmd->tx_argv[2];
goto outputCalma;
case CALMA_READ:
if (cmd->tx_argc != 3) goto wrongNumArgs;
/* Check for various common file extensions, including */
/* no extension (as-is), ".gds", ".gds2", and ".strm". */
for (ext = 0; gdsExts[ext] != NULL; ext++)
if ((f = PaOpen(cmd->tx_argv[2], "r", gdsExts[ext], Path,
(char *) NULL, &namep)) != (FILE *)NULL)
break;
if (f == (FILE *) NULL)
{
TxError("Cannot open %s.gds, %s.strm or %s to read "
"GDS-II stream input.\n",
cmd->tx_argv[2], cmd->tx_argv[2], cmd->tx_argv[2]);
return;
}
CalmaReadFile(f, namep);
(void) fclose(f);
return;
}
/*
* If control gets here, we're going to output GDS-II (stream)
* into the file given by namep.
*/
outputCalma:
dotptr = strrchr(namep, '.');
f = PaOpen(namep, "w", (dotptr == NULL) ? ".gds" : "", ".",
(char *) NULL, (char **) NULL);
if (f == (FILE *) NULL)
{
TxError("Cannot open %s%s to write GDS-II stream output\n", namep,
(dotptr == NULL) ? ".gds" : "");
return;
}
if (!CalmaWrite(rootDef, f))
{
TxError("I/O error in writing file %s.\n", namep);
TxError("File may be incompletely written.\n");
}
(void) fclose(f);
}
#endif
/*
* ----------------------------------------------------------------------------
*
* CmdCellname --
*
* Implement the "cellname" and "instance" commands. List the child(ren),
* parent(s), or name of the selected or indicated cell or instance.
*
* Usage:
* cellname [list] children|parents|exists|instances|
* celldef|self|childinst [name]
* or
* cellname [list] topcells|allcells|window
* or
* cellname delete [name]
* or
* cellname writeable [name] [true|false]
* or
* cellname rename [name] [newname]
* or
* cellname [list] filepath [path|"default"]
* or
* cellname property [name] [property_key [property_value]]
*
* Results:
* None.
*
* Side effects:
* Print out the names of all the cell currently loaded.
* Or (optionally) only the cell given.
*
* ----------------------------------------------------------------------------
*/
/* ARGSUSED */
void
CmdCellname(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
bool dolist = FALSE;
int option;
int locargc = cmd->tx_argc;
char *cellname = NULL;
void (*func)();
CellDef *newDef, *cellDef;
static char *cmdCellOption[] =
{
"children list children of selected or named cell",
"parents list parents of selected or named cell",
"exists true if loaded, false if not, or name of selected",
"self true if loaded, false if not, or name of selected",
"instances list instances of a cell definition",
"childinst list children instances of a cell definition",
"celldef name the cell definition of an instance",
"allcells list all cells",
"topcells list top-level cells",
"window list top-level cell of a layout window",
"create create a new cell definition",
"delete delete the named cell definition",
"filepath list the full path of the file for the cell",
"flags list option flags of the indicated cell definition",
"lock lock the named cell (prevent changes to cell use)",
"unlock unlock the named cell (allow changes to cell use)",
"property list or set cell definition properties",
"rename rename the indicated cell",
"writeable make the cell definition read-only or read-write",
"modified true if modified, false if not",
NULL
};
typedef enum { IDX_CHILDREN, IDX_PARENTS, IDX_EXISTS, IDX_SELF,
IDX_INSTANCE, IDX_CHILDINST, IDX_CELLDEF, IDX_ALLCELLS,
IDX_TOPCELLS, IDX_IN_WINDOW, IDX_CREATE,
IDX_DELETE, IDX_FILEPATH, IDX_FLAGS, IDX_LOCK, IDX_UNLOCK,
IDX_PROPERTY, IDX_RENAME, IDX_READWRITE,
IDX_MODIFIED } optionType;
if (strstr(cmd->tx_argv[0], "in"))
func = DBUsePrint;
else
func = DBCellPrint;
if (locargc > 1)
{
if (!strcmp(cmd->tx_argv[1], "list")) {
dolist = TRUE;
locargc--;
}
}
if (locargc > 5 || locargc < 2) goto badusage;
option = Lookup(cmd->tx_argv[1 + ((dolist) ? 1 : 0)], cmdCellOption);
if (option < 0) goto badusage;
if ((locargc > 3) && (option != IDX_RENAME) && (option != IDX_DELETE) &&
(option != IDX_READWRITE) && (option != IDX_PROPERTY) &&
(option != IDX_FILEPATH))
goto badusage;
if ((locargc > 4) && (option != IDX_PROPERTY))
goto badusage;
if (locargc >= 3) {
switch (option) {
case IDX_ALLCELLS:
case IDX_TOPCELLS:
case IDX_IN_WINDOW:
goto badusage;
break;
default:
cellname = cmd->tx_argv[2 + ((dolist) ? 1 : 0)];
}
}
if (func != DBUsePrint)
{
/* These functions only work with cell uses (instances) */
switch (option) {
case IDX_LOCK:
case IDX_UNLOCK:
TxError("Cell definitions cannot be locked. Use \"instance\"?\n");
TxError(" or do you mean \"cellname writeable\"?\n");
return;
}
}
else
{
/* These functions only work with cell definitions */
switch (option) {
case IDX_TOPCELLS:
TxError("Instances do not have a top level. Use \"cellname\"?\n");
return;
case IDX_IN_WINDOW: case IDX_READWRITE: case IDX_FLAGS:
case IDX_PROPERTY: case IDX_FILEPATH: case IDX_MODIFIED:
TxError("Function unimplemented for instances.\n");
return;
case IDX_DELETE:
TxError("Function unimplemented for instances. Use \"delete\"\n");
return;
case IDX_CREATE:
TxError("Function unimplemented for instances. Use \"getcell\"\n");
return;
case IDX_RENAME:
TxError("Function unimplemented for instances. Use \"identify\"\n");
return;
}
}
switch (option) {
case IDX_ALLCELLS:
(*func)(NULL, ALLCELLS, dolist);
break;
case IDX_TOPCELLS:
(*func)(NULL, TOPCELLS, dolist);
break;
case IDX_IN_WINDOW:
DBTopPrint(w, dolist);
break;
case IDX_SELF:
case IDX_EXISTS:
(*func)(cellname, SELF, dolist);
break;
case IDX_CELLDEF:
(*func)(cellname, ((func == DBUsePrint) ? OTHER : SELF), dolist);
break;
case IDX_INSTANCE:
(*func)(cellname, ((func == DBUsePrint) ? SELF : OTHER), dolist);
break;
case IDX_CHILDREN:
(*func)(cellname, CHILDREN, dolist);
break;
case IDX_CHILDINST:
(*func)(cellname, ((func == DBUsePrint) ? CHILDREN : CHILDINST), dolist);
break;
case IDX_PARENTS:
(*func)(cellname, PARENTS, dolist);
break;
case IDX_MODIFIED:
(*func)(cellname, MODIFIED, dolist);
break;
case IDX_PROPERTY:
if (cellname == NULL)
cellDef = EditRootDef;
else
cellDef = DBCellLookDef(cellname);
if (cellDef == (CellDef *) NULL)
TxError("Unknown cell %s\n", cellname);
else
CmdDoProperty(cellDef, cmd, 3 + ((dolist) ? 1 : 0));
break;
case IDX_DELETE:
/* Unload the cell definition and free memory */
/* Make sure selections are cleared or they may */
/* contain references to the deleted cell def. */
if ((locargc == 4) && !strcmp(cmd->tx_argv[3 + ((dolist) ? 1 : 0)],
"-noprompt"))
{
SelectClear();
DBCellDelete(cellname, TRUE);
}
else if (locargc == 3)
{
SelectClear();
DBCellDelete(cellname, FALSE);
}
else
TxError("Delete cell command missing cellname\n");
break;
case IDX_READWRITE:
if (cellname == NULL)
cellDef = EditRootDef;
else
cellDef = DBCellLookDef(cellname);
if (cellDef == (CellDef *) NULL)
{
TxError("Unknown cell %s\n", cellname);
break;
}
if (locargc == 3)
{
if (cellDef->cd_flags & CDNOEDIT)
#ifdef MAGIC_WRAPPER
if (dolist)
Tcl_SetResult(magicinterp, "read-only", 0);
else
#endif
TxPrintf("read-only\n");
else
#ifdef MAGIC_WRAPPER
if (dolist)
Tcl_SetResult(magicinterp, "writeable", 0);
else
#endif
TxPrintf("writeable\n");
}
else if (locargc == 4)
{
if (tolower(*cmd->tx_argv[3 + ((dolist) ? 1 : 0)]) == 't')
{
/* Check if file is already read-write */
if (!(cellDef->cd_flags & CDNOEDIT))
break;
/* Make file read-write */
#ifdef FILE_LOCKS
if (cellDef->cd_fd == -1)
dbReadOpen(cellDef, NULL, TRUE, NULL);
if (cellDef->cd_fd != -1)
cellDef->cd_flags &= ~CDNOEDIT;
else
TxError("Advisory lock held on cell %s\n", cellDef->cd_name);
#else
cellDef->cd_flags &= ~CDNOEDIT;
#endif
WindAreaChanged(w, &w->w_screenArea);
CmdSetWindCaption(EditCellUse, EditRootDef);
}
else /* "cellname writeable false" */
{
/* Check if file is already read-only */
if (cellDef->cd_flags & CDNOEDIT)
break;
/* Make file read-only */
cellDef->cd_flags |= CDNOEDIT;
#ifdef FILE_LOCKS
/* Release any advisory lock held on this file */
if (cellDef->cd_fd != -1)
{
close(cellDef->cd_fd);
cellDef->cd_fd = -1;
}
#endif
if (EditCellUse && (EditCellUse->cu_def == cellDef))
EditCellUse = (CellUse *)NULL;
if (EditRootDef == cellDef)
EditRootDef = (CellDef *)NULL;
WindAreaChanged(w, &w->w_screenArea);
CmdSetWindCaption(EditCellUse, (CellDef *)NULL);
}
}
break;
case IDX_FILEPATH:
if (cellname == NULL)
cellDef = EditRootDef;
else
cellDef = DBCellLookDef(cellname);
if (cellDef == (CellDef *) NULL)
{
TxError("Unknown cell %s\n", cellname);
break;
}
if (locargc == 3)
{
if (cellDef->cd_file == NULL)
{
#ifdef MAGIC_WRAPPER
if (dolist)
Tcl_SetResult(magicinterp, "default", 0);
else
#endif
TxPrintf("default\n");
}
else
{
char *pathend;
pathend = strrchr(cellDef->cd_file, '/');
if (pathend) *pathend = '\0';
#ifdef MAGIC_WRAPPER
if (dolist)
Tcl_SetResult(magicinterp, cellDef->cd_file, 0);
else
#endif
TxPrintf("%s\n", cellDef->cd_file);
if (pathend) *pathend = '/';
}
}
else if (locargc == 4)
{
char *filepath;
filepath = cmd->tx_argv[3 + ((dolist) ? 1 : 0)];
if (!strcmp(filepath, "default"))
{
if (cellDef->cd_file != NULL)
{
freeMagic(cellDef->cd_file);
cellDef->cd_file = NULL;
}
}
else
{
char *fullpath;
fullpath = (char *)mallocMagic(strlen(filepath) +
strlen(cellDef->cd_name) + 2);
sprintf(fullpath, "%s/%s", filepath, cellDef->cd_name);
if (cellDef->cd_file != NULL)
freeMagic(cellDef->cd_file);
cellDef->cd_file = fullpath;
}
}
break;
case IDX_FLAGS:
if (cellname == NULL)
cellDef = EditRootDef;
else
cellDef = DBCellLookDef(cellname);
if (cellDef == (CellDef *) NULL)
{
TxError("Unknown cell %s\n", cellname);
break;
}
#ifdef MAGIC_WRAPPER
if (cellDef->cd_flags & CDAVAILABLE)
Tcl_AppendElement(magicinterp, "available");
if (cellDef->cd_flags & CDMODIFIED)
Tcl_AppendElement(magicinterp, "modified");
if (cellDef->cd_flags & CDNOEDIT)
Tcl_AppendElement(magicinterp, "readonly");
#else
TxPrintf("Flag settings for cell %s:\n", cellname);
TxPrintf("in database: %s\n", (cellDef->cd_flags & CDAVAILABLE) ?
"true" : "false");
TxPrintf("modified: %s\n", (cellDef->cd_flags & CDMODIFIED) ?
"true" : "false");
TxPrintf("readonly: %s\n", (cellDef->cd_flags & CDNOEDIT) ?
"true" : "false");
#endif
break;
case IDX_RENAME:
/* Rename the cell and mark as modified. Do not write to disk. */
if (locargc != 4) goto badusage;
DBCellRename(cellname, cmd->tx_argv[3 + ((dolist) ? 1 : 0)]);
break;
case IDX_CREATE:
newDef = DBCellLookDef(cellname);
if (newDef == (CellDef *) NULL)
{
newDef = DBCellNewDef(cellname);
DBCellSetAvail(newDef);
}
break;
case IDX_LOCK:
DBLockUse(cellname, TRUE);
break;
case IDX_UNLOCK:
DBLockUse(cellname, FALSE);
break;
}
return;
badusage:
TxError("Usage: %s [list] children|parents|self|exists|"
"instances|celldef|delete [name]\n", cmd->tx_argv[0]);
TxError("or: %s [list] allcells|topcells|window\n", cmd->tx_argv[0]);
TxError("or: %s create name\n", cmd->tx_argv[0]);
TxError("or: %s rename name newname\n", cmd->tx_argv[0]);
TxError("or: %s [un]lock [name]\n", cmd->tx_argv[0]);
TxError("or: %s writeable [name] [true|false]\n", cmd->tx_argv[0]);
return;
}
/*
* ----------------------------------------------------------------------------
*
* CmdCif --
*
* Implement the "cif" command.
*
* Usage:
* cif option args
*
* Results:
* None.
*
* Side effects:
* There are no side effects on the circuit. Various options
* may produce cif files, read cif, or display cif information
* on the screen.
*
* ----------------------------------------------------------------------------
*/
#ifdef CIF_MODULE
#define ARRAY 0
#define HIER 1
#define AREALABELS 2
#define COVERAGE 3
#define CIF_DRC_CHECK 4
#define HELP 5
#define IDCELL 6
#define ISTYLE 7
#define CIF_LAMBDA 8
#define CIF_LIMIT 9
#define OSTYLE 10
#define CIF_PAINT 11
#define PREFIX 12
#define READ 13
#define RESCALE 14
#define CIF_SCALE 15
#define SEE 16
#define STATS 17
#define WARNING 18
#define CIF_WRITE 19
#define CIF_WRITE_FLAT 20
#define POLYGONS 21
#define UNFRACTURE 22
#define CIF_WARN_HELP CIF_WARN_END /* undefined by CIF module */
void
CmdCif(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int option, yesno;
char **msg, *namep;
CellDef *rootDef, *paintDef;
Rect box;
TileType layer;
TileTypeBitMask mask;
FILE *f;
bool wizardHelp;
bool flatCif = FALSE;
bool dolist = FALSE;
bool doforall = FALSE;
float curscale;
int argc = cmd->tx_argc;
char **argv = cmd->tx_argv;
static char *cmdCifWarnOptions[] = { "default", "none", "align",
"limit", "redirect", "help", 0 };
static char *cmdCifYesNo[] = { "no", "yes", 0 };
static char *cmdCifInOut[] = { "input", "output", 0 };
static char *cmdCifOption[] =
{
"*array layer display CIF layer under box (array only)",
"*hier layer display CIF layer under box (hier only)",
"arealabels yes|no enable/disable us of area label extension",
"coverage layer print area coverage of indicated layer",
"drccheck [yes|no] mark all cells as needing DRC checking",
"help print this help information",
"idcell yes|no enable/disable use of cell ID extension",
"istyle [style] change style for reading CIF to style",
"lambda in|out show microns per lambda for the current style",
"limit [value] limit internal grid subdivision by scalefactor value",
"ostyle [style] change style for writing CIF to style",
"paint ciflayer magiclayer [cellname]\n"
" generate CIF and paint into cell",
"prefix [path] prepend path to cell names in CIF output",
"read file read CIF from \"file\" into edit cell",
"rescale [yes|no] allow/disallow rescaling of internal grid",
"scale in|out show microns per internal units for the current style",
"see layer display CIF layer under box",
"statistics print out statistics for CIF generator",
"warning [option] set warning display options",
"write file output CIF for the window's root cell to \"file\"",
"flat file output flattened CIF for "
"the window's root cell to \"file\"",
"polygon subcells [yes|no]\n"
" put non-Manhattan polygons in subcells",
"unfracture [yes|no]\n"
" optimize non-Manhattan geometry",
NULL
};
if (argc == 1)
option = CIF_WRITE;
else
{
if (!strncmp(argv[1], "list", 4))
{
dolist = TRUE;
if (!strncmp(argv[1], "listall", 7))
{
doforall = TRUE;
}
argv++;
argc--;
}
option = Lookup(argv[1], cmdCifOption);
if (option < 0)
{
TxError("\"%s\" isn't a valid cif option.\n", argv[1]);
option = HELP;
argc = 2;
}
}
/* Only check for the presence of a window on options requiring one */
switch (option)
{
case HELP: case ISTYLE: case OSTYLE: case PREFIX:
case AREALABELS: case WARNING: case CIF_LIMIT:
case POLYGONS:
break;
default:
windCheckOnlyWindow(&w, DBWclientID);
if (w == (MagWindow *) NULL)
{
TxError("Point to a window first\n");
return;
}
rootDef = ((CellUse *) w->w_surfaceID)->cu_def;
if (argc == 1)
{
namep = strrchr(rootDef->cd_name, '/');
if (namep == (char *) NULL)
namep = rootDef->cd_name;
goto outputCIF;
}
}
switch (option)
{
case ARRAY:
if (argc == 4) {
if (!strncmp(argv[2], "write", 5))
{
if (!strncmp(argv[3], "dis", 3))
CIFArrayWriteDisable = TRUE;
else if (!strncmp(argv[3], "en", 2))
CIFArrayWriteDisable = FALSE;
else
TxPrintf("Subcell interaction output %s\n",
(CIFArrayWriteDisable) ? "disabled" : "enabled");
return;
}
else goto wrongNumArgs;
}
else if (argc != 3)
{
wrongNumArgs:
TxError("Wrong arguments in \"cif %s\" command:\n",
argv[1]);
TxError(" :cif %s\n", cmdCifOption[option]);
TxError("Try \":cif help\" for more help.\n");
return;
}
if (!ToolGetBox(&rootDef, &box))
{
TxError("Use the box to select the area in");
TxError(" which you want to see CIF.\n");
return;
}
CIFSeeHierLayer(rootDef, &box, argv[2], TRUE, FALSE);
return;
case HIER:
if (argc == 4) {
if (!strncmp(argv[2], "write", 5))
{
if (!strncmp(argv[3], "dis", 3))
CIFHierWriteDisable = TRUE;
else if (!strncmp(argv[3], "en", 2))
CIFHierWriteDisable = FALSE;
else
TxPrintf("Subcell interaction output %s\n",
(CIFHierWriteDisable) ? "disabled" : "enabled");
return;
}
else goto wrongNumArgs;
}
else if (argc != 3) goto wrongNumArgs;
if (!ToolGetBox(&rootDef, &box))
{
TxError("Use the box to select the area in");
TxError(" which you want to see CIF.\n");
return;
}
CIFSeeHierLayer(rootDef, &box, argv[2], FALSE, TRUE);
return;
case COVERAGE:
if ((argc == 4) && !strcmp(argv[3], "box"))
{
if (!ToolGetBox(&rootDef, &box))
{
TxError("Box requested but no cursor box exists\n");
return;
}
CIFCoverageLayer(rootDef, &box, argv[2]);
}
else if (argc == 3)
CIFCoverageLayer(rootDef, &rootDef->cd_bbox, argv[2]);
else
goto wrongNumArgs;
return;
case CIF_LAMBDA:
case CIF_SCALE:
if (argc != 3) goto wrongNumArgs;
yesno = Lookup(argv[2], cmdCifInOut); /* "input" or "output" */
if (yesno < 0)
goto wrongNumArgs;
if (yesno == 0) /* option "input" */
curscale = CIFGetInputScale(1000);
else
curscale = CIFGetOutputScale(1000);
if (option == CIF_LAMBDA)
{
TxPrintf("Warning: \"cif lambda\" is deprecated; use "
"\"cif scale\" instead. Units are not in lambda.\n");
}
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewDoubleObj((double)curscale));
#else
TxPrintf("One %s internal unit is %2.1f microns.\n", (yesno) ?
"output" : "input", curscale);
#endif
return;
case CIF_LIMIT:
if (argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(CIFRescaleLimit));
#else
TxPrintf("Rescale limit is %d\n", CIFRescaleLimit);
#endif
return;
}
else if (argc != 3)
goto wrongNumArgs;
else if (!StrIsInt(argv[2]))
goto wrongNumArgs;
CIFRescaleLimit = atoi(argv[2]);
if (CIFRescaleLimit > CIFMAXRESCALE)
{
CIFRescaleLimit = CIFMAXRESCALE;
TxError("Warning: rescale limit set at maximum value of %d\n",
CIFMAXRESCALE);
}
else if (CIFRescaleLimit < 1)
{
CIFRescaleLimit = 1;
TxError("Warning: rescale limit set at minimum value of 1\n");
}
return;
case AREALABELS:
if (argc > 3) goto wrongNumArgs;
if (argc == 3)
{
yesno = Lookup(argv[2], cmdCifYesNo);
if (yesno < 0)
goto wrongNumArgs;
CIFDoAreaLabels = yesno;
}
TxPrintf("Output of CIF area labels is now %s\n",
CIFDoAreaLabels ? "enabled" : "disabled");
return;
case HELP:
if ((argc == 3)
&& (strcmp(argv[2], "wizard") == 0))
wizardHelp = TRUE;
else wizardHelp = FALSE;
TxPrintf("CIF commands have the form \":cif option\",");
TxPrintf(" where option is one of:\n");
for (msg = &(cmdCifOption[0]); *msg != NULL; msg++)
{
if ((**msg == '*') && !wizardHelp) continue;
TxPrintf(" %s\n", *msg);
}
TxPrintf("If no option is given, CIF is output for the");
TxPrintf(" root cell.\n");
return;
case IDCELL:
if (argc > 3) goto wrongNumArgs;
if (argc == 3)
{
yesno = Lookup(argv[2], cmdCifYesNo);
if (yesno < 0)
goto wrongNumArgs;
CIFDoCellIdLabels = yesno;
}
TxPrintf ("Output of CIF cell ID labels is now %s\n",
CIFDoCellIdLabels ? "enabled" : "disabled");
return;
case ISTYLE:
if (argc == 3)
CIFSetReadStyle(argv[2]);
else if (argc == 2)
CIFPrintReadStyle(dolist, doforall, !doforall);
else goto wrongNumArgs;
return;
case POLYGONS:
if (argc == 3)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CIFSubcellPolygons));
#else
if (CIFSubcellPolygons)
TxPrintf("Non-manhattan polygons placed in subcells.\n");
else
TxPrintf("Non-manhattan polygons read as-is.\n");
#endif
return;
}
else if (argc != 4)
goto wrongNumArgs;
yesno = Lookup(argv[3], cmdCifYesNo);
if (yesno < 0)
goto wrongNumArgs;
CIFSubcellPolygons = yesno;
return;
case UNFRACTURE:
if (argc == 3)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CIFUnfracture));
#else
if (CIFUnfracture)
TxPrintf("Non-manhattan geometry optimized.\n");
else
TxPrintf("Non-manhattan geometry not optimized.\n");
#endif
return;
}
else if (argc != 4)
goto wrongNumArgs;
yesno = Lookup(argv[3], cmdCifYesNo);
if (yesno < 0)
goto wrongNumArgs;
CIFUnfracture = yesno;
return;
case PREFIX:
if (argc == 2)
StrDup (&CIFPathPrefix, NULL);
else if (argc == 3)
StrDup (&CIFPathPrefix, argv[2]);
else goto wrongNumArgs;
return;
case OSTYLE:
if (argc == 3)
CIFSetStyle(argv[2]);
else if (argc == 2)
CIFPrintStyle(dolist, doforall, !doforall);
else goto wrongNumArgs;
return;
case READ:
if (argc != 3) goto wrongNumArgs;
f = PaOpen(argv[2], "r", ".cif", Path,
(char *) NULL, (char **) NULL);
if (f == (FILE *) NULL)
{
TxError("Cannot open %s.cif to read CIF.\n", argv[2]);
return;
}
CIFReadFile(f);
(void) fclose(f);
return;
case RESCALE:
if (argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CIFRescaleAllow));
#else
TxPrintf("Internal grid rescaling %sallowed\n",
(CIFRescaleAllow) ? "" : "dis");
#endif
return;
}
else if (argc != 3)
goto wrongNumArgs;
yesno = Lookup(argv[2], cmdCifYesNo);
if (yesno < 0)
goto wrongNumArgs;
CIFRescaleAllow = yesno;
if (!CIFRescaleAllow)
CIFWarningLevel = CIF_WARN_LIMIT;
return;
case CIF_DRC_CHECK:
if (argc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(!CIFNoDRCCheck));
#else
TxPrintf("CIF cells are marked as%s requiring DRC checks.\n",
(CIFNoDRCCheck) ? " not" : " ");
#endif
return;
}
else if (argc != 3)
goto wrongNumArgs;
yesno = Lookup(argv[2], cmdCifYesNo);
if (yesno < 0)
goto wrongNumArgs;
CIFNoDRCCheck = !yesno;
return;
case SEE:
if (argc != 3) goto wrongNumArgs;
if (!ToolGetBox(&rootDef, &box))
{
TxError("Use the box to select the area in");
TxError(" which you want to see CIF.\n");
return;
}
CIFSeeLayer(rootDef, &box, argv[2]);
return;
case CIF_PAINT:
if ((argc != 4) && (argc != 5)) goto wrongNumArgs;
if (!ToolGetBox(&rootDef, &box))
{
TxError("Use the box to select the area in");
TxError(" which you want to paint CIF.\n");
return;
}
if (argc == 5)
{
paintDef = DBCellLookDef(argv[4]);
if (paintDef == (CellDef *)NULL)
{
paintDef = DBCellNewDef(argv[4]);
DBCellSetAvail(paintDef);
}
}
else
paintDef = rootDef;
layer = DBTechNoisyNameType(argv[3]);
if (layer >= TT_TECHDEPBASE)
{
CIFPaintLayer(rootDef, &box, argv[2], layer, paintDef);
/* Refresh the layout drawing */
TTMaskSetOnlyType(&mask, layer);
DBWAreaChanged(paintDef, &box, DBW_ALLWINDOWS, &mask);
DRCCheckThis(paintDef, TT_CHECKPAINT, &box);
}
return;
case STATS:
CIFPrintStats();
return;
case WARNING:
if (argc == 2)
{
TxPrintf("Warning display options: %s\n",
cmdCifWarnOptions[CIFWarningLevel]);
}
else
{
int suboption = Lookup(argv[2], cmdCifWarnOptions);
if (suboption < 0)
{
TxError("\"%s\" isn't a valid cif warning option.\n", argv[2]);
suboption = CIF_WARN_HELP;
}
if (suboption == CIF_WARN_HELP)
TxError("Valid options are: default, align, limit, "
"redirect, and none\n");
else
CIFWarningLevel = suboption;
if (suboption == CIF_WARN_REDIRECT)
{
if (argc == 4)
StrDup(&CIFErrorFilename, argv[3]);
else
StrDup(&CIFErrorFilename, NULL);
}
}
return;
case CIF_WRITE:
if (argc != 3) goto wrongNumArgs;
namep = argv[2];
goto outputCIF;
case CIF_WRITE_FLAT:
if (argc != 3) goto wrongNumArgs;
namep = argv[2];
flatCif = TRUE;
goto outputCIF;
}
/* If control gets here, we're going to output CIF into the
* file given by namep.
*/
outputCIF:
f = PaOpen(namep, "w", ".cif", ".", (char *) NULL, (char **) NULL);
if (f == (FILE *) NULL)
{
TxError("Cannot open %s.cif to write CIF\n", namep);
return;
}
if (flatCif == TRUE)
{
if (!CIFWriteFlat(rootDef, f))
{
TxError("I/O error in writing file %s.\n", namep);
TxError("File may be incompletely written.\n");
}
}
else
{
if (!CIFWrite(rootDef, f))
{
TxError("I/O error in writing file %s.\n", namep);
TxError("File may be incompletely written.\n");
}
}
(void) fclose(f);
}
#endif
/*
* ----------------------------------------------------------------------------
*
* CmdClockwise --
*
* Implement the "clockwise" command. Rotate the selection and the
* box clockwise around the point.
*
* Usage:
* clockwise [degrees] [-origin] or
* rotate [degrees] [-origin]
*
* Results:
* None.
*
* Side effects:
* Modifies the edit cell.
*
* Notes:
* "rotate" has been added as an alias for "clockwise" because the
* usual shorthand "clock" conflicts with the built-in Tcl command
* in the Tcl version. "clockwise" is still kept for backward
* compatibility but the command must contain at least "clockw" for
* the interpreter to uniquely identify it.
*
* The "-origin" option has been added to allow rotating stuff
* relative to the origin, insteadd of the lower-left corner.
*
* ----------------------------------------------------------------------------
*/
void
CmdClockwise(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Transform trans, t2;
int degrees, locargc;
Rect rootBox, bbox;
CellDef *rootDef;
bool noAdjust = FALSE;
locargc = cmd->tx_argc;
if (!strncmp(cmd->tx_argv[locargc - 1], "-orig", 5))
{
noAdjust = TRUE;
locargc--;
}
if (locargc == 1)
degrees = 90;
else if (locargc == 2)
{
if (!StrIsInt(cmd->tx_argv[1])) goto badusage;
degrees = atoi(cmd->tx_argv[1]);
}
else goto badusage;
if (!ToolGetEditBox((Rect *)NULL)) return;
if (degrees < 0) degrees += 360;
switch (degrees)
{
case 90:
t2 = Geo90Transform;
break;
case 180:
t2 = Geo180Transform;
break;
case 270:
t2 = Geo270Transform;
break;
default:
TxError("Rotation angle must be 90, 180, or 270 degrees\n");
return;
}
/* To rotate the selection, first rotate it around the origin
* then move it so its lower-left corner is at the same place
* that it used to be.
*/
if (noAdjust)
{
GeoTransRect(&t2, &SelectDef->cd_bbox, &bbox);
trans = t2;
}
else
{
GeoTransRect(&t2, &SelectDef->cd_bbox, &bbox);
GeoTranslateTrans(&t2, SelectDef->cd_bbox.r_xbot - bbox.r_xbot,
SelectDef->cd_bbox.r_ybot - bbox.r_ybot, &trans);
}
SelectTransform(&trans);
/* Rotate 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;
badusage:
TxError("Usage: %s [degrees]\n", cmd->tx_argv[0]);
}
/*
* ----------------------------------------------------------------------------
*
* CmdContact --
*
* Implement the "contact" command.
*
* Usage:
* contact <type>
*
* Results:
* None.
*
* Side effects:
* The area in the box is searched for the intersection of all residues
* of the contact type <type>. The intersecting areas are replaced with
* the contact type.
*
* ----------------------------------------------------------------------------
*/
typedef struct CCS
{
CellDef *rootDef;
TileTypeBitMask *rmask;
TileType tstart;
Rect area;
Rect clip;
LinkedRect *lhead;
} CCStruct;
void
CmdContact(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
TileType type, rtype;
TileTypeBitMask *rmask, smask;
CCStruct ccs;
Rect area;
int cmdContactFunc(); /* Forward declaration */
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)
{
TxError("Usage: %s <contact_type>\n", cmd->tx_argv[0]);
return;
}
if (!ToolGetEditBox(&area)) return;
type = DBTechNoisyNameType(cmd->tx_argv[1]);
if (type >= 0)
{
if (!DBIsContact(type))
{
TxError("Error: tile type \"%s\" is not a contact.\n",
cmd->tx_argv[1]);
return;
}
/* Find the first residue type */
rmask = DBResidueMask(type);
for (rtype = 0; rtype < DBNumUserLayers; rtype++)
if (TTMaskHasType(rmask, rtype))
break;
/* Find all tiles of one residue type */
ccs.lhead = (LinkedRect *)NULL;
ccs.rmask = rmask;
ccs.tstart = rtype;
ccs.rootDef = EditCellUse->cu_def;
ccs.clip = area;
TTMaskSetOnlyType(&smask, rtype);
DBSrPaintArea((Tile *) NULL, EditCellUse->cu_def->cd_planes[DBPlane(rtype)],
&area, &smask, cmdContactFunc, (ClientData) &ccs);
while (ccs.lhead != NULL)
{
TTMaskSetOnlyType(&smask, type);
TTMaskAndMask(&smask, &DBActiveLayerBits);
DBPaintMask(EditCellUse->cu_def, &ccs.lhead->r_r, &smask);
freeMagic(ccs.lhead);
ccs.lhead = ccs.lhead->r_next;
}
/* Refresh the layout drawing */
DBWAreaChanged(EditCellUse->cu_def, &area, DBW_ALLWINDOWS, &smask);
DRCCheckThis (EditCellUse->cu_def, TT_CHECKPAINT, &area);
}
}
/* For each tile in the first residue type, search for tiles of the
* next residue type, and add the intersection of areas to the linked
* rectangle list.
*/
int
cmdContactFunc(tile, ccs)
Tile *tile;
CCStruct *ccs;
{
TileType stype;
TileTypeBitMask smask;
int cmdContactFunc2(); /* Forward declaration */
TiToRect(tile, &ccs->area);
GeoClip(&ccs->area, &ccs->clip);
for (stype = ccs->tstart + 1; stype < DBNumUserLayers; stype++)
if (TTMaskHasType(ccs->rmask, stype))
break;
TTMaskSetOnlyType(&smask, stype);
DBSrPaintArea((Tile *) NULL, ccs->rootDef->cd_planes[DBPlane(stype)],
&ccs->area, &smask, cmdContactFunc2, (ClientData)ccs);
return 0;
}
int
cmdContactFunc2(tile, ccs)
Tile *tile;
CCStruct *ccs;
{
LinkedRect *newlr;
Rect area;
TiToRect(tile, &area);
GeoClip(&area, &ccs->area);
newlr = (LinkedRect *) mallocMagic(sizeof(LinkedRect));
newlr->r_r = area;
newlr->r_next = ccs->lhead;
ccs->lhead = newlr;
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* CmdCopy --
*
* Implement the "copy" command.
*
* Usage:
* copy [direction [amount]]
* copy to x y
*
* Results:
* None.
*
* Side effects:
* The selection is copied.
*
* ----------------------------------------------------------------------------
*/
void
CmdCopy(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Transform t;
Rect rootBox, newBox;
Point rootPoint, editPoint;
CellDef *rootDef;
int argpos;
if (cmd->tx_argc > 4)
{
badUsage:
TxError("Usage: %s [direction [amount]]\n", cmd->tx_argv[0]);
TxError(" or: %s to x y\n", cmd->tx_argv[0]);
return;
}
if (cmd->tx_argc > 1)
{
int indx, amountx, amounty;
int xdelta, ydelta;
if (!ToolGetEditBox((Rect *)NULL)) return;
if (strcmp(cmd->tx_argv[1], "to") == 0)
{
if (cmd->tx_argc != 4)
goto badUsage;
editPoint.p_x = cmdParseCoord(w, cmd->tx_argv[2], FALSE, TRUE);
editPoint.p_y = cmdParseCoord(w, cmd->tx_argv[3], FALSE, FALSE);
GeoTransPoint(&EditToRootTransform, &editPoint, &rootPoint);
goto copyToPoint;
}
indx = GeoNameToPos(cmd->tx_argv[1], FALSE, FALSE);
argpos = (indx < 0) ? 1 : 2;
if (cmd->tx_argc >= 3)
{
switch (indx)
{
case GEO_EAST: case GEO_WEST:
amountx = cmdParseCoord(w, cmd->tx_argv[argpos], TRUE, TRUE);
amounty = 0;
break;
case GEO_NORTH: case GEO_SOUTH:
amountx = 0;
amounty = cmdParseCoord(w, cmd->tx_argv[argpos], TRUE, FALSE);
break;
default:
amountx = cmdParseCoord(w, cmd->tx_argv[argpos], TRUE, TRUE);
amounty = cmdParseCoord(w, cmd->tx_argv[cmd->tx_argc - 1],
TRUE, FALSE);
break;
}
}
else
{
if (indx < 0) {
TxError("Improperly defined copy. . . direction needed.\n");
return;
}
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_NORTHWEST:
xdelta = -amountx;
ydelta = amounty;
break;
case GEO_NORTHEAST:
case -2:
xdelta = amountx;
ydelta = amounty;
break;
case GEO_SOUTHWEST:
xdelta = -amountx;
ydelta = -amounty;
break;
case GEO_SOUTHEAST:
xdelta = amountx;
ydelta = -amounty;
break;
case GEO_CENTER:
xdelta = 0;
ydelta = 0;
break;
default:
ASSERT(FALSE, "Bad direction in CmdCopy");
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.
*/
MagWindow *window;
window = ToolGetPoint(&rootPoint, (Rect *) NULL);
if ((window == NULL) ||
(EditRootDef != ((CellUse *) window->w_surfaceID)->cu_def))
{
TxError("\"Copy\" uses the point as the place to put down a\n");
TxError(" copy of the selection, but the point doesn't\n");
TxError(" point to the edit cell.\n");
return;
}
copyToPoint:
if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef))
{
TxError("\"Copy\" uses the box lower-left corner as a place\n");
TxError(" to pick up the selection for copying, but the box\n");
TxError(" isn't in a window containing the selection.\n");
return;
}
GeoTransTranslate(rootPoint.p_x - rootBox.r_xbot,
rootPoint.p_y - rootBox.r_ybot, &GeoIdentityTransform, &t);
GeoTransRect(&t, &rootBox, &newBox);
DBWSetBox(rootDef, &newBox);
}
SelectCopy(&t);
}
/*
* ----------------------------------------------------------------------------
*
* CmdCorner --
*
* Implement the "corner" command. Find all paint touching one side
* of the box, and paint it around two edges of the box in an "L"
* shape.
*
* Usage:
* corner firstDirection secondDirection [layers]
*
* Results:
* None.
*
* Side effects:
* The edit cell is modified.
*
* ----------------------------------------------------------------------------
*/
/* Data passed between CmdCorner and cmdCornerFunc: */
int cmdCornerDir1; /* First direction each wire must
* be extended.
*/
int cmdCornerDir2; /* Second direction each wire must
* be extended.
*/
Rect cmdCornerRootBox; /* Root coords of box. */
struct cmdCornerArea *cmdCornerList; /* List of areas to fill. */
typedef struct cifpathlist
{
TileType pathtype;
CIFPath *pathhead;
struct cifpathlist *cpl_next;
} CIFPathList;
typedef struct nmcornerpath
{
bool hasErr;
CIFPathList *pathlist;
} NMCornerPath;
void
CmdCorner(w, cmd)
MagWindow *w; /* Window in which command was invoked. */
TxCommand *cmd; /* Describes the command that was invoked. */
{
TileTypeBitMask maskBits;
Rect editBox;
SearchContext scx;
extern int cmdCornerFunc();
bool hasErr = FALSE;
int locargc = cmd->tx_argc;
extern int cmdBevelFunc();
bool dobevel = FALSE;
NMCornerPath cmdPathList;
if (cmd->tx_argc < 3 || cmd->tx_argc > 5)
{
TxError("Usage: %s direction1 direction2 [layers]\n",
cmd->tx_argv[0]);
return;
}
windCheckOnlyWindow(&w, DBWclientID);
if ( w == (MagWindow *) NULL )
{
TxError("Point to a window\n");
return;
}
/* Find and check validity of directions. */
cmdCornerDir1 = GeoNameToPos(cmd->tx_argv[1], TRUE, TRUE);
if (cmdCornerDir1 < 0)
return;
cmdCornerDir2 = GeoNameToPos(cmd->tx_argv[2], TRUE, TRUE);
if (cmdCornerDir2 < 0)
return;
if ((cmdCornerDir1 == GEO_NORTH) || (cmdCornerDir1 == GEO_SOUTH))
{
if ((cmdCornerDir2 == GEO_NORTH) || (cmdCornerDir2 == GEO_SOUTH))
{
TxPrintf("Can't corner-fill %s and then %s.\n",
cmd->tx_argv[1], cmd->tx_argv[2]);
return;
}
}
else
{
if ((cmdCornerDir2 == GEO_EAST) || (cmdCornerDir2 == GEO_WEST))
{
TxPrintf("Can't corner-fill %s and then %s.\n",
cmd->tx_argv[1], cmd->tx_argv[2]);
return;
}
}
/* Check for "bevel" keyword */
if (locargc > 3)
{
if (!strncmp(cmd->tx_argv[locargc - 1], "bevel", 5))
{
locargc--;
dobevel = TRUE;
}
}
/* Figure out which layers to fill. */
if (locargc < 4)
maskBits = DBAllButSpaceAndDRCBits;
else
{
if (!CmdParseLayers(cmd->tx_argv[3], &maskBits))
return;
}
/* Figure out which material to search for and invoke a search
* procedure to find it.
*/
if (!ToolGetEditBox(&editBox)) return;
GeoTransRect(&EditToRootTransform, &editBox, &cmdCornerRootBox);
scx.scx_area = cmdCornerRootBox;
switch (cmdCornerDir1)
{
case GEO_NORTH:
scx.scx_area.r_ytop = scx.scx_area.r_ybot + 1;
scx.scx_area.r_ybot -= 1;
break;
case GEO_SOUTH:
scx.scx_area.r_ybot = scx.scx_area.r_ytop - 1;
scx.scx_area.r_ytop += 1;
break;
case GEO_EAST:
scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1;
scx.scx_area.r_xbot -= 1;
break;
case GEO_WEST:
scx.scx_area.r_xbot = scx.scx_area.r_xtop - 1;
scx.scx_area.r_xtop += 1;
break;
}
scx.scx_use = (CellUse *) w->w_surfaceID;
scx.scx_trans = GeoIdentityTransform;
if (dobevel)
{
cmdPathList.hasErr = FALSE;
cmdPathList.pathlist = NULL;
(void) DBTreeSrTiles(&scx, &maskBits,
((DBWclientRec *) w->w_clientData)->dbw_bitmask,
cmdBevelFunc, (ClientData) &cmdPathList);
if (cmdPathList.hasErr)
TxError("There's not enough room in the box for all the wires.\n");
while (cmdPathList.pathlist != NULL)
{
/* It is a bit of a hack to use CIFPolyToRects here, but it */
/* beats rewriting the entire path parsing routine. */
LinkedRect *rectp;
PaintUndoInfo ui;
int pNum = DBPlane(cmdPathList.pathlist->pathtype);
PaintResultType *resultTbl =
DBStdPaintTbl(cmdPathList.pathlist->pathtype, pNum);
Plane *plane = EditRootDef->cd_planes[pNum];
ui.pu_def = EditRootDef;
ui.pu_pNum = pNum;
rectp = CIFPolyToRects(cmdPathList.pathlist->pathhead, plane,
resultTbl, &ui);
for (; rectp != NULL; rectp = rectp->r_next)
{
DBPaintPlane(plane, &rectp->r_r, resultTbl, &ui);
freeMagic((char *)rectp);
}
CIFFreePath(cmdPathList.pathlist->pathhead);
freeMagic((char *)cmdPathList.pathlist);
cmdPathList.pathlist = cmdPathList.pathlist->cpl_next;
}
}
else
{
cmdCornerList = (struct cmdCornerArea *) NULL;
(void) DBTreeSrTiles(&scx, &maskBits,
((DBWclientRec *) w->w_clientData)->dbw_bitmask,
cmdCornerFunc, (ClientData) &hasErr);
if (hasErr)
TxError("There's not enough room in the box for all the wires.\n");
/* Now that we've got all the material, scan over the list
* painting the material and freeing up the entries on the list.
*/
while (cmdCornerList != NULL)
{
DBPaint(EditCellUse->cu_def, &cmdCornerList->cca_area,
cmdCornerList->cca_type);
freeMagic((char *) cmdCornerList);
cmdCornerList = cmdCornerList->cca_next;
}
}
SelectClear();
DBAdjustLabels(EditCellUse->cu_def, &editBox);
DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &editBox);
DBWAreaChanged(EditCellUse->cu_def, &editBox, DBW_ALLWINDOWS, &maskBits);
DBReComputeBbox(EditCellUse->cu_def);
}
/*
* ----------------------------------------------------------------------------
*
* cmdCornerFunc --
*
* Search procedure called by DBTreeSrTiles from CmdCorner. Called once
* for each tile that crosses the appropriate boundary of the box.
* Makes an L-shaped 90 degree turn to extend a wire out of an
* adjacent side.
*
* Results:
* Returns 0 to keep the search alive.
*
* Side effects:
* Adds paint tiles to the display list. If there are tiles found
* that can't be cornered correctly, the clientData value is set
* to TRUE.
*
* ----------------------------------------------------------------------------
*/
int
cmdCornerFunc(tile, cxp)
Tile *tile; /* Tile to fill with. */
TreeContext *cxp; /* Describes state of search. */
{
Rect r1, r2, r3;
struct cmdCornerArea *cca;
bool *errPtr = (bool *) cxp->tc_filter->tf_arg;
/* Get the tile dimensions in root coordinates. Clip to the box.
*/
TiToRect(tile, &r1);
GeoTransRect(&cxp->tc_scx->scx_trans, &r1, &r2);
GeoClip(&r2, &cmdCornerRootBox);
/* Generate r2 and r3, the first and second legs of the L-shaped
* geometry to be painted for this tile.
*/
r3 = r2;
switch (cmdCornerDir1)
{
case GEO_NORTH:
if (cmdCornerDir2 == GEO_EAST)
{
r2.r_ytop = r3.r_ytop = cmdCornerRootBox.r_ytop
- (r2.r_xbot - cmdCornerRootBox.r_xbot);
r3.r_xtop = cmdCornerRootBox.r_xtop;
}
else
{
r2.r_ytop = r3.r_ytop = cmdCornerRootBox.r_ytop
- (cmdCornerRootBox.r_xtop - r2.r_xtop);
r3.r_xbot = cmdCornerRootBox.r_xbot;
}
r3.r_ybot = r3.r_ytop - (r2.r_xtop - r2.r_xbot);
if (r3.r_ybot < cmdCornerRootBox.r_ybot)
*errPtr = TRUE;
break;
case GEO_SOUTH:
if (cmdCornerDir2 == GEO_EAST)
{
r2.r_ybot = r3.r_ybot = cmdCornerRootBox.r_ybot
+ (r2.r_xbot - cmdCornerRootBox.r_xbot);
r3.r_xtop = cmdCornerRootBox.r_xtop;
}
else
{
r2.r_ybot = r3.r_ybot = cmdCornerRootBox.r_ybot
+ (cmdCornerRootBox.r_xtop - r2.r_xtop);
r3.r_xbot = cmdCornerRootBox.r_xbot;
}
r3.r_ytop = r3.r_ybot + (r2.r_xtop - r2.r_xbot);
if (r3.r_ytop > cmdCornerRootBox.r_ytop)
*errPtr = TRUE;
break;
case GEO_EAST:
if (cmdCornerDir2 == GEO_NORTH)
{
r2.r_xtop = r3.r_xtop = cmdCornerRootBox.r_xtop
- (r2.r_ybot - cmdCornerRootBox.r_ybot);
r3.r_ytop = cmdCornerRootBox.r_ytop;
}
else
{
r2.r_xtop = r3.r_xtop = cmdCornerRootBox.r_xtop
- (cmdCornerRootBox.r_ytop - r2.r_ytop);
r3.r_ybot = cmdCornerRootBox.r_ybot;
}
r3.r_xbot = r3.r_xtop - (r2.r_ytop - r2.r_ybot);
if (r3.r_xbot < cmdCornerRootBox.r_xbot)
*errPtr = TRUE;
break;
case GEO_WEST:
if (cmdCornerDir2 == GEO_NORTH)
{
r2.r_xbot = r3.r_xbot = cmdCornerRootBox.r_xbot
+ (r2.r_ybot - cmdCornerRootBox.r_ybot);
r3.r_ytop = cmdCornerRootBox.r_ytop;
}
else
{
r2.r_xbot = r3.r_xbot = cmdCornerRootBox.r_xbot
+ (cmdCornerRootBox.r_ytop - r2.r_ytop);
r3.r_ybot = cmdCornerRootBox.r_ybot;
}
r3.r_xtop = r2.r_xbot + (r2.r_ytop - r2.r_ybot);
if (r3.r_xtop > cmdCornerRootBox.r_xtop)
*errPtr = TRUE;
break;
}
/* Clip the resulting geometry to the box, translate to edit cell
* coords, and add to the paint list if non-NULL.
*/
GeoClip(&r2, &cmdCornerRootBox);
GeoTransRect(&RootToEditTransform, &r2, &r1);
if (!GEO_RECTNULL(&r1))
{
/* Add this rectangle to the list. */
cca = (struct cmdCornerArea *)
mallocMagic(sizeof(struct cmdCornerArea));
cca->cca_area = r1;
cca->cca_type = TiGetType(tile);
cca->cca_next = cmdCornerList;
cmdCornerList = cca;
}
GeoClip(&r3, &cmdCornerRootBox);
GeoTransRect(&RootToEditTransform, &r3, &r1);
if (!GEO_RECTNULL(&r1))
{
cca = (struct cmdCornerArea *)
mallocMagic(sizeof(struct cmdCornerArea));
cca->cca_area = r1;
cca->cca_type = TiGetType(tile);
cca->cca_next = cmdCornerList;
cmdCornerList = cca;
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* Convenience function for cmdBevelFunc. Allocates a CIFPath record.
*
* ----------------------------------------------------------------------------
*/
void
AddNewPoint(CIFPath **pathhead)
{
CIFPath *newpoint;
newpoint = (CIFPath *)mallocMagic(sizeof(CIFPath));
newpoint->cifp_next = *pathhead;
*pathhead = newpoint;
}
/*
* ----------------------------------------------------------------------------
*
* cmdBevelFunc --
*
* Search procedure called by DBTreeSrTiles from CmdCorner. Called once
* for each tile that crosses the appropriate boundary of the box.
* Makes a beveled 90 degree turn to extend a wire out of an
* adjacent side.
*
* Results:
* Returns 0 to keep the search alive.
*
* Side effects:
* Adds paint tiles to the display list. If there are tiles found
* that can't be cornered correctly, the clientData value is set
* to TRUE.
*
* ----------------------------------------------------------------------------
*/
int
cmdBevelFunc(tile, cxp)
Tile *tile; /* Tile to fill with. */
TreeContext *cxp; /* Describes state of search. */
{
Rect r1, r2, r3;
CIFPathList *cpl;
CIFPath *pptr, *pathhead = NULL;
NMCornerPath *pathrecord = (NMCornerPath *) cxp->tc_filter->tf_arg;
int wirewidth, boxwidth, boxheight;
TileType pathtype;
/* Get the tile dimensions in root coordinates. Clip to the box.
*/
TiToRect(tile, &r1);
GeoTransRect(&cxp->tc_scx->scx_trans, &r1, &r2);
GeoClip(&r2, &cmdCornerRootBox);
/* Find the Manhattan rectangles like cmdCornerFunc. Shrink
* these to a minimum square or rectangle. Finally, pick the
* path points off of the two boxes to generate the nonManhattan
* path.
*/
r3 = r2;
switch (cmdCornerDir1)
{
case GEO_NORTH:
pathtype = TiGetTopType(tile);
if (pathtype == TT_SPACE) return 0;
wirewidth = r2.r_xtop - r2.r_xbot;
if (cmdCornerDir2 == GEO_EAST)
{
r3.r_ytop = cmdCornerRootBox.r_ytop
- (r2.r_xbot - cmdCornerRootBox.r_xbot);
r3.r_xtop = r3.r_xbot = cmdCornerRootBox.r_xtop;
boxwidth = r3.r_xtop - r2.r_xbot;
boxheight = r3.r_ytop - r3.r_ybot;
if (boxwidth > boxheight)
r3.r_xbot = r2.r_xbot + boxheight;
r3.r_xbot -= wirewidth;
}
else
{
r3.r_ytop = cmdCornerRootBox.r_ytop
- (cmdCornerRootBox.r_xtop - r2.r_xtop);
r3.r_xbot = r3.r_xtop = cmdCornerRootBox.r_xbot;
boxwidth = r2.r_xtop - r3.r_xbot;
boxheight = r3.r_ytop - r3.r_ybot;
if (boxwidth > boxheight)
r3.r_xtop = r2.r_xtop - boxheight;
r3.r_xtop += wirewidth;
}
r3.r_ybot = r3.r_ytop - wirewidth;
if (r3.r_ybot < cmdCornerRootBox.r_ybot)
pathrecord->hasErr = TRUE;
if (boxheight > boxwidth)
r2.r_ytop = r3.r_ytop - boxwidth;
else
r2.r_ytop = r2.r_ybot;
r2.r_ytop += wirewidth;
/* Create nonManhattan path */
if (cmdCornerDir2 == GEO_EAST)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ybot;
if (boxheight > boxwidth)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ytop - wirewidth;
}
else if (boxwidth > boxheight)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot + wirewidth;
pathhead->cifp_y = r3.r_ybot;
}
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ytop;
}
else
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ybot;
if (boxheight > boxwidth)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ytop - wirewidth;
}
else if (boxwidth > boxheight)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop - wirewidth;
pathhead->cifp_y = r3.r_ybot;
}
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ytop;
}
break;
case GEO_SOUTH:
pathtype = TiGetBottomType(tile);
if (pathtype == TT_SPACE) return 0;
wirewidth = r2.r_xtop - r2.r_xbot;
if (cmdCornerDir2 == GEO_EAST)
{
r3.r_ybot = cmdCornerRootBox.r_ybot
+ (r2.r_xbot - cmdCornerRootBox.r_xbot);
r3.r_xtop = r3.r_xbot = cmdCornerRootBox.r_xtop;
boxwidth = r3.r_xtop - r2.r_xbot;
boxheight = r3.r_ytop - r3.r_ybot;
if (boxwidth > boxheight)
r3.r_xbot = r2.r_xbot + boxheight;
r3.r_xbot -= wirewidth;
}
else
{
r3.r_ybot = cmdCornerRootBox.r_ybot
+ (cmdCornerRootBox.r_xtop - r2.r_xtop);
r3.r_xbot = r3.r_xtop = cmdCornerRootBox.r_xbot;
boxwidth = r2.r_xtop - r3.r_xbot;
boxheight = r3.r_ytop - r3.r_ybot;
if (boxwidth > boxheight)
r3.r_xtop = r2.r_xtop - boxheight;
r3.r_xtop += wirewidth;
}
r3.r_ytop = r3.r_ybot + wirewidth;
if (r3.r_ytop > cmdCornerRootBox.r_ytop)
pathrecord->hasErr = TRUE;
if (boxheight > boxwidth)
r2.r_ybot = r3.r_ybot + boxwidth;
else
r2.r_ybot = r2.r_ytop;
r2.r_ybot -= wirewidth;
/* Create nonManhattan path */
if (cmdCornerDir2 == GEO_EAST)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ytop;
if (boxheight > boxwidth)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ybot + wirewidth;
}
else if (boxwidth > boxheight)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot + wirewidth;
pathhead->cifp_y = r3.r_ytop;
}
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ybot;
}
else
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ytop;
if (boxheight > boxwidth)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ybot + wirewidth;
}
else if (boxwidth > boxheight)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop - wirewidth;
pathhead->cifp_y = r3.r_ytop;
}
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ybot;
}
break;
case GEO_EAST:
pathtype = TiGetRightType(tile);
if (pathtype == TT_SPACE) return 0;
wirewidth = r2.r_ytop - r2.r_ybot;
if (cmdCornerDir2 == GEO_NORTH)
{
r3.r_xtop = cmdCornerRootBox.r_xtop
- (r2.r_ybot - cmdCornerRootBox.r_ybot);
r3.r_ytop = r3.r_ybot = cmdCornerRootBox.r_ytop;
boxwidth = r3.r_xtop = r3.r_xbot;
boxheight = r3.r_xtop = r2.r_ybot;
if (boxheight > boxwidth)
r3.r_ybot = r2.r_ybot + boxwidth;
r3.r_ybot -= wirewidth;
}
else
{
r3.r_xtop = cmdCornerRootBox.r_xtop
- (cmdCornerRootBox.r_ytop - r2.r_ytop);
r3.r_ybot = r3.r_ytop = cmdCornerRootBox.r_ybot;
boxwidth = r3.r_xtop = r3.r_xbot;
boxheight = r2.r_xtop = r3.r_ybot;
if (boxheight > boxwidth)
r3.r_ytop = r2.r_ytop - boxwidth;
r3.r_ytop += wirewidth;
}
r3.r_xbot = r3.r_xtop - wirewidth;
if (r3.r_xbot < cmdCornerRootBox.r_xbot)
pathrecord->hasErr = TRUE;
if (boxwidth > boxheight)
r2.r_xtop = r3.r_xtop - boxheight;
else
r2.r_xtop = r2.r_xbot;
r2.r_xtop += wirewidth;
/* Create nonManhattan path */
if (cmdCornerDir2 == GEO_NORTH)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ytop;
if (boxheight > boxwidth)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ybot + wirewidth;
}
else if (boxwidth > boxheight)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop - wirewidth;
pathhead->cifp_y = r2.r_ytop;
}
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ybot;
}
else
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ybot;
if (boxheight > boxwidth)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ytop - wirewidth;
}
else if (boxwidth > boxheight)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop - wirewidth;
pathhead->cifp_y = r2.r_ybot;
}
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ytop;
}
break;
case GEO_WEST:
pathtype = TiGetLeftType(tile);
if (pathtype == TT_SPACE) return 0;
wirewidth = r2.r_ytop - r2.r_ybot;
if (cmdCornerDir2 == GEO_NORTH)
{
r3.r_xbot = cmdCornerRootBox.r_xbot
+ (r2.r_ybot - cmdCornerRootBox.r_ybot);
r3.r_ytop = r3.r_ybot = cmdCornerRootBox.r_ytop;
boxwidth = r3.r_xtop = r3.r_xbot;
boxheight = r3.r_xtop = r2.r_ybot;
if (boxheight > boxwidth)
r3.r_ybot = r2.r_ybot + boxwidth;
r3.r_ybot -= wirewidth;
}
else
{
r3.r_xbot = cmdCornerRootBox.r_xbot
+ (cmdCornerRootBox.r_ytop - r2.r_ytop);
r3.r_ybot = r3.r_ytop = cmdCornerRootBox.r_ybot;
boxwidth = r3.r_xtop = r3.r_xbot;
boxheight = r2.r_xtop = r3.r_ybot;
if (boxheight > boxwidth)
r3.r_ytop = r2.r_ytop - boxwidth;
r3.r_ytop += wirewidth;
}
r3.r_xtop = r2.r_xbot + wirewidth;
if (r3.r_xtop > cmdCornerRootBox.r_xtop)
pathrecord->hasErr = TRUE;
if (boxwidth > boxheight)
r2.r_xbot = r3.r_xbot + boxheight;
else
r2.r_xbot = r2.r_xtop;
r2.r_xbot -= wirewidth;
/* Create nonManhattan path */
if (cmdCornerDir2 == GEO_NORTH)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ytop;
if (boxheight > boxwidth)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ybot + wirewidth;
}
else if (boxwidth > boxheight)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot + wirewidth;
pathhead->cifp_y = r2.r_ytop;
}
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ybot;
}
else
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot;
pathhead->cifp_y = r2.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ytop;
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xtop;
pathhead->cifp_y = r2.r_ybot;
if (boxheight > boxwidth)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ytop - wirewidth;
}
else if (boxwidth > boxheight)
{
AddNewPoint(&pathhead);
pathhead->cifp_x = r2.r_xbot + wirewidth;
pathhead->cifp_y = r2.r_ybot;
}
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xtop;
pathhead->cifp_y = r3.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ybot;
AddNewPoint(&pathhead);
pathhead->cifp_x = r3.r_xbot;
pathhead->cifp_y = r3.r_ytop;
}
break;
}
/* If either rectangle is NULL after clipping, free the point */
/* memory and return without adding the path. */
GeoClip(&r2, &cmdCornerRootBox);
GeoClip(&r3, &cmdCornerRootBox);
if (GEO_RECTNULL(&r2) || GEO_RECTNULL(&r3))
{
for (pptr = pathhead; pptr != NULL; pptr = pptr->cifp_next)
freeMagic((char *)pptr);
return 0;
}
/* Clip the point geometry to the box, and translate to edit cell
* coords.
*/
for (pptr = pathhead; pptr != NULL; pptr = pptr->cifp_next)
{
Point p = pptr->cifp_point;
GeoClipPoint(&p, &cmdCornerRootBox);
GeoTransPoint(&RootToEditTransform, &p, &pptr->cifp_point);
}
/* Create the path and add it to the list. */
cpl = (CIFPathList *) mallocMagic(sizeof(CIFPathList));
cpl->pathtype = pathtype;
cpl->cpl_next = pathrecord->pathlist;
cpl->pathhead = pathhead;
pathrecord->pathlist = cpl;
return 0; /* Keep the search going */
}
/*
* ----------------------------------------------------------------------------
*
* CmdCrash --
*
* Save cells to or recover cells from a crash backup file
*
* Usage:
* crash save|recover [file]
*
* Results:
* None.
*
* Side effects:
* Loads the database with multiple cells from the crash recovery
* file, and removes the crash recovery file.
*
* ----------------------------------------------------------------------------
*/
void
CmdCrash(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int option;
char *filename = NULL;
static char *cmdCrashOpt[] = {"save", "recover", 0};
if (cmd->tx_argc > 3)
TxError("Usage: %s save|recover [filename]\n", cmd->tx_argv[0]);
else if (cmd->tx_argc >= 2)
{
option = Lookup(cmd->tx_argv[1], cmdCrashOpt);
if (option < 0)
{
TxError("Usage: %s save|recover [filename]\n", cmd->tx_argv[0]);
return;
}
}
if (cmd->tx_argc == 3) filename = cmd->tx_argv[2];
switch(option) {
case 0: /* save */
DBWriteBackup(filename);
break;
case 1: /* recover */
DBFileRecovery(filename);
break;
}
}
/*
* ----------------------------------------------------------------------------
*
* CmdCrosshair --
*
* Manipulate the crosshair highlight
*
* Usage:
* crosshair <x> <y>
* crosshair off
*
* Results:
* None.
*
* Side effects:
* The crosshair is enabled, moved, or disabled.
* ----------------------------------------------------------------------------
*/
void
CmdCrosshair(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Point pos;
if (cmd->tx_argc == 2)
{
if (!strcmp(cmd->tx_argv[1], "off"))
{
/* Erase and turn off */
pos.p_x = pos.p_y = MINFINITY;
}
else
{
TxError("Usage: %s off|x y \n", cmd->tx_argv[0]);
return;
}
}
else if (cmd->tx_argc == 3)
{
pos.p_x = cmdParseCoord(w, cmd->tx_argv[1], FALSE, TRUE);
pos.p_y = cmdParseCoord(w, cmd->tx_argv[2], FALSE, FALSE);
}
else
{
TxError("Usage: %s off|x y \n", cmd->tx_argv[0]);
return;
}
DBWSetCrosshair(w, &pos);
}
/*
* ----------------------------------------------------------------------------
*
* CmdDelete --
*
* Implement the "delete" command.
*
* Usage:
* delete
*
* Results:
* None.
*
* Side effects:
* The selection is deleted.
*
* ----------------------------------------------------------------------------
*/
void
CmdDelete(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
if (cmd->tx_argc != 1) goto badusage;
if (!ToolGetEditBox((Rect *)NULL)) return;
SelectDelete("deleted", TRUE);
return;
badusage:
TxError("Usage: %s\n", cmd->tx_argv[0]);
}
/*
* ----------------------------------------------------------------------------
*
* CmdDown --
*
* Implement the "down" command.
* Use the cell that is currently selected as the edit cell. If more than
* one cell is selected, use the point to choose between them. Load the
* new edit cell into the window containing the point tool.
*
* Usage:
* down
*
* Results:
* None.
*
* Side effects:
* Sets EditCellUse.
*
* ----------------------------------------------------------------------------
*/
/* The following variable is set by cmdDownEnumFunc to signal that there
* was at least one cell in the selection.
*/
static bool cmdFoundNewDown;
void
CmdDown(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Rect area, pointArea;
int cmdEditRedisplayFunc(); /* External declaration. */
int cmdDownEnumFunc(); /* Forward declaration. */
if (cmd->tx_argc > 1)
{
TxError("Usage: edit\nMaybe you want the \"load\" command\n");
return;
}
/* Record the current edit cell's area for redisplay (now that it's
* not the edit cell, it will be displayed differently). Do this
* only in windows where the edit cell is displayed differently from
* other cells.
*/
GeoTransRect(&EditToRootTransform, &(EditCellUse->cu_def->cd_bbox), &area);
(void) WindSearch(DBWclientID, (ClientData) NULL,
(Rect *) NULL, cmdEditRedisplayFunc, (ClientData) &area);
/* Use the position of the point to select one of the currently-selected
* cells (if there are more than one). If worst comes to worst, just
* select any selected cell.
*/
(void) ToolGetPoint((Point *) NULL, &pointArea);
cmdFoundNewDown = FALSE;
(void) SelEnumCells(FALSE, (bool *) NULL, (SearchContext *) NULL,
cmdDownEnumFunc, (ClientData) &pointArea);
if (!cmdFoundNewDown)
TxError("You haven't selected a new cell to edit.\n");
/* Now record the new edit cell's area for redisplay. */
GeoTransRect(&EditToRootTransform, &(EditCellUse->cu_def->cd_bbox), &area);
(void) WindSearch(DBWclientID, (ClientData) NULL,
(Rect *) NULL, cmdEditRedisplayFunc, (ClientData) &area);
DBWloadWindow(w, EditCellUse->cu_def->cd_name, TRUE, FALSE, FALSE);
}
/* Search function to find the new edit cell: look for a cell use
* that contains the rectangle passed as argument. If we find such
* a use, return 1 to abort the search. Otherwise, save information
* about this use anyway: it'll become the edit cell if nothing
* better is found.
*/
/* ARGSUSED */
int
cmdDownEnumFunc(selUse, use, transform, area)
CellUse *selUse; /* Use from selection (not used). */
CellUse *use; /* Use from layout that corresponds to
* selUse (could be an array!).
*/
Transform *transform; /* Transform from use->cu_def to root coords. */
Rect *area; /* We're looking for a use containing this
* area, in root coords.
*/
{
Rect defArea, useArea;
/* Save this use as the default next edit cell, regardless of whether
* or not it overlaps the area we're interested in.
*/
EditToRootTransform = *transform;
GeoInvertTrans(transform, &RootToEditTransform);
EditCellUse = use;
EditRootDef = SelectRootDef;
cmdFoundNewDown = TRUE;
/* See if the bounding box of this use overlaps the area we're
* interested in.
*/
GeoTransRect(&RootToEditTransform, area, &defArea);
GeoTransRect(&use->cu_transform, &defArea, &useArea);
if (!GEO_OVERLAP(&useArea, &use->cu_bbox)) return 0;
return 1;
}
/*
* ----------------------------------------------------------------------------
*
* CmdDrc --
*
* Implement the "drc" command.
*
* Usage:
* drc option
*
* Results:
* None.
*
* Side effects:
* Most options have no side effects. The only major side
* effects are to turn continuous DRC on or off, or recheck an
* area of a cell.
*
* ----------------------------------------------------------------------------
*/
#define FLATCHECK 0
#define DRC_HALO 1
#define SHOWINT 2
#define DRC_STEPSIZE 3
#define CATCHUP 4
#define CHECK 5
#define COUNT 6
#define EUCLIDEAN 7
#define FIND 8
#define DRC_HELP 9
#define DRC_OFF 10
#define DRC_ON 11
#define DRC_STATUS 12
#define DRC_STYLE 13
#define PRINTRULES 14
#define RULESTATS 15
#define STATISTICS 16
#define WHY 17
void
CmdDrc(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
FILE * fp;
static int drc_nth = 1;
int option, result, radius;
Rect rootArea, area;
CellUse * rootUse, *use;
CellDef * rootDef;
Transform trans;
MagWindow * window;
char **msg;
bool wizardHelp;
bool incremental;
bool doforall = FALSE;
bool dolist = FALSE;
int count_total;
DRCCountList *dcl;
int argc = cmd->tx_argc;
char **argv = cmd->tx_argv;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj;
#endif
static char *cmdDrcOption[] =
{
"*flatcheck check box area by flattening",
"*halo [d] limit error checking to areas of d units",
"*showint radius show interaction area under box",
"*stepsize [d] change DRC step size to d units",
"catchup run checker and wait for it to complete",
"check recheck area under box in all cells",
"count [total] count error tiles in each cell under box",
"euclidean on|off enable/disable Euclidean geometry checking",
"find [nth] locate next (or nth) error in the layout",
"help print this help information",
"off turn off background checker",
"on reenable background checker",
"status report if the drc checker is on or off",
"style set the DRC style",
"printrules [file] print out design rules in file or on tty",
"rulestats print out stats about design rule database",
"statistics print out statistics gathered by checker",
"why print out reasons for errors under box",
NULL
};
if (argc < 2)
{
TxError("No option given in \":drc\" command.\n");
option = DRC_HELP;
}
else
{
if (!strncmp(argv[1], "list", 4))
{
dolist = TRUE;
if (!strncmp(argv[1], "listall", 7))
{
doforall = TRUE;
}
argv++;
argc--;
}
/* Make sure "list" or "listall" is followed by additional arguments */
if (argc < 2)
{
TxError("No option given in \":drc\" command.\n");
option = DRC_HELP;
}
option = Lookup(argv[1], cmdDrcOption);
if (option < 0)
{
TxError("%s isn't a valid drc option.\n", argv[1]);
option = DRC_HELP;
argc = 2;
}
if ((argc > 2) && (option != PRINTRULES) && (option != FIND)
&& (option != SHOWINT) && (option != DRC_HELP) && (option != EUCLIDEAN)
&& (option != DRC_STEPSIZE) && (option != DRC_HALO) && (option != COUNT)
&& (option != DRC_STYLE))
{
badusage:
TxError("Wrong arguments in \"drc %s\" command:\n", argv[1]);
TxError(" :drc %s\n", cmdDrcOption[option]);
TxError("Try \":drc help\" for more help.\n");
return;
}
}
switch (option)
{
case FLATCHECK:
window = ToolGetBoxWindow(&rootArea, (int *) NULL);
if (window == NULL) return;
rootUse = (CellUse *) window->w_surfaceID;
DRCFlatCheck(rootUse, &rootArea);
break;
case SHOWINT:
if (argc != 3) goto badusage;
radius = cmdParseCoord(w, argv[2], TRUE, FALSE);
if (radius < 0)
{
TxPrintf("Radius must not be negative\n");
return;
}
window = ToolGetBoxWindow(&rootArea, (int *) NULL);
if (window == NULL) return;
rootUse = (CellUse *) window->w_surfaceID;
if (!DRCFindInteractions(rootUse->cu_def, &rootArea,
radius, &area))
{
TxPrintf("No interactions in this area for that radius.\n");
return;
}
ToolMoveBox(TOOL_BL, &area.r_ll, FALSE, rootUse->cu_def);
ToolMoveCorner(TOOL_TR, &area.r_ur, FALSE, rootUse->cu_def);
break;
case DRC_STYLE:
if (argc == 2)
DRCPrintStyle(dolist, doforall, !doforall);
else
DRCSetStyle(argv[2]);
break;
case CATCHUP:
DRCCatchUp();
break;
case CHECK:
window = ToolGetBoxWindow(&rootArea, (int *) NULL);
if (window == NULL) return;
rootUse = (CellUse *) window->w_surfaceID;
DRCCheck(rootUse, &rootArea);
break;
case COUNT:
count_total = -1;
if (argc == 3)
if (!strncmp(argv[2], "total", 5))
count_total = 0;
#ifdef MAGIC_WRAPPER
if (count_total == -1) lobj = Tcl_NewListObj(0, NULL);
#endif
if ((window = w) == NULL)
{
window = ToolGetBoxWindow(&rootArea, (int *) NULL);
if (window == NULL) return;
}
else
rootArea = w->w_surfaceArea;
rootUse = (CellUse *) window->w_surfaceID;
dcl = DRCCount(rootUse, &rootArea, doforall);
while (dcl != NULL)
{
if (count_total >= 0)
count_total += dcl->dcl_count;
else
{
#ifdef MAGIC_WRAPPER
if (dolist)
{
Tcl_Obj *pobj = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(magicinterp, pobj,
Tcl_NewStringObj(dcl->dcl_def->cd_name, -1));
Tcl_ListObjAppendElement(magicinterp, pobj,
Tcl_NewIntObj(dcl->dcl_count));
Tcl_ListObjAppendElement(magicinterp, lobj, pobj);
}
else
{
#endif
if (dcl->dcl_count > 1)
TxPrintf("Cell %s has %d error tiles.\n",
dcl->dcl_def->cd_name, dcl->dcl_count);
else if (dcl->dcl_count == 1)
TxPrintf("Cell %s has just one error tile.\n",
dcl->dcl_def->cd_name);
#ifdef MAGIC_WRAPPER
}
#endif
}
freeMagic((char *)dcl);
dcl = dcl->dcl_next;
}
#ifdef MAGIC_WRAPPER
if (count_total >= 0)
{
if (dolist)
Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(count_total));
else
{
if ((DRCBackGround != DRC_SET_OFF) && (count_total == -1))
count_total = 0;
if (count_total >= 0)
TxPrintf("Total DRC errors found: %d\n", count_total);
}
}
else if (dolist)
Tcl_SetObjResult(magicinterp, lobj);
#else
if ((DRCBackGround != DRC_SET_OFF) && (count_total == -1))
count_total = 0;
if (count_gotal >= 0)
TxPrintf("Total DRC errors found: %d\n", count_total);
#endif
break;
case EUCLIDEAN:
if (argc == 2)
{
TxPrintf("DRC measurements are %s.\n", DRCEuclidean ?
"Euclidean" : "Manhattan");
}
else
{
if (!strcmp(argv[2], "on"))
DRCEuclidean = TRUE;
else
DRCEuclidean = FALSE;
}
break;
case FIND:
if ((window = w) == NULL)
{
window = ToolGetBoxWindow(&rootArea, (int *) NULL);
if (window == NULL) return;
}
else
rootArea = w->w_surfaceArea;
if (argc > 3) goto badusage;
rootUse = (CellUse *) window->w_surfaceID;
rootDef = rootUse->cu_def;
incremental = FALSE;
if (argc == 3 && StrIsInt(argv[2]))
{
drc_nth = atoi(argv[2]);
if (drc_nth <= 0) drc_nth = 1;
}
else
{
incremental = TRUE;
drc_nth++;
}
result = DRCFind(rootUse, &rootArea, &area, drc_nth);
if (incremental && (result < 0))
{
/* 2nd pass, if we exceeded the total number of errors */
drc_nth = 1;
result = DRCFind(rootUse, &rootArea, &area, drc_nth);
}
if (result > 0)
{
ToolMoveBox(TOOL_BL, &area.r_ll, FALSE, rootDef);
ToolMoveCorner(TOOL_TR, &area.r_ur, FALSE, rootDef);
#ifdef MAGIC_WRAPPER
if (!dolist)
#endif
TxPrintf("Error area #%d:\n", result);
DRCWhy(dolist, rootUse, &area);
}
else if (result < 0)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(-1));
if (!dolist)
#endif
TxPrintf("There aren't that many errors");
}
else
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(0));
if (!dolist)
#endif
TxPrintf("There are no errors in %s.\n", rootDef->cd_name);
}
break;
case DRC_HALO:
if (argc == 3)
{
DRCTechHalo = cmdScaleCoord(w, argv[2], FALSE, TRUE, 1);
if (DRCTechHalo > DRCCurStyle->DRCTechHalo)
DRCTechHalo = DRCCurStyle->DRCTechHalo;
if (DRCTechHalo < DRCCurStyle->DRCTechHalo)
TxPrintf("Warning: rulechecking limited to halo of "
"%d internal units (%d DRC units).\n",
DRCTechHalo,
DRCTechHalo * DRCCurStyle->DRCScaleFactorN
/ DRCCurStyle->DRCScaleFactorD);
else
TxPrintf("DRC checks all rules (halo of %d internal units, or"
" %d DRC units)\n",
DRCTechHalo,
DRCTechHalo * DRCCurStyle->DRCScaleFactorN
/ DRCCurStyle->DRCScaleFactorD);
}
else
{
TxPrintf("DRC halo is %d internal units (%d DRC units)\n",
DRCTechHalo,
DRCTechHalo * DRCCurStyle->DRCScaleFactorN
/ DRCCurStyle->DRCScaleFactorD);
if (DRCTechHalo != DRCCurStyle->DRCTechHalo)
TxPrintf("Maximum rule distance is %d internal units "
"(%d DRC units)\n",
DRCCurStyle->DRCTechHalo,
DRCCurStyle->DRCTechHalo * DRCCurStyle->DRCScaleFactorN
/ DRCCurStyle->DRCScaleFactorD);
}
break;
case DRC_HELP:
if ((argc == 3)
&& (strcmp(argv[2], "wizard") == 0))
wizardHelp = TRUE;
else wizardHelp = FALSE;
#ifdef MAGIC_WRAPPER
TxPrintf("DRC commands have the form \":drc [list|listall] option\",");
#else
TxPrintf("DRC commands have the form \":drc option\",");
#endif
TxPrintf(" where option is one of:\n");
for (msg = &(cmdDrcOption[0]); *msg != NULL; msg++)
{
if ((**msg == '*') && !wizardHelp) continue;
TxPrintf(" %s\n", *msg);
}
break;
case DRC_OFF:
DRCBackGround = DRC_SET_OFF;
#ifdef MAGIC_WRAPPER
DRCBreak();
if (TxInputRedirect != TX_INPUT_REDIRECTED)
#endif
TxSetPrompt('%'); /* Return prompt to "normal" */
break;
case DRC_ON:
/* Don't allow overriding of DRC_NOT_SET */
if (DRCBackGround == DRC_SET_OFF) DRCBackGround = DRC_SET_ON;
break;
case DRC_STATUS:
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp,
Tcl_NewBooleanObj((DRCBackGround == DRC_SET_OFF) ?
FALSE : TRUE));
#else
TxPrintf("DRC checker is %s.\n", (DRCBackGround == DRC_SET_OFF) ?
"off" : "on");
#endif
break;
case PRINTRULES:
if (argc > 3) goto badusage;
if (argc < 3)
fp = stdout;
else if ((fp = fopen (argv[2],"w")) == (FILE *) NULL)
{
TxError("Cannot write file %s\n", argv[2]);
return;
}
DRCPrintRulesTable (fp);
if (fp != stdout)
(void) fclose(fp);
break;
case RULESTATS:
DRCTechRuleStats();
break;
case STATISTICS:
DRCPrintStats();
break;
case DRC_STEPSIZE:
if (argc == 3)
{
DRCStepSize = cmdScaleCoord(w, argv[2], FALSE, TRUE, 1);
TxPrintf("DRC step size is now %d internal units (%d DRC units))\n",
DRCStepSize,
DRCStepSize * DRCCurStyle->DRCScaleFactorN
/ DRCCurStyle->DRCScaleFactorD);
}
else
{
TxPrintf("DRC step size is %d internal units (%d DRC units)\n",
DRCStepSize,
DRCStepSize * DRCCurStyle->DRCScaleFactorN
/ DRCCurStyle->DRCScaleFactorD);
if (DRCStepSize != (16 * DRCCurStyle->DRCTechHalo))
TxPrintf("Recommended step size is %d internal units"
" (%d DRC units)\n",
(16 * DRCCurStyle->DRCTechHalo),
(16 * DRCCurStyle->DRCTechHalo) *
DRCCurStyle->DRCScaleFactorN /
DRCCurStyle->DRCScaleFactorD);
}
break;
case WHY:
window = ToolGetBoxWindow(&rootArea, (int *) NULL);
if (window == NULL) return;
rootUse = (CellUse *) window->w_surfaceID;
#ifdef MAGIC_WRAPPER
if (doforall)
DRCWhyAll(rootUse, &rootArea, NULL);
else
#endif
DRCWhy(dolist, rootUse, &rootArea);
break;
}
return;
}
/*
* ----------------------------------------------------------------------------
*
* CmdDump --
*
* Implement the ":dump" command.
*
* Usage:
* dump cellName [child refPointChild] [parent refPointParent]
*
* where the refPoints are either a label name, e.g., SOCKET_A, or an x-y
* pair of integers, e.g., 100 200. The words "child" and "parent" are
* keywords, and may be abbreviated.
*
* Results:
* None.
*
* Side effects:
* Copies the contents of a given cell into the edit cell,
* so that refPointChild in the child cell (or the lower-left
* corner of its bounding box) ends up at location refPointParent
* in the edit cell (or the location of the box tool's lower-left).
*
* ----------------------------------------------------------------------------
*/
void
CmdDump(w, cmd)
MagWindow *w; /* Window in which command was invoked. */
TxCommand *cmd; /* Describes command arguments. */
{
SearchContext scx;
CellUse dummy;
if (cmdDumpParseArgs("dump", w, cmd, &dummy, &scx))
SelectDump(&scx);
}
/*
* ----------------------------------------------------------------------------
*
* cmdDumpParseArgs --
*
* Do the real work of the "dump" and "getcell" commands, by reading
* in the child cell to be used and figuring out the transform implied
* by the reference point arguments to the command.
*
* Results:
* TRUE on success; FALSE if arguments were missing or
* incorrect, or if the cell couldn't be found.
*
* Side effects:
* Fills in *dummy so that dummy->cu_def points to the cell
* specified by name in cmd->tx_argv[] (see CmdDump() for a
* description of the syntax of these args). Also fills in
* *scx so scx_use is dummy, scx_trans is the desired transform
* from dummy->cu_def back to root coordinates, and scx_area
* is the bounding box of dummy->cu_def. (Scx is set up
* directly for a call to SelectDump()).
*
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
bool
cmdDumpParseArgs(cmdName, w, cmd, dummy, scx)
char *cmdName; /* Either "dump" or "getcell" */
MagWindow *w; /* Window in which command was invoked (UNUSED) */
TxCommand *cmd; /* Arguments to command */
CellUse *dummy; /* Filled in to point to cell mentioned in command */
SearchContext *scx; /* Filled in with the transform from the child cell's
* def to ROOT coordinates, the bounding box of the
* child cell in child cell coordinates, and with
* scx_use = dummy, where dummy->cu_def is the child
* cell itself.
*/
{
Point childPoint, editPoint, rootPoint;
CellDef *def, *rootDef, *editDef;
bool hasChild, hasRoot, hasTrans, dereference;
Rect rootBox, bbox;
Transform *tx_cell, trans_cell;
char **av;
char *cellnameptr, *fullpathname;
int ac, clen;
if (cmd->tx_argc < 2)
{
TxError("Missing cell name in \"%s\" command.\n", cmdName);
goto usage;
}
if (EditCellUse == NULL)
{
TxError("No cell def being edited; cannot place cell use!\n");
return FALSE;
}
/* cellnameptr should not include any path components */
if ((cellnameptr = strrchr(cmd->tx_argv[1], '/')) != NULL)
{
cellnameptr++;
fullpathname = (char *)mallocMagic(strlen(cmd->tx_argv[1]) + 2);
strcpy(fullpathname, cmd->tx_argv[1]);
}
else
{
cellnameptr = cmd->tx_argv[1];
fullpathname = NULL;
}
/* If the name still has ".mag" attached, then strip it. */
clen = strlen(cellnameptr);
if ((clen > 4) && !strcmp(cellnameptr + clen - 4, ".mag"))
*(cellnameptr + clen - 4) = '\0';
/* Check for illegal characters in the cellname */
if (CmdIllegalChars(cellnameptr, "", "Cell name"))
{
if (fullpathname) freeMagic(fullpathname);
return (FALSE);
}
def = DBCellLookDef(cellnameptr);
if (def == (CellDef *) NULL)
def = DBCellNewDef(cellnameptr);
if (fullpathname != NULL)
{
/* Check if def already exists. If it points to a */
/* different file, then force a rename of the cell and */
/* flag a warning. */
if (def->cd_file != NULL)
{
/* Note: may want processing to see if absolute paths match */
if (strcmp(def->cd_file, fullpathname))
{
char uniqchar;
char *newcellname = (char *)mallocMagic(strlen(cellnameptr) + 3);
TxError("Warning: Cell file path mismatch. Existing cell has"
" path \"%s\", while %s path is \"%s\".\n",
def->cd_file, cmdName, fullpathname);
uniqchar = 'a';
while (def != NULL)
{
sprintf(newcellname, "%s_%c", cellnameptr, uniqchar);
def = DBCellLookDef(newcellname);
uniqchar++;
}
TxError("Renaming cell to \"%s\" to avoid conflict.", newcellname);
def = DBCellNewDef(cellnameptr);
def->cd_file = StrDup(&def->cd_file, fullpathname);
freeMagic(newcellname);
}
}
else
def->cd_file = StrDup(&def->cd_file, fullpathname);
freeMagic(fullpathname);
}
editDef = EditCellUse->cu_def;
/*
* The following line of code is a bit of a hack. It's needed to
* force DBCellRead to print an error message if it can't find the
* cell. Otherwise, if the cell wasn't found the last time it was
* looked for then no new error message will be printed.
*/
def->cd_flags &= ~CDNOTFOUND;
dereference = (def->cd_flags & CDDEREFERENCE) ? TRUE : FALSE;
if (!DBCellRead(def, (char *) NULL, TRUE, dereference, NULL))
return (FALSE);
DBReComputeBbox(def);
dummy->cu_def = def;
dummy->cu_transform = GeoIdentityTransform;
dummy->cu_expandMask = CU_DESCEND_SPECIAL;
if (DBIsAncestor(def, EditCellUse->cu_def))
{
TxError("The edit cell is already a descendant of \"%s\",\n",
cmd->tx_argv[1]);
TxError(" which means that you're trying to create a circular\n");
TxError(" structure. This isn't legal.\n");
return FALSE;
}
/*
* Get def's bounding box. If def is an abstract view with CDFIXEDBBOX
* set, then used the property FIXED_BBOX to set the bounding box.
*/
bbox = def->cd_bbox;
if (def->cd_flags & CDFIXEDBBOX)
{
char *propvalue;
bool found;
propvalue = (char *)DBPropGet(def, "FIXED_BBOX", &found);
if (found)
{
if (sscanf(propvalue, "%d %d %d %d", &bbox.r_xbot, &bbox.r_ybot,
&bbox.r_xtop, &bbox.r_ytop) != 4)
bbox = def->cd_bbox;
}
}
/*
* Parse the remainder of the arguments to find out the reference
* points in the child cell and the edit cell. Use the defaults
* of the lower-left corner of the child cell's bounding box, and
* the lower-left corner of the box tool, if the respective reference
* points weren't provided. (Lower-left of the box tool is interpreted
* in root coordinates).
*/
av = &cmd->tx_argv[2];
ac = cmd->tx_argc - 2;
hasChild = hasRoot = hasTrans = FALSE;
while (ac > 0)
{
static char *kwdNames[] = { "child", "parent", "90", "180", "270",
"v", "90v", "180v", "270v",
"h", "90h", "180h", "270h", 0 };
static char *refPointNames[] = { "ll", "lr", "ul", "ur", 0 };
Label *lab;
int n,p;
n = Lookup(av[0], kwdNames);
if (n < 0)
{
TxError("Unrecognized parent/child keyword: \"%s\"\n", av[0]);
goto usage;
}
switch (n)
{
case 0: /* Child */
if (ac < 2)
{
TxError("Keyword must be followed by a reference point\n");
goto usage;
}
if (StrIsInt(av[1]))
{
childPoint.p_x = atoi(av[1]);
if (ac < 3 || !StrIsInt(av[2]))
{
TxError("Must provide two coordinates\n");
goto usage;
}
childPoint.p_y = atoi(av[2]);
av += 3;
ac -= 3;
}
else
{
p = Lookup(av[1], refPointNames);
if (p == 0) /* lower left */
{
childPoint.p_x = bbox.r_ll.p_x;
childPoint.p_y = bbox.r_ll.p_y;
}
else if (p == 1) /* lower right */
{
childPoint.p_x = bbox.r_ur.p_x;
childPoint.p_y = bbox.r_ll.p_y;
}
else if (p == 2) /* upper left */
{
childPoint.p_x = bbox.r_ll.p_x;
childPoint.p_y = bbox.r_ur.p_y;
}
else if (p == 3) /* upper right */
{
childPoint.p_x = bbox.r_ur.p_x;
childPoint.p_y = bbox.r_ur.p_y;
}
else
{
childPoint = TiPlaneRect.r_ur;
(void) DBSrLabelLoc(dummy, av[1], cmdDumpFunc,
&childPoint);
if (childPoint.p_x == TiPlaneRect.r_xtop &&
childPoint.p_y == TiPlaneRect.r_ytop)
{
TxError("Couldn't find label \"%s\" in cell \"%s\".\n",
av[1], cmd->tx_argv[1]);
return FALSE;
}
}
av += 2;
ac -= 2;
}
hasChild = TRUE;
break;
case 1: /* Parent */
if (ac < 2)
{
TxError("Keyword must be followed by a reference point\n");
goto usage;
}
if (StrIsInt(av[1]))
{
editPoint.p_x = atoi(av[1]);
if (ac < 3 || !StrIsInt(av[2]))
{
TxError("Must provide two coordinates\n");
goto usage;
}
editPoint.p_y = atoi(av[2]);
av += 3;
ac -= 3;
GeoTransPoint(&EditToRootTransform, &editPoint,
&rootPoint);
}
else
{
p = Lookup(av[1], refPointNames);
if (p == 0) /* lower left */
{
if (!ToolGetBox(&rootDef, &rootBox) ||
(rootDef != EditRootDef)) goto box_error;
rootPoint.p_x = rootBox.r_ll.p_x;
rootPoint.p_y = rootBox.r_ll.p_y;
}
else if (p == 1) /* lower right */
{
if (!ToolGetBox(&rootDef, &rootBox) ||
(rootDef != EditRootDef)) goto box_error;
rootPoint.p_x = rootBox.r_ur.p_x;
rootPoint.p_y = rootBox.r_ll.p_y;
}
else if (p == 2) /* upper left */
{
if (!ToolGetBox(&rootDef, &rootBox) ||
(rootDef != EditRootDef)) goto box_error;
rootPoint.p_x = rootBox.r_ll.p_x;
rootPoint.p_y = rootBox.r_ur.p_y;
}
else if (p == 3) /* upper right */
{
if (!ToolGetBox(&rootDef, &rootBox) ||
(rootDef != EditRootDef)) goto box_error;
rootPoint.p_x = rootBox.r_ur.p_x;
rootPoint.p_y = rootBox.r_ur.p_y;
}
else
{
for (lab = editDef->cd_labels; lab; lab = lab->lab_next)
if (strcmp(lab->lab_text, av[1]) == 0)
break;
if (lab == NULL)
{
TxError("Couldn't find label \"%s\" in edit cell.\n",
av[1]);
return FALSE;
}
editPoint = lab->lab_rect.r_ll;
GeoTransPoint(&EditToRootTransform, &editPoint,
&rootPoint);
}
av += 2;
ac -= 2;
}
hasRoot = TRUE;
break;
case 2: /* 90 */
tx_cell = &Geo90Transform;
transform_cell:
if (ac < 2 )
{
default_action:
{
Rect r;
GeoTransRect(tx_cell, &bbox, &r);
GeoTranslateTrans(tx_cell, bbox.r_xbot - r.r_xbot,
bbox.r_ybot - r.r_ybot,
&trans_cell);
}
av += 1;
ac -= 1;
}
else if (Lookup(av[1], kwdNames)>=0)
{
goto default_action;
}
else
{
if (StrIsInt(av[1]))
{
editPoint.p_x = atoi(av[1]);
if (ac < 3 || !StrIsInt(av[2]))
{
TxError("Must provide two coordinates\n");
goto usage;
}
editPoint.p_y = atoi(av[2]);
av += 3;
ac -= 3;
}
else
{
p = Lookup(av[1], refPointNames);
if (p == 0) /* lower left */
{
editPoint.p_x = bbox.r_ll.p_x;
editPoint.p_y = bbox.r_ll.p_y;
}
else if (p == 1) /* lower right */
{
editPoint.p_x = bbox.r_ur.p_x;
editPoint.p_y = bbox.r_ll.p_y;
}
else if (p == 2) /* upper left */
{
editPoint.p_x = bbox.r_ll.p_x;
editPoint.p_y = bbox.r_ur.p_y;
}
else if (p == 3) /* upper right */
{
editPoint.p_x = bbox.r_ur.p_x;
editPoint.p_y = bbox.r_ur.p_y;
}
else
{
editPoint = TiPlaneRect.r_ur;
(void) DBSrLabelLoc(dummy, av[1], cmdDumpFunc,
&editPoint);
if (editPoint.p_x == TiPlaneRect.r_xtop &&
editPoint.p_y == TiPlaneRect.r_ytop)
{
TxError("Couldn't find label \"%s\" in cell \"%s\".\n",
av[1], cmd->tx_argv[1]);
return FALSE;
}
}
av += 2;
ac -= 2;
}
{
Point p;
GeoTransPoint(tx_cell, &editPoint, &p);
GeoTranslateTrans(tx_cell, editPoint.p_x - p.p_x,
editPoint.p_y - p.p_y, &trans_cell);
}
}
hasTrans = TRUE;
break;
case 3: /* 180 */
tx_cell = &Geo180Transform;
goto transform_cell;
case 4: /* 270 */
tx_cell = &Geo270Transform;
goto transform_cell;
case 5: /* v */
tx_cell = &GeoUpsideDownTransform;
goto transform_cell;
case 6: /* 90v */
tx_cell = &GeoRef45Transform;
goto transform_cell;
case 7: /* 180v */
tx_cell = &GeoSidewaysTransform;
goto transform_cell;
case 8: /* 270v */
tx_cell = &GeoRef135Transform;
goto transform_cell;
case 9: /* h */
tx_cell = &GeoSidewaysTransform;
goto transform_cell;
case 10: /* 90h */
tx_cell = &GeoRef135Transform;
goto transform_cell;
case 11: /* 180h */
tx_cell = &GeoUpsideDownTransform;
goto transform_cell;
case 12: /* 270h */
tx_cell = &GeoRef45Transform;
goto transform_cell;
}
}
/*
* Use the default values if explicit reference points weren't
* provided.
*/
if (!hasChild)
childPoint = bbox.r_ll;
if (!hasRoot)
{
if (!ToolGetBox(&rootDef, &rootBox))
{
box_error:
TxError("The box's lower-left corner must point to the place\n");
TxError(" in the edit cell where you'd like to put \"%s\".\n",
cmd->tx_argv[1]);
return FALSE;
}
else if (rootDef != EditRootDef)
{
TxError("The box is in cell \"%s\", not in the edit cell \"%s\"\n",
rootDef->cd_name, EditRootDef->cd_name);
return FALSE;
}
rootPoint = rootBox.r_ll;
}
if (!hasTrans)
{
trans_cell = GeoIdentityTransform;
}
scx->scx_use = dummy;
GeoTranslateTrans(&trans_cell, rootPoint.p_x - childPoint.p_x,
rootPoint.p_y - childPoint.p_y,
&scx->scx_trans);
scx->scx_area = bbox;
return TRUE;
usage:
TxError(
"Usage: %s cellName [child refPointChild] [parent refPointParent]\n",
cmdName);
TxError(" [transform [refPointTrans]],\n");
TxError(" where the refPoints are either a single label name,\n");
TxError(
" or ll for lower left corner, or lr for lower right corner\n");
TxError(
" or ul for upper left corner, or ur for upper right corner\n");
TxError(
" or a pair of integer coordinates, and the transform is one of\n");
TxError(
" 90, 180, 270, v, 90v, 180v, 270v, h, 90h, 180h, 270h.\n");
return FALSE;
}
/*
* cmdDumpFunc --
*
* Search function used to locate positioning label. It just computes
* the lower-left corner of the label and aborts the search.
*
* Results:
* Always returns 1.
*
* Side effects:
* Sets *point to the lower-left corner of the label.
*/
/* ARGSUSED */
int
cmdDumpFunc(rect, name, label, point)
Rect *rect; /* Root coordinates of the label. */
char *name; /* Label name (not used). */
Label *label; /* Pointer to label (not used). */
Point *point; /* Place to store label's lower-left. */
{
*point = rect->r_ll;
return 1;
}