1/*
2 * This file implements wrappers for persistent gdbm storage for the
3 * shared variable arrays.
4 *
5 * See the file "license.terms" for information on usage and redistribution
6 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
7 *
8 * RCS: @(#) $Id: psGdbm.c,v 1.2 2004/12/18 13:26:03 vasiljevic Exp $
9 * ----------------------------------------------------------------------------
10 */
11
12#ifdef HAVE_GDBM
13
14#include "threadSvCmd.h"
15#include <gdbm.h>
16#include <stdlib.h> /* For free() */
17
18/*
19 * Functions implementing the persistent store interface
20 */
21
22static ps_open_proc   ps_gdbm_open;
23static ps_close_proc  ps_gdbm_close;
24static ps_get_proc    ps_gdbm_get;
25static ps_put_proc    ps_gdbm_put;
26static ps_first_proc  ps_gdbm_first;
27static ps_next_proc   ps_gdbm_next;
28static ps_delete_proc ps_gdbm_delete;
29static ps_free_proc   ps_gdbm_free;
30static ps_geterr_proc ps_gdbm_geterr;
31
32/*
33 * This structure collects all the various pointers
34 * to the functions implementing the gdbm store.
35 */
36
37PsStore GdbmStore = {
38    "gdbm",
39    NULL,
40    ps_gdbm_open,
41    ps_gdbm_get,
42    ps_gdbm_put,
43    ps_gdbm_first,
44    ps_gdbm_next,
45    ps_gdbm_delete,
46    ps_gdbm_close,
47    ps_gdbm_free,
48    ps_gdbm_geterr,
49    NULL
50};
51
52/*
53 *-----------------------------------------------------------------------------
54 *
55 * Sv_RegisterGdbmStore --
56 *
57 *      Register the gdbm store with shared variable implementation.
58 *
59 * Results:
60 *      None.
61 *
62 * Side effects:
63 *      None.
64 *
65 *-----------------------------------------------------------------------------
66 */
67void
68Sv_RegisterGdbmStore(void)
69{
70    Sv_RegisterPsStore(&GdbmStore);
71}
72
73/*
74 *-----------------------------------------------------------------------------
75 *
76 * ps_gdbm_open --
77 *
78 *      Opens the dbm-based persistent storage.
79 *
80 * Results:
81 *      Opaque handle of the opened dbm storage.
82 *
83 * Side effects:
84 *      The gdbm file might be created if not found.
85 *
86 *-----------------------------------------------------------------------------
87 */
88static ClientData
89ps_gdbm_open(path)
90    const char *path;
91{
92    GDBM_FILE dbf;
93    char *ext;
94    Tcl_DString toext;
95
96    Tcl_DStringInit(&toext);
97    ext = Tcl_UtfToExternalDString(NULL, (char*)path, strlen(path), &toext);
98    dbf = gdbm_open(ext, 512, GDBM_WRCREAT|GDBM_SYNC|GDBM_NOLOCK, 0666, NULL);
99    Tcl_DStringFree(&toext);
100
101    return (ClientData)dbf;
102}
103
104/*
105 *-----------------------------------------------------------------------------
106 *
107 * ps_gdbm_close --
108 *
109 *      Closes the gdbm-based persistent storage.
110 *
111 * Results:
112 *      0 - ok
113 *
114 * Side effects:
115 *      None.
116 *
117 *-----------------------------------------------------------------------------
118 */
119static int
120ps_gdbm_close(handle)
121    ClientData handle;
122{
123    gdbm_close((GDBM_FILE)handle);
124
125    return 0;
126}
127
128/*
129 *-----------------------------------------------------------------------------
130 *
131 * ps_gdbm_get --
132 *
133 *      Retrieves data for the key from the dbm storage.
134 *
135 * Results:
136 *      1 - no such key
137 *      0 - ok
138 *
139 * Side effects:
140 *      Data returned must be freed by the caller.
141 *
142 *-----------------------------------------------------------------------------
143 */
144static int
145ps_gdbm_get(handle, key, dataptrptr, lenptr)
146     ClientData handle;
147     const char   *key;
148     char **dataptrptr;
149     int       *lenptr;
150{
151    GDBM_FILE dbf = (GDBM_FILE)handle;
152    datum drec, dkey;
153
154    dkey.dptr  = (char*)key;
155    dkey.dsize = strlen(key) + 1;
156
157    drec = gdbm_fetch(dbf, dkey);
158    if (drec.dptr == NULL) {
159        return 1;
160    }
161
162    *dataptrptr = drec.dptr;
163    *lenptr = drec.dsize;
164
165    return 0;
166}
167
168/*
169 *-----------------------------------------------------------------------------
170 *
171 * ps_gdbm_first --
172 *
173 *      Starts the iterator over the dbm file and returns the first record.
174 *
175 * Results:
176 *      1 - no more records in the iterator
177 *      0 - ok
178 *
179 * Side effects:
180 *      Data returned must be freed by the caller.
181 *
182 *-----------------------------------------------------------------------------
183 */
184static int
185ps_gdbm_first(handle, keyptrptr, dataptrptr, lenptr)
186    ClientData  handle;
187    char   **keyptrptr;
188    char  **dataptrptr;
189    int        *lenptr;
190{
191    GDBM_FILE dbf = (GDBM_FILE)handle;
192    datum drec, dkey;
193
194    dkey = gdbm_firstkey(dbf);
195    if (dkey.dptr == NULL) {
196        return 1;
197    }
198    drec = gdbm_fetch(dbf, dkey);
199    if (drec.dptr == NULL) {
200        return 1;
201    }
202
203    *dataptrptr = drec.dptr;
204    *lenptr = drec.dsize;
205    *keyptrptr = dkey.dptr;
206
207    return 0;
208}
209
210/*
211 *-----------------------------------------------------------------------------
212 *
213 * ps_gdbm_next --
214 *
215 *      Uses the iterator over the dbm file and returns the next record.
216 *
217 * Results:
218 *      1 - no more records in the iterator
219 *      0 - ok
220 *
221 * Side effects:
222 *      Data returned must be freed by the caller.
223 *
224 *-----------------------------------------------------------------------------
225 */
226static int ps_gdbm_next(handle, keyptrptr, dataptrptr, lenptr)
227    ClientData  handle;
228    char   **keyptrptr;
229    char  **dataptrptr;
230    int        *lenptr;
231{
232    GDBM_FILE dbf = (GDBM_FILE)handle;
233    datum drec, dkey, dnext;
234
235    dkey.dptr  = *keyptrptr;
236    dkey.dsize = strlen(*keyptrptr) + 1;
237
238    dnext = gdbm_nextkey(dbf, dkey);
239    free(*keyptrptr), *keyptrptr = NULL;
240
241    if (dnext.dptr == NULL) {
242        return 1;
243    }
244    drec = gdbm_fetch(dbf, dnext);
245    if (drec.dptr == NULL) {
246        return 1;
247    }
248
249    *dataptrptr = drec.dptr;
250    *lenptr = drec.dsize;
251    *keyptrptr = dnext.dptr;
252
253    return 0;
254}
255
256/*
257 *-----------------------------------------------------------------------------
258 *
259 * ps_gdbm_put --
260 *
261 *      Stores used data bound to a key in dbm storage.
262 *
263 * Results:
264 *      0 - ok
265 *     -1 - error; use ps_dbm_geterr to retrieve the error message
266 *
267 * Side effects:
268 *      If the key is already associated with some user data, this will
269 *      be replaced by the new data chunk.
270 *
271 *-----------------------------------------------------------------------------
272 */
273static int
274ps_gdbm_put(handle, key, dataptr, len)
275    ClientData handle;
276    const char   *key;
277    char     *dataptr;
278    int           len;
279{
280    GDBM_FILE dbf = (GDBM_FILE)handle;
281    datum drec, dkey;
282    int ret;
283
284    dkey.dptr  = (char*)key;
285    dkey.dsize = strlen(key) + 1;
286
287    drec.dptr  = dataptr;
288    drec.dsize = len;
289
290    ret = gdbm_store(dbf, dkey, drec, GDBM_REPLACE);
291    if (ret == -1) {
292        return -1;
293    }
294
295    return 0;
296}
297
298/*
299 *-----------------------------------------------------------------------------
300 *
301 * ps_gdbm_delete --
302 *
303 *      Deletes the key and associated data from the dbm storage.
304 *
305 * Results:
306 *      0 - ok
307 *     -1 - error; use ps_dbm_geterr to retrieve the error message
308 *
309 * Side effects:
310 *      If the key is already associated with some user data, this will
311 *      be replaced by the new data chunk.
312 *
313 *-----------------------------------------------------------------------------
314 */
315static int
316ps_gdbm_delete(handle, key)
317    ClientData handle;
318    const char   *key;
319{
320    GDBM_FILE dbf = (GDBM_FILE)handle;
321    datum dkey;
322    int ret;
323
324    dkey.dptr  = (char*)key;
325    dkey.dsize = strlen(key) + 1;
326
327    ret = gdbm_delete(dbf, dkey);
328    if (ret == -1) {
329        return -1;
330    }
331
332    return 0;
333}
334
335/*
336 *-----------------------------------------------------------------------------
337 *
338 * ps_gdbm_free --
339 *
340 *      Frees memory allocated by the gdbm implementation.
341 *
342 * Results:
343 *      None.
344 *
345 * Side effects:
346 *      Memory gets reclaimed.
347 *
348 *-----------------------------------------------------------------------------
349 */
350static void
351ps_gdbm_free(data)
352    char   *data;
353{
354    free(data);
355}
356
357/*
358 *-----------------------------------------------------------------------------
359 *
360 * ps_gdbm_geterr --
361 *
362 *      Retrieves the textual representation of the error caused
363 *      by the last dbm command.
364 *
365 * Results:
366 *      Pointer to the strimg message.
367 *
368 * Side effects:
369 *      None.
370 *
371 *-----------------------------------------------------------------------------
372 */
373static char*
374ps_gdbm_geterr(handle)
375    ClientData handle;
376{
377   /*
378    * The problem with gdbm interface is that it uses the global
379    * gdbm_errno variable which is not per-thread nor mutex
380    * protected. This variable is used to reference array of gdbm
381    * error text strings. It is very dangeours to use this in the
382    * MT-program without proper locking. For this kind of app
383    * we should not be concerned with that, since all ps_gdbm_xxx
384    * operations are performed under shared variable lock anyway.
385    */
386
387    return gdbm_strerror(gdbm_errno);
388}
389
390#endif  /* HAVE_GDBM */
391
392/* EOF $RCSfile*/
393
394/* Emacs Setup Variables */
395/* Local Variables:      */
396/* mode: C               */
397/* indent-tabs-mode: nil */
398/* c-basic-offset: 4     */
399/* End:                  */
400