#include <mplib1/gdbm_util.h>

The gbdm_util functions are a set of routines which provide convenient wrappers to the core gdbm routines.

GDBM is a GNU replacement for the UNIX standard DBM/NDBM hashed database routines. These routines can be thought of as providing access to a single database table stored in a single file (or a pair of files for the original versions) with a single unique key index.

DBM based files allow for storage of a block of arbitrary data of arbitrary size against a unique key, also of arbitrary size. The search/index method is based on a hash of the key data.

The GDBM code removes a number of limiting restrictions compared with its predecessors:

In common with its predecessors the GDBM routines have the restriction that a given file can only be accessed by a single program if write access is desired. This means that if the file is to be permanently accessed by some daemon then it will probably need to provide some sort of or reader/update service for other programs that wish to interact with the file's data.


Opening & Closing files

    struct gdbm_help
    {
    const	char		*name;
    const	char		*def_name;
    const	char		*blocksize;
    const	char		*cachesize;
    const	char		*fast;
    const	char		*conditional;
    void	(*fatal_func)(const char *);
    GDBM_FILE	*file_ptr;
    void	*user_ptr;
    };

    extern int gdbm_db_open( struct gdbm_help *gptr, int force_new );
The gdbm_db_open function opens a GDBM database file. The function takes two parameters, a pointer to a gdbm_help structure and a flag specifying whether any existing file should be wiped and a new one created.

The gdbm_help structure has a number of fields which allow for mapping of configuration file variables onto the various parameters expected by the underlying GDBM routines.

The name and def_name fields are pointers to strings which will be passed to the eval_config_default to provide the filename to be opened.

The blocksize and cachesize fields are pointers to names of configuration variables accessed by get_config_int to provide the corresponding parameters to the gdbm_open routine.

The fast and conditional fields are pointers to names of configuration variables accessed by get_config_flag. The fast field determines whether the GDBM_FAST flag is set on open. The conditional field, if true, causes the file not to be opened but still cause a successful return from the gdmb_open routine.

The fatal_func field is a pointer to an error function as required by the underlying gdbm_open routine.

The file_ptr field is used to stored the return value from the gdbm_open routine and should be used if direct access to the gdbm routines is desired.

The user_ptr field is provided to allow for additional data to be attached to the gdbm_help structure. It not used by any of the library routines.

The gdm_db_open function returns 1 if the open was successful (or not required due to the conditional flag) and zero if it failed.

    extern int gdbm_db_close( struct gdbm_help *gptr );
The gdbm_db_close function takes a pointer to a gdbm_help structure and closes any GDBM file associated with it.

There is a control routine that is also available which allows the current buffering mode of the GDBM file to be changed.

    extern int gdbm_db_sync_set( struct gdbm_help *gptr, int sync_mode );
This routine takes a pointer to a gdbm_help structure and a integer which is passed to the underlying gdbm_setopt call.

Record Access

The gbdm_util access routines work on the basis of records with keys which are NUL terminated strings. To produce these strings the routines require format strings and optional parameters which are used internally with vsprintf to produce the key itself.

    extern int store_some_record( GDBM_FILE file,
				    const void *ptr, size_t psize,
				    const char *format, ... );
This routine takes a GDBM file structure, a pointer to a data block, the size of that data block and the format string and any required parameters.

The routine returns 0 on success and non-zero otherwise.

    extern void *fetch_some_record( GDBM_FILE file, const char *format, ... );
This routine takes a GDBM file structure and a format string together with any required parameters. It returns a pointer to a copy of the stored structure or NULL if the record is not found. Note that the size of the object is not returned, it is assumed that the caller is using fixed-size records or that any variable size is encoded within the data record itself.

    extern int delete_some_record( GDBM_FILE file, const char *format, ... );
This routine takes a GDBM file structure and a format string together with any required parameters. It returns 0 if the record was deleted and non-zero otherwise.

    extern char *gdbm_key_gen( char *buf, const char *format, ... );
