/* * DBcellname.c -- * * CellUse/CellDef creation, deletion, naming. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/database/DBcellname.c,v 1.5 2009/07/27 00:56:56 tim Exp $"; #endif /* not lint */ #include #include /* for qsort() */ #include #include #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 * 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. * * Results: * Return TRUE if the cell was successfully renamed. Return FALSE * otherwise. * * Side Effects: * Cellname is changed. * * ---------------------------------------------------------------------------- */ bool DBCellRename(cellname, newname, doforce) char *cellname; char *newname; bool doforce; { 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) { TxError("Error: Attempt to rename internal cell \"%s\"\n", cellname); return FALSE; } /* 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) { 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; } } /* Good to go! */ UndoDisable(); result = DBCellRenameDef(celldef, newname); DBWAreaChanged(celldef, &celldef->cd_bbox, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); if (doforce && ((celldef->cd_flags & CDVENDORGDS) == CDVENDORGDS)) { char *chkgdsfile; bool isReadOnly; chkgdsfile = (char *)DBPropGet(celldef, "GDS_FILE", &isReadOnly); /* 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); } 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; 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; CellUse *celluse, *lastuse; 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; } /* 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; } /* Cleared to delete. . . now prompt user if the cell has changes. */ /* Last chance to save! */ if ((force == FALSE) && (celldef->cd_flags & (CDMODIFIED|CDBOXESCHANGED|CDSTAMPSCHANGED))) { static const char *yesno[] = { "no", "yes", 0 }; 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 */ /* so that WindUnload() will create a new one. */ if (!strcmp(cellname, UNNAMED)) DBCellRename(cellname, "__UNNAMED__", FALSE); /* For all top-level cell uses, check if any windows have this */ /* use. If so, load the window with (UNNAMED). */ UndoDisable(); for (celluse = celldef->cd_parents; celluse != (CellUse *) NULL; celluse = celluse->cu_nextuse) { if (celluse->cu_parent == (CellDef *) NULL) { WindUnload(celluse); freeMagic(celluse->cu_id); } freeMagic((char *)celluse); } celldef->cd_parents = (CellUse *)NULL; DBWResetBox(celldef); 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(). * * ---------------------------------------------------------------------------- */ 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; if (useID == NULL) { newID = (char *)mallocMagic(7); sprintf(newID, "(null)"); return (newID); } 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); } } 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); } /* * ---------------------------------------------------------------------------- * * 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 */ } /* * ---------------------------------------------------------------------------- * * 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); } } /* * ---------------------------------------------------------------------------- * 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) { /* Like strcasecmp() but compare sequences of digits numerically */ for (;;) { if (*s2 == '\0') return *s1 != '\0'; else if (*s1 == '\0') return 1; else if (!(isdigit(*s1) && isdigit(*s2))) { char c1, c2; c1 = tolower(*s1); c2 = tolower(*s2); if (c1 != c2) return (int)c1 - (int)c2; 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; } /* * ---------------------------------------------------------------------------- * * 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; { int found, numcells; HashSearch hs; HashEntry *entry; CellDef *celldef; CellUse *celluse; char **celllist; 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. */ numcells = dbCellDefTable.ht_nEntries; if (numcells == 0) numcells = 1; celllist = (char **)mallocMagic(numcells * sizeof(char *)); numcells = 0; 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) celllist[numcells++] = celldef->cd_name; } } qsort(celllist, numcells, sizeof(char *), qcompare); while (--numcells >= 0) { if (dolist) #ifdef MAGIC_WRAPPER Tcl_AppendElement(magicinterp, celllist[numcells]); #else TxPrintf("%s ", celllist[numcells]); #endif else TxPrintf(" %s\n", celllist[numcells]); } freeMagic(celllist); break; case TOPCELLS: /* * Print the name of all the 'top' cells. Sort alphabetically. */ numcells = dbCellDefTable.ht_nEntries; if (numcells == 0) numcells = 1; celllist = (char **)mallocMagic(numcells * sizeof(char *)); numcells = 0; 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) ) celllist[numcells++] = celldef->cd_name; } } } qsort(celllist, numcells, sizeof(char *), qcompare); while (--numcells >= 0) { if (dolist) #ifdef MAGIC_WRAPPER Tcl_AppendElement(magicinterp, celllist[numcells]); #else TxPrintf("%s ", celllist[numcells]); #endif else TxPrintf(" %s\n", celllist[numcells]); } freeMagic(celllist); 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) { if (EditRootDef == NULL) { if (!dolist) TxPrintf("No cells selected.\n"); } else { dbCellPrintInfo(EditRootDef, who, dolist); found = 1; break; } } } 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 * instance. * * ---------------------------------------------------------------------------- */ 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(); if ((CellName != NULL) && ((lasthier = strrchr(CellName, '/')) != NULL)) { char *prevhier; *lasthier = '\0'; prevhier = strrchr(CellName, '/'); if (prevhier == NULL) prevhier = CellName; else prevhier++; celldef = DBCellLookDef(CellName); *lasthier = '/'; } else if (EditCellUse != NULL) { /* Referenced cellDef is the current edit def */ celldef = EditCellUse->cu_def; } else return; 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; int dbLockUseFunc(); /* * * 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); if ((celldef != (CellDef *)NULL) && !(celldef->cd_flags & CDINTERNAL)) { 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); } } /* * ---------------------------------------------------------------------------- * * DBOrientUse -- * * This routine reports a cell instance's orientation. * UseName is the name of a specific CellUse. If NULL, then the * operation applies to all selected cell uses. * * Results: * None. * * Side effects: * 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)). * * ---------------------------------------------------------------------------- */ 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); if ((celldef != (CellDef *)NULL) && !(celldef->cd_flags & CDINTERNAL)) { 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; int orient; 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; } orient = -1; if (selUse != NULL) orient = GeoTransOrient(&selUse->cu_transform); else if (use != NULL) orient = GeoTransOrient(&use->cu_transform); if (orient != -1) { switch (orient) { #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; } /* * ---------------------------------------------------------------------------- * * 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); if ((celldef != (CellDef *)NULL) && !(celldef->cd_flags & CDINTERNAL)) { 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; char *propvalue; 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; propvalue = (char *)DBPropGet(use->cu_def, "FIXED_BBOX", &found); if (!found) bbox = use->cu_def->cd_bbox; else { if (sscanf(propvalue, "%d %d %d %d", &bbox.r_xbot, &bbox.r_ybot, &bbox.r_xtop, &bbox.r_ytop) != 4) bbox = use->cu_def->cd_bbox; } GeoTransRect(trans, &bbox, &refbox); #ifdef MAGIC_WRAPPER if (*dolist) { pobj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_xbot)); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_ybot)); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_xtop)); Tcl_ListObjAppendElement(magicinterp, pobj, Tcl_NewIntObj(refbox.r_ytop)); Tcl_SetObjResult(magicinterp, pobj); } else #endif TxPrintf("Abutment box: %d %d %d %d\n", refbox.r_xbot, refbox.r_ybot, refbox.r_xtop, refbox.r_ytop); return 0; } /* * ---------------------------------------------------------------------------- * * 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; entry = HashLookOnly(&dbCellDefTable, cellName); if (entry == (HashEntry *)NULL) return (CellDef *)NULL; return (CellDef *)HashGetValue(entry); } /* * ---------------------------------------------------------------------------- * * 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 * DBCellNewDef(cellName) char *cellName; /* Name by which the cell is known */ { CellDef *cellDef; HashEntry *entry; char *dotptr; if (cellName == (char *) NULL) cellName = UNNAMED; entry = HashFind(&dbCellDefTable, cellName); if (HashGetValue(entry) != NULL) return ((CellDef *) NULL); cellDef = DBCellDefAlloc(); HashSetValue(entry, (ClientData) cellDef); cellDef->cd_name = StrDup((char **) NULL, cellName); /* Strip any .mag extension off of the cell name */ dotptr = strrchr(cellDef->cd_name, '.'); if (dotptr && !strcmp(dotptr, ".mag")) *dotptr = '\0'; cellDef->cd_file = NULL; return (cellDef); } /* * ---------------------------------------------------------------------------- * * 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); cellDef->cd_cellPlane = BPNew(); cellDef->cd_planes[PL_ROUTER] = DBNewPlane((ClientData) NULL); 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); } /* * ---------------------------------------------------------------------------- * * 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 * they like, but should restore its value to CLIENTDEFAULT before exiting.) */ cellUse->cu_client = (ClientData) CLIENTDEFAULT; cellDef->cd_parents = cellUse; DBComputeUseBbox(cellUse); return (cellUse); } /* * ---------------------------------------------------------------------------- * * 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); if (HashGetValue(newEntry) != NULL) 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); } /* * ---------------------------------------------------------------------------- * * 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); 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; } /* * ---------------------------------------------------------------------------- * * 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(); DBClearCellPlane(cellDef); /* Remove instances only */ BPFree(cellDef->cd_cellPlane); /* Remove the cell plane itself */ TiFreePlane(cellDef->cd_planes[PL_ROUTER]); for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++) { DBFreePaintPlane(cellDef->cd_planes[pNum]); TiFreePlane(cellDef->cd_planes[pNum]); cellDef->cd_planes[pNum] = (Plane *) NULL; } for (lab = cellDef->cd_labels; lab; lab = lab->lab_next) freeMagic((char *) lab); SigEnableInterrupts(); HashKill(&cellDef->cd_idHash); freeMagic((char *) cellDef); } /* * ---------------------------------------------------------------------------- * * 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); } /* * ---------------------------------------------------------------------------- * * 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 * is invoked. */ 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; } /* * ---------------------------------------------------------------------------- * * 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. * * *** 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. * * 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++; /* 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; } /* * ---------------------------------------------------------------------------- * * 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); } /* * ---------------------------------------------------------------------------- * * 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; /* 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) { /* Try again without ignoring the delimiter */ if (delimit != NULL) he = HashLookOnly(&parentDef->cd_idHash, id); if (he == NULL) return (CellUse *) NULL; } return (CellUse *) HashGetValue(he); } /* * ---------------------------------------------------------------------------- * * 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); } /* * ---------------------------------------------------------------------------- * * 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); } /* * ---------------------------------------------------------------------------- * * 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)) { if (dbWarnUniqueIds) TxError("Duplicate instance-id for cell %s (%s) will be renamed\n", use->cu_def->cd_name, use->cu_id); DBUnLinkCell(use, parentDef); freeMagic(use->cu_id); use->cu_id = (char *) NULL; } HashSetValue(he, use); } return (0); } /* * ---------------------------------------------------------------------------- * * 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); } /* * ---------------------------------------------------------------------------- * * 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); } /* * ---------------------------------------------------------------------------- * * 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; if ((he = HashLookOnly(&parentDef->cd_idHash, use->cu_id))) HashSetValue(he, (ClientData) NULL); } /* * ---------------------------------------------------------------------------- * * 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. * The CellDef pointed to by *pydef has the CDINTERNAL flag * 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) { *pydef = DBCellNewDef(yname); 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. */ }