1 /*
2  * DBexpand.c --
3  *
4  * Expansion and unexpansion of cells
5  *
6  *     *********************************************************************
7  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
8  *     * Permission to use, copy, modify, and distribute this              *
9  *     * software and its documentation for any purpose and without        *
10  *     * fee is hereby granted, provided that the above copyright          *
11  *     * notice appear in all copies.  The University of California        *
12  *     * makes no representations about the suitability of this            *
13  *     * software for any purpose.  It is provided "as is" without         *
14  *     * express or implied warranty.  Export of this software outside     *
15  *     * of the United States of America may require an export license.    *
16  *     *********************************************************************
17  */
18 
19 #ifndef lint
20 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/database/DBexpand.c,v 1.3 2010/08/15 14:35:47 tim Exp $";
21 #endif  /* not lint */
22 
23 #include <stdio.h>
24 #include "utils/magic.h"
25 #include "utils/geometry.h"
26 #include "tiles/tile.h"
27 #include "utils/hash.h"
28 #include "database/database.h"
29 #include "database/databaseInt.h"
30 #include "textio/textio.h"
31 #include "utils/utils.h"
32 #include "utils/stack.h"
33 
34     /*
35      * Argument passed down to search functions when searching for
36      * cells to expand or unexpand.
37      */
38 struct expandArg
39 {
40     bool	ea_deref;	/* TRUE if root def dereference flag is set */
41     int		ea_xmask;	/* Expand mask. */
42     int		(*ea_func)();	/* Function to call for each cell whose
43 				 * status is changed.
44 				 */
45     ClientData	ea_arg;		/* Argument to pass to func. */
46 };
47 
48 /*
49  * ----------------------------------------------------------------------------
50  *
51  * DBExpand --
52  *
53  * Expand/unexpand a single CellUse.
54  *
55  * Results:
56  *	None.
57  *
58  * Side effects:
59  *	If expandFlag is TRUE, sets all the bits of expandMask in
60  *	the flags of the given cellUse expandFlag is FALSE, clears
61  *	all bits of expandMask.
62  *
63  *	If expandFlag is TRUE and the cell being expanded has not
64  *	been read in, reads it in from disk.
65  *
66  * ----------------------------------------------------------------------------
67  */
68 
69 void
DBExpand(cellUse,expandMask,expandFlag)70 DBExpand(cellUse, expandMask, expandFlag)
71     CellUse *cellUse;
72     int expandMask;
73     bool expandFlag;
74 {
75     CellDef *def;
76 
77     if (DBDescendSubcell(cellUse, expandMask) == expandFlag)
78 	return;
79 
80     if (expandFlag)
81     {
82 	def = cellUse->cu_def;
83 	if ((def->cd_flags & CDAVAILABLE) == 0)
84 	{
85 	    bool dereference = (def->cd_flags & CDDEREFERENCE) ? TRUE : FALSE;
86 	    if (!DBCellRead(def, (char *) NULL, TRUE, dereference, NULL))
87 		return;
88 	    /* Note:  we don't have to recompute the bbox here, because
89 	     * if it changed, then a timestamp violation must have occurred
90 	     * and the timestamp manager will take care of recomputing
91 	     * the bbox.
92 	     */
93 	}
94 	cellUse->cu_expandMask |= expandMask;
95     }
96     else
97 	cellUse->cu_expandMask &= ~expandMask;
98 }
99 
100 /*
101  * ----------------------------------------------------------------------------
102  *
103  * DBExpandAll --
104  *
105  * Recursively expand/unexpand all cells which intersect or are
106  * contained within the given rectangle.  Furthermore, if func is
107  * non-NULL, it is invoked for each cell whose status has changed,
108  * just after the change has been made.  The calling sequence is
109  *
110  *     int
111  *     func(cellUse, cdarg)
112  *	   CellUse *cellUse;
113  *	   ClientData cdarg;
114  *     {
115  *     }
116  *
117  * In the calls to func, cellUse is the use whose expand bit has just
118  * been changed, and cdarg is the argument that the caller gave to us.
119  * Func should normally return 0.  If it returns a non-zero value, then
120  * the call terminates immediately and no more cells are expanded.
121  *
122  * Results:
123  *	None.
124  *
125  * Side effects:
126  *	If expandFlag is TRUE, sets all the bits specified by
127  *	expandMask in the flags of each CellUse found to intersect
128  *	the given rectangle.  If expandFlag is FALSE, clears all bits
129  *	of expandMask.
130  * ----------------------------------------------------------------------------
131  */
132 
133 void
DBExpandAll(rootUse,rootRect,expandMask,expandFlag,func,cdarg)134 DBExpandAll(rootUse, rootRect, expandMask, expandFlag, func, cdarg)
135     CellUse *rootUse;	/* Root cell use from which search begins */
136     Rect *rootRect;	/* Area to be expanded, in root coordinates */
137     int expandMask;	/* Window mask in which cell is to be expanded */
138     bool expandFlag;	/* TRUE => expand, FALSE => unexpand */
139     int (*func)();	/* Function to call for each cell whose expansion
140 			 * status is modified.  NULL means don't call anyone.
141 			 */
142     ClientData cdarg;	/* Argument to pass to func. */
143 {
144     int dbExpandFunc(), dbUnexpandFunc();
145     SearchContext scontext;
146     struct expandArg arg;
147     bool dereference = (rootUse->cu_def->cd_flags & CDDEREFERENCE) ?
148 		    TRUE : FALSE;
149 
150     if ((rootUse->cu_def->cd_flags & CDAVAILABLE) == 0)
151     {
152 	(void) DBCellRead(rootUse->cu_def, (char *) NULL, TRUE, dereference, NULL);
153     }
154 
155     /*
156      * Walk through the area and set the expansion state
157      * appropriately.
158      */
159 
160     arg.ea_xmask = expandMask;
161     arg.ea_func = func;
162     arg.ea_arg = cdarg;
163     arg.ea_deref = dereference;
164 
165     scontext.scx_use = rootUse;
166     scontext.scx_trans = GeoIdentityTransform;
167     scontext.scx_area = *rootRect;
168     if (expandFlag)
169 	DBCellSrArea(&scontext, dbExpandFunc, (ClientData) &arg);
170     else
171 	DBCellSrArea(&scontext, dbUnexpandFunc, (ClientData) &arg);
172 }
173 
174 /*
175  * dbExpandFunc --
176  *
177  * Filter function called by DBCellSrArea on behalf of DBExpandAll above
178  * when cells are being expanded.
179  */
180 
181 int
dbExpandFunc(scx,arg)182 dbExpandFunc(scx, arg)
183     SearchContext *scx;	/* Pointer to search context containing
184 					 * child use, search area in coor-
185 					 * dinates of the child use, and
186 					 * transform back to "root".
187 					 */
188     struct expandArg *arg;	/* Client data from caller */
189 {
190     CellUse *childUse = scx->scx_use;
191     int n = DBLambda[1];
192 
193     /*
194      * Change the expansion status of this cell if necessary.  Call the
195      * client's function if the expansion status has changed.
196      */
197 
198     if (!DBDescendSubcell(childUse, arg->ea_xmask))
199     {
200 	/* If the cell is unavailable, then don't expand it.
201 	 */
202 	if ((childUse->cu_def->cd_flags & CDAVAILABLE) == 0)
203 	    if(!DBCellRead(childUse->cu_def, (char *) NULL, TRUE, arg->ea_deref, NULL))
204 	    {
205 		TxError("Cell %s is unavailable.  It could not be expanded.\n",
206 			childUse->cu_def->cd_name);
207 		return 2;
208 	    }
209 
210 	childUse->cu_expandMask |= arg->ea_xmask;
211 	if (arg->ea_func != NULL)
212 	{
213 	    if ((*arg->ea_func)(childUse, arg->ea_arg) != 0) return 1;
214 	}
215     }
216 
217     if (DBCellSrArea(scx, dbExpandFunc, (ClientData) arg))
218 	return 1;
219     return 2;
220 }
221 
222 /*
223  * dbUnexpandFunc --
224  *
225  * Filter function called by DBCellSrArea on behalf of DBExpandAll above
226  * when cells are being unexpanded.
227  */
228 
229 int
dbUnexpandFunc(scx,arg)230 dbUnexpandFunc(scx, arg)
231     SearchContext *scx;	/* Pointer to search context containing
232 					 * child use, search area in coor-
233 					 * dinates of the child use, and
234 					 * transform back to "root".
235 					 */
236     struct expandArg *arg;	/* Client data from caller */
237 {
238     CellUse *childUse = scx->scx_use;
239 
240     /*
241      * Change the expansion status of this cell if necessary.
242      */
243 
244     if (DBDescendSubcell(childUse, arg->ea_xmask))
245     {
246 	if (!GEO_SURROUND(&childUse->cu_def->cd_bbox, &scx->scx_area)
247 	    || GEO_SURROUND(&scx->scx_area, &childUse->cu_def->cd_bbox))
248 	{
249 	    childUse->cu_expandMask &= ~arg->ea_xmask;
250 
251 	    /* Call the client's function, if there is one. */
252 
253 	    if (arg->ea_func != NULL)
254 	    {
255 		if ((*arg->ea_func)(childUse, arg->ea_arg) != 0) return 1;
256 	    }
257 	}
258     }
259 
260     /* Don't recursively search things that aren't already expanded. */
261 
262     else return 2;
263 
264     if (DBCellSrArea(scx, dbUnexpandFunc, (ClientData) arg))
265 	return 1;
266     return 2;
267 }
268 
269 /*
270  * ----------------------------------------------------------------------------
271  *
272  * DBCellReadArea --
273  *
274  * Recursively read all cells which intersect or are contained within
275  * the given rectangle.
276  *
277  * Results:
278  *	If "halt_on_error" is TRUE, then return 1 if any subcell could not
279  *	be read.  Otherwise, return 0.
280  *
281  * Side effects:
282  *	May make new cells known to the database.  Sets the CDAVAILABLE
283  *	bit in all cells intersecting the search area.
284  *
285  * ----------------------------------------------------------------------------
286  */
287 
288 int
DBCellReadArea(rootUse,rootRect,halt_on_error)289 DBCellReadArea(rootUse, rootRect, halt_on_error)
290     CellUse *rootUse;	/* Root cell use from which search begins */
291     Rect *rootRect;	/* Area to be read, in root coordinates */
292     bool halt_on_error;	/* If TRUE, failure to find a cell causes a halt */
293 {
294     int dbReadAreaFunc();
295     SearchContext scontext;
296 
297     scontext.scx_use = rootUse;
298     scontext.scx_trans = GeoIdentityTransform;
299     scontext.scx_area = *rootRect;
300     if (dbReadAreaFunc(&scontext, halt_on_error) == 1)
301 	return 1;
302 
303     return 0;
304 }
305 
306 int
dbReadAreaFunc(scx,halt_on_error)307 dbReadAreaFunc(scx, halt_on_error)
308     SearchContext *scx;	/* Pointer to context specifying
309 					 * the cell use to be read in, and
310 					 * an area to be recursively read in
311 					 * coordinates of the cell use's def.
312 					 */
313     bool halt_on_error;	/* If TURE, failure to find a cell causes a halt */
314 {
315     CellDef *def = scx->scx_use->cu_def;
316 
317     if ((def->cd_flags & CDAVAILABLE) == 0)
318     {
319 	bool dereference = (def->cd_flags & CDDEREFERENCE) ? TRUE : FALSE;
320 	if (DBCellRead(def, (char *)NULL, TRUE, dereference, NULL) == FALSE)
321 	    if (halt_on_error)
322 		return 1;
323 
324 	/* Note: we don't have to invoke DBReComputeBbox here because
325 	 * if the bbox changed then there was a timestamp mismatch and
326 	 * the timestamp code will take care of the bounding box later.
327 	 */
328     }
329 
330     if (DBCellSrArea(scx, dbReadAreaFunc, (ClientData)halt_on_error))
331 	if (halt_on_error)
332 	    return 1;
333 
334     /* Be clever about handling arrays:  if the search area covers this
335      * whole definition, then there's no need to look at any other
336      * array elements, since we've already expanded the entire area
337      * of the definition.
338      */
339 
340     if (GEO_SURROUND(&scx->scx_area, &scx->scx_use->cu_def->cd_bbox))
341 	return 2;
342 
343     return 0;
344 }
345