This routine takes a pointer to character buffer, a format string and any required parameters. It generates the key string in the same manner as the store_, fetch_, delete_ routines do.


Example Use

A typical use of these routines would be in a manner similar to the example code shown below. The suite of routines provide access to some common structure of data that is stored, whilst hiding the exact storage mechanism.

    /* file: eric.h  -  user data structure */
    struct eric
    {
    int num_erics;
    char eric_type[4];
    char real_name[20];
    };



    /* file - eric.c - eric storage routines */

    #include <mplib1/gbdm_util.h>
    #include "eric.h"

    static char eric_format[]="%s,%d";
    static struct gdbm_help eric_help =
    {
    "ERIC_FILE", "$HOME/gdbm_files/eric/eric.dat",
    NULL, NULL, NULL, NULL, NULL, NULL, NULL };

    int open_eric( void )
    {   return( gdbm_db_open( &eric_help, 0 ) ); }

    int close_eric( void )
    {   return( gdbm_db_close( &eric_help ) ); }

    struct eric * fetch_eric( char *e_type, int e_num )
    {
	return( (struct eric *)fetch_some_record( *eric_help.file_ptr,
						    eric_format,
						    e_type, e_num ) );
    }

    int store_eric( struct eric *e_ptr )
    {
	return( store_some_record( *eric_help.file_ptr,
				    e_ptr, sizeof(struct eric),
				    eric_format, e_ptr->eric_type,
				    e_ptr->num_erics ) );
    }

    int delete_eric( char *e_type, int e_num )
    {
	return( delete_some_record( *eric_help.file_ptr,
				    eric_format,
				    e_type, e_num ) );
    }


Traversing All Records

There is an additional function which allows for the traversing of all records in a GDBM file.
    typedef int (* gdbm_index_dump_t)(FILE *, char *, void *);

    extern int gdbm_index_dump( FILE *fp,
				struct gdbm_help *gptr,
				int flags,
				gdbm_index_dump_t dfunc );
Where the fp is a file handle where output should be sent, and the gptr is a pointer to a gdbm_help structure. The flags value should be a bit-wise OR of the following flags
GDBM_IDUMP_SORT - alphabetically sort the keys.
sorts the keys before calling the display routine. This requires that the machine is capable of storing all the keys in memory at once.
GDBM_IDUMP_NOCASE - make the key sort case insensitive.
the same restriction noted above also applies.
GDBM_IDUMP_DATA
Fetch the corresponding data record before calling the display routine. The restriction noted above does not apply to the data records since these are fetched and freed for each key (except when required not to, see note below about return values from the display function).
GDBM_IDUMP_HDR - Output a simple header and trailer.
print the name of the data file (gdbm_help.name)
The function pointer, if provided, should be to a function which takes as its parameters a FILE pointer, a character pointer which is the record key, and pointer to the data record (if requested via the flags).

The routine should return an integer which should be built from a bit-wise OR of possible flags. Currently there is only one flag that may be set which is GDBM__IDUMP_KEEP_DATA which causes the walk routine not to free the fetched data item. Should the key be required to be retained, a copy should be made.

The exact behaviour of the gdbm_index_dump function can vary depending on which parameters and flags are provided.

In the simplest case the routine can be used to print the key values held in the file. Based on the previous example;

    void walk_erics( void )
    {
	gdbm_index_dump( stdout, &eric_help,
			0, NULL );
    }
would produce output of the form.
    Key: ET,99140
    Key: TY,203
    Key: FS,28
    Key: AI,102283
Note that the output is unsorted, this is due to the nature of the underlying GDBM walk routine (gdbm_firstkey, gdbm_nextkey).

To have the keys in a sorted order, with the simple headers the routine would be called as follows

    void walk_erics( void )
    {
	gdbm_index_dump( stdout, &eric_help,
			GDBM_IDUMP_SORT | GDBM_IDUMP_HDR,
			NULL );
    }
would produce output of the form.
       Index dump of ERIC_FILE
    Key: AI,102283
    Key: ET,99140
    Key: FS,28
    Key: TY,203