mirror of
https://github.com/RTimothyEdwards/magic.git
synced 2026-08-30 18:02:32 +02:00
previous behavior that had inadvertently been changed. In recent versions, "load <absolute_path> -dereference" would incorrectly apply the dereferencing to <absolute_path> rather than just its subcells. Cleaned up the code around DBCellRead() in the process, so everything is more straightforward (although probably more could be done in that regard).
610 lines
17 KiB
C
610 lines
17 KiB
C
/*
|
|
* DBlabel2.c --
|
|
*
|
|
* Label searching primitives.
|
|
*
|
|
* *********************************************************************
|
|
* * 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/DBlabel2.c,v 1.3 2010/09/12 20:32:31 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/hash.h"
|
|
#include "database/database.h"
|
|
#include "database/databaseInt.h"
|
|
#include "utils/malloc.h"
|
|
|
|
/*
|
|
* The following structure is used to pass data between DBNearestLabel
|
|
* and its search function.
|
|
*/
|
|
struct nldata
|
|
{
|
|
int nld_distance; /* Square of distance to nearest
|
|
* label seen so far.
|
|
*/
|
|
Point *nld_point; /* Reference: find nearest label to this */
|
|
Rect *nld_labelArea; /* Fill in with area of nearest label */
|
|
char *nld_name; /* Fill in with name of nearest label */
|
|
bool nld_gotLabel; /* TRUE means some label was found */
|
|
};
|
|
|
|
/*
|
|
* The following structure is used to pass the arguments of
|
|
* DBSearchLabel down to the filter function.
|
|
*/
|
|
|
|
#define MAXLABPATHSIZE 256
|
|
|
|
typedef struct {
|
|
char *labSrPattern; /* Pattern for matching. */
|
|
int (*labSrFunc)(); /* Function to apply to each label found */
|
|
ClientData labSrArg; /* Client data of caller */
|
|
} labSrStruct;
|
|
|
|
/* Forward declarations */
|
|
|
|
extern void DBTreeFindUse();
|
|
|
|
bool dbParseArray();
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* DBSearchLabel --
|
|
*
|
|
* Search for all occurrences of a point label matching the pattern in the
|
|
* region rect in the indicated cell and all of its children. On each label
|
|
* matching the pattern found in the area, the supplied procedure is invoked.
|
|
*
|
|
* The supplied procedure should be of the form
|
|
* int
|
|
* func(scx, label, tpath, cdarg)
|
|
* SearchContext *scx;
|
|
* Label *label;
|
|
* TerminalPath *tpath;
|
|
* ClientData cdarg;
|
|
* {
|
|
* }
|
|
*
|
|
* In the above, scx is a search context specifying the cell use whose
|
|
* def was found to contain the label, and label is a pointer to the
|
|
* Label structure itself. The transform specified in scx is from
|
|
* coordinates of the def of the cell containing the label to "root"
|
|
* coordinates. Func should normally return 0. If it returns 1 then
|
|
* the search is aborted.
|
|
*
|
|
* Results:
|
|
* If the search terminates normally, 0 is returned. 1 is
|
|
* returned if the search was aborted.
|
|
*
|
|
* Side effects:
|
|
* Applies the supplied procedure to each tile containing a label
|
|
* matching the pattern.
|
|
*
|
|
* WARNING: because of the way regex(3) works, it is possible to be
|
|
* searching for at most one pattern at a time.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
DBSearchLabel(scx, mask, xMask, pattern, func, cdarg)
|
|
SearchContext *scx; /* Search context: specifies CellUse,
|
|
* transform to "root" coordinates, and
|
|
* an area to search.
|
|
*/
|
|
TileTypeBitMask *mask; /* Only search for labels on these layers */
|
|
int xMask; /* Expansion state mask for searching. Cell
|
|
* uses are only considered to be expanded
|
|
* when their expand masks have all the bits
|
|
* of xMask set.
|
|
*/
|
|
char *pattern; /* Pattern for which to search */
|
|
int (*func)(); /* Function to apply to each match */
|
|
ClientData cdarg; /* Argument to pass to function */
|
|
{
|
|
TerminalPath tpath;
|
|
int dbSrLabelFunc();
|
|
labSrStruct labsr;
|
|
char labSrStr[MAXLABPATHSIZE]; /* String buffer in which the full pathname
|
|
* of each label is assembled for handing
|
|
* to the filter function.
|
|
*/
|
|
|
|
labsr.labSrPattern = pattern;
|
|
labsr.labSrFunc = func;
|
|
labsr.labSrArg = cdarg;
|
|
tpath.tp_first = tpath.tp_next = labSrStr;
|
|
tpath.tp_last = &(labSrStr[sizeof(labSrStr) - 2]);
|
|
|
|
if (DBTreeSrLabels(scx, mask, xMask, &tpath, TF_LABEL_ATTACH,
|
|
dbSrLabelFunc, (ClientData) &labsr))
|
|
return 1;
|
|
else return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* dbSrLabelFunc --
|
|
*
|
|
* Filter procedure applied to all labels by DBTreeSrLabels(). For
|
|
* each label that matches the pattern set by DBSearchLabel(), the
|
|
* filter function (*cdarg->lsa_func)() is applied.
|
|
*
|
|
* Results:
|
|
* Always return 0 to keep the search going.
|
|
*
|
|
* Side effects:
|
|
* Applies the supplied procedure to each label matching the pattern.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
dbSrLabelFunc(scx, label, tpath, labsr)
|
|
SearchContext *scx; /* Contains pointer to use in which label
|
|
* occurred, and transform back to root
|
|
* coordinates.
|
|
*/
|
|
Label *label; /* Label itself */
|
|
TerminalPath *tpath; /* Full pathname of the terminal */
|
|
labSrStruct *labsr; /* Information passed to this routine */
|
|
{
|
|
if (Match(labsr->labSrPattern, label->lab_text))
|
|
if ((*labsr->labSrFunc)(scx, label, tpath, labsr->labSrArg))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* DBSrLabelLoc --
|
|
*
|
|
* This procedure finds the locations of all labels with a given
|
|
* hierarchical name. For each label found, a client-supplied
|
|
* search function is called. The search function has the form:
|
|
*
|
|
* int
|
|
* func(rect, name, label, cdarg)
|
|
* Rect *rect;
|
|
* char *name;
|
|
* Label *label;
|
|
* ClientData cdarg;
|
|
*
|
|
* Rect is the location of the label, in the coordinates of rootUse->cu_def,
|
|
* name is the label's hierarchical name (just the parameter passed to us),
|
|
* label is a pointer to the label, and cdarg is the client data passed in
|
|
* to us by the client. Note that there can be more than one label with the
|
|
* same name. Func should normally return 0. If it returns 1, then the
|
|
* search is aborted.
|
|
*
|
|
* Results:
|
|
* The return value is 0, unless func returned a non-zero value,
|
|
* in which case the return value is 1.
|
|
*
|
|
* Side effects:
|
|
* Whatever the search function does.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
DBSrLabelLoc(rootUse, name, func, cdarg)
|
|
CellUse *rootUse; /* Cell in which to search. */
|
|
char *name; /* A hierarchical label name consisting of zero or more
|
|
* use-ids followed by a label name (fields separated
|
|
* with slashes).
|
|
*/
|
|
int (*func)(); /* Applied to each instance of the label name */
|
|
ClientData cdarg; /* Data to pass through to (*func)() */
|
|
{
|
|
CellDef *def;
|
|
SearchContext scx;
|
|
char *cp;
|
|
Label *lab;
|
|
char csave;
|
|
Rect r;
|
|
|
|
if (cp = strrchr(name, '/'))
|
|
{
|
|
csave = *cp;
|
|
*cp = '\0';
|
|
DBTreeFindUse(name, rootUse, &scx);
|
|
*cp = csave;
|
|
if (scx.scx_use == NULL)
|
|
return 0;
|
|
cp++;
|
|
}
|
|
else
|
|
{
|
|
scx.scx_use = rootUse;
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
cp = name;
|
|
}
|
|
|
|
def = scx.scx_use->cu_def;
|
|
for (lab = def->cd_labels; lab; lab = lab->lab_next)
|
|
if (lab->lab_text[0] == *cp && strcmp(lab->lab_text, cp) == 0)
|
|
{
|
|
GeoTransRect(&scx.scx_trans, &lab->lab_rect, &r);
|
|
if ((*func)(&r, name, lab, cdarg))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* DBTreeFindUse --
|
|
*
|
|
* This procedure finds the cell use with the given hierarchical name.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Sets scx->scx_use to the cell use found, with scx->scx_trans
|
|
* and scx->scx_x, scx->scx_y also valid. If the cell was not
|
|
* found, leaves scx->scx_use set to NULL.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
DBTreeFindUse(name, use, scx)
|
|
char *name;
|
|
CellUse *use;
|
|
SearchContext *scx;
|
|
{
|
|
char *cp;
|
|
HashEntry *he;
|
|
CellDef *def;
|
|
char csave;
|
|
|
|
def = use->cu_def;
|
|
scx->scx_use = (CellUse *) NULL;
|
|
scx->scx_trans = GeoIdentityTransform;
|
|
scx->scx_x = scx->scx_y = 0;
|
|
while (*name)
|
|
{
|
|
/*
|
|
* Make sure that the cell whose children are being searched
|
|
* is read in from disk.
|
|
*/
|
|
if ((def->cd_flags & CDAVAILABLE) == 0)
|
|
(void) DBCellRead(def, TRUE, TRUE, NULL);
|
|
|
|
cp = name;
|
|
he = HashLookOnly(&def->cd_idHash, name);
|
|
if (he == NULL || HashGetValue(he) == NULL)
|
|
{
|
|
/*
|
|
* Pull off the next component of path up to but not including
|
|
* any array subscripts.
|
|
* NOTE: This should check the array bounds and only remove
|
|
* array components that are expected, not array components
|
|
* embedded in the name.
|
|
*/
|
|
for (; *cp && *cp != '[' && *cp != '/'; cp++)
|
|
/* Nothing */;
|
|
csave = *cp;
|
|
*cp = '\0';
|
|
he = HashLookOnly(&def->cd_idHash, name);
|
|
*cp = csave;
|
|
if (he == NULL || HashGetValue(he) == NULL)
|
|
return;
|
|
}
|
|
use = (CellUse *) HashGetValue(he);
|
|
def = use->cu_def;
|
|
|
|
/*
|
|
* Pull off array subscripts and build next stage in transform.
|
|
* Return NULL if the number of subscripts specified doesn't
|
|
* match the number that are implied by the array, if use is
|
|
* an array.
|
|
*/
|
|
if (!dbParseArray(cp, use, scx))
|
|
{
|
|
/* Allow non-indexed match of array */
|
|
if (strcmp(name, use->cu_id)) return;
|
|
/* Check for both 1- and 2-dimensional arrays */
|
|
if (!dbParseArray("[0][0]", use, scx))
|
|
if (!dbParseArray("[0]", use, scx))
|
|
return;
|
|
break;
|
|
}
|
|
while (*cp && *cp++ != '/')
|
|
/* Nothing */;
|
|
name = cp;
|
|
}
|
|
|
|
/* Ensure that the leaf cell is read in */
|
|
def = use->cu_def;
|
|
if ((def->cd_flags & CDAVAILABLE) == 0)
|
|
DBCellRead(def, TRUE, TRUE, NULL);
|
|
|
|
scx->scx_use = use;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* dbParseArray --
|
|
*
|
|
* Pull off the array subscripts starting at 'cp' (there may be none),
|
|
* checking to ensure that there are the correct number for 'use' and that
|
|
* they are in range. Store these in scx->scx_x and scx->scx_y, and use them
|
|
* to update scx->scx_trans to be use->cu_trans (as adjusted for the indicated
|
|
* array element) followed by the old value of scx->scx_trans.
|
|
*
|
|
* Results:
|
|
* Returns TRUE on success, FALSE on error.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
dbParseArray(cp, use, scx)
|
|
char *cp;
|
|
CellUse *use;
|
|
SearchContext *scx;
|
|
{
|
|
int xdelta, ydelta, i1, i2, indexCount;
|
|
Transform trans, trans2;
|
|
|
|
/*
|
|
* The transform stuff is a little tricky because if there
|
|
* was only one index given we don't know whether it's the
|
|
* x- or y-index. Make sure the number of indices specified
|
|
* matches the number of dimensions in an array, and that
|
|
* the indices are in range.
|
|
*/
|
|
indexCount = 0;
|
|
if (*cp == '[')
|
|
{
|
|
if (sscanf(cp, "[%d][%d]", &i1, &i2) == 2)
|
|
{
|
|
indexCount = 2;
|
|
while (*cp++ != ']') /* Nothing */;
|
|
while (*cp++ != ']') /* Nothing */;
|
|
}
|
|
else if (sscanf(cp, "[%d,%d]", &i1, &i2) == 2)
|
|
{
|
|
indexCount = 2;
|
|
while (*cp++ != ']') /* Nothing */;
|
|
}
|
|
else if (sscanf(cp, "[%d]", &i1) == 1)
|
|
{
|
|
indexCount = 1;
|
|
while (*cp++ != ']') /* Nothing */;
|
|
}
|
|
|
|
if (indexCount && *cp != '\0' && *cp != '/')
|
|
return FALSE;
|
|
}
|
|
|
|
switch (indexCount)
|
|
{
|
|
case 0:
|
|
if (use->cu_xlo != use->cu_xhi || use->cu_ylo != use->cu_yhi)
|
|
return FALSE;
|
|
scx->scx_x = use->cu_xlo;
|
|
scx->scx_y = use->cu_ylo;
|
|
break;
|
|
case 1:
|
|
if (use->cu_xlo == use->cu_xhi)
|
|
{
|
|
scx->scx_x = use->cu_xlo;
|
|
scx->scx_y = i1;
|
|
}
|
|
else if (use->cu_ylo == use->cu_yhi)
|
|
{
|
|
scx->scx_x = i1;
|
|
scx->scx_y = use->cu_ylo;
|
|
}
|
|
else return FALSE;
|
|
break;
|
|
case 2:
|
|
if (use->cu_xlo == use->cu_xhi || use->cu_ylo == use->cu_yhi)
|
|
return FALSE;
|
|
scx->scx_y = i1;
|
|
scx->scx_x = i2;
|
|
break;
|
|
}
|
|
|
|
if (use->cu_xhi > use->cu_xlo)
|
|
{
|
|
if (scx->scx_x < use->cu_xlo || scx->scx_x > use->cu_xhi)
|
|
return FALSE;
|
|
xdelta = use->cu_xsep * (scx->scx_x - use->cu_xlo);
|
|
}
|
|
else
|
|
{
|
|
if (scx->scx_x > use->cu_xlo || scx->scx_x < use->cu_xhi)
|
|
return FALSE;
|
|
xdelta = use->cu_xsep * (use->cu_xlo - scx->scx_x);
|
|
}
|
|
if (use->cu_yhi > use->cu_ylo)
|
|
{
|
|
if (scx->scx_y < use->cu_ylo || scx->scx_y > use->cu_yhi)
|
|
return FALSE;
|
|
ydelta = use->cu_ysep * (scx->scx_y - use->cu_ylo);
|
|
}
|
|
else
|
|
{
|
|
if (scx->scx_y > use->cu_ylo || scx->scx_y < use->cu_yhi)
|
|
return FALSE;
|
|
ydelta = use->cu_ysep * (use->cu_ylo - scx->scx_y);
|
|
}
|
|
|
|
GeoTransTranslate(xdelta, ydelta, &use->cu_transform, &trans);
|
|
GeoTransTrans(&trans, &scx->scx_trans, &trans2);
|
|
scx->scx_trans = trans2;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* DBNearestLabel --
|
|
*
|
|
* This procedure finds the nearest label to a given point
|
|
* and returns its hierarchical name and location.
|
|
*
|
|
* Results:
|
|
* Area is searched in cellUse to find the nearest label
|
|
* to point. TRUE is returned if any label was found.
|
|
* If there is no label in the given area, FALSE is
|
|
* returned.
|
|
*
|
|
* Side effects:
|
|
* The parameter labelArea is filled in with the location of
|
|
* the label, if one was found. LabelName is filled in with
|
|
* the hierarchical name of the label.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
DBNearestLabel(cellUse, area, point, xMask, labelArea, labelName, length)
|
|
CellUse *cellUse; /* Start search at this cell. */
|
|
Rect *area; /* Search this area of cellUse. */
|
|
Point *point; /* Find nearest label to this point. */
|
|
int xMask; /* Recursively search subcells as long
|
|
* as their expand masks, when anded with
|
|
* xMask, are equal to xMask. 0 means search
|
|
* all the way down through the hierarchy.
|
|
*/
|
|
Rect *labelArea; /* To be filled in with area of closest
|
|
* label. NULL means ignore.
|
|
*/
|
|
char *labelName; /* Fill this in with name of label, unless
|
|
* NULL.
|
|
*/
|
|
int length; /* This gives the maximum number of chars
|
|
* that may be used in labelName, including
|
|
* the NULL character to terminate.
|
|
*/
|
|
{
|
|
TerminalPath tPath, *tp;
|
|
SearchContext scx;
|
|
char *name;
|
|
struct nldata funcData;
|
|
extern int dbNearestLabelFunc();
|
|
|
|
/* Allocate space to generate a label name, and set up information
|
|
* for the DBTreeSrLabels call.
|
|
*/
|
|
|
|
if (labelName == NULL) tp = NULL, name = NULL;
|
|
else
|
|
{
|
|
name = (char *) mallocMagic((unsigned) (length));
|
|
tPath.tp_first = name;
|
|
tPath.tp_next = name;
|
|
tPath.tp_last = name + length - 1;
|
|
tp = &tPath;
|
|
}
|
|
scx.scx_use = cellUse;
|
|
scx.scx_area = *area;
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
funcData.nld_point = point;
|
|
funcData.nld_labelArea = labelArea;
|
|
funcData.nld_name = labelName;
|
|
funcData.nld_gotLabel = FALSE;
|
|
|
|
(void) DBTreeSrLabels(&scx, &DBAllTypeBits, xMask, tp, TF_LABEL_ATTACH,
|
|
dbNearestLabelFunc, (ClientData) &funcData);
|
|
|
|
if (name) freeMagic(name);
|
|
|
|
if (!funcData.nld_gotLabel) return FALSE;
|
|
else return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* dbNearestLabelFunc --
|
|
*
|
|
* This function is called by DBTreeSrLabels for each label
|
|
* found as part of DBNearestLabel.
|
|
*
|
|
* Results:
|
|
* Always returns 0 to continue the search.
|
|
*
|
|
* Side effects:
|
|
* If this is the closest label seen so far, update the information
|
|
* passed via funcData.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
dbNearestLabelFunc(scx, label, tpath, funcData)
|
|
SearchContext *scx; /* Describes state of search. */
|
|
Label *label; /* Label found. */
|
|
TerminalPath *tpath; /* Name of label. */
|
|
struct nldata *funcData; /* Parameters to DBNearestLabel (passed as
|
|
* ClientData).
|
|
*/
|
|
{
|
|
int x, y, distance, left, used;
|
|
Rect area;
|
|
char *src, *dst;
|
|
|
|
GeoTransRect(&scx->scx_trans, &label->lab_rect, &area);
|
|
x = (area.r_xtop + area.r_xbot)/2 - funcData->nld_point->p_x;
|
|
y = (area.r_ytop + area.r_ybot)/2 - funcData->nld_point->p_y;
|
|
distance = x*x + y*y;
|
|
if ((funcData->nld_gotLabel) && (distance > funcData->nld_distance))
|
|
return 0;
|
|
funcData->nld_distance = distance;
|
|
funcData->nld_gotLabel = TRUE;
|
|
|
|
if (funcData->nld_labelArea != NULL)
|
|
*(funcData->nld_labelArea) = area;
|
|
if (funcData->nld_name != NULL)
|
|
{
|
|
left = tpath->tp_last - tpath->tp_next;
|
|
used = tpath->tp_next - tpath->tp_first;
|
|
(void) strncpy(funcData->nld_name, tpath->tp_first, used);
|
|
dst = funcData->nld_name + used;
|
|
src = label->lab_text;
|
|
for ( ; left > 0; left -= 1)
|
|
{
|
|
if (*src == 0) break;
|
|
*dst++ = *src++;
|
|
}
|
|
*dst = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|