magic/commands/CmdSubrs.c

1376 lines
35 KiB
C
Raw Normal View History

/*
* 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
2024-10-04 13:09:00 +02:00
static const 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(CellDef *def, char *newName, bool tryRename, bool noninteractive);
extern int cmdSaveWindSet(MagWindow *window, CellDef *def);
extern void CmdSetWindCaption(CellUse *newEditUse, CellDef *rootDef);
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.
* Traditional (backwards-compatible) behavior: 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.
* Current behavior: Use of the "units" command to set the units to
* any value other than "default" causes cmdScaleCoord() to parse any
* units provided without an identifying suffix as the units indicted by
* the "units" command. Once the "units" command has been issued, the
* values are dependent on DBWUnits and not on DBWSnapToGrid.
*
* Additional behavior from magic version 8.3.596: A single command
* option can use simple expressions using '+', '-', '*', and '/'. These
* can be passed as a single token, without spaces, or within a string
* token deliniated by quotes or braces, per usual Tcl syntax. Unlike
* the Tcl "expr" command, this can solve arithmetic expressions of
* suffixed values, evaluated independently such that different suffixes
* may be used (e.g., "1g + 3um" meaning 1 grid pitch plus 3 microns).
*
* 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
*
* ----------------------------------------------------------------------------
*/
#define PARSEOP_NONE 0
#define PARSEOP_ADD 1
#define PARSEOP_SUB 2
#define PARSEOP_MUL 3
#define PARSEOP_DIV 4
#define PARSEOP_END 5
int
cmdScaleCoord(
MagWindow *w,
char *arg,
bool is_relative,
bool is_x,
int scale)
{
char *endptr;
double dval = 0;
int mscale = 1, curunits;
int retval, curval, parseop;
DBWclientRec *crec;
if (*arg == '{' || *arg == '"') arg++;
while (isspace(*arg) && (*arg != '\0')) arg++;
parseop = PARSEOP_NONE;
retval = 0;
while (*arg != '\0')
{
dval = strtod(arg, &endptr);
dval *= (double)scale;
mscale = -1;
if (endptr == arg)
{
/* strtod() error condition */
TxError("Coordinate value cannot be parsed: assuming 0\n");
curval = 0;
break;
}
/* Original behavior was to accept un-suffixed values according to the
* "snap" setting. This behavior remains in effect until the "units"
* command is used, in which case units follow the selected units
* value indepedendently of the snap setting.
*
* Updated 12/24/2026 to handle space-separated values (in which
* *endptr may be a space as well as NULL).
*/
if (DBWUnits == DBW_UNITS_DEFAULT)
curunits = DBWSnapToGrid;
else
curunits = DBWUnits & DBW_UNITS_TYPE_MASK;
if ((*endptr == 'l')
|| (((*endptr == '\0') || isspace(*endptr))
&& (curunits == DBW_UNITS_LAMBDA)))
{
/* lambda or default units */
dval *= (double)DBLambda[1];
dval /= (double)DBLambda[0];
}
else if ((*endptr == 'i')
|| (((*endptr == '\0') || isspace(*endptr))
&& (curunits == DBW_UNITS_INTERNAL)))
{
/* internal units */
}
else if ((*endptr == 'g')
|| (((*endptr == '\0') || isspace(*endptr))
&& (curunits == DBW_UNITS_USER)))
{
/* grid units */
if (w == (MagWindow *)NULL)
{
windCheckOnlyWindow(&w, DBWclientID);
if (w == (MagWindow *)NULL)
{
curval = round(dval); /* Default, if window is unknown */
break;
}
}
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;
}
}
else if (((*endptr == '\0') || isspace(*endptr))
&& (curunits == DBW_UNITS_MICRONS))
{
mscale = 1000;
}
else if (*endptr != '\0')
{
/* 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);
mscale = -1;
}
}
else if ((*endptr == 'u') && !isalnum(*(endptr + 1)))
/* 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) && (*endptr != '+') && (*endptr != '-') &&
(*endptr != '*') && (*endptr != '/'))
{
TxError("Unknown coordinate type at \"%s\"; assuming internal units\n",
endptr);
mscale = -1;
}
}
if (mscale != -1)
dval /= CIFGetOutputScale(mscale);
curval = round(dval);
switch (parseop)
{
case PARSEOP_NONE:
retval = curval;
break;
case PARSEOP_ADD:
retval += curval;
break;
case PARSEOP_SUB:
retval -= curval;
break;
case PARSEOP_MUL:
retval *= curval;
break;
case PARSEOP_DIV:
retval /= curval;
break;
}
parseop = PARSEOP_NONE;
while (*endptr != '\0')
{
switch (*endptr)
{
case '}':
case '"':
parseop = PARSEOP_END;
break;
case '+':
parseop = PARSEOP_ADD;
endptr++;
break;
case '-':
parseop = PARSEOP_SUB;
endptr++;
break;
case '*':
parseop = PARSEOP_MUL;
endptr++;
break;
case '/':
parseop = PARSEOP_DIV;
endptr++;
break;
case ' ':
case '\t':
endptr++;
break;
default:
/* Should this flag an error? */
return retval;
}
if (parseop != PARSEOP_NONE) break;
}
arg = endptr;
while (isspace(*arg) && (*arg != '\0')) arg++;
}
return retval;
}
/*
* ----------------------------------------------------------------------------
*
* 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(
MagWindow *w,
char *arg,
bool is_relative,
bool 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(void)
{
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(
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(
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
{
const char *layer_name;
int layer_value;
}
const special[] =
{
{"$", LN_DOLLAR},
{"*", LN_ALL},
{"errors", LN_ERRORS},
{"labels", LN_LABELS},
{"subcell", LN_CELL},
{"connect", LN_CONNECT},
{0},
};
TTMaskZero(mask);
36 x warning: suggest parentheses around assignment used as truth value bpMain.c:265:11: warning: suggest parentheses around assignment used as truth value CmdSubrs.c:413:12: warning: suggest parentheses around assignment used as truth value CmdSubrs.c:759:13: warning: suggest parentheses around assignment used as truth value CmdFI.c:1600:12: warning: suggest parentheses around assignment used as truth value DBcell.c:109:12: warning: suggest parentheses around assignment used as truth value DBcellname.c:2512:9: warning: suggest parentheses around assignment used as truth value DBcellsrch.c:95:12: warning: suggest parentheses around assignment used as truth value DBio.c:3998:9: warning: suggest parentheses around assignment used as truth value DBlabel2.c:228:9: warning: suggest parentheses around assignment used as truth value DBWelement.c:195:12: warning: suggest parentheses around assignment used as truth value DBWelement.c:197:13: warning: suggest parentheses around assignment used as truth value DBWelement.c:295:12: warning: suggest parentheses around assignment used as truth value DBWelement.c:297:13: warning: suggest parentheses around assignment used as truth value DBWelement.c:637:12: warning: suggest parentheses around assignment used as truth value DBWelement.c:639:13: warning: suggest parentheses around assignment used as truth value DBWelement.c:679:12: warning: suggest parentheses around assignment used as truth value DBWelement.c:681:13: warning: suggest parentheses around assignment used as truth value DRCtech.c:1219:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:1368:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:1471:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:1543:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:1676:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:1772:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:1919:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:2099:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:2324:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:2713:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:2830:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:2944:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:3097:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:3144:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:3181:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:3215:9: warning: suggest parentheses around assignment used as truth value DRCtech.c:3227:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:3261:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:3459:17: warning: suggest parentheses around assignment used as truth value GCC14 -Wall cleanup series [-Wparentheses]
2024-10-04 18:19:27 +02:00
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, (const 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(
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 *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, unless fileName is a .tcl file (scripted output) */
if ((strlen(fileName) <= 4) || strcmp(fileName + strlen(fileName) - 4, ".tcl"))
{
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(
CellDef *def,
char *newName,
bool tryRename,
bool noninteractive)
{
static const char * const 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)
{
36 x warning: suggest parentheses around assignment used as truth value bpMain.c:265:11: warning: suggest parentheses around assignment used as truth value CmdSubrs.c:413:12: warning: suggest parentheses around assignment used as truth value CmdSubrs.c:759:13: warning: suggest parentheses around assignment used as truth value CmdFI.c:1600:12: warning: suggest parentheses around assignment used as truth value DBcell.c:109:12: warning: suggest parentheses around assignment used as truth value DBcellname.c:2512:9: warning: suggest parentheses around assignment used as truth value DBcellsrch.c:95:12: warning: suggest parentheses around assignment used as truth value DBio.c:3998:9: warning: suggest parentheses around assignment used as truth value DBlabel2.c:228:9: warning: suggest parentheses around assignment used as truth value DBWelement.c:195:12: warning: suggest parentheses around assignment used as truth value DBWelement.c:197:13: warning: suggest parentheses around assignment used as truth value DBWelement.c:295:12: warning: suggest parentheses around assignment used as truth value DBWelement.c:297:13: warning: suggest parentheses around assignment used as truth value DBWelement.c:637:12: warning: suggest parentheses around assignment used as truth value DBWelement.c:639:13: warning: suggest parentheses around assignment used as truth value DBWelement.c:679:12: warning: suggest parentheses around assignment used as truth value DBWelement.c:681:13: warning: suggest parentheses around assignment used as truth value DRCtech.c:1219:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:1368:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:1471:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:1543:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:1676:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:1772:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:1919:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:2099:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:2324:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:2713:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:2830:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:2944:17: warning: suggest parentheses around assignment used as truth value DRCtech.c:3097:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:3144:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:3181:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:3215:9: warning: suggest parentheses around assignment used as truth value DRCtech.c:3227:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:3261:21: warning: suggest parentheses around assignment used as truth value DRCtech.c:3459:17: warning: suggest parentheses around assignment used as truth value GCC14 -Wall cleanup series [-Wparentheses]
2024-10-04 18:19:27 +02:00
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(
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(
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(
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(MagWindow *window, ClientData clientData); /* UNUSED */
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
*
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
cmdWindSet(
Add ClientData parameter to indirect-call callbacks for WASM WASM call_indirect enforces an exact type match between the caller and the callee. Many Magic callbacks had K&R-style () forward declarations and a single-argument definition, but were passed to iterators that always push a trailing ClientData argument. Native builds tolerated the mismatch via loose prototypes; WASM traps with "indirect call signature mismatch". Added the missing ClientData (or, where the concrete type is known, FindRegion *) parameter to: * calma/CalmaRead.c, calma/CalmaWrite.c, calma/CalmaWriteZ.c — calmaWriteInitFunc * cif/CIFwrite.c — cifWriteInitFunc * commands/CmdSubrs.c — cmdWindSet * database/DBtimestmp.c — dbStampFunc * dbwind/DBWelement.c — dbwElementAlways1 * dbwind/DBWfdback.c — dbwfbWindFunc * dbwind/DBWhlights.c — DBWHLRedrawWind * ext2spice/ext2hier.c — spcnodeHierVisit * extract/ExtBasic.c — extSDTileFunc, extTransPerimFunc, extAnnularTileFunc, extResistorTileFunc * extract/ExtMain.c — extDefInitFunc * extract/ExtTimes.c — extTimesInitFunc Also adjusted commands/CmdE.c and commands/CmdTZ.c: SelectExpand was being called with four arguments (the legacy surroundFlag), but its real signature has been three arguments for years (the surround mode is encoded in the expandType bit). The fourth argument was redundant (DB_EXPAND_SURROUND in arg 2 is the source of truth) and rejected by WASM. Native behavior is unchanged. The added parameters are unused in the function bodies; they exist only to satisfy the indirect-call signature.
2026-05-04 13:29:11 +02:00
MagWindow *window,
ClientData clientData) /* UNUSED */
{
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 *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 *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(void)
{
int count, code;
int cmdWarnWriteFunc(CellDef *cellDef, int *pcount);
static const char * const 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 *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(
CellUse *cu,
int bitmask,
bool expand)
{
extern int cmdExpand1func(CellUse *cu, ClientData bitmask);
/* first, expand this cell use */
DBExpand(cu, bitmask, expand ? DB_EXPAND : DB_UNEXPAND);
/* now, unexpand its direct children (ONE LEVEL ONLY) */
if (expand)
(void) DBCellEnum(cu->cu_def, cmdExpand1func, INT2CD(bitmask));
}
int
cmdExpand1func(
CellUse *cu,
ClientData bitmask)
{
DBExpand(cu, (int)CD2INT(bitmask), DB_UNEXPAND);
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(
Transform *pTrans) /* If non-NULL, transform from selected
* cell to root coords is stored here.
*/
{
CellUse *result = NULL;
int cmdGetSelFunc(CellUse *selUse, CellUse *realUse, Transform *transform, CellUse **pResult); /* Forward declaration. */
cmdSelTrans = pTrans;
(void) SelEnumCells(FALSE, (bool *) NULL, (SearchContext *) NULL,
cmdGetSelFunc, (ClientData) &result);
return result;
}
/* ARGSUSED */
int
cmdGetSelFunc(
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. */
}
/* The Open Group, Sep 2006, Austin/317 deprecated isascii(),
* Apparently it cannot be used portably in a localized application.
*/
static int
magic_isascii(int c)
{
return (c & ~0x7f) == 0;
}
/*
* ----------------------------------------------------------------------------
*
* 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(
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 (!magic_isascii(*p)) goto error;
if (iscntrl(*p)) goto error;
for (bad = illegal; *bad != 0; bad++)
{
if (*bad == *p) goto error;
}
continue;
error:
if (!magic_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;
}