2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* DBcellname.c --
|
|
|
|
|
*
|
|
|
|
|
* CellUse/CellDef creation, deletion, naming.
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* * 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. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/database/DBcellname.c,v 1.5 2009/07/27 00:56:56 tim Exp $";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2021-04-19 16:08:58 +02:00
|
|
|
#include <stdlib.h> /* for qsort() */
|
2017-04-25 14:41:48 +02:00
|
|
|
#include <string.h>
|
2021-04-19 16:08:58 +02:00
|
|
|
#include <ctype.h>
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
#include "tcltk/tclmagic.h"
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "utils/utils.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "database/databaseInt.h"
|
|
|
|
|
#include "select/select.h"
|
|
|
|
|
#include "utils/signals.h"
|
|
|
|
|
#include "utils/undo.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "windows/windows.h"
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "dbwind/dbwind.h"
|
|
|
|
|
#include "utils/main.h"
|
|
|
|
|
#include "drc/drc.h"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Hash tables for CellDefs and CellUses
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define NCELLDEFBUCKETS 64 /* Initial number of buckets for CellDef tbl */
|
|
|
|
|
#define NCELLUSEBUCKETS 128 /* Initial number of buckets for CellUse tbl */
|
|
|
|
|
|
|
|
|
|
HashTable dbCellDefTable;
|
|
|
|
|
HashTable dbUniqueDefTable;
|
|
|
|
|
HashTable dbUniqueNameTable;
|
|
|
|
|
bool dbWarnUniqueIds;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Routines used before defined
|
|
|
|
|
*/
|
|
|
|
|
extern CellDef *DBCellDefAlloc();
|
|
|
|
|
extern int dbLinkFunc();
|
|
|
|
|
extern void dbUsePrintInfo();
|
|
|
|
|
extern void DBsetUseIdHash();
|
|
|
|
|
extern void DBUnLinkCell();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Routines from other database modules
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
extern void DBSetUseIdHash();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellRename --
|
|
|
|
|
*
|
|
|
|
|
* Rename a cell. This differs from the "save" command in that the
|
|
|
|
|
* cell is not immediately written to disk. However, the cell is
|
2021-07-29 23:34:39 +02:00
|
|
|
* marked as modified. If "doforce" is TRUE, then allow renaming
|
|
|
|
|
* of vendor (read-only) cells. Because vendor cells are tied to
|
|
|
|
|
* a GDS file, then the vendor status gets revoked and the pointer
|
|
|
|
|
* to the GDS file gets removed.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Return TRUE if the cell was successfully renamed. Return FALSE
|
|
|
|
|
* otherwise.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Cellname is changed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
bool
|
2021-07-29 23:34:39 +02:00
|
|
|
DBCellRename(cellname, newname, doforce)
|
2017-04-25 14:41:48 +02:00
|
|
|
char *cellname;
|
|
|
|
|
char *newname;
|
2021-07-29 23:34:39 +02:00
|
|
|
bool doforce;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
bool result;
|
|
|
|
|
|
|
|
|
|
entry = HashLookOnly(&dbCellDefTable, cellname);
|
|
|
|
|
if (entry == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("No such cell \"%s\"\n", cellname);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
celldef = (CellDef *)HashGetValue(entry);
|
|
|
|
|
if (celldef == NULL) return FALSE;
|
|
|
|
|
|
|
|
|
|
if ((celldef->cd_flags & CDINTERNAL) == CDINTERNAL)
|
|
|
|
|
{
|
2020-05-12 18:31:39 +02:00
|
|
|
TxError("Error: Attempt to rename internal cell \"%s\"\n", cellname);
|
2017-04-25 14:41:48 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-14 16:36:07 +02:00
|
|
|
/* Cannot rename a cell with the name of an existing cell */
|
|
|
|
|
entry = HashLookOnly(&dbCellDefTable, newname);
|
|
|
|
|
if (entry != NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Cannot rename; cell \"%s\" already exists!\n", newname);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-12 18:03:38 +02:00
|
|
|
/* Disallow renaming if the cell has the READONLY flag set, */
|
|
|
|
|
/* because the cellname must match the name in the GDS */
|
|
|
|
|
/* file referenced. */
|
|
|
|
|
|
|
|
|
|
if ((celldef->cd_flags & CDVENDORGDS) == CDVENDORGDS)
|
|
|
|
|
{
|
2021-07-29 23:34:39 +02:00
|
|
|
if (doforce)
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Warning: Renaming read-only cell \"%s\"\n", cellname);
|
|
|
|
|
TxPrintf("Read-only status will be revoked and GDS file pointer removed.\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Error: Attempt to rename read-only cell \"%s\"\n", cellname);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-05-12 18:03:38 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Good to go! */
|
|
|
|
|
|
|
|
|
|
UndoDisable();
|
|
|
|
|
result = DBCellRenameDef(celldef, newname);
|
|
|
|
|
DBWAreaChanged(celldef, &celldef->cd_bbox, DBW_ALLWINDOWS,
|
|
|
|
|
(TileTypeBitMask *) NULL);
|
2021-07-29 23:34:39 +02:00
|
|
|
|
|
|
|
|
if (doforce && ((celldef->cd_flags & CDVENDORGDS) == CDVENDORGDS))
|
|
|
|
|
{
|
|
|
|
|
bool isReadOnly;
|
|
|
|
|
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
DBPropGet(celldef, "GDS_FILE", &isReadOnly);
|
2021-07-29 23:34:39 +02:00
|
|
|
/* Note that clearing GDS_FILE will also clear CDVENDORGDS flag */
|
|
|
|
|
if (isReadOnly) DBPropPut(celldef, "GDS_FILE", NULL);
|
|
|
|
|
|
|
|
|
|
DBPropGet(celldef, "GDS_START", &isReadOnly);
|
|
|
|
|
if (isReadOnly) DBPropPut(celldef, "GDS_START", NULL);
|
|
|
|
|
DBPropGet(celldef, "GDS_END", &isReadOnly);
|
|
|
|
|
if (isReadOnly) DBPropPut(celldef, "GDS_END", NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
UndoEnable();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBEnumerateTypes
|
|
|
|
|
*
|
|
|
|
|
* Generate a mask of all known types in all known cells.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Returns the OR of the layer mask of each cell definition in
|
|
|
|
|
* the pointer to rMask.
|
|
|
|
|
*
|
|
|
|
|
* Notes:
|
|
|
|
|
* cd_types needs to be properly handled through paint/erase
|
|
|
|
|
* operations, or else we should not rely on cd_types.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBEnumerateTypes(rMask)
|
|
|
|
|
TileTypeBitMask *rMask;
|
|
|
|
|
{
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
CellDef *cellDef;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskZero(rMask);
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while ((entry = HashNext(&dbCellDefTable, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
cellDef = (CellDef *) HashGetValue(entry);
|
|
|
|
|
if (cellDef != (CellDef *)NULL)
|
|
|
|
|
if ((cellDef->cd_flags & CDINTERNAL) != CDINTERNAL)
|
|
|
|
|
TTMaskSetMask(rMask, &cellDef->cd_types);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellDelete --
|
|
|
|
|
*
|
|
|
|
|
* Destroy a cell.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Return TRUE if the cell was successfully eliminated. Return FALSE
|
|
|
|
|
* if the cell could not be deleted (i.e., has dependencies).
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Frees memory. May prompt the user to save, if the cell has been
|
|
|
|
|
* changed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
bool
|
|
|
|
|
DBCellDelete(cellname, force)
|
|
|
|
|
char *cellname;
|
|
|
|
|
bool force;
|
|
|
|
|
{
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
CellDef *celldef;
|
2024-11-26 19:43:27 +01:00
|
|
|
CellUse *celluse, *lastuse;
|
2017-04-25 14:41:48 +02:00
|
|
|
bool result;
|
|
|
|
|
|
|
|
|
|
entry = HashLookOnly(&dbCellDefTable, cellname);
|
|
|
|
|
if (entry == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("No such cell \"%s\"\n", cellname);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
celldef = (CellDef *)HashGetValue(entry);
|
|
|
|
|
if (celldef == NULL) return FALSE;
|
|
|
|
|
|
|
|
|
|
if ((celldef->cd_flags & CDINTERNAL) == CDINTERNAL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Attempt to delete internal cell \"%s\"\n", cellname);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the cell has any instances which are not top-level */
|
|
|
|
|
/* uses, don't allow the deletion. */
|
|
|
|
|
|
|
|
|
|
for (celluse = celldef->cd_parents; celluse != (CellUse *) NULL;
|
|
|
|
|
celluse = celluse->cu_nextuse)
|
|
|
|
|
if (celluse->cu_parent != (CellDef *) NULL)
|
|
|
|
|
if ((celluse->cu_parent->cd_flags & CDINTERNAL) != CDINTERNAL)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (celluse != (CellUse *)NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Cell has non-top-level dependency in use \"%s\"\n",
|
|
|
|
|
celluse->cu_id);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 19:43:27 +01:00
|
|
|
/* 2nd pass: If there are instances of the cell in */
|
|
|
|
|
/* internal cells like SelectDef, etc., then remove the use */
|
|
|
|
|
/* from the definition. */
|
|
|
|
|
|
|
|
|
|
lastuse = NULL;
|
|
|
|
|
celluse = celldef->cd_parents;
|
|
|
|
|
while (celluse != (CellUse *) NULL)
|
|
|
|
|
{
|
|
|
|
|
if (celluse->cu_parent != (CellDef *)NULL)
|
|
|
|
|
{
|
|
|
|
|
if ((celluse->cu_parent->cd_flags & CDINTERNAL) == CDINTERNAL)
|
|
|
|
|
{
|
|
|
|
|
DBDeleteCell(celluse);
|
|
|
|
|
celluse = lastuse;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lastuse = celluse;
|
|
|
|
|
if (lastuse == NULL)
|
|
|
|
|
celluse = celldef->cd_parents;
|
|
|
|
|
else
|
|
|
|
|
celluse = celluse->cu_nextuse;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Cleared to delete. . . now prompt user if the cell has changes. */
|
|
|
|
|
/* Last chance to save! */
|
|
|
|
|
|
|
|
|
|
if ((force == FALSE) &&
|
|
|
|
|
(celldef->cd_flags & (CDMODIFIED|CDBOXESCHANGED|CDSTAMPSCHANGED)))
|
|
|
|
|
{
|
2025-01-31 18:36:21 +01:00
|
|
|
static const char *yesno[] = { "no", "yes", 0 };
|
2017-04-25 14:41:48 +02:00
|
|
|
int code;
|
|
|
|
|
char *prompt = TxPrintString("Cell %s has been modified.\n Do you"
|
|
|
|
|
" want to delete it and lose all changes? ",
|
|
|
|
|
cellname);
|
|
|
|
|
code = TxDialog(prompt, yesno, 0);
|
|
|
|
|
if (code == 0) return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we're destroying the cell pointed to by dbUndoLastCell, we */
|
|
|
|
|
/* need to NULL it and flush the Undo queue. */
|
|
|
|
|
|
|
|
|
|
DBUndoReset(celldef);
|
|
|
|
|
|
|
|
|
|
/* If we're destroying "UNNAMED", then we want to rename it first */
|
2020-05-23 23:13:14 +02:00
|
|
|
/* so that WindUnload() will create a new one. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (!strcmp(cellname, UNNAMED))
|
2024-09-30 04:48:27 +02:00
|
|
|
DBCellRename(cellname, "__UNNAMED__", FALSE);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* For all top-level cell uses, check if any windows have this */
|
|
|
|
|
/* use. If so, load the window with (UNNAMED). */
|
|
|
|
|
|
|
|
|
|
UndoDisable();
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
2017-04-25 14:41:48 +02:00
|
|
|
for (celluse = celldef->cd_parents; celluse != (CellUse *) NULL;
|
|
|
|
|
celluse = celluse->cu_nextuse)
|
|
|
|
|
{
|
|
|
|
|
if (celluse->cu_parent == (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
WindUnload(celluse);
|
|
|
|
|
freeMagic(celluse->cu_id);
|
|
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, (char *)celluse);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2017-04-25 14:41:48 +02:00
|
|
|
celldef->cd_parents = (CellUse *)NULL;
|
|
|
|
|
|
2021-04-25 04:13:30 +02:00
|
|
|
DBWResetBox(celldef);
|
2017-04-25 14:41:48 +02:00
|
|
|
result = DBCellDeleteDef(celldef);
|
|
|
|
|
|
|
|
|
|
if (result == FALSE)
|
|
|
|
|
TxError("Error: Deleted all cell uses, but could not delete cell.\n");
|
|
|
|
|
|
|
|
|
|
UndoEnable();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellInit --
|
|
|
|
|
*
|
|
|
|
|
* Initialize the world of the cell.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Sets up the symbol tables for CellDefs and CellUses.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBCellInit()
|
|
|
|
|
{
|
|
|
|
|
HashInit(&dbCellDefTable, NCELLDEFBUCKETS, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* dbGetUseName --
|
|
|
|
|
*
|
|
|
|
|
* Returns the name of the indicated cell use. If the use is part of
|
|
|
|
|
* an array, it returns the largest array member in x, y, or both.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* An allocated char * array which must be free'd using freeMagic().
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
char *
|
|
|
|
|
dbGetUseName(celluse)
|
|
|
|
|
CellUse *celluse;
|
|
|
|
|
{
|
|
|
|
|
char *useID, *newID, xbuf[10], ybuf[10];
|
|
|
|
|
int arxl, aryl, arxh, aryh;
|
|
|
|
|
int newsize;
|
|
|
|
|
bool isx, isy;
|
|
|
|
|
|
|
|
|
|
arxl = celluse->cu_array.ar_xlo;
|
|
|
|
|
aryl = celluse->cu_array.ar_ylo;
|
|
|
|
|
arxh = celluse->cu_array.ar_xhi;
|
|
|
|
|
aryh = celluse->cu_array.ar_yhi;
|
|
|
|
|
|
|
|
|
|
isx = (arxh == arxl) ? FALSE : TRUE;
|
|
|
|
|
isy = (aryh == aryl) ? FALSE : TRUE;
|
|
|
|
|
|
|
|
|
|
xbuf[0] = '\0';
|
|
|
|
|
ybuf[0] = '\0';
|
|
|
|
|
|
|
|
|
|
useID = celluse->cu_id;
|
2025-01-12 23:36:05 +01:00
|
|
|
if (useID == NULL)
|
|
|
|
|
{
|
|
|
|
|
newID = (char *)mallocMagic(7);
|
|
|
|
|
sprintf(newID, "(null)");
|
|
|
|
|
return (newID);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
newsize = strlen(useID) + 1;
|
|
|
|
|
if (isx || isy)
|
|
|
|
|
{
|
|
|
|
|
newsize += 4;
|
|
|
|
|
if (isx && isy) newsize += 1;
|
|
|
|
|
if (isx)
|
|
|
|
|
{
|
|
|
|
|
snprintf(xbuf, 9, "%d", arxl);
|
|
|
|
|
newsize += strlen(xbuf);
|
|
|
|
|
}
|
|
|
|
|
if (isy)
|
|
|
|
|
{
|
|
|
|
|
snprintf(ybuf, 9, "%d", aryl);
|
|
|
|
|
newsize += strlen(ybuf);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
newID = (char *)mallocMagic(newsize);
|
|
|
|
|
strcpy(newID, useID);
|
|
|
|
|
if (isx || isy)
|
|
|
|
|
{
|
|
|
|
|
strcat(newID, "\\[");
|
|
|
|
|
if (isx) strcat(newID, xbuf);
|
|
|
|
|
if (isx && isy) strcat(newID, ",");
|
|
|
|
|
if (isy) strcat(newID, ybuf);
|
|
|
|
|
strcat(newID, "\\]");
|
|
|
|
|
}
|
|
|
|
|
return (newID);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* dbCellPrintInfo --
|
|
|
|
|
*
|
|
|
|
|
* This is the working function for DBCellPrint.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None (Tcl returns list if "dolist" is true in magic Tcl version).
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Stuff is printed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
dbCellPrintInfo(StartDef, who, dolist)
|
|
|
|
|
CellDef *StartDef;
|
|
|
|
|
int who;
|
|
|
|
|
bool dolist;
|
|
|
|
|
{
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
CellUse *celluse;
|
|
|
|
|
char *cu_name;
|
|
|
|
|
bool topdone;
|
|
|
|
|
|
|
|
|
|
switch (who) {
|
|
|
|
|
case SELF:
|
|
|
|
|
|
|
|
|
|
if (StartDef->cd_name == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, "1");
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("TRUE\n");
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf("Cell is currently loaded.\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, StartDef->cd_name);
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("%s\n", StartDef->cd_name);
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf("Cell %s is currently loaded.\n", StartDef->cd_name);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OTHER:
|
|
|
|
|
if (!dolist)
|
|
|
|
|
TxPrintf("Names of cell instances:\n");
|
|
|
|
|
|
|
|
|
|
for (celluse = StartDef->cd_parents; celluse != NULL;
|
|
|
|
|
celluse = celluse->cu_nextuse)
|
|
|
|
|
{
|
|
|
|
|
if ((celluse->cu_parent != NULL) &&
|
|
|
|
|
((celluse->cu_parent->cd_flags & CDINTERNAL)
|
|
|
|
|
== CDINTERNAL))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (celluse->cu_id != NULL)
|
|
|
|
|
{
|
|
|
|
|
cu_name = dbGetUseName(celluse);
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, cu_name);
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("%s\n", cu_name);
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf(" %s\n", cu_name);
|
|
|
|
|
freeMagic(cu_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CHILDINST:
|
|
|
|
|
if (!dolist)
|
|
|
|
|
TxPrintf("Def %s's children are:\n", StartDef->cd_name);
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while ((entry = HashNext(&StartDef->cd_idHash, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
celluse = (CellUse *)HashGetValue(entry);
|
|
|
|
|
if (celluse != (CellUse *)NULL)
|
|
|
|
|
dbCellUsePrintFunc(celluse, &dolist);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PARENTS:
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* Print out a list of all the parents by scanning the 'use' list.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (StartDef->cd_name == NULL && (!dolist))
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Cell's parents are:\n");
|
|
|
|
|
}
|
|
|
|
|
else if (!dolist)
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Cell %s's parents are:\n", StartDef->cd_name);
|
|
|
|
|
}
|
|
|
|
|
for (celluse = StartDef->cd_parents; celluse != (CellUse *) NULL;
|
|
|
|
|
celluse = celluse->cu_nextuse)
|
|
|
|
|
{
|
|
|
|
|
if (celluse->cu_parent != (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
celluse->cu_parent->cd_client = (ClientData) 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (celluse = StartDef->cd_parents; celluse != (CellUse *) NULL;
|
|
|
|
|
celluse = celluse->cu_nextuse)
|
|
|
|
|
{
|
|
|
|
|
if (celluse->cu_parent != (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
if (celluse->cu_parent->cd_client == (ClientData) 1)
|
|
|
|
|
{
|
|
|
|
|
celluse->cu_parent->cd_client = (ClientData) NULL;
|
|
|
|
|
if ( (celluse->cu_parent->cd_flags & CDINTERNAL)
|
|
|
|
|
!= CDINTERNAL)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp,
|
|
|
|
|
celluse->cu_parent->cd_name);
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("%s ", celluse->cu_parent->cd_name);
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf(" %s\n", celluse->cu_parent->cd_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CHILDREN:
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* Print out a list of all the children by checking all the cells.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (StartDef->cd_name == NULL && (!dolist))
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Cell's children are:\n");
|
|
|
|
|
}
|
|
|
|
|
else if (!dolist)
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Cell %s's children are:\n", StartDef->cd_name);
|
|
|
|
|
}
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
celldef = (CellDef *) HashGetValue(entry);
|
|
|
|
|
if (!celldef) continue;
|
|
|
|
|
for (celluse = celldef->cd_parents; celluse != (CellUse *) NULL;
|
|
|
|
|
celluse = celluse->cu_nextuse)
|
|
|
|
|
{
|
|
|
|
|
if (celluse->cu_parent == StartDef)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, celldef->cd_name);
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("%s ", celldef->cd_name);
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf(" %s\n", celldef->cd_name);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} /* endswitch */
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBTopPrint --
|
|
|
|
|
*
|
|
|
|
|
* This routine prints the cell definition name of the topmost instance
|
|
|
|
|
* in the window.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
DBTopPrint(mw, dolist)
|
|
|
|
|
MagWindow *mw;
|
|
|
|
|
bool dolist;
|
|
|
|
|
{
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
CellUse *celluse;
|
|
|
|
|
|
|
|
|
|
if (mw == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("No window was selected for search.\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
celluse = (CellUse *)mw->w_surfaceID;
|
|
|
|
|
celldef = celluse->cu_def;
|
|
|
|
|
|
|
|
|
|
if (celldef == NULL) return;
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
if (dolist)
|
|
|
|
|
Tcl_AppendElement(magicinterp, celldef->cd_name);
|
|
|
|
|
else
|
|
|
|
|
#endif
|
|
|
|
|
TxPrintf("Top-level cell in the window is: %s\n", celldef->cd_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-19 16:08:58 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* Simple natural sort routine
|
|
|
|
|
* https://stackoverflow.com/questions/34518/natural-sorting-algorithm
|
|
|
|
|
* By Norman Ramsey, edited for style.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int strcmpbynum(const char *s1, const char *s2)
|
|
|
|
|
{
|
2023-02-28 04:22:09 +01:00
|
|
|
/* Like strcasecmp() but compare sequences of digits numerically */
|
2021-04-19 16:08:58 +02:00
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
if (*s2 == '\0')
|
|
|
|
|
return *s1 != '\0';
|
|
|
|
|
else if (*s1 == '\0')
|
|
|
|
|
return 1;
|
|
|
|
|
else if (!(isdigit(*s1) && isdigit(*s2)))
|
|
|
|
|
{
|
2023-02-28 04:22:09 +01:00
|
|
|
char c1, c2;
|
|
|
|
|
c1 = tolower(*s1);
|
|
|
|
|
c2 = tolower(*s2);
|
|
|
|
|
if (c1 != c2)
|
|
|
|
|
return (int)c1 - (int)c2;
|
2021-04-19 16:08:58 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++s1;
|
|
|
|
|
++s2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char *lim1, *lim2;
|
|
|
|
|
unsigned long n1 = strtoul(s1, &lim1, 10);
|
|
|
|
|
unsigned long n2 = strtoul(s2, &lim2, 10);
|
|
|
|
|
if (n1 > n2)
|
|
|
|
|
return 1;
|
|
|
|
|
else if (n1 < n2)
|
|
|
|
|
return -1;
|
|
|
|
|
s1 = lim1;
|
|
|
|
|
s2 = lim2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* Sort routine for qsort() to be used by DBCellPrint(). Sorts in alphabetical
|
|
|
|
|
* order using the natural sort routine above. List is reverse sorted since
|
|
|
|
|
* the code below prints from the end to the beginning of the list.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
qcompare(const void *one, const void *two)
|
|
|
|
|
{
|
|
|
|
|
int cval;
|
|
|
|
|
|
|
|
|
|
char *s1 = *((char **)one);
|
|
|
|
|
char *s2 = *((char **)two);
|
|
|
|
|
cval = strcmpbynum(s1, s2);
|
|
|
|
|
return -cval;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellPrint --
|
|
|
|
|
*
|
|
|
|
|
* This routine prints out cell names.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Stuff is printed.
|
|
|
|
|
*
|
|
|
|
|
* Notes: "who" takes one of the options defined in database.h:
|
|
|
|
|
* PARENTS, CHILDREN, SELF, OTHER, ALLCELLS, MODIFIED, or TOPCELLS.
|
|
|
|
|
* "SELF" lists celldef names (most useful to list the name of
|
|
|
|
|
* a selected cell). "OTHER" lists instance names.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBCellPrint(CellName, who, dolist)
|
|
|
|
|
char *CellName;
|
|
|
|
|
int who;
|
|
|
|
|
bool dolist;
|
|
|
|
|
{
|
2021-04-19 16:08:58 +02:00
|
|
|
int found, numcells;
|
2017-04-25 14:41:48 +02:00
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
CellUse *celluse;
|
2021-04-19 16:08:58 +02:00
|
|
|
char **celllist;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (!dolist)
|
|
|
|
|
{
|
|
|
|
|
switch (who)
|
|
|
|
|
{
|
|
|
|
|
case ALLCELLS:
|
|
|
|
|
TxPrintf("Cell currently loaded:\n");
|
|
|
|
|
break;
|
|
|
|
|
case MODIFIED:
|
|
|
|
|
TxPrintf("Modified cells:\n");
|
|
|
|
|
break;
|
|
|
|
|
case TOPCELLS:
|
|
|
|
|
TxPrintf("Top level cells are:\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (who)
|
|
|
|
|
{
|
|
|
|
|
case ALLCELLS:
|
|
|
|
|
case MODIFIED:
|
|
|
|
|
/*
|
|
|
|
|
* Print the name of all the 'known' cells.
|
|
|
|
|
* If "MODIFIED", print only those cells that have the
|
|
|
|
|
* CDMODIFIED flag set.
|
|
|
|
|
*/
|
|
|
|
|
|
2021-04-19 16:08:58 +02:00
|
|
|
numcells = dbCellDefTable.ht_nEntries;
|
|
|
|
|
if (numcells == 0) numcells = 1;
|
|
|
|
|
celllist = (char **)mallocMagic(numcells * sizeof(char *));
|
|
|
|
|
numcells = 0;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
celldef = (CellDef *) HashGetValue(entry);
|
|
|
|
|
if (celldef != (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
if (((celldef->cd_flags & CDINTERNAL) != CDINTERNAL) &&
|
|
|
|
|
((who != MODIFIED) ||
|
|
|
|
|
(celldef->cd_flags & CDMODIFIED)))
|
|
|
|
|
if (celldef->cd_name != NULL)
|
2021-04-19 16:08:58 +02:00
|
|
|
celllist[numcells++] = celldef->cd_name;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qsort(celllist, numcells, sizeof(char *), qcompare);
|
|
|
|
|
|
|
|
|
|
while (--numcells >= 0)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
2017-04-25 14:41:48 +02:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2021-04-19 16:08:58 +02:00
|
|
|
Tcl_AppendElement(magicinterp, celllist[numcells]);
|
2017-04-25 14:41:48 +02:00
|
|
|
#else
|
2021-04-19 16:08:58 +02:00
|
|
|
TxPrintf("%s ", celllist[numcells]);
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif
|
2021-04-19 16:08:58 +02:00
|
|
|
else
|
|
|
|
|
TxPrintf(" %s\n", celllist[numcells]);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-04-19 16:08:58 +02:00
|
|
|
|
|
|
|
|
freeMagic(celllist);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TOPCELLS:
|
|
|
|
|
/*
|
2021-04-19 16:08:58 +02:00
|
|
|
* Print the name of all the 'top' cells. Sort alphabetically.
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
|
2021-04-19 16:08:58 +02:00
|
|
|
numcells = dbCellDefTable.ht_nEntries;
|
|
|
|
|
if (numcells == 0) numcells = 1;
|
|
|
|
|
celllist = (char **)mallocMagic(numcells * sizeof(char *));
|
|
|
|
|
numcells = 0;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
celldef = (CellDef *) HashGetValue(entry);
|
|
|
|
|
if (celldef != (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
if ( (celldef->cd_flags & CDINTERNAL) != CDINTERNAL)
|
|
|
|
|
{
|
|
|
|
|
found = 0;
|
|
|
|
|
for (celluse = celldef->cd_parents;
|
|
|
|
|
celluse != (CellUse *) NULL;
|
|
|
|
|
celluse = celluse->cu_nextuse)
|
|
|
|
|
{
|
|
|
|
|
if (celluse->cu_parent != (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
if ( (celluse->cu_parent->cd_flags & CDINTERNAL)
|
|
|
|
|
!= CDINTERNAL)
|
|
|
|
|
{
|
|
|
|
|
found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( (found == 0) && (celldef->cd_name != NULL) )
|
2021-04-19 16:08:58 +02:00
|
|
|
celllist[numcells++] = celldef->cd_name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qsort(celllist, numcells, sizeof(char *), qcompare);
|
|
|
|
|
|
|
|
|
|
while (--numcells >= 0)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
2017-04-25 14:41:48 +02:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2021-04-19 16:08:58 +02:00
|
|
|
Tcl_AppendElement(magicinterp, celllist[numcells]);
|
2017-04-25 14:41:48 +02:00
|
|
|
#else
|
2021-04-19 16:08:58 +02:00
|
|
|
TxPrintf("%s ", celllist[numcells]);
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif
|
2021-04-19 16:08:58 +02:00
|
|
|
else
|
|
|
|
|
TxPrintf(" %s\n", celllist[numcells]);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-04-19 16:08:58 +02:00
|
|
|
freeMagic(celllist);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check to see if a cell name was specified. If not,
|
|
|
|
|
* search for selected cells.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (CellName == NULL)
|
|
|
|
|
{
|
|
|
|
|
found = 0;
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
celldef = (CellDef *) HashGetValue(entry);
|
|
|
|
|
if (celldef != (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
for (celluse = celldef->cd_parents;
|
|
|
|
|
celluse != (CellUse *) NULL;
|
|
|
|
|
celluse = celluse->cu_nextuse)
|
|
|
|
|
{
|
|
|
|
|
if (celluse->cu_parent == SelectDef)
|
|
|
|
|
{
|
|
|
|
|
dbCellPrintInfo(celldef, who, dolist);
|
|
|
|
|
found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (found == 0)
|
|
|
|
|
{
|
2021-11-25 18:40:51 +01:00
|
|
|
if (EditRootDef == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (!dolist)
|
|
|
|
|
TxPrintf("No cells selected.\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dbCellPrintInfo(EditRootDef, who, dolist);
|
|
|
|
|
found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
celldef = DBCellLookDef(CellName);
|
|
|
|
|
if (celldef == (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, "0");
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("Not loaded\n");
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxError("Cell %s is not currently loaded.\n", CellName);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dbCellPrintInfo(celldef, who, dolist);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} /* endswitch */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* dbUsePrintInfo --
|
|
|
|
|
*
|
|
|
|
|
* This is the working function for DBUsePrint.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None (Tcl returns list if "dolist" is true in magic Tcl version).
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Stuff is printed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
dbUsePrintInfo(StartUse, who, dolist)
|
|
|
|
|
CellUse *StartUse;
|
|
|
|
|
int who;
|
|
|
|
|
bool dolist;
|
|
|
|
|
{
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
CellUse *celluse;
|
|
|
|
|
char *cu_name;
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
|
|
|
|
|
int dbCellUsePrintFunc();
|
|
|
|
|
|
|
|
|
|
/* fprintf(stderr, "dbCellUseInfo called with %s, %d, %s\n",
|
|
|
|
|
* StartUse->cu_id == NULL ? "(unnamed)" : StartUse->cu_id,
|
|
|
|
|
* who, (dolist) ? "list" : "no list" );
|
|
|
|
|
* fflush(stderr);
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
switch (who) {
|
|
|
|
|
case SELF:
|
|
|
|
|
if (StartUse->cu_id == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, "1");
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("TRUE\n");
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf("Use is currently loaded.\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cu_name = dbGetUseName(StartUse);
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, cu_name);
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("%s\n", cu_name);
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf("Use %s is currently loaded.\n", cu_name);
|
|
|
|
|
freeMagic(cu_name);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OTHER:
|
|
|
|
|
if (StartUse->cu_def->cd_name == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, "0");
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("FALSE\n");
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf("Cell definition has no name.\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, StartUse->cu_def->cd_name);
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("%s\n", StartUse->cu_def->cd_name);
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf("Cell definition is %s.\n", StartUse->cu_def->cd_name);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PARENTS:
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* Print out a list of all the parents by scanning the 'use' list.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (StartUse->cu_id == NULL && (!dolist))
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Use's parent is:\n");
|
|
|
|
|
}
|
|
|
|
|
else if (!dolist)
|
|
|
|
|
{
|
|
|
|
|
cu_name = dbGetUseName(StartUse);
|
|
|
|
|
TxPrintf("Use %s's parent is:\n", cu_name);
|
|
|
|
|
freeMagic(cu_name);
|
|
|
|
|
}
|
|
|
|
|
if (StartUse->cu_parent != (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
if ((StartUse->cu_parent->cd_flags & CDINTERNAL)
|
|
|
|
|
!= CDINTERNAL)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp,
|
|
|
|
|
StartUse->cu_parent->cd_name);
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("%s ", StartUse->cu_parent->cd_name);
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf(" %s\n", StartUse->cu_parent->cd_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CHILDREN:
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* Print out a list of all the children by checking all the cells.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (StartUse->cu_id == NULL && (!dolist))
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Use's children are:\n");
|
|
|
|
|
}
|
|
|
|
|
else if (!dolist)
|
|
|
|
|
{
|
|
|
|
|
cu_name = dbGetUseName(StartUse);
|
|
|
|
|
TxPrintf("Use %s's children are:\n", cu_name);
|
|
|
|
|
freeMagic(cu_name);
|
|
|
|
|
}
|
|
|
|
|
celldef = StartUse->cu_def;
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while ((entry = HashNext(&celldef->cd_idHash, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
celluse = (CellUse *)HashGetValue(entry);
|
|
|
|
|
if (celluse != (CellUse *)NULL)
|
|
|
|
|
dbCellUsePrintFunc(celluse, &dolist);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} /* endswitch */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBUsePrint --
|
|
|
|
|
*
|
|
|
|
|
* This routine prints out cell use names.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Stuff is printed.
|
|
|
|
|
*
|
|
|
|
|
* Notes: "who" takes one of the options defined in database.h:
|
|
|
|
|
* PARENTS, CHILDREN, SELF, OTHER, or ALLCELLS.
|
|
|
|
|
* "SELF" lists instance names (most useful to list the name of
|
|
|
|
|
* a selected instance). "OTHER" lists the celldef name of the
|
|
|
|
|
* instance.
|
|
|
|
|
*
|
|
|
|
|
* CellName should be referenced either to the current edit cell,
|
|
|
|
|
* if it is not a hierarchical name; otherwise, if it is a
|
|
|
|
|
* hierarchical name, the instance before the last '/' is mapped
|
|
|
|
|
* to its cellDef, and that cellDef is searched for the indicated
|
2020-05-23 23:13:14 +02:00
|
|
|
* instance.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBUsePrint(CellName, who, dolist)
|
|
|
|
|
char *CellName;
|
|
|
|
|
int who;
|
|
|
|
|
bool dolist;
|
|
|
|
|
{
|
|
|
|
|
int found;
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
CellUse *celluse;
|
|
|
|
|
char *lasthier;
|
|
|
|
|
|
|
|
|
|
int dbCellUsePrintFunc();
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if ((CellName != NULL) && ((lasthier = strrchr(CellName, '/')) != NULL))
|
|
|
|
|
{
|
|
|
|
|
char *prevhier;
|
|
|
|
|
*lasthier = '\0';
|
|
|
|
|
prevhier = strrchr(CellName, '/');
|
|
|
|
|
if (prevhier == NULL)
|
2020-05-23 23:13:14 +02:00
|
|
|
prevhier = CellName;
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
prevhier++;
|
|
|
|
|
|
|
|
|
|
celldef = DBCellLookDef(CellName);
|
|
|
|
|
*lasthier = '/';
|
|
|
|
|
}
|
2024-05-24 18:43:36 +02:00
|
|
|
else if (EditCellUse != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
/* Referenced cellDef is the current edit def */
|
|
|
|
|
celldef = EditCellUse->cu_def;
|
|
|
|
|
}
|
2024-05-24 18:43:36 +02:00
|
|
|
else
|
|
|
|
|
return;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
switch (who)
|
|
|
|
|
{
|
|
|
|
|
case ALLCELLS:
|
|
|
|
|
/*
|
|
|
|
|
* Print the name of all the 'known' cell uses (hierarchical names).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* Check to see if a cell name was specified. If not,
|
|
|
|
|
* search for selected cells.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (CellName == NULL)
|
|
|
|
|
{
|
|
|
|
|
found = 0;
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
celldef = (CellDef *) HashGetValue(entry);
|
|
|
|
|
if (celldef != (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
for (celluse = celldef->cd_parents;
|
|
|
|
|
celluse != (CellUse *) NULL;
|
|
|
|
|
celluse = celluse->cu_nextuse)
|
|
|
|
|
{
|
|
|
|
|
if (celluse->cu_parent == SelectDef)
|
|
|
|
|
{
|
|
|
|
|
dbUsePrintInfo(celluse, who, dolist);
|
|
|
|
|
found = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (found == 0)
|
|
|
|
|
if (!dolist)
|
|
|
|
|
TxPrintf("No cells selected.\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
celluse = DBFindUse(CellName, celldef);
|
|
|
|
|
|
|
|
|
|
if (celluse == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (!dolist)
|
|
|
|
|
TxError("Cell %s is not currently loaded.\n", CellName);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dbUsePrintInfo(celluse, who, dolist);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
} /* endswitch */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
dbCellUsePrintFunc(cellUse, dolist)
|
|
|
|
|
CellUse *cellUse;
|
|
|
|
|
bool *dolist;
|
|
|
|
|
{
|
|
|
|
|
char *cu_name;
|
|
|
|
|
|
|
|
|
|
if (cellUse->cu_id != NULL)
|
|
|
|
|
{
|
|
|
|
|
cu_name = dbGetUseName(cellUse);
|
|
|
|
|
if (*dolist)
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, cu_name);
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("%s ", cu_name);
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
TxPrintf(" %s\n", cu_name);
|
|
|
|
|
freeMagic(cu_name);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* dbLockUseFunc()
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
dbLockUseFunc(selUse, use, transform, data)
|
|
|
|
|
CellUse *selUse; /* Use from selection cell */
|
|
|
|
|
CellUse *use; /* Use from layout corresponding to selection */
|
|
|
|
|
Transform *transform;
|
|
|
|
|
ClientData data;
|
|
|
|
|
{
|
|
|
|
|
bool dolock = *((bool *)data);
|
|
|
|
|
|
|
|
|
|
if (EditCellUse && !DBIsChild(use, EditCellUse))
|
|
|
|
|
{
|
|
|
|
|
TxError("Cell %s (%s) isn't a child of the edit cell.\n",
|
|
|
|
|
use->cu_id, use->cu_def->cd_name);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if ((dolock && (use->cu_flags & CU_LOCKED)) ||
|
|
|
|
|
(!dolock && !(use->cu_flags & CU_LOCKED)))
|
|
|
|
|
return 0; /* nothing to do */
|
|
|
|
|
|
|
|
|
|
if (UndoIsEnabled()) DBUndoCellUse(use, UNDO_CELL_LOCKDOWN);
|
|
|
|
|
if (dolock) use->cu_flags |= CU_LOCKED;
|
|
|
|
|
else use->cu_flags &= ~CU_LOCKED;
|
|
|
|
|
if (UndoIsEnabled()) DBUndoCellUse(use, UNDO_CELL_LOCKDOWN);
|
|
|
|
|
|
|
|
|
|
if (selUse != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (dolock) selUse->cu_flags |= CU_LOCKED;
|
|
|
|
|
else selUse->cu_flags &= ~CU_LOCKED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBWAreaChanged(use->cu_parent, &use->cu_bbox,
|
|
|
|
|
(int) ~(use->cu_expandMask), &DBAllButSpaceBits);
|
|
|
|
|
DBWHLRedraw(EditRootDef, &selUse->cu_bbox, TRUE);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBLockUse --
|
|
|
|
|
*
|
|
|
|
|
* This routine sets or clears a cell instance's lock flag
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* cu_flags changed for indicated cell use.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBLockUse(UseName, bval)
|
|
|
|
|
char *UseName;
|
|
|
|
|
bool bval;
|
|
|
|
|
{
|
|
|
|
|
int found;
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
CellUse *celluse;
|
|
|
|
|
|
2020-06-01 22:49:59 +02:00
|
|
|
int dbLockUseFunc();
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* Check to see if a cell name was specified. If not,
|
|
|
|
|
* search for selected cells.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (UseName == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (EditCellUse == NULL)
|
|
|
|
|
TxError("Cannot set lock in a non-edit cell!\n");
|
|
|
|
|
else
|
|
|
|
|
SelEnumCells(TRUE, (int *)NULL, (SearchContext *)NULL,
|
|
|
|
|
dbLockUseFunc, (ClientData)&bval);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
|
|
|
|
|
bzero(&scx, sizeof(SearchContext));
|
|
|
|
|
found = 0;
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
celldef = (CellDef *) HashGetValue(entry);
|
2024-01-21 17:00:19 +01:00
|
|
|
if ((celldef != (CellDef *)NULL) && !(celldef->cd_flags & CDINTERNAL))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
celluse = celldef->cd_parents; /* only need one */
|
|
|
|
|
if (celluse != (CellUse *)NULL) {
|
|
|
|
|
DBTreeFindUse(UseName, celluse, &scx);
|
|
|
|
|
if (scx.scx_use != NULL) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scx.scx_use == NULL)
|
|
|
|
|
TxError("Cell %s is not currently loaded.\n", UseName);
|
|
|
|
|
else
|
|
|
|
|
dbLockUseFunc(NULL, scx.scx_use, NULL, (ClientData)&bval);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-01 22:49:59 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBOrientUse --
|
|
|
|
|
*
|
2020-10-30 16:20:19 +01:00
|
|
|
* This routine reports a cell instance's orientation.
|
2020-06-01 22:49:59 +02:00
|
|
|
* UseName is the name of a specific CellUse. If NULL, then the
|
2020-10-30 16:20:19 +01:00
|
|
|
* operation applies to all selected cell uses.
|
2020-06-01 22:49:59 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
2020-10-30 16:20:19 +01:00
|
|
|
* In the Tcl/Tk implementation, the result is set in the interpreter.
|
|
|
|
|
*
|
|
|
|
|
* Notes:
|
|
|
|
|
* This routine only reports orientation. Setting orientation must
|
|
|
|
|
* be done through the selection interface (i.e., commands "sideways",
|
|
|
|
|
* "upsidedown", "clockwise" ("rotate"), or "orient" (added 10/30/2020)).
|
2020-06-01 22:49:59 +02:00
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBOrientUse(UseName, dodef)
|
|
|
|
|
char *UseName;
|
|
|
|
|
bool dodef;
|
|
|
|
|
{
|
|
|
|
|
int found;
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
CellUse *celluse;
|
|
|
|
|
|
|
|
|
|
int dbOrientUseFunc();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* Check to see if a cell name was specified. If not, then search
|
|
|
|
|
* for selected cells.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (UseName == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (EditCellUse == NULL)
|
|
|
|
|
TxError("Cannot set orientation of a non-edit cell!\n");
|
|
|
|
|
else
|
|
|
|
|
SelEnumCells(TRUE, (int *)NULL, (SearchContext *)NULL,
|
|
|
|
|
dbOrientUseFunc, (ClientData)&dodef);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
|
|
|
|
|
bzero(&scx, sizeof(SearchContext));
|
|
|
|
|
found = 0;
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
celldef = (CellDef *) HashGetValue(entry);
|
2024-01-21 17:00:19 +01:00
|
|
|
if ((celldef != (CellDef *)NULL) && !(celldef->cd_flags & CDINTERNAL))
|
2020-06-01 22:49:59 +02:00
|
|
|
{
|
|
|
|
|
celluse = celldef->cd_parents; /* only need one */
|
|
|
|
|
if (celluse != (CellUse *)NULL) {
|
|
|
|
|
DBTreeFindUse(UseName, celluse, &scx);
|
|
|
|
|
if (scx.scx_use != NULL) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scx.scx_use == NULL)
|
|
|
|
|
TxError("Cell %s is not currently loaded.\n", UseName);
|
|
|
|
|
else
|
|
|
|
|
dbOrientUseFunc(NULL, scx.scx_use, NULL, (ClientData)&dodef);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* dbOrientUseFunc()
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* For corresponding enumerations, see GeoTransOrient() */
|
|
|
|
|
enum def_orient {ORIENT_NORTH, ORIENT_SOUTH, ORIENT_EAST, ORIENT_WEST,
|
|
|
|
|
ORIENT_FLIPPED_NORTH, ORIENT_FLIPPED_SOUTH, ORIENT_FLIPPED_EAST,
|
|
|
|
|
ORIENT_FLIPPED_WEST};
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
dbOrientUseFunc(selUse, use, transform, data)
|
|
|
|
|
CellUse *selUse; /* Use from selection cell */
|
|
|
|
|
CellUse *use; /* Use from layout corresponding to selection */
|
|
|
|
|
Transform *transform;
|
|
|
|
|
ClientData data;
|
|
|
|
|
{
|
|
|
|
|
bool *dodef = (bool *)data;
|
2020-10-30 16:20:19 +01:00
|
|
|
int orient;
|
2020-06-01 22:49:59 +02:00
|
|
|
|
|
|
|
|
if (EditCellUse && !DBIsChild(use, EditCellUse))
|
|
|
|
|
{
|
|
|
|
|
TxError("Cell %s (%s) isn't a child of the edit cell.\n",
|
|
|
|
|
use->cu_id, use->cu_def->cd_name);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 16:20:19 +01:00
|
|
|
orient = -1;
|
|
|
|
|
|
2020-06-01 22:49:59 +02:00
|
|
|
if (selUse != NULL)
|
2020-10-30 16:20:19 +01:00
|
|
|
orient = GeoTransOrient(&selUse->cu_transform);
|
|
|
|
|
else if (use != NULL)
|
|
|
|
|
orient = GeoTransOrient(&use->cu_transform);
|
|
|
|
|
|
|
|
|
|
if (orient != -1)
|
2020-06-01 22:49:59 +02:00
|
|
|
{
|
2020-10-30 16:20:19 +01:00
|
|
|
switch (orient) {
|
2020-06-01 22:49:59 +02:00
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
case ORIENT_NORTH:
|
|
|
|
|
Tcl_AppendElement(magicinterp, (*dodef) ? "N" : "0");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_EAST:
|
|
|
|
|
Tcl_AppendElement(magicinterp, (*dodef) ? "E" : "90");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_SOUTH:
|
|
|
|
|
Tcl_AppendElement(magicinterp, (*dodef) ? "S" : "180");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_WEST:
|
|
|
|
|
Tcl_AppendElement(magicinterp, (*dodef) ? "W" : "270");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_FLIPPED_NORTH:
|
|
|
|
|
Tcl_AppendElement(magicinterp, (*dodef) ? "FN" : "0h");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_FLIPPED_EAST:
|
|
|
|
|
Tcl_AppendElement(magicinterp, (*dodef) ? "FE" : "90h");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_FLIPPED_SOUTH:
|
|
|
|
|
Tcl_AppendElement(magicinterp, (*dodef) ? "FS" : "180h");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_FLIPPED_WEST:
|
|
|
|
|
Tcl_AppendElement(magicinterp, (*dodef) ? "FW" : "270h");
|
|
|
|
|
break;
|
|
|
|
|
#else
|
|
|
|
|
case ORIENT_NORTH:
|
|
|
|
|
TxPrintf((*dodef) ? "N" : "0");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_EAST:
|
|
|
|
|
TxPrintf((*dodef) ? "E" : "90");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_SOUTH:
|
|
|
|
|
TxPrintf((*dodef) ? "S" : "180");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_WEST:
|
|
|
|
|
TxPrintf((*dodef) ? "W" : "270");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_FLIPPED_NORTH:
|
|
|
|
|
TxPrintf((*dodef) ? "FN" : "0h");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_FLIPPED_EAST:
|
|
|
|
|
TxPrintf((*dodef) ? "FE" : "90h");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_FLIPPED_SOUTH:
|
|
|
|
|
TxPrintf((*dodef) ? "FS" : "180h");
|
|
|
|
|
break;
|
|
|
|
|
case ORIENT_FLIPPED_WEST:
|
|
|
|
|
TxPrintf((*dodef) ? "FW" : "270h");
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 16:20:19 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBAbutmentUse --
|
|
|
|
|
*
|
|
|
|
|
* This routine reports the cell instance's abutment box in the
|
|
|
|
|
* coordinate system of the parent (edit) cell.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBAbutmentUse(UseName, dolist)
|
|
|
|
|
char *UseName;
|
|
|
|
|
bool dolist;
|
|
|
|
|
{
|
|
|
|
|
int found;
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
CellUse *celluse;
|
|
|
|
|
|
|
|
|
|
int dbAbutmentUseFunc();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
|
|
|
|
* Check to see if a cell name was specified. If not, then search
|
|
|
|
|
* for selected cells.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (UseName == NULL)
|
|
|
|
|
{
|
|
|
|
|
SelEnumCells(TRUE, (int *)NULL, (SearchContext *)NULL,
|
|
|
|
|
dbAbutmentUseFunc, (ClientData)&dolist);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
|
|
|
|
|
bzero(&scx, sizeof(SearchContext));
|
|
|
|
|
found = 0;
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while( (entry = HashNext(&dbCellDefTable, &hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
celldef = (CellDef *) HashGetValue(entry);
|
2024-01-21 17:00:19 +01:00
|
|
|
if ((celldef != (CellDef *)NULL) && !(celldef->cd_flags & CDINTERNAL))
|
2020-10-30 16:20:19 +01:00
|
|
|
{
|
|
|
|
|
celluse = celldef->cd_parents; /* only need one */
|
|
|
|
|
if (celluse != (CellUse *)NULL) {
|
|
|
|
|
DBTreeFindUse(UseName, celluse, &scx);
|
|
|
|
|
if (scx.scx_use != NULL) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scx.scx_use == NULL)
|
|
|
|
|
TxError("Cell %s is not currently loaded.\n", UseName);
|
|
|
|
|
else
|
|
|
|
|
dbAbutmentUseFunc(NULL, scx.scx_use, NULL, (ClientData)&dolist);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* dbAbutmentUseFunc()
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
dbAbutmentUseFunc(selUse, use, transform, data)
|
|
|
|
|
CellUse *selUse; /* Use from selection cell */
|
|
|
|
|
CellUse *use; /* Use from layout corresponding to selection */
|
|
|
|
|
Transform *transform;
|
|
|
|
|
ClientData data;
|
|
|
|
|
{
|
|
|
|
|
Rect bbox, refbox;
|
|
|
|
|
Transform *trans;
|
2026-02-19 16:27:03 +01:00
|
|
|
PropertyRecord *proprec;
|
2020-10-30 16:20:19 +01:00
|
|
|
char *propvalue;
|
2026-01-24 22:19:12 +01:00
|
|
|
char *refllx, *reflly, *refurx, *refury;
|
2020-10-30 16:20:19 +01:00
|
|
|
bool found;
|
|
|
|
|
bool *dolist = (bool *)data;
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_Obj *pobj;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (EditCellUse && !DBIsChild(use, EditCellUse))
|
|
|
|
|
{
|
|
|
|
|
TxError("Cell %s (%s) isn't a child of the edit cell.\n",
|
|
|
|
|
use->cu_id, use->cu_def->cd_name);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (use == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("No instance in selection!\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trans = &use->cu_transform;
|
2026-02-19 16:27:03 +01:00
|
|
|
proprec = DBPropGet(use->cu_def, "FIXED_BBOX", &found);
|
2020-10-30 16:20:19 +01:00
|
|
|
if (!found)
|
|
|
|
|
bbox = use->cu_def->cd_bbox;
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-02-19 16:27:03 +01:00
|
|
|
if ((proprec->prop_type == PROPERTY_TYPE_DIMENSION) &&
|
|
|
|
|
(proprec->prop_len == 4))
|
|
|
|
|
{
|
|
|
|
|
bbox.r_xbot = proprec->prop_value.prop_integer[0];
|
|
|
|
|
bbox.r_ybot = proprec->prop_value.prop_integer[1];
|
|
|
|
|
bbox.r_xtop = proprec->prop_value.prop_integer[2];
|
|
|
|
|
bbox.r_ytop = proprec->prop_value.prop_integer[3];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Unable to parse the cell's FIXED_BBOX property; using "
|
|
|
|
|
"the instance bounding box instead.\n");
|
2020-10-30 16:20:19 +01:00
|
|
|
bbox = use->cu_def->cd_bbox;
|
2026-02-19 16:27:03 +01:00
|
|
|
}
|
2020-10-30 16:20:19 +01:00
|
|
|
}
|
|
|
|
|
GeoTransRect(trans, &bbox, &refbox);
|
|
|
|
|
|
2026-01-24 22:19:12 +01:00
|
|
|
/* NOTE: Ideally, the MagWindow pointer should get passed to this routine */
|
|
|
|
|
refllx = DBWPrintValue(refbox.r_xbot, (MagWindow *)NULL, TRUE);
|
|
|
|
|
reflly = DBWPrintValue(refbox.r_ybot, (MagWindow *)NULL, FALSE);
|
|
|
|
|
refurx = DBWPrintValue(refbox.r_xtop, (MagWindow *)NULL, TRUE);
|
|
|
|
|
refury = DBWPrintValue(refbox.r_ytop, (MagWindow *)NULL, FALSE);
|
|
|
|
|
|
2020-10-30 16:20:19 +01:00
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
if (*dolist)
|
|
|
|
|
{
|
|
|
|
|
pobj = Tcl_NewListObj(0, NULL);
|
2026-01-24 22:19:12 +01:00
|
|
|
Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewStringObj(refllx, -1));
|
|
|
|
|
Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewStringObj(reflly, -1));
|
|
|
|
|
Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewStringObj(refurx, -1));
|
|
|
|
|
Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewStringObj(refury, -1));
|
2020-10-30 16:20:19 +01:00
|
|
|
Tcl_SetObjResult(magicinterp, pobj);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
#endif
|
2026-01-24 22:19:12 +01:00
|
|
|
TxPrintf("Abutment box: %s %s %s %s\n", refllx, reflly, refurx, refury);
|
2020-10-30 16:20:19 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellLookDef --
|
|
|
|
|
*
|
|
|
|
|
* Find the definition of the cell with the given name.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns a pointer to the CellDef with the given name if it
|
|
|
|
|
* exists. Otherwise, returns (CellDef *) NULL.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
CellDef *
|
|
|
|
|
DBCellLookDef(cellName)
|
|
|
|
|
char *cellName;
|
|
|
|
|
{
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
|
2021-06-17 18:40:21 +02:00
|
|
|
entry = HashLookOnly(&dbCellDefTable, cellName);
|
|
|
|
|
if (entry == (HashEntry *)NULL) return (CellDef *)NULL;
|
|
|
|
|
return (CellDef *)HashGetValue(entry);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellNewDef --
|
|
|
|
|
*
|
|
|
|
|
* Create a new cell definition with the given name. There must not
|
|
|
|
|
* be any cells already known with the same name.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns a pointer to the newly created CellDef. The CellDef
|
|
|
|
|
* is completely initialized, showing no uses and having all
|
|
|
|
|
* tile planes initialized via TiNewPlane() to contain a single
|
|
|
|
|
* space tile. The filename associated with the cell is set to
|
|
|
|
|
* the name supplied, but no attempt is made to open it or create
|
|
|
|
|
* it.
|
|
|
|
|
*
|
|
|
|
|
* If the cellName supplied is NULL, the cell is entered into
|
|
|
|
|
* the symbol table with a name of UNNAMED.
|
|
|
|
|
*
|
|
|
|
|
* Returns NULL if a cell by the given name already exists.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The name of the CellDef is entered into the symbol table
|
|
|
|
|
* of known cells.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
CellDef *
|
2020-03-21 17:40:35 +01:00
|
|
|
DBCellNewDef(cellName)
|
2017-04-25 14:41:48 +02:00
|
|
|
char *cellName; /* Name by which the cell is known */
|
|
|
|
|
{
|
|
|
|
|
CellDef *cellDef;
|
|
|
|
|
HashEntry *entry;
|
2020-03-21 17:40:35 +01:00
|
|
|
char *dotptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (cellName == (char *) NULL)
|
|
|
|
|
cellName = UNNAMED;
|
|
|
|
|
|
|
|
|
|
entry = HashFind(&dbCellDefTable, cellName);
|
2024-10-02 07:59:00 +02:00
|
|
|
if (HashGetValue(entry) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
return ((CellDef *) NULL);
|
|
|
|
|
|
|
|
|
|
cellDef = DBCellDefAlloc();
|
|
|
|
|
HashSetValue(entry, (ClientData) cellDef);
|
|
|
|
|
cellDef->cd_name = StrDup((char **) NULL, cellName);
|
2020-03-21 17:40:35 +01:00
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
/* Strip any .mag extension off of the cell name */
|
2020-03-21 17:40:35 +01:00
|
|
|
dotptr = strrchr(cellDef->cd_name, '.');
|
|
|
|
|
if (dotptr && !strcmp(dotptr, ".mag")) *dotptr = '\0';
|
|
|
|
|
|
|
|
|
|
cellDef->cd_file = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
return (cellDef);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellDefAlloc --
|
|
|
|
|
*
|
|
|
|
|
* Create a new cell definition structure. The new def is not added
|
|
|
|
|
* to any symbol tables.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns a pointer to the newly created CellDef. The CellDef
|
|
|
|
|
* is completely initialized, showing no uses and having all
|
|
|
|
|
* tile planes initialized via TiNewPlane() to contain a single
|
|
|
|
|
* space tile.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
CellDef *
|
|
|
|
|
DBCellDefAlloc()
|
|
|
|
|
{
|
|
|
|
|
CellDef *cellDef;
|
|
|
|
|
int pNum;
|
|
|
|
|
|
|
|
|
|
cellDef = (CellDef *) mallocMagic((unsigned) (sizeof (CellDef)));
|
|
|
|
|
cellDef->cd_flags = 0;
|
|
|
|
|
cellDef->cd_bbox.r_xbot = 0;
|
|
|
|
|
cellDef->cd_bbox.r_ybot = 0;
|
|
|
|
|
cellDef->cd_bbox.r_xtop = 1;
|
|
|
|
|
cellDef->cd_bbox.r_ytop = 1;
|
|
|
|
|
cellDef->cd_extended = cellDef->cd_bbox;
|
|
|
|
|
cellDef->cd_name = (char *) NULL;
|
|
|
|
|
cellDef->cd_file = (char *) NULL;
|
|
|
|
|
#ifdef FILE_LOCKS
|
|
|
|
|
cellDef->cd_fd = -1;
|
|
|
|
|
#endif
|
|
|
|
|
cellDef->cd_parents = (CellUse *) NULL;
|
|
|
|
|
cellDef->cd_labels = (Label *) NULL;
|
|
|
|
|
cellDef->cd_lastLabel = (Label *) NULL;
|
|
|
|
|
cellDef->cd_client = (ClientData) 0;
|
|
|
|
|
cellDef->cd_props = (ClientData) NULL;
|
|
|
|
|
cellDef->cd_timestamp = 0;
|
|
|
|
|
TTMaskZero(&cellDef->cd_types);
|
|
|
|
|
HashInit(&cellDef->cd_idHash, 16, HT_STRINGKEYS);
|
|
|
|
|
|
2019-03-23 00:58:47 +01:00
|
|
|
cellDef->cd_cellPlane = BPNew();
|
|
|
|
|
cellDef->cd_planes[PL_ROUTER] = DBNewPlane((ClientData) NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
cellDef->cd_planes[pNum] = DBNewPlane((ClientData) TT_SPACE);
|
|
|
|
|
|
|
|
|
|
/* Definitively zero out all other plane entries */
|
|
|
|
|
for (pNum = DBNumPlanes; pNum < MAXPLANES; pNum++)
|
|
|
|
|
cellDef->cd_planes[pNum] = NULL;
|
|
|
|
|
|
|
|
|
|
return (cellDef);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellNewUse --
|
|
|
|
|
*
|
|
|
|
|
* Create a new cell use of the supplied CellDef.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns a pointer to the new CellUse. The CellUse is initialized
|
|
|
|
|
* to reflect that cellDef is its definition. The transform is
|
|
|
|
|
* initialized to the identity, and the parent pointer initialized
|
|
|
|
|
* to NULL.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Updates the use list for cellDef.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
CellUse *
|
|
|
|
|
DBCellNewUse(cellDef, useName)
|
|
|
|
|
CellDef *cellDef; /* Pointer to definition of the cell */
|
|
|
|
|
char *useName; /* Pointer to use identifier for the cell. This may
|
|
|
|
|
* be NULL, in which case a unique use identifier is
|
|
|
|
|
* generated automatically when the cell use is linked
|
|
|
|
|
* into a parent def.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
CellUse *cellUse;
|
|
|
|
|
|
|
|
|
|
cellUse = (CellUse *) mallocMagic((unsigned) (sizeof (CellUse)));
|
|
|
|
|
cellUse->cu_id = StrDup((char **) NULL, useName);
|
|
|
|
|
cellUse->cu_flags = (unsigned char)0;
|
|
|
|
|
cellUse->cu_expandMask = 0;
|
|
|
|
|
cellUse->cu_transform = GeoIdentityTransform;
|
|
|
|
|
cellUse->cu_def = cellDef;
|
|
|
|
|
cellUse->cu_parent = (CellDef *) NULL;
|
|
|
|
|
cellUse->cu_xlo = 0;
|
|
|
|
|
cellUse->cu_ylo = 0;
|
|
|
|
|
cellUse->cu_xhi = 0;
|
|
|
|
|
cellUse->cu_yhi = 0;
|
|
|
|
|
cellUse->cu_xsep = 0;
|
|
|
|
|
cellUse->cu_ysep = 0;
|
|
|
|
|
cellUse->cu_nextuse = cellDef->cd_parents;
|
|
|
|
|
|
|
|
|
|
/* Initial client field */
|
|
|
|
|
/* (commands can use this field for whatever
|
2020-05-23 23:13:14 +02:00
|
|
|
* they like, but should restore its value to CLIENTDEFAULT before exiting.)
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
cellUse->cu_client = (ClientData) CLIENTDEFAULT;
|
|
|
|
|
|
|
|
|
|
cellDef->cd_parents = cellUse;
|
|
|
|
|
DBComputeUseBbox(cellUse);
|
|
|
|
|
return (cellUse);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellRenameDef --
|
|
|
|
|
*
|
|
|
|
|
* Renames the indicated CellDef.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if successful, FALSE if the new name was not unique.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The name of the CellDef is entered into the symbol table
|
|
|
|
|
* of known cells. The CDMODIFIED bit is set in the flags
|
|
|
|
|
* of each of the parents of the CellDef to force them to
|
|
|
|
|
* be written out using the new name.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
DBCellRenameDef(cellDef, newName)
|
|
|
|
|
CellDef *cellDef; /* Pointer to CellDef being renamed */
|
|
|
|
|
char *newName; /* Pointer to new name */
|
|
|
|
|
{
|
|
|
|
|
HashEntry *oldEntry, *newEntry;
|
|
|
|
|
CellUse *parent;
|
|
|
|
|
|
|
|
|
|
oldEntry = HashFind(&dbCellDefTable, cellDef->cd_name);
|
|
|
|
|
ASSERT(HashGetValue(oldEntry) == (ClientData) cellDef, "DBCellRenameDef");
|
|
|
|
|
|
|
|
|
|
newEntry = HashFind(&dbCellDefTable, newName);
|
2024-10-02 07:59:00 +02:00
|
|
|
if (HashGetValue(newEntry) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
return (FALSE);
|
|
|
|
|
|
|
|
|
|
HashSetValue(oldEntry, (ClientData) NULL);
|
|
|
|
|
HashSetValue(newEntry, (ClientData) cellDef);
|
|
|
|
|
(void) StrDup(&cellDef->cd_name, newName);
|
|
|
|
|
|
|
|
|
|
for (parent = cellDef->cd_parents; parent; parent = parent->cu_nextuse)
|
|
|
|
|
if (parent->cu_parent)
|
|
|
|
|
parent->cu_parent->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
|
|
|
|
|
|
|
|
|
|
return (TRUE);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellDeleteDef --
|
|
|
|
|
*
|
|
|
|
|
* Removes the CellDef from the symbol table of known CellDefs and
|
|
|
|
|
* frees the storage allocated to the CellDef. The CellDef must have
|
|
|
|
|
* no CellUses.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if successful, FALSE if there were any outstanding
|
|
|
|
|
* CellUses found.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The CellDef is removed from the table of known CellDefs.
|
|
|
|
|
* All storage for the CellDef and its tile planes is freed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
DBCellDeleteDef(cellDef)
|
|
|
|
|
CellDef *cellDef; /* Pointer to CellDef to be deleted */
|
|
|
|
|
{
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
|
|
|
|
|
if (cellDef->cd_parents != (CellUse *) NULL)
|
|
|
|
|
return (FALSE);
|
|
|
|
|
|
|
|
|
|
entry = HashFind(&dbCellDefTable, cellDef->cd_name);
|
|
|
|
|
ASSERT(HashGetValue(entry) == (ClientData) cellDef, "DBCellDeleteDef");
|
|
|
|
|
HashSetValue(entry, (ClientData) NULL);
|
2025-10-20 17:03:03 +02:00
|
|
|
HashRemove(&dbCellDefTable, cellDef->cd_name);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (cellDef->cd_props)
|
|
|
|
|
DBPropClearAll(cellDef);
|
|
|
|
|
|
|
|
|
|
/* Need to check the DRC pending queue, and remove */
|
|
|
|
|
/* this cell if it's there. */
|
|
|
|
|
DRCRemovePending(cellDef);
|
|
|
|
|
|
|
|
|
|
DBCellDefFree(cellDef);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellDefFree --
|
|
|
|
|
*
|
|
|
|
|
* Does all the dirty work of freeing up stuff inside a celldef.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* All memory associated with the cellDef is freed. This may
|
|
|
|
|
* cause lower-level cellUses and defs to be freed up. This
|
|
|
|
|
* procedure is separated from DBDeleteCellDef so that it can
|
|
|
|
|
* be used for cells that aren't in the hash table (e.g. cells
|
|
|
|
|
* used by the window manager).
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBCellDefFree(cellDef)
|
|
|
|
|
CellDef *cellDef;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
int pNum;
|
|
|
|
|
Label *lab;
|
|
|
|
|
|
|
|
|
|
if (cellDef->cd_file != (char *) NULL)
|
|
|
|
|
freeMagic(cellDef->cd_file);
|
|
|
|
|
if (cellDef->cd_name != (char *) NULL)
|
|
|
|
|
freeMagic(cellDef->cd_name);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We want the following searching to be non-interruptible
|
|
|
|
|
* to guarantee that all storage gets freed.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
SigDisableInterrupts();
|
2019-03-23 00:58:47 +01:00
|
|
|
DBClearCellPlane(cellDef); /* Remove instances only */
|
|
|
|
|
BPFree(cellDef->cd_cellPlane); /* Remove the cell plane itself */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2019-03-23 00:58:47 +01:00
|
|
|
TiFreePlane(cellDef->cd_planes[PL_ROUTER]);
|
2017-04-25 14:41:48 +02:00
|
|
|
for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
{
|
|
|
|
|
DBFreePaintPlane(cellDef->cd_planes[pNum]);
|
|
|
|
|
TiFreePlane(cellDef->cd_planes[pNum]);
|
|
|
|
|
cellDef->cd_planes[pNum] = (Plane *) NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
2017-04-25 14:41:48 +02:00
|
|
|
for (lab = cellDef->cd_labels; lab; lab = lab->lab_next)
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, (char *) lab);
|
|
|
|
|
freeMagic1_end(&mm1);
|
2017-04-25 14:41:48 +02:00
|
|
|
SigEnableInterrupts();
|
|
|
|
|
HashKill(&cellDef->cd_idHash);
|
|
|
|
|
|
|
|
|
|
freeMagic((char *) cellDef);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellDeleteUse --
|
|
|
|
|
*
|
|
|
|
|
* Frees the storage allocated to the CellUse.
|
|
|
|
|
*
|
|
|
|
|
* It is required that the CellUse has been removed from any CellTileBodies
|
|
|
|
|
* in the subcell plane of its parent. The parent pointer for this
|
|
|
|
|
* CellUse must therefore be NULL.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if the CellUse was successfully removed, FALSE if
|
|
|
|
|
* the parent pointer were not NULL.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* All storage for the CellUse is freed.
|
|
|
|
|
* The list of all CellUses associated with a given CellDef is
|
|
|
|
|
* updated to reflect the absence of the deleted CellUse.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
DBCellDeleteUse(cellUse)
|
|
|
|
|
CellUse *cellUse; /* Pointer to CellUse to be deleted */
|
|
|
|
|
{
|
|
|
|
|
CellDef *cellDef;
|
|
|
|
|
CellUse *useptr;
|
|
|
|
|
|
|
|
|
|
if (cellUse->cu_parent != (CellDef *) NULL)
|
|
|
|
|
return (FALSE);
|
|
|
|
|
|
|
|
|
|
cellDef = cellUse->cu_def;
|
|
|
|
|
if (cellUse->cu_id != (char *) NULL)
|
|
|
|
|
freeMagic(cellUse->cu_id);
|
|
|
|
|
cellUse->cu_id = (char *) NULL;
|
|
|
|
|
cellUse->cu_def = (CellDef *) NULL;
|
|
|
|
|
|
|
|
|
|
ASSERT(cellDef->cd_parents != (CellUse *) NULL, "DBCellDeleteUse");
|
|
|
|
|
|
|
|
|
|
if (cellDef->cd_parents == cellUse)
|
|
|
|
|
cellDef->cd_parents = cellUse->cu_nextuse;
|
|
|
|
|
else for (useptr = cellDef->cd_parents; useptr != NULL;
|
|
|
|
|
useptr = useptr->cu_nextuse)
|
|
|
|
|
{
|
|
|
|
|
if (useptr->cu_nextuse == cellUse)
|
|
|
|
|
{
|
|
|
|
|
useptr->cu_nextuse = cellUse->cu_nextuse;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
freeMagic((char *) cellUse);
|
|
|
|
|
return (TRUE);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBCellSrDefs --
|
|
|
|
|
*
|
|
|
|
|
* Search for all cell definitions matching a given pattern.
|
|
|
|
|
* For each cell definition whose flag word contains any of the
|
|
|
|
|
* bits in pattern, the supplied procedure is invoked.
|
|
|
|
|
*
|
|
|
|
|
* The procedure should be of the following form:
|
|
|
|
|
* int
|
|
|
|
|
* func(cellDef, cdata)
|
|
|
|
|
* CellDef *cellDef;
|
|
|
|
|
* ClientData cdata;
|
|
|
|
|
* {
|
|
|
|
|
* }
|
|
|
|
|
* Func should normally return 0. If it returns 1 then the
|
|
|
|
|
* search is aborted.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns 1 if the search completed normally, 1 if it aborted.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Whatever the user-supplied procedure does.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
DBCellSrDefs(pattern, func, cdata)
|
|
|
|
|
int pattern; /* Used for selecting cell definitions. If any
|
|
|
|
|
* of the bits in the pattern are in a def->cd_flags,
|
|
|
|
|
* or if pattern is 0, the user-supplied function
|
2020-05-23 23:13:14 +02:00
|
|
|
* is invoked.
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
int (*func)(); /* Function to be applied to each matching CellDef */
|
|
|
|
|
ClientData cdata; /* Client data also passed to function */
|
|
|
|
|
{
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
CellDef *cellDef;
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while ((he = HashNext(&dbCellDefTable, &hs)) != (HashEntry *) NULL)
|
|
|
|
|
{
|
|
|
|
|
cellDef = (CellDef *) HashGetValue(he);
|
|
|
|
|
if (cellDef == (CellDef *) NULL)
|
|
|
|
|
continue;
|
|
|
|
|
if ((pattern != 0) && !(cellDef->cd_flags & pattern))
|
|
|
|
|
continue;
|
|
|
|
|
if ((*func)(cellDef, cdata)) return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBLinkCell --
|
|
|
|
|
*
|
|
|
|
|
* Set the cu_id for the supplied CellUse appropriately for linking into
|
|
|
|
|
* the parent CellDef. If the cu_id is NULL, a cu_id unique within the
|
|
|
|
|
* CellDef is automatically generated and stored in cu_id; otherwise, the
|
|
|
|
|
* one supplied in cu_id is used.
|
|
|
|
|
*
|
|
|
|
|
* *** WARNING ***
|
|
|
|
|
*
|
|
|
|
|
* This operation is not recorded on the undo list, as it always accompanies
|
|
|
|
|
* the creation of a new cell use.
|
|
|
|
|
*
|
2022-09-07 23:02:36 +02:00
|
|
|
* *** ANOTHER WARNING ***
|
|
|
|
|
*
|
|
|
|
|
* Do NOT pass a NULL cu_id to this routine when doing bulk cell copies,
|
|
|
|
|
* because an entire hash table of entries for the parent def will be created
|
|
|
|
|
* and searched for every cell being linked, making the routine run-time
|
|
|
|
|
* exponential with the number of links. It is better to run
|
|
|
|
|
* DBGenerateUniqueIDs() instead.
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Results:
|
|
|
|
|
* TRUE if the CellUse is unique within the parent CellDef, FALSE
|
|
|
|
|
* if there would be a name conflict. If the cu_id of the CellUse
|
|
|
|
|
* is NULL, TRUE is always returned; FALSE is only returned if
|
|
|
|
|
* there is an existing name which would conflict with names already
|
|
|
|
|
* present in the CellUse.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Will set cu_id to an automatically generated instance id if
|
|
|
|
|
* it was originally NULL.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
DBLinkCell(use, parentDef)
|
|
|
|
|
CellUse *use;
|
|
|
|
|
CellDef *parentDef;
|
|
|
|
|
{
|
|
|
|
|
char useId[100], *lastName;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if (use->cu_id)
|
|
|
|
|
{
|
|
|
|
|
if (DBFindUse(use->cu_id, parentDef))
|
|
|
|
|
return FALSE;
|
|
|
|
|
DBSetUseIdHash(use, parentDef);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HashInit(&dbUniqueNameTable, 32, 0); /* Indexed by use-id */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Uses can't contain slashes (otherwise they'd interfere with
|
|
|
|
|
* terminal naming conventions). If the cellName has a slash,
|
|
|
|
|
* just use the part of it after the last slash.
|
|
|
|
|
*/
|
|
|
|
|
lastName = strrchr(use->cu_def->cd_name, '/');
|
|
|
|
|
if (lastName == NULL) lastName = use->cu_def->cd_name;
|
|
|
|
|
else lastName++;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* This search must not be interrupted */
|
|
|
|
|
SigDisableInterrupts();
|
|
|
|
|
(void) DBCellEnum(parentDef, dbLinkFunc, (ClientData) lastName);
|
|
|
|
|
SigEnableInterrupts();
|
|
|
|
|
|
|
|
|
|
/* This loop terminates only when an empty useid is found */
|
|
|
|
|
/* This loop should *not* terminate on interrupt, because */
|
|
|
|
|
/* lots of code relies on a NULL cu_id being passed to ensure */
|
|
|
|
|
/* that the cell gets linked in. */
|
|
|
|
|
|
|
|
|
|
for (n = 0;; n++)
|
|
|
|
|
{
|
|
|
|
|
(void) sprintf(useId, "%s_%d", lastName, n);
|
|
|
|
|
he = HashLookOnly(&dbUniqueNameTable, useId);
|
|
|
|
|
if (he == (HashEntry *) NULL)
|
|
|
|
|
{
|
|
|
|
|
HashKill(&dbUniqueNameTable);
|
|
|
|
|
use->cu_id = StrDup((char **) NULL, useId);
|
|
|
|
|
DBSetUseIdHash(use, parentDef);
|
|
|
|
|
return (TRUE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Never gets here */
|
|
|
|
|
/* HashKill(&dbUniqueNameTable); */
|
|
|
|
|
/* return (FALSE); */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* dbLinkFunc --
|
|
|
|
|
*
|
|
|
|
|
* Filter function called via DBCellEnum by DBLinkCell above.
|
|
|
|
|
* Creates an entry in the hash table dbUniqueNameTable to
|
|
|
|
|
* indicate that the name "defname_#" is not available.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
dbLinkFunc(cellUse, defname)
|
|
|
|
|
CellUse *cellUse;
|
|
|
|
|
char *defname;
|
|
|
|
|
{
|
|
|
|
|
char *usep = cellUse->cu_id;
|
|
|
|
|
|
|
|
|
|
/* Skip in the unlikely event that this cell has no use-id */
|
|
|
|
|
if (usep == (char *) NULL)
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Only add names whose initial part matches 'defname',
|
|
|
|
|
* and which are of the form 'defname_something'.
|
|
|
|
|
*/
|
|
|
|
|
while (*defname)
|
|
|
|
|
if (*defname++ != *usep++)
|
|
|
|
|
return 0;
|
|
|
|
|
if (*usep++ != '_') return 0;
|
|
|
|
|
if (*usep == '\0') return 0;
|
|
|
|
|
|
|
|
|
|
/* Remember this name as being in use */
|
|
|
|
|
(void) HashFind(&dbUniqueNameTable, cellUse->cu_id);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBReLinkCell --
|
|
|
|
|
*
|
|
|
|
|
* Change the instance id of the supplied CellUse.
|
|
|
|
|
* If the instance id is non-NULL, and the new id is the same
|
|
|
|
|
* as the old one, we do nothing.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns TRUE if successful, FALSE if the new name was not
|
|
|
|
|
* unique within the parent def.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* May modify the cu_id of the supplied CellUse.
|
|
|
|
|
* Marks the parent of the cell use as having been modified.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
DBReLinkCell(cellUse, newName)
|
|
|
|
|
CellUse *cellUse;
|
|
|
|
|
char *newName;
|
|
|
|
|
{
|
|
|
|
|
if (cellUse->cu_id && strcmp(cellUse->cu_id, newName) == 0)
|
|
|
|
|
return (TRUE);
|
|
|
|
|
|
|
|
|
|
if (DBFindUse(newName, cellUse->cu_parent))
|
|
|
|
|
return (FALSE);
|
|
|
|
|
|
|
|
|
|
if (cellUse->cu_parent)
|
|
|
|
|
cellUse->cu_parent->cd_flags |= CDMODIFIED;
|
|
|
|
|
|
|
|
|
|
/* Old id (may be NULL) */
|
|
|
|
|
if (cellUse->cu_id)
|
|
|
|
|
DBUnLinkCell(cellUse, cellUse->cu_parent);
|
|
|
|
|
if (UndoIsEnabled()) DBUndoCellUse(cellUse, UNDO_CELL_CLRID);
|
|
|
|
|
|
|
|
|
|
/* New id */
|
|
|
|
|
(void) StrDup(&cellUse->cu_id, newName);
|
|
|
|
|
DBSetUseIdHash(cellUse, cellUse->cu_parent);
|
|
|
|
|
if (UndoIsEnabled()) DBUndoCellUse(cellUse, UNDO_CELL_SETID);
|
|
|
|
|
return (TRUE);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBFindUse --
|
|
|
|
|
*
|
|
|
|
|
* Find a CellUse with the given name in the supplied parent CellDef.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns a pointer to the found CellUse, or NULL if it was not
|
|
|
|
|
* found.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
CellUse *
|
|
|
|
|
DBFindUse(id, parentDef)
|
|
|
|
|
char *id;
|
|
|
|
|
CellDef *parentDef;
|
|
|
|
|
{
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
char *delimit;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Sanity checks */
|
|
|
|
|
if (id == NULL) return NULL;
|
|
|
|
|
if (parentDef == NULL) return NULL;
|
|
|
|
|
|
|
|
|
|
/* Array delimiters should be ignored */
|
|
|
|
|
if ((delimit = strrchr(id, '[')) != NULL) *delimit = '\0';
|
|
|
|
|
|
|
|
|
|
he = HashLookOnly(&parentDef->cd_idHash, id);
|
|
|
|
|
if (delimit != NULL) *delimit = '[';
|
|
|
|
|
if (he == NULL)
|
2024-04-05 23:47:44 +02:00
|
|
|
{
|
|
|
|
|
/* Try again without ignoring the delimiter */
|
|
|
|
|
if (delimit != NULL)
|
|
|
|
|
he = HashLookOnly(&parentDef->cd_idHash, id);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-04-05 23:47:44 +02:00
|
|
|
if (he == NULL)
|
|
|
|
|
return (CellUse *) NULL;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
return (CellUse *) HashGetValue(he);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBGenerateUniqueIds --
|
|
|
|
|
*
|
|
|
|
|
* Make certain that the use-id of each CellUse under 'def' is unique
|
|
|
|
|
* if it exists. If duplicates are detected, all but one is freed and
|
|
|
|
|
* set to NULL.
|
|
|
|
|
*
|
|
|
|
|
* The second pass consists of giving each CellUse beneath 'def' with
|
|
|
|
|
* a NULL use-id a uniquely generated one.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* May modify the use-id's of the cells in the cell plane of 'def'.
|
|
|
|
|
* Prints error messages if use-ids had to be reassigned.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBGenerateUniqueIds(def, warn)
|
|
|
|
|
CellDef *def;
|
|
|
|
|
bool warn; /* If TRUE, warn user when we assign new ids */
|
|
|
|
|
{
|
|
|
|
|
int dbFindNamesFunc();
|
|
|
|
|
int dbGenerateUniqueIdsFunc();
|
|
|
|
|
|
|
|
|
|
dbWarnUniqueIds = warn;
|
|
|
|
|
HashInit(&dbUniqueDefTable, 32, 1); /* Indexed by (CellDef *) */
|
|
|
|
|
HashInit(&dbUniqueNameTable, 32, 0); /* Indexed by use-id */
|
|
|
|
|
|
|
|
|
|
/* Build up tables of names, complaining about duplicates */
|
|
|
|
|
(void) DBCellEnum(def, dbFindNamesFunc, (ClientData) def);
|
|
|
|
|
|
|
|
|
|
/* Assign unique use-ids to all cells */
|
|
|
|
|
(void) DBCellEnum(def, dbGenerateUniqueIdsFunc, (ClientData) def);
|
|
|
|
|
|
|
|
|
|
HashKill(&dbUniqueDefTable);
|
|
|
|
|
HashKill(&dbUniqueNameTable);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2025-03-26 19:45:46 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBSelectionUniqueIds --
|
|
|
|
|
*
|
|
|
|
|
* This is similar to DBGenerateUniqueIds(), but the purpose is to make
|
|
|
|
|
* sure that cell IDs in the selection do not collide with IDs in the
|
|
|
|
|
* definition. This is done before copying from Select2Def back to the
|
|
|
|
|
* root edit CellDef. Otherwise, any copied cell takes the name of the
|
|
|
|
|
* original, and the original gets renamed, which is unexpected behavior.
|
|
|
|
|
* Called only from SelectCopy().
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* May modify the use-id's of the cells in the cell plane of 'selDef'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBSelectionUniqueIds(selDef, rootDef)
|
|
|
|
|
CellDef *selDef; /* Should be Select2Def */
|
|
|
|
|
CellDef *rootDef; /* Should be EditRootDef */
|
|
|
|
|
{
|
|
|
|
|
int dbFindNamesFunc();
|
|
|
|
|
int dbGenerateUniqueIdsFunc();
|
|
|
|
|
|
|
|
|
|
dbWarnUniqueIds = FALSE;
|
|
|
|
|
HashInit(&dbUniqueDefTable, 32, 1); /* Indexed by (CellDef *) */
|
|
|
|
|
HashInit(&dbUniqueNameTable, 32, 0); /* Indexed by use-id */
|
|
|
|
|
|
|
|
|
|
/* Build up tables of names */
|
|
|
|
|
DBCellEnum(rootDef, dbFindNamesFunc, (ClientData) rootDef);
|
|
|
|
|
DBCellEnum(selDef, dbFindNamesFunc, (ClientData) selDef);
|
|
|
|
|
|
|
|
|
|
/* Assign unique use-ids to all cells in the selection */
|
|
|
|
|
DBCellEnum(selDef, dbGenerateUniqueIdsFunc, (ClientData) selDef);
|
|
|
|
|
|
|
|
|
|
HashKill(&dbUniqueDefTable);
|
|
|
|
|
HashKill(&dbUniqueNameTable);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* dbFindNamesFunc --
|
|
|
|
|
*
|
|
|
|
|
* Called via DBCellEnum() on behalf of DBGenerateUniqueIds() above,
|
|
|
|
|
* for each subcell of the def just processed. If any cell has a
|
|
|
|
|
* use-id, we add it to our table of in-use names (dbUniqueNameTable).
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns 0 always.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* If the name already is in the table, free this cell's use-id
|
|
|
|
|
* and set it to NULL. In any event, add the name to the table.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
dbFindNamesFunc(use, parentDef)
|
|
|
|
|
CellUse *use;
|
|
|
|
|
CellDef *parentDef;
|
|
|
|
|
{
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
|
|
|
|
if (use->cu_id)
|
|
|
|
|
{
|
|
|
|
|
he = HashFind(&dbUniqueNameTable, use->cu_id);
|
|
|
|
|
if (HashGetValue(he))
|
|
|
|
|
{
|
2022-09-07 23:02:36 +02:00
|
|
|
if (dbWarnUniqueIds)
|
|
|
|
|
TxError("Duplicate instance-id for cell %s (%s) will be renamed\n",
|
|
|
|
|
use->cu_def->cd_name, use->cu_id);
|
2017-04-25 14:41:48 +02:00
|
|
|
DBUnLinkCell(use, parentDef);
|
|
|
|
|
freeMagic(use->cu_id);
|
|
|
|
|
use->cu_id = (char *) NULL;
|
|
|
|
|
}
|
|
|
|
|
HashSetValue(he, use);
|
|
|
|
|
}
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* dbGenerateUniqueIdsFunc --
|
|
|
|
|
*
|
|
|
|
|
* Called via DBCellEnum() on behalf of DBGenerateUniqueIds() above,
|
|
|
|
|
* for each subcell of the def just processed. If the cell has no
|
|
|
|
|
* use-id, we generate one automatically. In any event, install the
|
|
|
|
|
* use identifier for each cell processed in the HashTable
|
|
|
|
|
* parentDef->cd_idHash.
|
|
|
|
|
*
|
|
|
|
|
* Algorithm:
|
|
|
|
|
* We generate unique use-id's of the form def_# where #
|
|
|
|
|
* is an integer such that no other use-id in this cell has
|
|
|
|
|
* the same name. The HashTable dbUniqueDefTable is indexed
|
|
|
|
|
* by CellDef and contains the highest sequence number (#
|
|
|
|
|
* above) processed for that CellDef. Each time we process
|
|
|
|
|
* a cell, we start with this sequence number and continue
|
|
|
|
|
* to increment it until we find a name that is not in
|
|
|
|
|
* calmaNamesTable, then generate the use-id, and store
|
|
|
|
|
* the next sequence number in the HashEntry for the CellDef.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns 0 always.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
dbGenerateUniqueIdsFunc(use, parentDef)
|
|
|
|
|
CellUse *use;
|
|
|
|
|
CellDef *parentDef;
|
|
|
|
|
{
|
|
|
|
|
HashEntry *hedef, *hename;
|
|
|
|
|
int suffix;
|
|
|
|
|
char name[1024];
|
|
|
|
|
|
|
|
|
|
if (use->cu_id)
|
|
|
|
|
goto setHash;
|
|
|
|
|
|
|
|
|
|
hedef = HashFind(&dbUniqueDefTable, (char *) use->cu_def);
|
|
|
|
|
for (suffix = (spointertype) HashGetValue(hedef); ; suffix++)
|
|
|
|
|
{
|
|
|
|
|
(void) sprintf(name, "%s_%d", use->cu_def->cd_name, suffix);
|
|
|
|
|
hename = HashLookOnly(&dbUniqueNameTable, name);
|
|
|
|
|
if (hename == NULL)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dbWarnUniqueIds)
|
|
|
|
|
TxPrintf("Setting instance-id of cell %s to %s\n",
|
|
|
|
|
use->cu_def->cd_name, name);
|
|
|
|
|
use->cu_id = StrDup((char **) NULL, name);
|
|
|
|
|
HashSetValue(hedef, (spointertype)suffix + 1);
|
|
|
|
|
|
|
|
|
|
setHash:
|
|
|
|
|
DBSetUseIdHash(use, parentDef);
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBSetUseIdHash --
|
|
|
|
|
*
|
|
|
|
|
* Update the use-id hash table in parentDef to reflect the fact
|
|
|
|
|
* that 'use' now has instance-id use->cu_id.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBSetUseIdHash(use, parentDef)
|
|
|
|
|
CellUse *use;
|
|
|
|
|
CellDef *parentDef;
|
|
|
|
|
{
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
|
|
|
|
he = HashFind(&parentDef->cd_idHash, use->cu_id);
|
|
|
|
|
HashSetValue(he, (ClientData) use);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBUnLinkCell --
|
|
|
|
|
*
|
|
|
|
|
* Update the use-id hash table in parentDef to reflect the fact
|
|
|
|
|
* that 'use' no longer is known by instance-id use->cu_id.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBUnLinkCell(use, parentDef)
|
|
|
|
|
CellUse *use;
|
|
|
|
|
CellDef *parentDef;
|
|
|
|
|
{
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
2024-10-04 18:19:27 +02:00
|
|
|
if ((he = HashLookOnly(&parentDef->cd_idHash, use->cu_id)))
|
2017-04-25 14:41:48 +02:00
|
|
|
HashSetValue(he, (ClientData) NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DBNewYank --
|
|
|
|
|
*
|
|
|
|
|
* Create a new yank buffer with name 'yname'.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Fills in *pydef with a newly created CellDef by that name, and
|
|
|
|
|
* *pyuse with a newly created CellUse pointing to the new def.
|
2024-01-21 17:00:19 +01:00
|
|
|
* The CellDef pointed to by *pydef has the CDINTERNAL flag
|
2017-04-25 14:41:48 +02:00
|
|
|
* set, and is marked as being available.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DBNewYank(yname, pyuse, pydef)
|
|
|
|
|
char *yname; /* Name of yank buffer */
|
|
|
|
|
CellUse **pyuse; /* Pointer to new cell use is stored in *pyuse */
|
|
|
|
|
CellDef **pydef; /* Similarly for def */
|
|
|
|
|
{
|
|
|
|
|
*pydef = DBCellLookDef(yname);
|
|
|
|
|
if (*pydef == (CellDef *) NULL)
|
|
|
|
|
{
|
2020-03-21 17:40:35 +01:00
|
|
|
*pydef = DBCellNewDef(yname);
|
2017-04-25 14:41:48 +02:00
|
|
|
ASSERT(*pydef != (CellDef *) NULL, "DBNewYank");
|
|
|
|
|
DBCellSetAvail(*pydef);
|
|
|
|
|
(*pydef)->cd_flags |= CDINTERNAL;
|
|
|
|
|
}
|
|
|
|
|
*pyuse = DBCellNewUse(*pydef, (char *) NULL);
|
|
|
|
|
DBSetTrans(*pyuse, &GeoIdentityTransform);
|
|
|
|
|
(*pyuse)->cu_expandMask = CU_DESCEND_SPECIAL; /* This is always expanded. */
|
|
|
|
|
}
|
|
|
|
|
|