Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtdocbook / tcl / tclPreserve.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: tclPreserve.c /main/2 1996/08/08 14:46:12 cde-hp $ */
24 /* 
25  * tclPreserve.c --
26  *
27  *      This file contains a collection of procedures that are used
28  *      to make sure that widget records and other data structures
29  *      aren't reallocated when there are nested procedures that
30  *      depend on their existence.
31  *
32  * Copyright (c) 1991-1994 The Regents of the University of California.
33  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
34  *
35  * See the file "license.terms" for information on usage and redistribution
36  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
37  *
38  * SCCS: @(#) tclPreserve.c 1.14 96/03/20 08:24:37
39  */
40
41 #include "tclInt.h"
42
43 /*
44  * The following data structure is used to keep track of all the
45  * Tcl_Preserve calls that are still in effect.  It grows as needed
46  * to accommodate any number of calls in effect.
47  */
48
49 typedef struct {
50     ClientData clientData;      /* Address of preserved block. */
51     int refCount;               /* Number of Tcl_Preserve calls in effect
52                                  * for block. */
53     int mustFree;               /* Non-zero means Tcl_EventuallyFree was
54                                  * called while a Tcl_Preserve call was in
55                                  * effect, so the structure must be freed
56                                  * when refCount becomes zero. */
57     Tcl_FreeProc *freeProc;     /* Procedure to call to free. */
58 } Reference;
59
60 static Reference *refArray;     /* First in array of references. */
61 static int spaceAvl = 0;        /* Total number of structures available
62                                  * at *firstRefPtr. */
63 static int inUse = 0;           /* Count of structures currently in use
64                                  * in refArray. */
65 #define INITIAL_SIZE 2
66
67 /*
68  * Static routines in this file:
69  */
70
71 static void     PreserveExitProc _ANSI_ARGS_((ClientData clientData));
72
73 \f
74 /*
75  *----------------------------------------------------------------------
76  *
77  * PreserveExitProc --
78  *
79  *      Called during exit processing to clean up the reference array.
80  *
81  * Results:
82  *      None.
83  *
84  * Side effects:
85  *      Frees the storage of the reference array.
86  *
87  *----------------------------------------------------------------------
88  */
89
90         /* ARGSUSED */
91 static void
92 PreserveExitProc(clientData)
93     ClientData clientData;              /* NULL -Unused. */
94 {
95     if (spaceAvl != 0) {
96         ckfree((char *) refArray);
97         refArray = (Reference *) NULL;
98         inUse = 0;
99         spaceAvl = 0;
100     }
101 }
102 \f
103 /*
104  *----------------------------------------------------------------------
105  *
106  * Tcl_Preserve --
107  *
108  *      This procedure is used by a procedure to declare its interest
109  *      in a particular block of memory, so that the block will not be
110  *      reallocated until a matching call to Tcl_Release has been made.
111  *
112  * Results:
113  *      None.
114  *
115  * Side effects:
116  *      Information is retained so that the block of memory will
117  *      not be freed until at least the matching call to Tcl_Release.
118  *
119  *----------------------------------------------------------------------
120  */
121
122 void
123 Tcl_Preserve(clientData)
124     ClientData clientData;      /* Pointer to malloc'ed block of memory. */
125 {
126     Reference *refPtr;
127     int i;
128
129     /*
130      * See if there is already a reference for this pointer.  If so,
131      * just increment its reference count.
132      */
133
134     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
135         if (refPtr->clientData == clientData) {
136             refPtr->refCount++;
137             return;
138         }
139     }
140
141     /*
142      * Make a reference array if it doesn't already exist, or make it
143      * bigger if it is full.
144      */
145
146     if (inUse == spaceAvl) {
147         if (spaceAvl == 0) {
148             Tcl_CreateExitHandler((Tcl_ExitProc *) PreserveExitProc,
149                     (ClientData) NULL);
150             refArray = (Reference *) ckalloc((unsigned)
151                     (INITIAL_SIZE*sizeof(Reference)));
152             spaceAvl = INITIAL_SIZE;
153         } else {
154             Reference *new;
155
156             new = (Reference *) ckalloc((unsigned)
157                     (2*spaceAvl*sizeof(Reference)));
158             memcpy((VOID *) new, (VOID *) refArray,
159                     spaceAvl*sizeof(Reference));
160             ckfree((char *) refArray);
161             refArray = new;
162             spaceAvl *= 2;
163         }
164     }
165
166     /*
167      * Make a new entry for the new reference.
168      */
169
170     refPtr = &refArray[inUse];
171     refPtr->clientData = clientData;
172     refPtr->refCount = 1;
173     refPtr->mustFree = 0;
174     inUse += 1;
175 }
176 \f
177 /*
178  *----------------------------------------------------------------------
179  *
180  * Tcl_Release --
181  *
182  *      This procedure is called to cancel a previous call to
183  *      Tcl_Preserve, thereby allowing a block of memory to be
184  *      freed (if no one else cares about it).
185  *
186  * Results:
187  *      None.
188  *
189  * Side effects:
190  *      If Tcl_EventuallyFree has been called for clientData, and if
191  *      no other call to Tcl_Preserve is still in effect, the block of
192  *      memory is freed.
193  *
194  *----------------------------------------------------------------------
195  */
196
197 void
198 Tcl_Release(clientData)
199     ClientData clientData;      /* Pointer to malloc'ed block of memory. */
200 {
201     Reference *refPtr;
202     int mustFree;
203     Tcl_FreeProc *freeProc;
204     int i;
205
206     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
207         if (refPtr->clientData != clientData) {
208             continue;
209         }
210         refPtr->refCount--;
211         if (refPtr->refCount == 0) {
212
213             /*
214              * Must remove information from the slot before calling freeProc
215              * to avoid reentrancy problems if the freeProc calls Tcl_Preserve
216              * on the same clientData. Copy down the last reference in the
217              * array to overwrite the current slot.
218              */
219
220             freeProc = refPtr->freeProc;
221             mustFree = refPtr->mustFree;
222             inUse--;
223             if (i < inUse) {
224                 refArray[i] = refArray[inUse];
225             }
226             if (mustFree) {
227                 if ((freeProc == TCL_DYNAMIC) ||
228                         (freeProc == (Tcl_FreeProc *) free)) {
229                     ckfree((char *) clientData);
230                 } else {
231                     (*freeProc)((char *) clientData);
232                 }
233             }
234         }
235         return;
236     }
237
238     /*
239      * Reference not found.  This is a bug in the caller.
240      */
241
242     panic("Tcl_Release couldn't find reference for 0x%x", clientData);
243 }
244 \f
245 /*
246  *----------------------------------------------------------------------
247  *
248  * Tcl_EventuallyFree --
249  *
250  *      Free up a block of memory, unless a call to Tcl_Preserve is in
251  *      effect for that block.  In this case, defer the free until all
252  *      calls to Tcl_Preserve have been undone by matching calls to
253  *      Tcl_Release.
254  *
255  * Results:
256  *      None.
257  *
258  * Side effects:
259  *      Ptr may be released by calling free().
260  *
261  *----------------------------------------------------------------------
262  */
263
264 void
265 Tcl_EventuallyFree(clientData, freeProc)
266     ClientData clientData;      /* Pointer to malloc'ed block of memory. */
267     Tcl_FreeProc *freeProc;     /* Procedure to actually do free. */
268 {
269     Reference *refPtr;
270     int i;
271
272     /*
273      * See if there is a reference for this pointer.  If so, set its
274      * "mustFree" flag (the flag had better not be set already!).
275      */
276
277     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
278         if (refPtr->clientData != clientData) {
279             continue;
280         }
281         if (refPtr->mustFree) {
282             panic("Tcl_EventuallyFree called twice for 0x%x\n", clientData);
283         }
284         refPtr->mustFree = 1;
285         refPtr->freeProc = freeProc;
286         return;
287     }
288
289     /*
290      * No reference for this block.  Free it now.
291      */
292
293     if ((freeProc == TCL_DYNAMIC) || (freeProc == (Tcl_FreeProc *) free)) {
294         ckfree((char *) clientData);
295     } else {
296         (*freeProc)((char *)clientData);
297     }
298 }