e576bb2002-10-11Martin Nilsson /* || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. || $Id: gdbmmod.c,v 1.21 2002/10/11 01:39:40 nilsson Exp $ */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) #include "global.h"
e576bb2002-10-11Martin Nilsson RCSID("$Id: gdbmmod.c,v 1.21 2002/10/11 01:39:40 nilsson Exp $");
a80a9c1997-02-11Fredrik Hübinette (Hubbe) #include "gdbm_machine.h" #include "threads.h" /* Todo: make sure only one thread accesses the same gdbmmod */ #include "interpret.h" #include "svalue.h" #include "stralloc.h" #include "array.h" #include "object.h"
bb55f81997-03-16Fredrik Hübinette (Hubbe) #include "pike_macros.h"
a80a9c1997-02-11Fredrik Hübinette (Hubbe) 
6dc2772000-07-28Fredrik Hübinette (Hubbe) #if defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM)
a80a9c1997-02-11Fredrik Hübinette (Hubbe) #include <gdbm.h>
2505872002-09-25Marcus Comstedt #endif /* defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM) */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) 
6ad2372002-05-11Martin Nilsson /* THIS MUST BE INCLUDED LAST */ #include "module_magic.h"
2505872002-09-25Marcus Comstedt #if defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM) #ifdef _REENTRANT static MUTEX_T gdbm_lock STATIC_MUTEX_INIT; #endif
6ad2372002-05-11Martin Nilsson #define sp Pike_sp
a80a9c1997-02-11Fredrik Hübinette (Hubbe) struct gdbm_glue { GDBM_FILE dbf; };
39221e2000-07-07Henrik Grubbström (Grubba) #define THIS ((struct gdbm_glue *)(Pike_fp->current_storage))
a80a9c1997-02-11Fredrik Hübinette (Hubbe) 
be478c1997-08-30Henrik Grubbström (Grubba) static void do_free(void)
a80a9c1997-02-11Fredrik Hübinette (Hubbe) { if(THIS->dbf) { GDBM_FILE dbf; dbf=THIS->dbf; THIS->dbf=0; THREADS_ALLOW(); mt_lock(& gdbm_lock); gdbm_close(dbf); mt_unlock(& gdbm_lock); THREADS_DISALLOW(); } } static int fixmods(char *mods) { int mode; mode=0; while(1) { switch(*(mods++)) { case 0: switch(mode & 15) {
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  default: Pike_error("No mode given for gdbm->open()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  case 1|16: case 1: mode=GDBM_READER; break; case 3: mode=GDBM_WRITER; break; case 3|16: mode=GDBM_WRITER | GDBM_FAST; break; case 7: mode=GDBM_WRCREAT; break; case 7|16: mode=GDBM_WRCREAT | GDBM_FAST; break; case 15: mode=GDBM_NEWDB; break; case 15|16: mode=GDBM_NEWDB | GDBM_FAST; break; } return mode; case 'r': case 'R': mode|=1; break; case 'w': case 'W': mode|=3; break; case 'c': case 'C': mode|=7; break; case 't': case 'T': mode|=15; break; case 'f': case 'F': mode|=16; break; default:
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Bad mode flag in gdbm->open.\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  } } } void gdbmmod_fatal(char *err) {
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("GDBM: %s\n",err);
a80a9c1997-02-11Fredrik Hübinette (Hubbe) }
fc293f2001-10-26Martin Nilsson /*! @module Gdbm */ /*! @class gdbm */ /*! @decl void create(void|string file, void|string mode) *! *! Without arguments, this function does nothing. With one argument it *! opens the given file as a gdbm database, if this fails for some *! reason, an error will be generated. If a second argument is present, *! it specifies how to open the database using one or more of the follow *! flags in a string: *! *! @string *! @value r *! Open database for reading *! @value w *! Open database for writing *! @value c *! Create database if it does not exist *! @value t *! Overwrite existing database *! @value f *! Fast mode *! @endstring *! *! The fast mode prevents the database from syncronizing each change *! in the database immediately. This is dangerous because the database *! can be left in an unusable state if Pike is terminated abnormally. *! *! The default mode is @tt{"rwc"@}. *! *! @note *! The gdbm manual states that it is important that the database is *! closed properly. Unfortunately this will not be the case if Pike *! calls exit() or returns from main(). You should therefore make sure *! you call close or destruct your gdbm objects when exiting your *! program. This will probably be done automatically in the future. */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) static void gdbmmod_create(INT32 args) { struct gdbm_glue *this=THIS; do_free(); if(args) { GDBM_FILE tmp; struct pike_string *tmp2; int rwmode = GDBM_WRCREAT; if(sp[-args].type != T_STRING)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Bad argument 1 to gdbm->create()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  if(args>1) { if(sp[1-args].type != T_STRING)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Bad argument 2 to gdbm->create()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  rwmode=fixmods(sp[1-args].u.string->str); } tmp2=sp[-args].u.string; THREADS_ALLOW(); mt_lock(& gdbm_lock); tmp=gdbm_open(tmp2->str, 512, rwmode, 00666, gdbmmod_fatal); mt_unlock(& gdbm_lock); THREADS_DISALLOW();
39221e2000-07-07Henrik Grubbström (Grubba)  if(!Pike_fp->current_object->prog)
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  { if(tmp) gdbm_close(tmp);
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Object destructed in gdbm->open()n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  } THIS->dbf=tmp; pop_n_elems(args); if(!THIS->dbf)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Failed to open GDBM database.\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  } } #define STRING_TO_DATUM(dat, st) dat.dptr=st->str,dat.dsize=st->len; #define DATUM_TO_STRING(dat) make_shared_binary_string(dat.dptr, dat.dsize)
fc293f2001-10-26Martin Nilsson /*! @decl string fetch(string key) *! @decl string `[](string key) *! *! Return the data associated with the key 'key' in the database. *! If there was no such key in the database, zero is returned. */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) static void gdbmmod_fetch(INT32 args) { struct gdbm_glue *this=THIS; datum key,ret; if(!args)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Too few arguments to gdbm->fetch()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  if(sp[-args].type != T_STRING)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Bad argument 1 to gdbm->fetch()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  if(!THIS->dbf)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("GDBM database not open.\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  STRING_TO_DATUM(key, sp[-args].u.string); THREADS_ALLOW(); mt_lock(& gdbm_lock); ret=gdbm_fetch(this->dbf, key); mt_unlock(& gdbm_lock); THREADS_DISALLOW(); pop_n_elems(args); if(ret.dptr) { push_string(DATUM_TO_STRING(ret)); free(ret.dptr); }else{ push_int(0); } }
fc293f2001-10-26Martin Nilsson /*! @decl int delete(string key) *! *! Remove a key from the database. Note that no error will be generated *! if the key does not exist. */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) static void gdbmmod_delete(INT32 args) { struct gdbm_glue *this=THIS; datum key; int ret; if(!args)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Too few arguments to gdbm->delete()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  if(sp[-args].type != T_STRING)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Bad argument 1 to gdbm->delete()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  if(!this->dbf)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("GDBM database not open.\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  STRING_TO_DATUM(key, sp[-args].u.string); THREADS_ALLOW(); mt_lock(& gdbm_lock); ret=gdbm_delete(this->dbf, key); mt_unlock(& gdbm_lock); THREADS_DISALLOW(); pop_n_elems(args); push_int(0); }
fc293f2001-10-26Martin Nilsson /*! @decl string firstkey() *! *! Return the first key in the database, this can be any key in the *! database. */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) static void gdbmmod_firstkey(INT32 args) { struct gdbm_glue *this=THIS; datum ret; pop_n_elems(args);
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  if(!this->dbf) Pike_error("GDBM database not open.\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  THREADS_ALLOW(); mt_lock(& gdbm_lock); ret=gdbm_firstkey(this->dbf); mt_unlock(& gdbm_lock); THREADS_DISALLOW(); if(ret.dptr) { push_string(DATUM_TO_STRING(ret)); free(ret.dptr); }else{ push_int(0); } }
fc293f2001-10-26Martin Nilsson /*! @decl string nextkey(string key) *! *! This returns the key in database that follows the key 'key' key. *! This is of course used to iterate over all keys in the database. *! *! @example *! // Write the contents of the database *! for(key=gdbm->firstkey(); k; k=gdbm->nextkey(k)) *! write(k+":"+gdbm->fetch(k)+"\n"); */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) static void gdbmmod_nextkey(INT32 args) { struct gdbm_glue *this=THIS; datum key,ret; if(!args)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Too few arguments to gdbm->nextkey()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  if(sp[-args].type != T_STRING)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Bad argument 1 to gdbm->nextkey()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  if(!THIS->dbf)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("GDBM database not open.\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  STRING_TO_DATUM(key, sp[-args].u.string); THREADS_ALLOW(); mt_lock(& gdbm_lock); ret=gdbm_nextkey(this->dbf, key); mt_unlock(& gdbm_lock); THREADS_DISALLOW(); pop_n_elems(args); if(ret.dptr) { push_string(DATUM_TO_STRING(ret)); free(ret.dptr); }else{ push_int(0); } }
fc293f2001-10-26Martin Nilsson /*! @decl int store(string key, string data) *! @decl int `[]= (string key, stirng data) *! *! Associate the contents of 'data' with the key 'key'. If the key 'key' *! already exists in the database the data for that key will be replaced. *! If it does not exist it will be added. An error will be generated if *! the database was not open for writing. *! *! @example *! gdbm[key] = data; */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) static void gdbmmod_store(INT32 args) { struct gdbm_glue *this=THIS; datum key,data;
04878b2001-01-23John W. Pierce  int method = GDBM_REPLACE;
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  int ret; if(args<2)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Too few arguments to gdbm->store()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  if(sp[-args].type != T_STRING)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Bad argument 1 to gdbm->store()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  if(sp[1-args].type != T_STRING)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Bad argument 2 to gdbm->store()\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe) 
04878b2001-01-23John W. Pierce  if (args > 2) { if (sp[2-args].type != T_INT) { Pike_error("Bad argument 3 to gdbm->store()\n"); } if (sp[2-args].u.integer) { method = GDBM_INSERT; } }
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  if(!THIS->dbf)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("GDBM database not open.\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  STRING_TO_DATUM(key, sp[-args].u.string); STRING_TO_DATUM(data, sp[1-args].u.string); THREADS_ALLOW(); mt_lock(& gdbm_lock);
04878b2001-01-23John W. Pierce  ret=gdbm_store(this->dbf, key, data, method);
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  mt_unlock(& gdbm_lock); THREADS_DISALLOW();
04878b2001-01-23John W. Pierce  if(ret == -1) {
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("GDBM database not open for writing.\n");
04878b2001-01-23John W. Pierce  } else if (ret == 1) { Pike_error("Duplicate key.\n"); }
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  pop_n_elems(args); push_int(ret == 0); }
fc293f2001-10-26Martin Nilsson /*! @decl int reorganize() *! *! Deletions and insertions into the database can cause fragmentation *! which will make the database bigger. This routine reorganizes the *! contents to get rid of fragmentation. Note however that this function *! can take a LOT of time to run. */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) static void gdbmmod_reorganize(INT32 args) { struct gdbm_glue *this=THIS; int ret; pop_n_elems(args);
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  if(!THIS->dbf) Pike_error("GDBM database not open.\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  THREADS_ALLOW(); mt_lock(& gdbm_lock); ret=gdbm_reorganize(this->dbf); mt_unlock(& gdbm_lock); THREADS_DISALLOW(); pop_n_elems(args); push_int(ret); }
fc293f2001-10-26Martin Nilsson /*! @decl void sync() *! *! When opening the database with the 'f' flag writings to the database *! can be cached in memory for a long time. Calling sync will write *! all such caches to disk and not return until everything is stored *! on the disk. */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) static void gdbmmod_sync(INT32 args) { struct gdbm_glue *this=THIS; pop_n_elems(args);
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  if(!THIS->dbf) Pike_error("GDBM database not open.\n");
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  THREADS_ALLOW(); mt_lock(& gdbm_lock); gdbm_sync(this->dbf); mt_unlock(& gdbm_lock); THREADS_DISALLOW(); push_int(0); }
fc293f2001-10-26Martin Nilsson /*! @decl void close() *! *! Closes the database. */
a80a9c1997-02-11Fredrik Hübinette (Hubbe) static void gdbmmod_close(INT32 args) { pop_n_elems(args);
1458b91997-08-31Henrik Grubbström (Grubba)  do_free();
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  push_int(0); } static void init_gdbm_glue(struct object *o) { THIS->dbf=0; } static void exit_gdbm_glue(struct object *o) { do_free(); }
fc293f2001-10-26Martin Nilsson /*! @endclass */ /*! @endmodule */ #endif /* defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM) */
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  void pike_module_exit(void) {} void pike_module_init(void) { #if defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM) start_new_program();
90e9781999-01-31Fredrik Hübinette (Hubbe)  ADD_STORAGE(struct gdbm_glue);
a80a9c1997-02-11Fredrik Hübinette (Hubbe) 
45ee5d1999-02-10Fredrik Hübinette (Hubbe)  /* function(void|string,void|string:void) */
04878b2001-01-23John W. Pierce  ADD_FUNCTION("create", gdbmmod_create,
4aa1e72001-01-23Henrik Grubbström (Grubba)  tFunc(tOr(tVoid,tStr) tOr(tVoid,tStr), tVoid), 0 /*ID_STATIC*/);
45ee5d1999-02-10Fredrik Hübinette (Hubbe)  /* function(:void) */
e64f5a1999-06-19Fredrik Hübinette (Hubbe)  ADD_FUNCTION("close",gdbmmod_close,tFunc(tNone,tVoid),0);
04878b2001-01-23John W. Pierce  /* function(string, string, int(0..1)|void: int) */ ADD_FUNCTION("store", gdbmmod_store, tFunc(tStr tStr tOr(tInt01, tVoid), tInt), 0); /* function(string, string, int(0..1)|void: int) */ ADD_FUNCTION("`[]=", gdbmmod_store, tFunc(tStr tStr tOr(tInt01, tVoid), tInt), 0);
45ee5d1999-02-10Fredrik Hübinette (Hubbe)  /* function(string:string) */ ADD_FUNCTION("fetch",gdbmmod_fetch,tFunc(tStr,tStr),0); /* function(string:string) */ ADD_FUNCTION("`[]",gdbmmod_fetch,tFunc(tStr,tStr),0); /* function(string:int) */ ADD_FUNCTION("delete",gdbmmod_delete,tFunc(tStr,tInt),0); /* function(:string) */
e64f5a1999-06-19Fredrik Hübinette (Hubbe)  ADD_FUNCTION("firstkey",gdbmmod_firstkey,tFunc(tNone,tStr),0);
45ee5d1999-02-10Fredrik Hübinette (Hubbe)  /* function(string:string) */ ADD_FUNCTION("nextkey",gdbmmod_nextkey,tFunc(tStr,tStr),0); /* function(:int) */
e64f5a1999-06-19Fredrik Hübinette (Hubbe)  ADD_FUNCTION("reorganize",gdbmmod_reorganize,tFunc(tNone,tInt),0);
45ee5d1999-02-10Fredrik Hübinette (Hubbe)  /* function(:void) */
e64f5a1999-06-19Fredrik Hübinette (Hubbe)  ADD_FUNCTION("sync",gdbmmod_sync,tFunc(tNone,tVoid),0);
a80a9c1997-02-11Fredrik Hübinette (Hubbe)  set_init_callback(init_gdbm_glue); set_exit_callback(exit_gdbm_glue); end_class("gdbm",0); #endif }