pike.git
/
src
/
modules
/
Gdbm
/
gdbmmod.c
version
»
Context lines:
10
20
40
80
file
none
3
pike.git/src/modules/Gdbm/gdbmmod.c:1:
/* || 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. */
-
#include "
global
.h"
+
#include "
module
.h"
#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 "module.h"
+
#include "program.h" #include "module_support.h" #if defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM) #include <gdbm.h> #ifdef _REENTRANT
-
static MUTEX_T gdbm_lock STATIC_MUTEX_INIT;
+
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;
}
-
+
}
/* Compat with old gdbm. */ #ifndef GDBM_SYNC #define GDBM_SYNC 0 #endif #ifndef GDBM_NOLOCK #define GDBM_NOLOCK 0 #endif static int fixmods(char *mods)
pike.git/src/modules/Gdbm/gdbmmod.c:98:
case 's': case 'S': flags |= GDBM_SYNC; break; /* NOTE: This flag is inverted! */ case 'l': case 'L': flags &= ~GDBM_NOLOCK; break; default: Pike_error("Bad mode flag '%c' in gdbm->open.\n", mods[-1]); } } }
-
void gdbmmod_fatal(char *err)
+
void gdbmmod_fatal(
const
char *err)
{ Pike_error("GDBM: %s\n",err); } /*! @module Gdbm
-
+
*!
+
*! This module provides an interface to the GNU dbm database.
+
*!
+
*! The basic use of GDBM is to store key/data pairs in a data
+
*! file. Each key must be unique and each key is paired with only one
+
*! data item.
+
*!
+
*! The library provides primitives for storing key/data pairs,
+
*! searching and retrieving the data by its key and deleting a key
+
*! along with its data. It also support sequential iteration over all
+
*! key/data pairs in a database.
+
*!
+
*! The @[DB] class also overloads enough operators to make it behave
+
*! a lot like a @tt{mapping(string(8bit):string(8bit))@}, you can index it,
+
*! assign indices and loop over it using foreach.
+
*!
*/
-
/*! @class
gdbm
+
/*! @class
DB
*/
-
/*! @decl void create(void|string file, void|string mode)
+
/*! @decl
protected
void create(void|string file, void|string
(99..119)
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
pike.git/src/modules/Gdbm/gdbmmod.c:145:
*! 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 @expr{"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
.
+
*! program.
+
*!
+
*!
@[atexit] might
be
useful.
+
*!
+
*!
This is very important if
the
database is used with the 'l' flag
.
*/ 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)
pike.git/src/modules/Gdbm/gdbmmod.c:184:
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->
open
()
n
");
+
Pike_error("Object destructed in gdbm->
create
()
\n
");
} this->dbf=tmp;
-
pop_n_elems(args);
+
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)
-
/*! @decl string fetch(string key)
-
*! @decl string `[](string key)
+
/*! @decl string
(8bit)
fetch(string
(8bit)
key)
+
*! @decl string
(8bit)
`[](string
(8bit)
key)
*! *! Return the data associated with the key 'key' in the database. *! If there was no such key in the database, zero is returned. */
-
+
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");
pike.git/src/modules/Gdbm/gdbmmod.c:233:
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
);
+
push_
undefined
();
} } /*! @decl int(0..1) delete(string key) *! *! Remove a key from the database. Returns 1 if successful, *! otherwise 0, e.g. when the item is not present or the *! database is read only. */
-
+
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");
pike.git/src/modules/Gdbm/gdbmmod.c:270:
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 ); }
+
/*! @decl string(8bit) _m_delete( string(8bit) key )
+
*!
+
*! Provides overloading of the @[m_delete] function.
+
*!
+
*! Will return the value the key had before it was removed, if any
+
*!
+
*! If the key exists but deletion fails (usually due to a read only
+
*! database) this function will throw an error.
+
*/
+
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();
+
}
+
}
+
/*! @decl string firstkey() *! *! Return the first key in the database, this can be any key in the *! database.
-
+
*!
+
*! Used together with @[nextkey] the databse can be iterated.
+
*!
+
*! @note
+
*! The database also works as an @[Iterator], and can be used as the
+
*! first argument to @[foreach].
+
*!
+
*! Adding or removing keys will change the iteration order,
+
*! this can cause keys to be skipped while iterating.
+
*!
+
*!
+
*! @example
+
*! @code
+
*! // Write the contents of the database
+
*! for(key=gdbm->firstkey(); k; k=gdbm->nextkey(k))
+
*! write(k+":"+gdbm->fetch(k)+"\n");
+
*! @endcode
+
*!
+
*! Or, using foreach
+
*! @code
+
*! // Write the contents of the database
+
*! foreach( db; string key; string value )
+
*! write(key+":"+value+"\n");
+
*! @endcode
*/ 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");
pike.git/src/modules/Gdbm/gdbmmod.c:299:
if(ret.dptr) { push_string(DATUM_TO_STRING(ret)); free(ret.dptr); }else{ push_int(0); } }
-
/*! @decl string nextkey(string key)
+
/*! @decl string
(8bit)
nextkey(string
(8bit)
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. *!
-
+
*! @note
+
*! Changing (adding or removing keys) the database while iterating
+
*! can cause keys to be skipped.
+
*!
+
*! The database also works as an @[Iterator], and can be used as the
+
*! first argument to @[foreach].
+
*!
*! @example
-
+
*! @code
*! // Write the contents of the database *! for(key=gdbm->firstkey(); k; k=gdbm->nextkey(k)) *! write(k+":"+gdbm->fetch(k)+"\n");
-
+
*! @endcode
+
*!
+
*! Or, using foreach
+
*! @code
+
*! // Write the contents of the database
+
*! foreach( db; string key; string value )
+
*! write(key+":"+value+"\n");
+
*! @endcode
+
*!
*/ 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.git/src/modules/Gdbm/gdbmmod.c:341:
pop_n_elems(args); if(ret.dptr) { push_string(DATUM_TO_STRING(ret)); free(ret.dptr); }else{ push_int(0); } }
-
/*! @decl string `[]= (string key, string data)
+
/*! @decl string `[]= (string
(8bit)
key, string
(8bit)
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; *! *! @returns
pike.git/src/modules/Gdbm/gdbmmod.c:436:
gdbmmod_store(args); pop_stack(); push_int(1); } /*! @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.
+
*! can take a LOT of time to run
if the database is big
.
*/ 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();
pike.git/src/modules/Gdbm/gdbmmod.c:463:
} /*! @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. */
-
static void gdbmmod_sync(INT32 args)
+
static void gdbmmod_sync(INT32
UNUSED(
args)
)
{ struct gdbm_glue *this=THIS;
-
pop_n_elems(args);
-
+
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();
-
push_int(0);
+
}
-
/*! @decl
void
close
()
+
+
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();
+
}
+
+
/*! @decl
array(string(8bit))
_indices
()
*!
-
*!
Closes
the
database
.
+
*!
Provides
overloading of @[indices].
+
*!
+
*! @note
+
*! Mainly useful when debugging,
the
returned list might not fit in
+
*! memory for large databases
.
*/
-
+
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_
close
(INT32 args)
+
/*! @decl array(string(8bit)) _values()
+
*!
+
*! Provides overloading of @[values].
+
*!
+
*! @note
+
*! Mainly useful when debugging, the returned list might not fit in
+
*! memory for large databases.
+
*/
+
static void gdbmmod_
values
(INT32
UNUSED(
args)
)
{
-
pop
_
n
_
elems
(
args
);
+
struct gdbm
_
glue *this=THIS;
+
struct svalue *start = Pike
_
sp;
+
gdbmmod_iter_first
(
0
);
+
pop_stack();
-
do
_
free
();
-
push
_
int
(0);
+
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
init
_
gdbm
_
glue
(
struct
object *
UNUSED(
o
))
+
static void
gdbmmod
_
get
_
iterator
(
INT32
UNUSED(
args
))
{
-
THIS
->
dbf
=0;
+
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();
}
-
+
+
/*! @decl void close()
+
*!
+
*! Closes the database. The object is no longer usable after this function has been called.
+
*!
+
*! This is also done automatically when the object is destructed for
+
*! any reason (running out of references or explicit destruct, as an
+
*! example)
+
*/
+
+
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 );
+
}
+
/*! @endclass */
-
+
/*! @class Iterator
+
*! @inherit predef::Iterator
+
*!
+
*! Object keeping track of an iteration over a @[DB]
+
*!
+
*! @note
+
*! Can not be usefully constructed manually, instead use the database
+
*! as the first argument to @[foreach] or @[predef::get_iterator]
+
*!
+
*/
+
+
/*! @endclass
+
*/
+
/*! @endmodule */ #endif /* defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM) */
-
PIKE_MODULE_EXIT
{}
+
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); /* function(void|string,void|string:void) */ ADD_FUNCTION("create", gdbmmod_create,
-
tFunc(tOr(tVoid,tStr) tOr(tVoid,tStr), tVoid),
0 /*
ID_PROTECTED
*/
);
+
tFunc(tOr(tVoid,tStr) tOr(tVoid,tStr), tVoid), ID_PROTECTED);
/* function(:void) */ ADD_FUNCTION("close",gdbmmod_close,tFunc(tNone,tVoid),0); /* function(string, string, int(0..1)|void: int) */ ADD_FUNCTION("store", gdbmmod_store_compat,
-
tFunc(
tStr
tStr
tOr(tInt01, tVoid), tInt), 0);
+
tFunc(
tStr8
tStr8
tOr(tInt01, tVoid), tInt), 0);
/* function(string, string, int(0..1)|void: string) */ ADD_FUNCTION("`[]=", gdbmmod_store,
-
tFunc(
tStr
tSetvar(0,
tStr
) tOr(tInt01, tVoid), tVar(0)), 0);
+
tFunc(
tStr8
tSetvar(0,
tStr8
) tOr(tInt01, tVoid), tVar(0)), 0);
/* function(string:string) */
-
ADD_FUNCTION("fetch",gdbmmod_fetch,tFunc(tStr,
tStr
),0);
+
ADD_FUNCTION("fetch",gdbmmod_fetch,tFunc(tStr,
tStr8
),0);
/* function(string:string) */
-
ADD_FUNCTION("`[]",gdbmmod_fetch,tFunc(tStr,
tStr
),0);
+
ADD_FUNCTION("`[]",gdbmmod_fetch,tFunc(tStr,
tStr8
),0);
/* function(string:int) */ ADD_FUNCTION("delete",gdbmmod_delete,tFunc(tStr,tInt01),0); /* function(:string) */
-
ADD_FUNCTION("firstkey",gdbmmod_firstkey,tFunc(tNone,
tStr
),0);
+
ADD_FUNCTION("firstkey",gdbmmod_firstkey,tFunc(tNone,
tStr8
),0);
/* function(string:string) */
-
ADD_FUNCTION("nextkey",gdbmmod_nextkey,tFunc(tStr,
tStr
),0);
+
ADD_FUNCTION("nextkey",gdbmmod_nextkey,tFunc(tStr,
tStr8
),0);
/* function(:int) */ ADD_FUNCTION("reorganize",gdbmmod_reorganize,tFunc(tNone,tInt),0); /* function(:void) */ ADD_FUNCTION("sync",gdbmmod_sync,tFunc(tNone,tVoid),0);
-
set
_
init
_
callback
(
init
_
gdbm
_
glue
);
+
/* iterator API.*/
+
ADD
_
FUNCTION("
_
get_iterator", gdbmmod_get_iterator,tFunc
(
tNone,tObj),0);
+
+
/* some mapping operator overloading */
+
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);
-
end_
class
("gdbm",0);
+
db =
end_
program
(
);
+
add_program_constant(
"
DB", db, 0 );
+
add_program_constant( "
gdbm",
db,
0
);
/* compat (...-7.8). */
+
free_program(db);
+
+
start_new_program();
+
ADD_STORAGE(struct gdbm_glue);
+
ADD_FUNCTION("first", gdbmmod_iter_first,tFunc(tNone,tInt01),0);
+
ADD_FUNCTION("next", gdbmmod_iter_next, tFunc(tNone,tInt01),0);
+
ADD_FUNCTION("index", gdbmmod_iter_index,tFunc(tNone,tStr8),0);
+
ADD_FUNCTION("value", gdbmmod_iter_value,tFunc(tNone,tStr8),0);
+
ADD_FUNCTION("`!", gdbmmod_iter_no_value,tFunc(tNone,tInt01),0);
+
set_exit_callback(exit_gdbm_iterator);
+
iterator = end_program();
+
add_program_constant( "Iterator", iterator, 0 );
+
free_program(iterator);
#else
-
if(!TEST_COMPAT(7,6))
+
HIDE_MODULE(); #endif }