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