1 /* Copyright (C) 1992-1998 The Geometry Center
2  * Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips
3  *
4  * This file is part of Geomview.
5  *
6  * Geomview is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * Geomview is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with Geomview; see the file COPYING.  If not, write
18  * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
19  * USA, or visit http://www.gnu.org.
20  */
21 
22 #if HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25 
26 #if 0
27 static char copyright[] = "Copyright (C) 1992-1998 The Geometry Center\n\
28 Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips";
29 #endif
30 
31 
32 /* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */
33 
34 #include <string.h>
35 #include "handleP.h"
36 #include "freelist.h"
37 
38 static DBLLIST(AllHandles);
39 static DEF_FREELIST(HRef);
40 static DEF_FREELIST(Handle);
41 
HRefFreeListPrune(void)42 void HRefFreeListPrune(void)
43 {
44   FreeListNode *old;
45   size_t size = 0;
46 
47   while (HRefFreeList) {
48     old = HRefFreeList;
49     HRefFreeList = old->next;
50     OOGLFree(old);
51     size += sizeof(HRef);
52   }
53   OOGLWarn("Freed %ld bytes.\n", size);
54 }
55 
HandleFreeListPrune(void)56 void HandleFreeListPrune(void)
57 {
58   FreeListNode *old;
59   size_t size = 0;
60 
61   while (HandleFreeList) {
62     old = HandleFreeList;
63     HandleFreeList = old->next;
64     OOGLFree(old);
65     size += sizeof(Handle);
66   }
67   OOGLWarn("Freed %ld bytes.\n", size);
68 }
69 
70 #define HANDLE_DEBUG 0
71 
72 void handle_dump(void);
73 
74 static HandleOps NullOps = {
75   "null",
76 };
77 
HandlesSetObjSaved(bool obj_saved)78 void HandlesSetObjSaved(bool obj_saved)
79 {
80   HandleOps *ops;
81   Handle *h;
82 
83   DblListIterateNoDelete(&AllHandles, HandleOps, node, ops) {
84     DblListIterateNoDelete(&ops->handles, Handle, opsnode, h) {
85       h->obj_saved = obj_saved;
86     }
87   }
88 }
89 
90 /* Iterate over Ref->handles.
91  *
92  * This function increases the ref-count of the handle, only this way
93  * the caller may safely delete the returned handle; if pos != NULL,
94  * then we first iterate to the next element in the list, and then
95  * call HandleDelete() for pos to undo the REFGET from the previous
96  * call. The caller should always iterate until we return NULL,
97  * otherwise spurious ref-counts will be the result.
98  */
HandleRefIterate(Ref * r,Handle * pos)99 Handle *HandleRefIterate(Ref *r, Handle *pos)
100 {
101   if (pos == NULL) {
102     return DblListEmpty(&r->handles)
103       ? NULL
104       : REFGET(Handle, DblListContainer(r->handles.next, Handle, objnode));
105   } else {
106     DblListNode *next = pos->objnode.next;
107 
108     HandleDelete(pos); /* undo REFGET(), possibly really delete the handle */
109     return next == &r->handles
110       ? NULL
111       : REFGET(Handle, DblListContainer(next, Handle, objnode));
112   }
113 }
114 
115 /* iterate over Pool->handles */
HandlePoolIterate(Pool * pool,Handle * pos)116 Handle *HandlePoolIterate(Pool *pool, Handle *pos)
117 {
118   if (pos == NULL) {
119     return DblListEmpty(&pool->handles)
120       ? NULL
121       : REFGET(Handle, DblListContainer(pool->handles.next, Handle, poolnode));
122   } else {
123     DblListNode *next = pos->objnode.next;
124 
125     HandleDelete(pos);
126     return next == &pool->handles
127       ? NULL
128       : REFGET(Handle, DblListContainer(next, Handle, poolnode));
129   }
130 }
131 
132 void
HandleUpdRef(Handle ** hp,Ref * parent,Ref ** objp)133 HandleUpdRef(Handle **hp, Ref *parent, Ref **objp)
134 {
135   Handle *h;
136 
137   (void)parent;
138 
139   if((h = *hp) != NULL && objp != NULL && h->object != *objp) {
140     if (*objp) {
141       if (h->ops->delete) {
142 	(*h->ops->delete)(*objp);
143       } else {
144 	REFPUT(*objp);
145       }
146     }
147     *objp = REFGET(Ref, h->object);
148   }
149 }
150 
HandleUnregister(Handle ** hp)151 void HandleUnregister(Handle **hp)
152 {
153   Handle *h;
154   HRef *r, *rn;
155 
156   if (hp == NULL || (h = *hp) == NULL) {
157     return;
158   }
159 
160   DblListIterate(&h->refs, HRef, node, r, rn) {
161     if(r->hp == hp) {
162       DblListDelete(&r->node);
163       memset(r, 0, sizeof(*r));
164       FREELIST_FREE(HRef, r);
165       REFPUT(h);
166     }
167   }
168 }
169 
HandleUnregisterJust(Handle ** hp,Ref * obj,void * info,void (* update)P ((Handle **,Ref *,void *)))170 void HandleUnregisterJust(Handle **hp, Ref *obj, void *info,
171 			  void (*update) P((Handle **, Ref *, void *)))
172 {
173   Handle *h;
174   HRef *rp, *rnext;
175 
176   if(hp == NULL || (h = *hp) == NULL) {
177     return;
178   }
179 
180   DblListIterate(&h->refs, HRef, node, rp, rnext) {
181     if(rp->hp == hp &&
182        (obj == NULL || rp->parentobj == obj) &&
183        (info == NULL || rp->info == info) &&
184        (update == NULL || rp->update == update)) {
185       DblListDelete(&rp->node);
186       memset(rp, 0, sizeof(*rp));
187       FREELIST_FREE(HRef, rp);
188       REFPUT(h);
189     }
190   }
191 }
192 
193 /*
194  * Remove all callbacks on any handle with the given properties.
195  */
HandleUnregisterAll(Ref * obj,void * info,void (* update)P ((Handle **,Ref *,void *)))196 void HandleUnregisterAll(Ref *obj,
197 			 void *info,
198 			 void (*update) P((Handle **, Ref *, void *)))
199 {
200   HandleOps *ops;
201   Handle *h;
202   HRef *r, *rn;
203 
204   DblListIterateNoDelete(&AllHandles, HandleOps, node, ops) {
205     DblListIterateNoDelete(&ops->handles, Handle, opsnode, h) {
206       DblListIterate(&h->refs, HRef, node, r, rn) {
207 	if((obj == NULL || r->parentobj == obj) &&
208  	   (info == NULL || r->info == info) &&
209 	   (update == NULL || r->update == update)) {
210 	  DblListDelete(&r->node);
211 	  memset(r, 0, sizeof(*r));
212 	  FREELIST_FREE(HRef, r);
213 	  REFPUT(h);
214 	}
215       }
216     }
217   }
218 }
219 
220 static
handleupdate(Handle * h,HRef * rp)221 void handleupdate(Handle *h, HRef *rp)
222 {
223   if(rp->update && h == *rp->hp) {
224     (*rp->update)(rp->hp, rp->parentobj, rp->info);
225   } else { /* BUG */
226     OOGLError(1, "handleupdate mismatch: h %x %s, rp->hp %x, *rp->hp %x, rp->parentobj %x, rp->update %x",
227 	      h, h->name, rp->hp, *rp->hp, rp->parentobj, rp->update);
228     if(*rp->hp) {
229       OOGLError(1, "... *rp->hp->name %s", (*rp->hp)->name);
230     }
231   }
232 }
233 
HandleRegister(Handle ** hp,Ref * parentobj,void * info,void (* update)P ((Handle **,Ref *,void *)))234 int HandleRegister(Handle **hp,
235 		   Ref *parentobj,
236 		   void *info,
237 		   void (*update) P((Handle **, Ref *, void *)))
238 {
239   Handle *h;
240   HRef *rp;
241 
242   if (hp == NULL || (h = *hp) == NULL) {
243     return false;
244   }
245 
246   DblListIterateNoDelete(&h->refs, HRef, node, rp) {
247     if (rp->hp == hp && rp->parentobj == parentobj && rp->info == info) {
248       goto update_out;
249     }
250   }
251 
252   FREELIST_NEW(HRef, rp);
253 
254   REFINCR(h);
255 
256   rp->hp = hp;
257   rp->parentobj = parentobj;
258   rp->info = info;
259 
260   DblListAdd(&h->refs, &rp->node);
261 
262  update_out:
263   rp->update = update;
264   handleupdate(h, rp);
265   return true;
266 }
267 
268 char *
HandleName(Handle * h)269 HandleName(Handle *h)
270 {
271   return h ? h->name : NULL;
272 }
273 
HandleOpsByName(char * name)274 HandleOps *HandleOpsByName(char *name)
275 {
276   HandleOps *ops;
277 
278   DblListIterateNoDelete(&AllHandles, HandleOps, node, ops) {
279     if (strcmp(name, ops->prefix) == 0) {
280       return ops;
281     }
282   }
283   return NULL;
284 }
285 
286 Handle *
HandleByName(char * name,HandleOps * ops)287 HandleByName(char *name, HandleOps *ops)
288 {
289   Handle *h;
290 
291   if (ops == NULL) {
292     DblListIterateNoDelete(&AllHandles, HandleOps, node, ops) {
293       DblListIterateNoDelete(&ops->handles, Handle, opsnode, h) {
294 	if (strcmp(h->name, name) == 0) {
295 	  return REFGET(Handle, h);
296 	}
297       }
298     }
299   } else {
300     if (ops->handles.next == NULL) {
301       DblListInit(&ops->handles);
302       DblListAdd(&AllHandles, &ops->node);
303     }
304     DblListIterateNoDelete(&ops->handles, Handle, opsnode, h) {
305       if (strcmp(h->name, name) == 0) {
306 	return REFGET(Handle, h);
307       }
308     }
309   }
310   return NULL;
311 }
312 
313 static Handle *
handlecreate(char * name,HandleOps * ops)314 handlecreate(char *name, HandleOps *ops)
315 {
316   Handle *h;
317 
318 #if HANDLE_DEBUG
319   OOGLWarn("Creating handle with name \"%s\"", name);
320 #endif
321 
322   FREELIST_NEW(Handle, h);
323 
324   RefInit((Ref *)h, HANDLEMAGIC);
325   h->ops = ops = ops ? ops : &NullOps;
326   h->name = strdup(name);
327   h->object = NULL;
328   h->whence = NULL;
329   h->permanent = false;
330 
331   DblListInit(&h->refs);
332 
333   /* The following two are nodes, not list-heads, but this way we can
334    * safely call DblListDelete(), even if there is no associated list.
335    */
336   DblListInit(&h->objnode);
337   DblListInit(&h->poolnode);
338 
339   if (ops->handles.next == NULL) {
340     DblListInit(&ops->handles);
341     DblListAdd(&AllHandles, &ops->node);
342   }
343   DblListAddTail(&ops->handles, &h->opsnode);
344 
345   /*  handle_dump();*/
346 
347   return h;
348 }
349 
350 /*
351  * Assign a new object to a Handle.
352  */
353 int
HandleSetObject(Handle * h,Ref * object)354 HandleSetObject(Handle *h, Ref *object)
355 {
356   HRef *ref;
357 
358   if(h == NULL) {
359     return false;
360   }
361 
362   if(h->object == object) {
363     return true;
364   }
365 
366   DblListDelete(&h->objnode);
367 
368   if (h->object) {
369     if (h->ops->delete != NULL) {
370       (*h->ops->delete)(h->object);
371     } else {
372       REFPUT(h->object);
373     }
374   }
375   h->object = REFGET(Ref, object);
376   if (object) {
377     DblListAddTail(&object->handles, &h->objnode);
378   }
379 
380   DblListIterateNoDelete(&h->refs, HRef, node, ref) {
381     handleupdate(h, ref);
382   }
383 
384   return true;
385 }
386 
387 /*
388  * Unconditionally create a new handle.
389  */
390 Handle *
HandleDoCreate(char * name,HandleOps * ops)391 HandleDoCreate(char *name, HandleOps *ops)
392 {
393   Handle *h;
394 
395   h = handlecreate(name, ops);
396 
397   return h;
398 }
399 
400 /*
401  * Create a Handle, don't reassign its value.
402  */
403 Handle *
HandleCreate(char * name,HandleOps * ops)404 HandleCreate(char *name, HandleOps *ops)
405 {
406   Handle *h;
407 
408   h = HandleByName(name, ops);
409   if (h == NULL) {
410     h = handlecreate(name, ops);
411   }
412   return h;
413 }
414 
415 /*
416  * Global handle: increase the ref-count twice.
417  */
418 Handle *
HandleCreateGlobal(char * name,HandleOps * ops)419 HandleCreateGlobal(char *name, HandleOps *ops)
420 {
421   Handle *h;
422 
423   h = HandleCreate(name, ops);
424   if (!h->permanent) {
425     h->permanent = true;
426     return REFGET(Handle, h);
427   } else {
428     return h;
429   }
430 }
431 
432 /*
433  * Create a Handle (or reassign its value if it already exists)
434  */
435 Handle *
HandleAssign(char * name,HandleOps * ops,Ref * object)436 HandleAssign(char *name, HandleOps *ops, Ref *object)
437 {
438   Handle *h;
439 
440   h = HandleCreate(name, ops);
441   HandleSetObject(h, object);
442   return h;
443 }
444 
445 /*
446  * Return the current object held by a Handle.
447  */
448 Ref *
HandleObject(Handle * h)449 HandleObject(Handle *h)
450 {
451   return h->object;
452 }
453 
454 Pool *
HandlePool(Handle * h)455 HandlePool(Handle *h)
456 {
457   return h->whence;
458 }
459 
HandlePDelete(Handle ** hp)460 void HandlePDelete(Handle **hp)
461 {
462   if(hp && *hp) {
463     HandleUnregister(hp);
464     HandleDelete(*hp);
465     *hp = NULL;
466   }
467 }
468 
469 /* Simple debugging aid: dump the list of currently defined handles
470  * and their ref-counts, and the ref. counts of the attached object
471  * (if any).
472  */
handle_dump(void)473 void handle_dump(void)
474 {
475   HandleOps *ops;
476   Handle *h;
477 
478   OOGLWarn("Active handles:");
479 
480   DblListIterateNoDelete(&AllHandles, HandleOps, node, ops) {
481     DblListIterateNoDelete(&ops->handles, Handle, opsnode, h) {
482       OOGLWarn("  %s[%s]@%p (%s: #%d, o: #%d )",
483 	       ops->prefix, h->name, (void *)h,
484 	       h->permanent ? "H" : "h",
485 	       RefCount((Ref *)h),
486 	       h->object ? RefCount((Ref *)h->object) : -1);
487     }
488   }
489 }
490 
491 /* In principle, it is not necessary to distinguish between permanent
492  * and non-permanent handles IF the ref-counts are kept up to date.
493  */
HandleDelete(Handle * h)494 void HandleDelete(Handle *h)
495 {
496   if (h == NULL) {
497     return;
498   }
499 
500   if (h->magic != HANDLEMAGIC) {
501     OOGLWarn("Internal warning: HandleDelete of non-Handle %x (%x != %x)",
502 	     h, h->magic, HANDLEMAGIC);
503     return;
504   }
505 
506   if (REFPUT(h) > 0) {
507     return;
508   }
509 
510 #if HANDLE_DEBUG
511   OOGLWarn("Deleting handle with name \"%s\"", h->name);
512   if (!DblListEmpty(&h->refs)) {
513     OOGLWarn("Deleting handle with active back references.");
514     abort();
515   }
516 #endif
517 
518   /* remove ourselves from various lists */
519   DblListDelete(&h->objnode);
520   DblListDelete(&h->opsnode);
521   DblListDelete(&h->poolnode);
522 
523   if (h->object) {
524     /* undo RefIncr() from HandleSetObject() */
525     if (h->ops->delete) {
526       (*h->ops->delete)(h->object);
527     } else {
528       REFPUT(h->object);
529     }
530   }
531 
532    /* Don't let Pool code think we have something cached in this handle! */
533   if (h->whence && h->whence->seekable) {
534     h->whence->flags &= ~PF_ANY;
535     if (!h->permanent) {
536 #if 1
537       /* Should we do this? If we are not permanent and attached to a
538        * pool, then we are a dummy pool-handle assigned to an object
539        * just read from the pool. Still the pool could belong to a
540        * pipe, and maybe we should not close and delete the pool.
541        *
542        * I think closing and deleting is ok here ...
543        *
544        * If we are permanent, then we should not close whence.
545        */
546       PoolClose(h->whence);
547       PoolDelete(h->whence);
548 #endif
549     }
550   }
551 
552   if(h->name) {
553     free(h->name);
554   }
555 
556   memset(h, 0, sizeof(*h));
557   FREELIST_FREE(Handle, h);
558 
559   /*  handle_dump();*/
560 }
561 
562 /*
563  * Local Variables: ***
564  * mode: c ***
565  * c-basic-offset: 2 ***
566  * End: ***
567  */
568