1250 lines
32 KiB
C
1250 lines
32 KiB
C
/*
|
|
* CmdSubrs.c --
|
|
*
|
|
* The functions in this file are local to the commands module
|
|
* and not intended to be used by its clients.
|
|
*
|
|
* *********************************************************************
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
* * Permission to use, copy, modify, and distribute this *
|
|
* * software and its documentation for any purpose and without *
|
|
* * fee is hereby granted, provided that the above copyright *
|
|
* * notice appear in all copies. The University of California *
|
|
* * makes no representations about the suitability of this *
|
|
* * software for any purpose. It is provided "as is" without *
|
|
* * express or implied warranty. Export of this software outside *
|
|
* * of the United States of America may require an export license. *
|
|
* *********************************************************************
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/commands/CmdSubrs.c,v 1.2 2010/06/24 12:37:15 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <math.h> /* For round() function */
|
|
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "utils/utils.h"
|
|
#include "database/database.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/hash.h"
|
|
#include "utils/malloc.h"
|
|
#include "windows/windows.h"
|
|
#include "dbwind/dbwind.h"
|
|
#include "utils/main.h"
|
|
#include "commands/commands.h"
|
|
#include "textio/textio.h"
|
|
#include "cif/cif.h"
|
|
#include "drc/drc.h"
|
|
#include "textio/txcommands.h"
|
|
#include "utils/undo.h"
|
|
#include "utils/macros.h"
|
|
#include "sim/sim.h"
|
|
#include "select/select.h"
|
|
#ifdef SCHEME_INTERPRETER
|
|
#include "lisp/lisp.h"
|
|
#endif
|
|
|
|
/* Forward declarations */
|
|
|
|
extern char *cmdCheckNewName();
|
|
extern int cmdSaveWindSet();
|
|
extern void CmdSetWindCaption();
|
|
|
|
TileTypeBitMask CmdYMLabel;
|
|
TileTypeBitMask CmdYMCell;
|
|
TileTypeBitMask CmdYMAllButSpace;
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cmdScaleCoord --
|
|
*
|
|
* Replaces the use of atoi() in command parsing of coordinates. Allows
|
|
* coordinates to be declared in internal units, lambda, user units, or
|
|
* microns, and translates to internal units accordingly. It also allows
|
|
* coordinates to be specified in floating-point.
|
|
* A suffix of "i" indicates internal units, a suffix of "l" indicates
|
|
* lambda, a suffix of "g" indicates the user grid, and a suffix in metric
|
|
* notation ("nm", "um", "mm", "cm") indicates natural units. Other valid
|
|
* units are "cu" or "centimicrons" for centimicrons, or "microns" for um.
|
|
* Units without any suffix are assumed to be in lambda if "snap"
|
|
* (DBWSnapToGrid) is set to lambda, grid units if "snap" is set to the
|
|
* user grid, and internal units otherwise.
|
|
*
|
|
* MagWindow argument w is used only with grid-based snapping, to find
|
|
* the value of the grid for the given window. In this case, because the
|
|
* grid specifies an offset, "is_relative" specifies whether the given
|
|
* coordinate is a relative measurement (ignore offset) or an absolute
|
|
* position. Because the grid can be different in X and Y, "is_x"
|
|
* specifies whether the given distance is in the X (TRUE) or Y (FALSE)
|
|
* direction.
|
|
*
|
|
* This is the "general-purpose" routine, taking a value "scale" so
|
|
* that returned units can be a fraction of internal units, e.g., to
|
|
* represent wire centerlines (scale = 2) or highlights (any scale).
|
|
*
|
|
* Results:
|
|
* Integer representing the given coordinate in internal units,
|
|
* multiplied up by "scale".
|
|
*
|
|
* Side effects:
|
|
* None
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
cmdScaleCoord(w, arg, is_relative, is_x, scale)
|
|
MagWindow *w;
|
|
char *arg;
|
|
bool is_relative, is_x;
|
|
int scale;
|
|
{
|
|
char *endptr;
|
|
double dval = 0;
|
|
int mscale = 1;
|
|
DBWclientRec *crec;
|
|
|
|
if (*arg == '{') arg++;
|
|
while (isspace(*arg)) arg++;
|
|
|
|
dval = strtod(arg, &endptr);
|
|
dval *= (double)scale;
|
|
|
|
if (endptr == arg)
|
|
{
|
|
/* strtod() error condition */
|
|
TxError("Coordinate value cannot be parsed: assuming 0\n");
|
|
return 0;
|
|
}
|
|
|
|
else if ((*endptr == 'l')
|
|
|| ((*endptr == '\0') && (DBWSnapToGrid == DBW_SNAP_LAMBDA)))
|
|
{
|
|
/* lambda or default units */
|
|
dval *= (double)DBLambda[1];
|
|
dval /= (double)DBLambda[0];
|
|
return round(dval);
|
|
}
|
|
else if ((*endptr == 'i')
|
|
|| ((*endptr == '\0') && (DBWSnapToGrid == DBW_SNAP_INTERNAL)))
|
|
{
|
|
/* internal units */
|
|
return round(dval);
|
|
}
|
|
else if ((*endptr == 'g')
|
|
|| ((*endptr == '\0') && (DBWSnapToGrid == DBW_SNAP_USER)))
|
|
{
|
|
/* grid units */
|
|
if (w == (MagWindow *)NULL)
|
|
{
|
|
windCheckOnlyWindow(&w, DBWclientID);
|
|
if (w == (MagWindow *)NULL)
|
|
return round(dval); /* Default, if window is unknown */
|
|
}
|
|
crec = (DBWclientRec *) w->w_clientData;
|
|
if (is_x)
|
|
{
|
|
dval *= (double)(crec->dbw_gridRect.r_xtop
|
|
- crec->dbw_gridRect.r_xbot);
|
|
if (!is_relative)
|
|
dval += (double)crec->dbw_gridRect.r_xbot;
|
|
}
|
|
else
|
|
{
|
|
dval *= (double)(crec->dbw_gridRect.r_ytop
|
|
- crec->dbw_gridRect.r_ybot);
|
|
if (!is_relative)
|
|
dval += (double)crec->dbw_gridRect.r_ybot;
|
|
}
|
|
return round(dval);
|
|
}
|
|
else
|
|
{
|
|
/* natural units referred to the current cifoutput style */
|
|
if (*(endptr + 1) == 'm')
|
|
{
|
|
switch (*endptr)
|
|
{
|
|
case 'n':
|
|
mscale = 1;
|
|
break;
|
|
case 'u':
|
|
mscale = 1000;
|
|
break;
|
|
case 'm':
|
|
mscale = 1000000;
|
|
break;
|
|
case 'c':
|
|
mscale = 10000000;
|
|
break;
|
|
default:
|
|
TxError("Unknown metric prefix \"%cm\"; assuming internal units\n",
|
|
*endptr);
|
|
return round(dval);
|
|
}
|
|
}
|
|
else if (!strcmp(endptr, "u"))
|
|
/* Maybe "u" is too ambiguous but it is very commonly used as
|
|
* an abbreviation for "micron".
|
|
*/
|
|
mscale = 1000;
|
|
else if (!strncmp(endptr, "micron", 6))
|
|
mscale = 1000;
|
|
else if (!strncmp(endptr, "centimicron", 11) || !strcmp(endptr, "cu"))
|
|
mscale = 10;
|
|
else if (!isspace(*endptr))
|
|
{
|
|
TxError("Unknown coordinate type \"%s\"; assuming internal units\n",
|
|
endptr);
|
|
return round(dval);
|
|
}
|
|
}
|
|
if (!isspace(*endptr))
|
|
dval /= CIFGetOutputScale(mscale);
|
|
return round(dval);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cmdParseCoord ---
|
|
*
|
|
* This is the "normal" usage, calling cmdScaleCoord at a scale of 1.
|
|
* This routine should be used in all circumstances where the result
|
|
* is expected in internal units.
|
|
*
|
|
* Results:
|
|
* Integer representing the given coordinate in internal units
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
cmdParseCoord(w, arg, is_relative, is_x)
|
|
MagWindow *w;
|
|
char *arg;
|
|
bool is_relative, is_x;
|
|
{
|
|
return cmdScaleCoord(w, arg, is_relative, is_x, 1);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CmdInit --
|
|
*
|
|
* Initialization for the commands module.
|
|
* All we do now is set up the TileTypeBitMasks CmdYMLabel, CmdYMCell
|
|
* and CmdYMAllButSpace.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
CmdInit()
|
|
{
|
|
TTMaskZero(&CmdYMLabel);
|
|
TTMaskSetType(&CmdYMLabel, L_LABEL);
|
|
|
|
TTMaskZero(&CmdYMCell);
|
|
TTMaskSetType(&CmdYMCell, L_CELL);
|
|
|
|
CmdYMAllButSpace = DBAllButSpaceBits;
|
|
TTMaskClearType(&CmdYMAllButSpace, L_CELL);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cmdFlushCell --
|
|
*
|
|
* Throw away all changes made within Magic to the specified cell,
|
|
* and re-read it from disk. If no cell is specified, the default
|
|
* is the current edit cell.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* THIS IS NOT UNDO-ABLE!
|
|
* Modifies the specified CellDef, but marks it as being unmodified.
|
|
* All parents of the CellDef are re-DRC'ed over both the old and
|
|
* new areas of the cell.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
cmdFlushCell(def, force_deref)
|
|
CellDef *def;
|
|
bool force_deref;
|
|
{
|
|
CellUse *parentUse;
|
|
|
|
if (def == NULL) return;
|
|
|
|
/* Disallow flushing a cell that contains the edit cell as a child */
|
|
if (EditCellUse && (EditCellUse->cu_parent == def))
|
|
{
|
|
TxError("Cannot flush cell whose subcell is being edited.\n");
|
|
TxError("%s not flushed\n", def->cd_name);
|
|
return;
|
|
}
|
|
|
|
UndoFlush();
|
|
|
|
if (force_deref)
|
|
{
|
|
/* Force dereferencing */
|
|
def->cd_flags |= CDDEREFERENCE;
|
|
freeMagic(def->cd_file);
|
|
def->cd_file = NULL;
|
|
}
|
|
|
|
DBWAreaChanged(def, &def->cd_bbox, DBW_ALLWINDOWS,
|
|
(TileTypeBitMask *) NULL);
|
|
for (parentUse = def->cd_parents; parentUse != NULL;
|
|
parentUse = parentUse->cu_nextuse)
|
|
{
|
|
if (parentUse->cu_parent == NULL) continue;
|
|
DRCCheckThis(parentUse->cu_parent, TT_CHECKSUBCELL,
|
|
&parentUse->cu_bbox);
|
|
}
|
|
DBCellClearDef(def);
|
|
DBCellClearAvail(def);
|
|
(void) DBCellRead(def, TRUE, TRUE, NULL);
|
|
DBCellSetAvail(def);
|
|
DBReComputeBbox(def);
|
|
DBCellSetModified(def, FALSE);
|
|
DBWAreaChanged(def, &def->cd_bbox, DBW_ALLWINDOWS,
|
|
&DBAllButSpaceBits);
|
|
for (parentUse = def->cd_parents; parentUse != NULL;
|
|
parentUse = parentUse->cu_nextuse)
|
|
{
|
|
if (parentUse->cu_parent == NULL) continue;
|
|
DRCCheckThis(parentUse->cu_parent, TT_CHECKSUBCELL,
|
|
&parentUse->cu_bbox);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CmdParseLayers --
|
|
*
|
|
* Convert a string specifying a collection of layers into a TileTypeBitMask
|
|
* representing the layers specified.
|
|
*
|
|
* A special layer, '$', refers to all tile types underneath the point
|
|
* tool, except for the DRC "CHECKxxx" types.
|
|
*
|
|
* The layer '*' refers to all tile types except for "check-this" and
|
|
* the label and cell pseudo-types.
|
|
*
|
|
* Results:
|
|
* TRUE on success, FALSE if any layers are unrecognized.
|
|
*
|
|
* Side effects:
|
|
* Prints an error message if any layers are unrecognized.
|
|
* Sets bits in 'mask' according to layers in layer specification.
|
|
* Leaves 'mask' set to 0 if any layers are unrecognized.
|
|
*
|
|
* Eventually, this routine should return a "minimal" TileTypeBitMask,
|
|
* ie, one with the minimum number of bits set consistent with the
|
|
* string supplied it.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
CmdParseLayers(s, mask)
|
|
char *s;
|
|
TileTypeBitMask *mask;
|
|
{
|
|
TileTypeBitMask newmask, tempmask;
|
|
char *dp, c;
|
|
char name[50];
|
|
TileType type;
|
|
Rect rootRect;
|
|
MagWindow *window;
|
|
DBWclientRec *crec;
|
|
bool adding = TRUE;
|
|
int which, i;
|
|
#define LN_CELL 0
|
|
#define LN_LABELS 1
|
|
#define LN_ALL 2
|
|
#define LN_DOLLAR 3
|
|
#define LN_ERRORS 4
|
|
#define LN_CONNECT 5
|
|
static struct
|
|
{
|
|
char *layer_name;
|
|
int layer_value;
|
|
}
|
|
special[] =
|
|
{
|
|
"$", LN_DOLLAR,
|
|
"*", LN_ALL,
|
|
"errors", LN_ERRORS,
|
|
"labels", LN_LABELS,
|
|
"subcell", LN_CELL,
|
|
"connect", LN_CONNECT,
|
|
0,
|
|
};
|
|
|
|
|
|
TTMaskZero(mask);
|
|
while (c = *s++)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '-':
|
|
adding = FALSE;
|
|
continue;
|
|
case '+':
|
|
adding = TRUE;
|
|
continue;
|
|
case ',':
|
|
case ' ':
|
|
continue;
|
|
}
|
|
|
|
dp = name; *dp++ = c;
|
|
while (*s && *s != ',' && *s != '+' && *s != '-' && *s != ' ')
|
|
*dp++ = *s++;
|
|
*dp = '\0';
|
|
if (name[0] == '\0')
|
|
continue;
|
|
|
|
TTMaskZero(&newmask);
|
|
|
|
type = DBTechNameTypes(name, &newmask);
|
|
if (type == -2)
|
|
{
|
|
which = LookupStruct(name, (LookupTable *) special, sizeof special[0]);
|
|
if (which >= 0)
|
|
{
|
|
switch (special[which].layer_value)
|
|
{
|
|
case LN_LABELS:
|
|
TTMaskSetType(&newmask, L_LABEL);
|
|
break;
|
|
case LN_CELL:
|
|
TTMaskSetType(&newmask, L_CELL);
|
|
break;
|
|
/*
|
|
* All layers currently beneath the point tool.
|
|
* Currently, neither labels nor cells are ever included
|
|
* in this.
|
|
*/
|
|
case LN_DOLLAR:
|
|
window = CmdGetRootPoint((Point *) NULL, &rootRect);
|
|
if ((window == (MagWindow *) NULL)
|
|
|| (window->w_client != DBWclientID))
|
|
return (FALSE);
|
|
crec = (DBWclientRec *) window->w_clientData;
|
|
DBSeeTypesAll(((CellUse *)window->w_surfaceID),
|
|
&rootRect, crec->dbw_bitmask, &newmask);
|
|
TTMaskAndMask(&newmask, &crec->dbw_visibleLayers);
|
|
tempmask = DBAllButSpaceAndDRCBits;
|
|
TTMaskSetType(&tempmask, TT_SPACE);
|
|
TTMaskAndMask(&newmask, &tempmask);
|
|
break;
|
|
/*
|
|
* Everything but labels and subcells
|
|
*/
|
|
case LN_ALL:
|
|
newmask = DBAllButSpaceAndDRCBits;
|
|
TTMaskClearType(&newmask, L_LABEL);
|
|
TTMaskClearType(&newmask, L_CELL);
|
|
break;
|
|
/*
|
|
* All DRC error layers.
|
|
*/
|
|
case LN_ERRORS:
|
|
TTMaskSetType(&newmask, TT_ERROR_P);
|
|
TTMaskSetType(&newmask, TT_ERROR_S);
|
|
TTMaskSetType(&newmask, TT_ERROR_PS);
|
|
break;
|
|
/*
|
|
* Add in all layers connected to layers already parsed
|
|
*/
|
|
case LN_CONNECT:
|
|
for (type = TT_TECHDEPBASE; type < DBNumTypes; type++)
|
|
if (TTMaskHasType(mask, type))
|
|
{
|
|
TileType ttype;
|
|
for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
|
|
if (DBConnectsTo(type, ttype))
|
|
TTMaskSetType(&newmask, ttype);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TxError("Unrecognized layer: %s\n", name);
|
|
printTypes:
|
|
DBTechPrintTypes(&DBAllButSpaceAndDRCBits, FALSE);
|
|
for (i = 0; ; i++)
|
|
{
|
|
if (special[i].layer_name == NULL) break;
|
|
TxError(" %s\n", special[i].layer_name);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else if (type == -1)
|
|
{
|
|
TxError("Ambiguous layer: %s\n", name);
|
|
goto printTypes;
|
|
}
|
|
|
|
if (adding)
|
|
{
|
|
TTMaskSetMask(mask, &newmask);
|
|
}
|
|
else
|
|
{
|
|
TTMaskClearMask(mask, &newmask);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cmdMaskToType --
|
|
*
|
|
* Convert a TileTypeBitMask into a TileType.
|
|
*
|
|
* Results:
|
|
* Returns -1 if more than one type bit is set in the TileTypeBitMask;
|
|
* otherwise, returns the TileType of the bit set.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
TileType
|
|
cmdMaskToType(mask)
|
|
TileTypeBitMask *mask;
|
|
{
|
|
TileType type, t;
|
|
|
|
type = -1;
|
|
for (t = TT_SELECTBASE; t < DBNumTypes; t++)
|
|
{
|
|
if (TTMaskHasType(mask, t))
|
|
{
|
|
if (type >= 0)
|
|
return (-1);
|
|
type = t;
|
|
}
|
|
}
|
|
|
|
if (type < 0)
|
|
return (TT_SPACE);
|
|
return (type);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cmdSaveCell --
|
|
*
|
|
* Save a given cell out to disk.
|
|
* If a filename is given, the cell is written out to that file;
|
|
* otherwise, the cell is written out to the file stored with the
|
|
* cellDef, or to a newly created file of the same name as the
|
|
* cellDef. If there is no name associated with the cell, the
|
|
* save is disallowed.
|
|
*
|
|
* The name of the cell is set to the filename, if it is specified.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Writes the cell out to a disk file.
|
|
* Clears the modified bit in the cd_flags.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
cmdSaveCell(cellDef, newName, noninteractive, tryRename)
|
|
CellDef *cellDef; /* Pointer to def of cell to be saved */
|
|
char *newName; /* Pointer to name of file in which cell is to be
|
|
* saved. May be NULL, in which case the name from
|
|
* the CellDef is taken.
|
|
*/
|
|
bool noninteractive;/* If true, try hard but don't ask the user
|
|
* questions.
|
|
*/
|
|
bool tryRename; /* We should rename the cell to the name of the
|
|
* place where it was saved.
|
|
*/
|
|
{
|
|
char *fileName = newName;
|
|
|
|
/* Eliminate the phony labels added for use by rsim */
|
|
#ifndef NO_SIM_MODULE
|
|
SimEraseLabels();
|
|
#endif
|
|
|
|
/*
|
|
* Whenever the "unnamed" cell is saved, the name of the
|
|
* cell changes to the name of the file in which it was
|
|
* saved.
|
|
*/
|
|
|
|
if (strcmp(cellDef->cd_name, UNNAMED) == 0)
|
|
{
|
|
if (newName == NULL)
|
|
TxPrintf("Must specify name for cell %s.\n", UNNAMED);
|
|
fileName = cmdCheckNewName(cellDef, newName, TRUE, noninteractive);
|
|
if (fileName == NULL) return;
|
|
}
|
|
else if (newName != NULL)
|
|
{
|
|
fileName = cmdCheckNewName(cellDef, newName, TRUE, noninteractive);
|
|
if (fileName == NULL) return;
|
|
}
|
|
else
|
|
{
|
|
if (cellDef->cd_file == NULL)
|
|
{
|
|
fileName = cmdCheckNewName(cellDef, cellDef->cd_name,
|
|
TRUE, noninteractive);
|
|
if (fileName == NULL) return;
|
|
}
|
|
}
|
|
|
|
DBUpdateStamps(cellDef);
|
|
if (!DBCellWrite(cellDef, fileName))
|
|
{
|
|
TxError("Could not write file. Cell not written.\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!tryRename || (fileName == NULL) || (strcmp(cellDef->cd_name, fileName) == 0))
|
|
goto cleanup;
|
|
|
|
/* Rename the cell */
|
|
if (!DBCellRenameDef(cellDef, fileName))
|
|
{
|
|
/* This should never happen */
|
|
TxError("Magic error: there is already a cell named \"%s\"\n",
|
|
fileName);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (EditCellUse && (cellDef == EditCellUse->cu_def))
|
|
{
|
|
/*
|
|
* The cell is the edit cell.
|
|
* All windows with compatible roots should show
|
|
* a caption of "root EDITING edit"
|
|
*/
|
|
CmdSetWindCaption(EditCellUse, EditRootDef);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The cell is not the edit cell.
|
|
* We want to find all windows for which this is
|
|
* the root cell and update their captions.
|
|
*/
|
|
(void) WindSearch(DBWclientID, (ClientData) NULL, (Rect *) NULL,
|
|
cmdSaveWindSet, (ClientData) cellDef);
|
|
}
|
|
|
|
cleanup:
|
|
if ((fileName != newName) && (fileName != cellDef->cd_name))
|
|
freeMagic(fileName);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cmdCheckNewName --
|
|
*
|
|
* Get the name of the file in which the argument CellDef is to
|
|
* be saved, if a name was not already provided.
|
|
* If the name of the file is different from the name of the cell,
|
|
* check to make sure that the file doesn't already exist.
|
|
* If the CellDef is to be renamed after saving it, check to make
|
|
* sure that no cell already exists by the new name.
|
|
*
|
|
* Results:
|
|
* Returns a pointer to a string holding the filename in which
|
|
* the cell is to be saved, or NULL if the save should be aborted.
|
|
*
|
|
* Side effects:
|
|
* May prompt the user for a new file name if one is required.
|
|
* If the filename returned was one typed by the user in response to
|
|
* this prompt, it overwrites the previous such filename returned,
|
|
* as it is stored in a static array.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
char *
|
|
cmdCheckNewName(def, newName, tryRename, noninteractive)
|
|
CellDef *def;
|
|
char *newName;
|
|
bool tryRename;
|
|
bool noninteractive;
|
|
{
|
|
static char *yesno[] = { "no", "yes", 0 };
|
|
char *filename;
|
|
char *prompt;
|
|
char *returnname;
|
|
int code;
|
|
FILE *f;
|
|
|
|
returnname = newName;
|
|
|
|
again:
|
|
if (returnname == NULL)
|
|
{
|
|
if (noninteractive) {
|
|
TxError("Can't write file named '%s'\n", def->cd_name);
|
|
return NULL;
|
|
};
|
|
TxPrintf("File for cell %s: [hit return to abort save] ", def->cd_name);
|
|
returnname = (char *)mallocMagic(1024 * sizeof(char));
|
|
if (TxGetLine(returnname, sizeof returnname) == NULL || returnname[0] == '\0')
|
|
{
|
|
TxPrintf("Cell not saved.\n");
|
|
freeMagic(returnname);
|
|
return ((char *) NULL);
|
|
}
|
|
if (CmdIllegalChars(returnname, "[],", "Cell name"))
|
|
{
|
|
freeMagic(returnname);
|
|
goto again;
|
|
}
|
|
}
|
|
|
|
/* Remove any ".mag" file extension from the name */
|
|
if (strlen(returnname) > 4)
|
|
if (!strcmp(returnname + strlen(returnname) - 4, ".mag"))
|
|
*(returnname + strlen(returnname) - 4) = '\0';
|
|
|
|
if (strcmp(returnname, def->cd_name) != 0)
|
|
{
|
|
if (f = PaOpen(returnname, "r", DBSuffix, ".", (char *) NULL, &filename))
|
|
{
|
|
(void) fclose(f);
|
|
if (noninteractive) {
|
|
TxError("Overwriting file '%s' with cell '%s'\n", filename,
|
|
def->cd_name);
|
|
}
|
|
else {
|
|
prompt = TxPrintString("File %s already exists.\n"
|
|
" Overwrite it with %s? ", filename, def->cd_name);
|
|
code = TxDialog(prompt, yesno, 0);
|
|
|
|
if (code == 0)
|
|
{
|
|
/* No -- don't overwrite */
|
|
if (returnname != newName) freeMagic(returnname);
|
|
returnname = NULL;
|
|
goto again;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tryRename && DBCellLookDef(returnname) != NULL)
|
|
{
|
|
TxError("Can't rename cell '%s' to '%s' because that cell already exists.\n",
|
|
def->cd_name, returnname);
|
|
if (returnname != newName) freeMagic(returnname);
|
|
if (noninteractive) return NULL;
|
|
returnname = NULL;
|
|
goto again;
|
|
}
|
|
}
|
|
return (returnname);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* nameEllipsis ---
|
|
*
|
|
* Truncate a string an append an ellipsis ("...") to the end if the string
|
|
* will overflow a fixed array length.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static char *
|
|
nameEllipsis(name, maxlen, prefix)
|
|
char *name;
|
|
int maxlen;
|
|
char **prefix;
|
|
{
|
|
int l = strlen(name);
|
|
|
|
if (l < maxlen)
|
|
{
|
|
*prefix = "";
|
|
return name;
|
|
}
|
|
else
|
|
{
|
|
*prefix = "...";
|
|
return &name[l - maxlen + 3];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cmdSaveWindSet --
|
|
*
|
|
* Filter function for cmdSaveCell() above.
|
|
* Called by WindSearch() with each window.
|
|
*
|
|
* The idea is to change only those captions in windows whose root
|
|
* uses are instances of the def 'def'. Sets the caption of each such
|
|
* window to:
|
|
*
|
|
* def [NOT BEING EDITED]
|
|
*
|
|
* Results:
|
|
* Always 0 to keep the search going.
|
|
*
|
|
* Side effects:
|
|
* Modifies captions and clientData for the window
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
cmdSaveWindSet(window, def)
|
|
MagWindow *window;
|
|
CellDef *def;
|
|
{
|
|
char caption[200];
|
|
CellDef *rootDef;
|
|
char *name, *name_pfx;
|
|
|
|
rootDef = ((CellUse *) window->w_surfaceID)->cu_def;
|
|
if (rootDef != def)
|
|
return 0;
|
|
|
|
name = nameEllipsis(def->cd_name, 175, &name_pfx);
|
|
(void) snprintf(caption, sizeof(caption), "%s%s [NOT BEING EDITED]", name_pfx, name);
|
|
(void) StrDup(&window->w_iconname, def->cd_name);
|
|
WindCaption(window, caption);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CmdSetWindCaption --
|
|
*
|
|
* Update the captions of all windows to reflect a new EditCell.
|
|
* The caption of each window having the same root cell def as the
|
|
* one in which the Edit Cell was selected is set to show that the
|
|
* window is subediting the new edit cell. The captions in all
|
|
* other windows show that these windows are not subediting the
|
|
* edit cell.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Modifies captions and clientData for each window.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
* The following are used to pass information down to the filter
|
|
* function applied by WindSearch(), since it is not intended that
|
|
* CmdSetWindCaption() be re-entrant.
|
|
*/
|
|
|
|
static CellDef *newEditDef; /* Pointer to new edit cell def */
|
|
static CellDef *newRootDef; /* Pointer to root def of window in which
|
|
* new edit cell was selected. This is
|
|
* used to determine whether the edit cell
|
|
* is being edited in a window or not.
|
|
*/
|
|
|
|
void
|
|
CmdSetWindCaption(newEditUse, rootDef)
|
|
CellUse *newEditUse; /* Pointer to new edit cell use */
|
|
CellDef *rootDef; /* Root cell def of the window in which the
|
|
* edit cell was selected.
|
|
*/
|
|
{
|
|
int cmdWindSet();
|
|
|
|
newEditDef = (newEditUse) ? newEditUse->cu_def : NULL;
|
|
newRootDef = rootDef;
|
|
(void) WindSearch(DBWclientID, (ClientData) NULL, (Rect *) NULL,
|
|
cmdWindSet, (ClientData) 0);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* cmdWindSet --
|
|
*
|
|
* Filter function for CmdSetWindCaption() above.
|
|
* Called by WindSearch() with each window.
|
|
*
|
|
* If the window is compatible with the new edit cell, sets the caption
|
|
* for that window to be
|
|
*
|
|
* RootDef EDITING EditDef
|
|
*
|
|
* Otherwise, sets the caption to be
|
|
*
|
|
* RootDef [NOT BEING EDITED]
|
|
*
|
|
* Results:
|
|
* Always 0 to keep the function going.
|
|
*
|
|
* Side effects:
|
|
* Modifies captions and clientData for the window
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
cmdWindSet(window)
|
|
MagWindow *window;
|
|
{
|
|
char caption[200];
|
|
CellDef *wDef;
|
|
char *name[2], *name_pfx[2];
|
|
|
|
wDef = ((CellUse *) window->w_surfaceID)->cu_def;
|
|
|
|
|
|
|
|
if (wDef != newRootDef) {
|
|
name[0] = nameEllipsis(wDef->cd_name, 175, &name_pfx[0]);
|
|
(void) snprintf(caption, sizeof(caption), "%s%s [NOT BEING EDITED]",
|
|
name_pfx[0], name[0]);
|
|
} else {
|
|
name[0] = nameEllipsis(wDef->cd_name, 90, &name_pfx[0]);
|
|
name[1] = nameEllipsis(newEditDef->cd_name, 90, &name_pfx[1]);
|
|
(void) snprintf(caption, sizeof(caption), "%s%s EDITING %s%s",
|
|
name_pfx[0], name[0], name_pfx[1], name[1]);
|
|
|
|
#ifdef SCHEME_INTERPRETER
|
|
/* Add a binding to scheme variable "edit-cell" */
|
|
LispSetEdit (newEditDef->cd_name);
|
|
#endif
|
|
}
|
|
|
|
(void) StrDup(&window->w_iconname, wDef->cd_name);
|
|
WindCaption(window, caption);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CmdGetRootPoint --
|
|
*
|
|
* Get the window containing the point tool, and return (in root cell
|
|
* coordinates for that window) the coordinates of the point, and of
|
|
* a minimum-grid-size rectangle enclosing the point.
|
|
*
|
|
* Results:
|
|
* Pointer to window containing the point tool, or NULL if the
|
|
* point tool is not present.
|
|
*
|
|
* Side effects:
|
|
* Sets *point to be the coordinates of the point tool in root
|
|
* coordinates, and *rect to be the minimum-grid-size enclosing
|
|
* rectangle.
|
|
*
|
|
* Prints an error message if the point is not found.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
MagWindow *
|
|
CmdGetRootPoint(point, rect)
|
|
Point *point;
|
|
Rect *rect;
|
|
{
|
|
MagWindow *window;
|
|
|
|
window = ToolGetPoint(point, rect);
|
|
if (window == (MagWindow *) NULL)
|
|
TxError("Crosshair not in a valid window for this command\n");
|
|
|
|
return (window);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CmdGetEditPoint --
|
|
*
|
|
* Get the window containing the point tool, and return (in edit cell
|
|
* coordinates for that window) the coordinates of the point, and of
|
|
* a minimum-grid-size rectangle enclosing the point.
|
|
*
|
|
* Results:
|
|
* Pointer to window containing the point tool, or NULL if the
|
|
* point tool is not present.
|
|
*
|
|
* Side effects:
|
|
* Sets *point to be the coordinates of the point tool in edit
|
|
* coordinates, and *rect to be the minimum-grid-size enclosing
|
|
* rectangle.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
MagWindow *
|
|
CmdGetEditPoint(point, rect)
|
|
Point *point;
|
|
Rect *rect;
|
|
{
|
|
MagWindow *window;
|
|
Rect rootRect;
|
|
Point rootPoint;
|
|
|
|
window = CmdGetRootPoint(&rootPoint, &rootRect);
|
|
if (window != (MagWindow *) NULL)
|
|
{
|
|
GeoTransRect(&RootToEditTransform, &rootRect, rect);
|
|
GeoTransPoint(&RootToEditTransform, &rootPoint, point);
|
|
}
|
|
|
|
return (window);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CmdWarnWrite --
|
|
*
|
|
* Check to see if there are modified and unwritten cells and ask the
|
|
* user whether he wants to stay in magic or lose all these cells.
|
|
*
|
|
* Results:
|
|
* TRUE if the user wishes to continue anyway without writing the
|
|
* modified cells out to disk, FALSE if not.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
CmdWarnWrite()
|
|
{
|
|
int count, code;
|
|
int cmdWarnWriteFunc();
|
|
static char *yesno[] = { "no", "yes", 0 };
|
|
char *prompt;
|
|
|
|
count = 0;
|
|
(void) DBCellSrDefs(CDMODIFIED|CDBOXESCHANGED|CDSTAMPSCHANGED,
|
|
cmdWarnWriteFunc, (ClientData) &count);
|
|
if (count == 0)
|
|
return TRUE;
|
|
|
|
prompt = TxPrintString("%d Magic cell%s been modified.\n Do you"
|
|
" want to exit magic and lose %s? ", count,
|
|
count == 1 ? " has" : "s have",
|
|
count == 1 ? "it" : "them");
|
|
code = TxDialog(prompt, yesno, 0);
|
|
return (code) ? TRUE : FALSE;
|
|
}
|
|
|
|
int
|
|
cmdWarnWriteFunc(cellDef, pcount)
|
|
CellDef *cellDef;
|
|
int *pcount;
|
|
{
|
|
if ((cellDef->cd_flags & CDINTERNAL) == 0)
|
|
(*pcount)++;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* cmdExpandOneLevel --
|
|
*
|
|
* Expand (unexpand) a cell, and unexpand all of its children. This is
|
|
* called by commands such as getcell, expand current cell, and load.
|
|
* Don't bother to unexpand children if we are unexpanding this cell.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
cmdExpandOneLevel(cu, bitmask, expand)
|
|
CellUse *cu;
|
|
int bitmask;
|
|
bool expand;
|
|
{
|
|
extern int cmdExpand1func();
|
|
|
|
/* first, expand this cell use */
|
|
DBExpand(cu, bitmask, expand);
|
|
|
|
/* now, unexpand its direct children (ONE LEVEL ONLY) */
|
|
if (expand)
|
|
(void) DBCellEnum(cu->cu_def, cmdExpand1func, (ClientData) bitmask);
|
|
}
|
|
|
|
int
|
|
cmdExpand1func(cu, bitmask)
|
|
CellUse *cu;
|
|
ClientData bitmask;
|
|
{
|
|
DBExpand(cu, (int) bitmask, FALSE);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CmdGetSelectedCell --
|
|
*
|
|
* This procedure returns a pointer to the selected cell.
|
|
*
|
|
* Results:
|
|
* The return value is a pointer to the selected cell. If more
|
|
* than one cell is selected, the upper-leftmost cell is returned.
|
|
* If no cell is selected, NULL is returned.
|
|
*
|
|
* Side effects:
|
|
* If pTrans isn't NULL, the area it points to is modified to hold
|
|
* the transform from coords of the selected cell to root coords.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Transform *cmdSelTrans; /* Shared between CmdGetSelectedCell and
|
|
* cmdGetCellFunc.
|
|
*/
|
|
|
|
CellUse *
|
|
CmdGetSelectedCell(pTrans)
|
|
Transform *pTrans; /* If non-NULL, transform from selected
|
|
* cell to root coords is stored here.
|
|
*/
|
|
{
|
|
CellUse *result = NULL;
|
|
int cmdGetSelFunc(); /* Forward declaration. */
|
|
|
|
cmdSelTrans = pTrans;
|
|
(void) SelEnumCells(FALSE, (bool *) NULL, (SearchContext *) NULL,
|
|
cmdGetSelFunc, (ClientData) &result);
|
|
return result;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
cmdGetSelFunc(selUse, realUse, transform, pResult)
|
|
CellUse *selUse; /* Not used. */
|
|
CellUse *realUse; /* The first selected use. */
|
|
Transform *transform; /* Transform from coords of realUse to root. */
|
|
CellUse **pResult; /* Store realUse here. */
|
|
{
|
|
*pResult = realUse;
|
|
if (cmdSelTrans != NULL)
|
|
*cmdSelTrans = *transform;
|
|
return 1; /* Skip any other selected cells. */
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CmdIllegalChars --
|
|
*
|
|
* Checks a string for any of a number of illegal characters.
|
|
* If any is found, it's printed in an error message.
|
|
*
|
|
* Results:
|
|
* TRUE is returned if any of the characters in "illegal" is
|
|
* also in "string", or if "string" contains any control or
|
|
* non-ASCII characters. Otherwise, FALSE is returned.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
CmdIllegalChars(string, illegal, msg)
|
|
char *string; /* String to check for illegal chars. */
|
|
char *illegal; /* String containing illegal chars. */
|
|
char *msg; /* String identifying what string is
|
|
* supposed to represent, for ease in
|
|
* printing error messages.
|
|
*/
|
|
{
|
|
char *p, *bad;
|
|
|
|
for (p = string; *p != 0; p++)
|
|
{
|
|
if (!isascii(*p)) goto error;
|
|
if (iscntrl(*p)) goto error;
|
|
for (bad = illegal; *bad != 0; bad++)
|
|
{
|
|
if (*bad == *p) goto error;
|
|
}
|
|
continue;
|
|
|
|
error:
|
|
if (!isascii(*p) || iscntrl(*p))
|
|
{
|
|
TxError("%s contains illegal control character 0x%x\n",
|
|
msg, *p);
|
|
}
|
|
else TxError("%s contains illegal character \"%c\"\n",
|
|
msg, *p);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|