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