|
|
|
|
|
|
#include "module.h" |
#include "gdbm_machine.h" |
#include "threads.h" |
|
|
|
#include "interpret.h" |
#include "module_support.h" |
|
#if defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM) |
|
#include <gdbm.h> |
|
#ifdef _REENTRANT |
static PIKE_MUTEX_T gdbm_lock STATIC_MUTEX_INIT; |
#endif |
|
#define sp Pike_sp |
static struct program *iterator; |
struct gdbm_glue |
{ |
GDBM_FILE dbf; |
struct pike_string *iter; |
}; |
|
#define THIS ((struct gdbm_glue *)(Pike_fp->current_storage)) |
|
static void do_free(void) |
{ |
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(); |
} |
if(THIS->iter) |
{ |
free_string(THIS->iter); |
THIS->iter=0; |
} |
} |
|
|
#ifndef GDBM_SYNC |
#define GDBM_SYNC 0 |
#endif |
#ifndef GDBM_NOLOCK |
#define GDBM_NOLOCK 0 |
#endif |
|
static int fixmods(char *mods) |
{ |
int mode = 0; |
int flags = GDBM_NOLOCK; |
while(1) |
{ |
switch(*(mods++)) |
{ |
case 0: |
switch(mode) { |
default: |
case 0x0: |
Pike_error("No mode given for gdbm->open()\n"); |
case 0x1: |
return GDBM_READER; |
case 0x3: |
return GDBM_WRITER | flags; |
case 0x7: |
return GDBM_WRCREAT | flags; |
case 0xf: |
return GDBM_NEWDB | flags; |
} |
|
case 'r': case 'R': mode = 0x1; break; |
case 'w': case 'W': mode = 0x3; break; |
case 'c': case 'C': mode = 0x7; break; |
case 't': case 'T': mode = 0xf; break; |
|
|
|
|
case 'f': case 'F': flags |= GDBM_FAST; break; |
|
|
|
|
|
case 's': case 'S': flags |= GDBM_SYNC; break; |
|
case 'l': case 'L': flags &= ~GDBM_NOLOCK; break; |
|
default: |
Pike_error("Bad mode flag '%c' in gdbm->open.\n", mods[-1]); |
} |
} |
} |
|
void gdbmmod_fatal(const char *err) |
{ |
Pike_error("GDBM: %s\n",err); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void gdbmmod_create(INT32 args) |
{ |
struct gdbm_glue *this=THIS; |
do_free(); |
if(!args) |
Pike_error("Need at least one argument to Gdbm.DB, the filename\n"); |
if(args) |
{ |
GDBM_FILE tmp; |
struct pike_string *tmp2; |
int rwmode = GDBM_WRCREAT|GDBM_NOLOCK; |
|
if(TYPEOF(sp[-args]) != T_STRING) |
Pike_error("Bad argument 1 to gdbm->create()\n"); |
|
if(args>1) |
{ |
if(TYPEOF(sp[1-args]) != T_STRING) |
Pike_error("Bad argument 2 to gdbm->create()\n"); |
|
rwmode=fixmods(sp[1-args].u.string->str); |
} |
|
if (this->dbf) { |
do_free(); |
} |
|
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(); |
|
if(!Pike_fp->current_object->prog) |
{ |
if(tmp) gdbm_close(tmp); |
Pike_error("Object destructed in gdbm->create()\n"); |
} |
this->dbf=tmp; |
|
if(!this->dbf) |
Pike_error("Failed to open GDBM database: %d: %s.\n", |
gdbm_errno, gdbm_strerror(gdbm_errno)); |
} |
} |
|
#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) |
|
|
|
|
|
|
|
static void gdbmmod_fetch(INT32 args) |
{ |
struct gdbm_glue *this=THIS; |
datum key,ret; |
|
if(!args) |
Pike_error("Too few arguments to gdbm->fetch()\n"); |
|
if(TYPEOF(sp[-args]) != T_STRING) |
Pike_error("Bad argument 1 to gdbm->fetch()\n"); |
|
if(!THIS->dbf) |
Pike_error("GDBM database not open.\n"); |
|
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_undefined(); |
} |
} |
|
|
|
|
|
|
|
static void gdbmmod_delete(INT32 args) |
{ |
struct gdbm_glue *this=THIS; |
datum key; |
int ret; |
if(!args) |
Pike_error("Too few arguments to gdbm->delete()\n"); |
|
if(TYPEOF(sp[-args]) != T_STRING) |
Pike_error("Bad argument 1 to gdbm->delete()\n"); |
|
if(!this->dbf) |
Pike_error("GDBM database not open.\n"); |
|
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( ret==0 ); |
} |
|
|
|
|
|
|
|
|
|
|
static void gdbmmod_m_delete(INT32 args) |
{ |
struct gdbm_glue *this=THIS; |
datum key, ret; |
|
if(TYPEOF(sp[-args]) != T_STRING) |
{ |
push_undefined(); |
return; |
} |
|
if(!this->dbf) |
Pike_error("GDBM database not open.\n"); |
|
STRING_TO_DATUM(key, sp[-args].u.string); |
|
THREADS_ALLOW(); |
mt_lock(& gdbm_lock); |
ret=gdbm_fetch(this->dbf, key); |
if( ret.dptr ) |
if( gdbm_delete(this->dbf, key) ) |
Pike_error("Failed to delete key from database.\n"); |
mt_unlock(& gdbm_lock); |
THREADS_DISALLOW(); |
if(ret.dptr) |
{ |
push_string(DATUM_TO_STRING(ret)); |
free(ret.dptr); |
}else{ |
push_undefined(); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void gdbmmod_firstkey(INT32 args) |
{ |
struct gdbm_glue *this=THIS; |
datum ret; |
pop_n_elems(args); |
|
if(!this->dbf) Pike_error("GDBM database not open.\n"); |
|
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); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void gdbmmod_nextkey(INT32 args) |
{ |
struct gdbm_glue *this=THIS; |
datum key,ret; |
if(!args) |
Pike_error("Too few arguments to gdbm->nextkey()\n"); |
|
if(TYPEOF(sp[-args]) != T_STRING) |
Pike_error("Bad argument 1 to gdbm->nextkey()\n"); |
|
if(!THIS->dbf) |
Pike_error("GDBM database not open.\n"); |
|
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); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void gdbmmod_store(INT32 args) |
{ |
struct gdbm_glue *this=THIS; |
datum key,data; |
int method = GDBM_REPLACE; |
int ret; |
if(args<2) |
Pike_error("Too few arguments to gdbm->store()\n"); |
|
if(TYPEOF(sp[-args]) != T_STRING) |
Pike_error("Bad argument 1 to gdbm->store()\n"); |
|
if(TYPEOF(sp[1-args]) != T_STRING) |
Pike_error("Bad argument 2 to gdbm->store()\n"); |
|
if (args > 2) { |
if (TYPEOF(sp[2-args]) != T_INT) { |
Pike_error("Bad argument 3 to gdbm->store()\n"); |
} |
if (sp[2-args].u.integer) { |
method = GDBM_INSERT; |
} |
} |
|
if(!THIS->dbf) |
Pike_error("GDBM database not open.\n"); |
|
STRING_TO_DATUM(key, sp[-args].u.string); |
STRING_TO_DATUM(data, sp[1-args].u.string); |
|
THREADS_ALLOW(); |
mt_lock(& gdbm_lock); |
ret=gdbm_store(this->dbf, key, data, method); |
mt_unlock(& gdbm_lock); |
THREADS_DISALLOW(); |
|
if(ret == -1) { |
Pike_error("GDBM database not open for writing.\n"); |
} else if (ret == 1) { |
Pike_error("Duplicate key.\n"); |
} |
ref_push_string(sp[1-args].u.string); |
stack_pop_n_elems_keep_top(args); |
} |
|
|
static void gdbmmod_store_compat(INT32 args) |
{ |
gdbmmod_store(args); |
pop_stack(); |
push_int(1); |
} |
|
|
|
|
|
|
|
|
|
static void gdbmmod_reorganize(INT32 args) |
{ |
struct gdbm_glue *this=THIS; |
int ret; |
pop_n_elems(args); |
|
if(!THIS->dbf) Pike_error("GDBM database not open.\n"); |
THREADS_ALLOW(); |
mt_lock(& gdbm_lock); |
ret=gdbm_reorganize(this->dbf); |
mt_unlock(& gdbm_lock); |
THREADS_DISALLOW(); |
pop_n_elems(args); |
push_int(ret); |
} |
|
|
|
|
|
|
|
|
|
static void gdbmmod_sync(INT32 UNUSED(args)) |
{ |
struct gdbm_glue *this=THIS; |
if(!THIS->dbf) Pike_error("GDBM database not open.\n"); |
THREADS_ALLOW(); |
mt_lock(& gdbm_lock); |
gdbm_sync(this->dbf); |
mt_unlock(& gdbm_lock); |
THREADS_DISALLOW(); |
} |
|
|
static void gdbmmod_iter_first(INT32 UNUSED(args)) |
{ |
struct gdbm_glue *this=THIS; |
gdbmmod_firstkey(0); |
if( Pike_sp[-1].u.string ) |
this->iter = Pike_sp[-1].u.string; |
Pike_sp--; |
push_int( !!this->iter ); |
} |
|
static void gdbmmod_iter_next(INT32 UNUSED(args)) |
{ |
struct gdbm_glue *this=THIS; |
if(!this->iter) |
{ |
push_undefined(); |
return; |
} |
push_string( this->iter ); |
gdbmmod_nextkey(1); |
if( TYPEOF(Pike_sp[-1]) != PIKE_T_STRING ) |
{ |
this->iter = 0; |
push_undefined(); |
return; |
} |
this->iter = Pike_sp[-1].u.string; |
push_int(1); |
return; |
|
} |
|
static void gdbmmod_iter_index(INT32 UNUSED(args)) |
{ |
struct gdbm_glue *this=THIS; |
if( this->iter ) |
ref_push_string( this->iter ); |
else |
push_undefined(); |
} |
|
static void gdbmmod_iter_no_value(INT32 UNUSED(args)) |
{ |
push_int( !THIS->iter ); |
} |
|
static void gdbmmod_iter_value(INT32 UNUSED(args)) |
{ |
struct gdbm_glue *this=THIS; |
if( this->iter ) |
{ |
ref_push_string( this->iter ); |
gdbmmod_fetch(1); |
} |
else |
push_undefined(); |
} |
|
|
|
|
|
|
|
|
|
static void gdbmmod_indices(INT32 UNUSED(args)) |
{ |
struct gdbm_glue *this=THIS; |
struct svalue *start = Pike_sp; |
gdbmmod_iter_first(0); |
pop_stack(); |
while( this->iter ) |
{ |
ref_push_string( this->iter ); |
gdbmmod_iter_next(0); |
pop_stack(); |
} |
push_array(aggregate_array( Pike_sp-start )); |
} |
|
|
|
|
|
|
|
|
|
static void gdbmmod_values(INT32 UNUSED(args)) |
{ |
struct gdbm_glue *this=THIS; |
struct svalue *start = Pike_sp; |
gdbmmod_iter_first(0); |
pop_stack(); |
|
while( this->iter ) |
{ |
ref_push_string( this->iter ); |
gdbmmod_fetch(1); |
gdbmmod_iter_next(0); |
pop_stack(); |
} |
push_array(aggregate_array( Pike_sp-start )); |
} |
|
static void gdbmmod_get_iterator(INT32 UNUSED(args)) |
{ |
push_object( clone_object( iterator, 0 ) ); |
*((struct gdbm_glue *)Pike_sp[-1].u.object->storage) = *THIS; |
apply(Pike_sp[-1].u.object, "first", 0); |
pop_stack(); |
} |
|
|
|
|
|
|
|
|
|
|
|
static void gdbmmod_close(INT32 UNUSED(args)) |
{ |
do_free(); |
} |
|
static void exit_gdbm_glue(struct object *UNUSED(o)) |
{ |
do_free(); |
} |
|
static void exit_gdbm_iterator(struct object *UNUSED(o)) |
{ |
if( THIS->iter ) |
free_string( THIS->iter ); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM) */ |
|
PIKE_MODULE_EXIT { |
} |
|
PIKE_MODULE_INIT |
{ |
struct program *db; |
#if defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM) |
start_new_program(); |
ADD_STORAGE(struct gdbm_glue); |
|
|
ADD_FUNCTION("create", gdbmmod_create, |
tFunc(tOr(tVoid,tStr) tOr(tVoid,tStr), tVoid), ID_PROTECTED); |
|
|
ADD_FUNCTION("close",gdbmmod_close,tFunc(tNone,tVoid),0); |
|
ADD_FUNCTION("store", gdbmmod_store_compat, |
tFunc(tStr8 tStr8 tOr(tInt01, tVoid), tInt), 0); |
|
ADD_FUNCTION("`[]=", gdbmmod_store, |
tFunc(tStr8 tSetvar(0, tStr8) tOr(tInt01, tVoid), tVar(0)), 0); |
|
ADD_FUNCTION("fetch",gdbmmod_fetch,tFunc(tStr,tStr8),0); |
|
ADD_FUNCTION("`[]",gdbmmod_fetch,tFunc(tStr,tStr8),0); |
|
ADD_FUNCTION("delete",gdbmmod_delete,tFunc(tStr,tInt01),0); |
|
ADD_FUNCTION("firstkey",gdbmmod_firstkey,tFunc(tNone,tStr8),0); |
|
ADD_FUNCTION("nextkey",gdbmmod_nextkey,tFunc(tStr,tStr8),0); |
|
ADD_FUNCTION("reorganize",gdbmmod_reorganize,tFunc(tNone,tInt),0); |
|
ADD_FUNCTION("sync",gdbmmod_sync,tFunc(tNone,tVoid),0); |
|
|
ADD_FUNCTION("_get_iterator", gdbmmod_get_iterator,tFunc(tNone,tObj),0); |
|
|
ADD_FUNCTION("_m_delete",gdbmmod_m_delete,tFunc(tStr,tStr8),0); |
ADD_FUNCTION("_values",gdbmmod_values,tFunc(tNone,tArr(tStr8)),0); |
ADD_FUNCTION("_indices",gdbmmod_indices,tFunc(tNone,tArr(tStr8)),0); |
|
set_exit_callback(exit_gdbm_glue); |
db = end_program(); |
add_program_constant( "DB", db, 0 ); |
add_program_constant( "gdbm", db, 0 ); |
free_program(db); |
|
start_new_program(); |
ADD_STORAGE(struct gdbm_glue); |
ADD_FUNCTION("first", gdbmmod_iter_first,tFunc(tNone,tInt01),0); |
ADD_FUNCTION("_iterator_next", gdbmmod_iter_next, tFunc(tNone,tInt01), |
ID_PROTECTED); |
ADD_FUNCTION("_iterator_index", gdbmmod_iter_index, tFunc(tNone,tStr8), |
ID_PROTECTED); |
ADD_FUNCTION("_iterator_value", gdbmmod_iter_value, tFunc(tNone,tStr8), |
ID_PROTECTED); |
ADD_FUNCTION("`!", gdbmmod_iter_no_value, tFunc(tNone,tInt01), |
ID_PROTECTED); |
set_exit_callback(exit_gdbm_iterator); |
iterator = end_program(); |
add_program_constant( "Iterator", iterator, 0 ); |
free_program(iterator); |
#else |
HIDE_MODULE(); |
#endif |
} |
|
|