/*
Copyright (c) 2008-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/



/**	@file prqdbe.c	Database backend module.

This file contains the functions to store key/value pairs in different
database types.
*/



/**	Inside the prqdbe module. */
#define PRQDBE_C	1
#include "prqd.h"

#line 51 "prqdbe.ctr"



/** Names and abbreviations for database types. */
static char *db_type_names[] = { "b$db", "g$dbm", "n$dbm", NULL };



#if USE_DB_H
/**	Open a Berkeley DB file.
	@param	pj	Pointer to prqd job structure.
	@return	1 on success, 0 on error.
*/
int
prqdbe_bdb_open DK_P1(PJ *,pj)
{
  int back = 0;
  u_int32_t flags;
  DB *dbp;
  
  if(db_create(&((pj->dbbe).c.bdb.dbp), NULL, 0) == 0) {
    flags = DB_CREATE; dbp = (pj->dbbe).c.bdb.dbp;
    if(dbp->open(dbp, NULL, (pj->dbbe).c.bdb.fn, NULL, DB_BTREE, flags, 0) == 0) {
      back = 1;
    } else {
      pj->e1 = 1;
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(69));
    }
  } else {
    pj->e1 = 1;
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(70), (pj->dbbe).c.bdb.fn);
  } 
  return back;
}
#endif



#if USE_GDBM_H
/**	Open a GDBM database file.
	@param	pj	Pointer to prqd job structure.
	@return	1 on success, 0 on error.
*/
int
prqdbe_gdbm_open DK_P1(PJ *,pj)
{
  int back = 0;
  
  if((pj->dbbe).c.gdbm.fn) {
    (pj->dbbe).c.gdbm.myfn = dkstr_dup((pj->dbbe).c.gdbm.fn);
    if((pj->dbbe).c.gdbm.myfn) {
      back = 1;
    } else {
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(42));
    }
  }
  
  return back;
}
#endif



#if USE_NDBM_H
/**	Open NDBM database file.
	@param	pj	Pointer to prqd job structure.
	@return	1 on success, 0 on error.
*/
int
prqdbe_ndbm_open DK_P1(PJ *,pj)
{
  int back = 0;
  
  (pj->dbbe).c.ndbm.db = NULL;
  (pj->dbbe).c.ndbm.db = dbm_open((pj->dbbe).c.ndbm.fn, (O_RDWR | O_CREAT), 0660);
  if((pj->dbbe).c.ndbm.db) {
    back = 1;	
  } else {
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(70), (pj->dbbe).c.ndbm.fn);
  }
  
  return back;
}
#endif



/**	Open a database file.
	@param	pj	Pointer to prqd job structure.
	@return	1 on success, 0 on error.
*/
int
prqdbe_open DK_P1(PJ *,pj)
{
  int back = 0;
  char *dn, *p1, *p2;
  
  if((pj->prqdc)->dbname) {		
    dn = (pj->prqdc)->dbname;
    p1 = dkstr_chr(dn, ':');
    if(p1) {				
      *p1 = '\0';
      p2 = p1; p2++;
      p2 = dkstr_start(p2, NULL);
      if(p2) {				
        (pj->dbbe).tp = dkstr_array_abbr(db_type_names, (pj->prqdc)->dbname, '$', 0);
	if((pj->dbbe).tp > -1) {	
	  (pj->dbbe).tp += 1;
	  switch((pj->dbbe).tp) {
#if USE_DB_H
	    case DATABASE_TYPE_BDB: {	
	      (pj->dbbe).c.bdb.fn = p2;
	      (pj->dbbe).c.bdb.dbp = NULL;
	      back = prqdbe_bdb_open(pj);
	    } break;
#else
  	    case DATABASE_TYPE_BDB: {   
	      pj->e1 = 1;
              prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68));
	    } break;
#endif
#if USE_GDBM_H
	    case DATABASE_TYPE_GDBM: {	
	      (pj->dbbe).c.gdbm.fn = p2;
	      back = prqdbe_gdbm_open(pj);
	    } break;
#else
  	    case DATABASE_TYPE_GDBM: {  
	      pj->e1 = 1;
              prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68));
	    } break;
#endif
#if USE_NDBM_H
	    case DATABASE_TYPE_NDBM: {	
	      (pj->dbbe).c.ndbm.fn = p2;
	      back = prqdbe_ndbm_open(pj);
	    } break;
#else
  	    case DATABASE_TYPE_NDBM: {  
	      pj->e1 = 1;
              prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68));
	    } break;
#endif
	    default: {
	      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(74), (pj->prqdc)->dbname);
	      pj->e1 = 1;
	    } break;
	  }
	} else {			
	  prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(74), (pj->prqdc)->dbname);
	  pj->e1 = 1;
	}
      } else {				
	prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(73));
	pj->e1 = 1;
      }
      *p1 = ':';
    } else {				
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(72));
      pj->e1 =1;
    }
  } else {				
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(71));
    pj->e1 = 1;
  } 
  return back;
}



#if USE_DB_H
/**	Store key/value pair in Berkeley DB.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key string.
	@param	v	Value string.
	@return	1 on success, 0 on error.
*/
static
int
prqdbe_bdb_store DK_P3(PJ *,pj, char *,k, char *,v)
{
  int back = 0;
  DBT kd, vd;
  if((pj->dbbe).c.bdb.dbp) {
    kd.data = k; kd.size = 1 + strlen(k);
    vd.data = v; vd.size = 1 + strlen(v);
    if(((pj->dbbe).c.bdb.dbp)->put((pj->dbbe).c.bdb.dbp,NULL,&kd,&vd,0) == 0) {
      back = 1;
    }
  }
  return back;
}
#endif



#if USE_GDBM_H
/**	Store key/value pair in GDBM database.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key string.
	@param	v	Value string.
	@return	1 on success, 0 on error.
*/
static
int
prqdbe_gdbm_store DK_P3(PJ *,pj, char *,k, char *,v)
{
  int back = 0, ret = 1;
  datum kd, kv;
  GDBM_FILE gdbmf;
  if((pj->dbbe).c.gdbm.myfn) {
    gdbmf = gdbm_open((pj->dbbe).c.gdbm.myfn, 512, GDBM_WRCREAT, 0660, NULL);
    if(gdbmf) {
      kd.dptr = k; kd.dsize = 1 + strlen(k);
      kv.dptr = v; kd.dsize = 1 + strlen(v);
      ret = gdbm_store(gdbmf, kd, kv, GDBM_INSERT);
      if(ret == 0) {
        back = 1;
      } else {
        kd.dptr = k; kd.dsize = 1 + strlen(k);
	kv.dptr = v; kd.dsize = 1 + strlen(v);
	ret = gdbm_store(gdbmf, kd, kv, GDBM_REPLACE);
	if(ret == 0) {
	  back = 1;
	}
      }
      gdbm_close(gdbmf);
    } else {
      /* ERROR: Failed to open gdbm database */
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(70), (pj->dbbe).c.gdbm.myfn);
    }
  } else {
    /* ERROR: No file name */
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(73));
  }
  return back;
}
#endif



#if USE_NDBM_H
/**	Store key/value pair in NDBM database.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key string.
	@param	v	Value string.
	@return	1 on success, 0 on error.
*/
static
int
prqdbe_ndbm_store DK_P3(PJ *,pj, char *,k, char *,v)
{
  int back = 0, i = 0;
  datum kd, vd;
  if( (pj->dbbe).c.ndbm.db ) {
    kd.dptr = k; kd.dsize = 1 + strlen(k);
    vd.dptr = v; vd.dsize = 1 + strlen(v);
    i = dbm_store((pj->dbbe).c.ndbm.db, kd, vd, DBM_INSERT);
    if(i == 0) {
      back = 1;
    } else {
      kd.dptr = k; kd.dsize = 1 + strlen(k);
      vd.dptr = v; vd.dsize = 1 + strlen(v);
      if(dbm_store((pj->dbbe).c.ndbm.db, kd, vd, DBM_REPLACE) == 0) {
        back = 1;
      }
    }
  }
}
#endif



/**	Store key/value pair in database.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key string.
	@param	v	Value string.
	@return	1 on success, 0 on error.
*/
int
prqdbe_store DK_P3(PJ *,pj, char *,k, char *,v)
{
  int back = 0;
  if((pj) && (k) && (v)) { 
    prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(27), k, v);
    switch((pj->dbbe).tp) {
      case DATABASE_TYPE_BDB: {
#if USE_DB_H
        back = prqdbe_bdb_store(pj, k, v);
#else
        prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68)); pj->e1 = 1;
#endif
      } break;
      case DATABASE_TYPE_GDBM: {
#if USE_GDBM_H
        back = prqdbe_gdbm_store(pj, k, v);
#else
        prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68)); pj->e1 = 1;
#endif
      } break;
      case DATABASE_TYPE_NDBM: {
#if USE_NDBM_H
        back = prqdbe_ndbm_store(pj, k, v);
#else
        prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68)); pj->e1 = 1;
#endif
      } break;
    }
    if(!back) {
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(35), k, v);
    }
  }
  return back;
}



#if USE_DB_H
/**	Retrieve entry from Berkeley DB.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key string.
	@param	v	Pointer to buffer for value.
	@param	sz	Size of v in bytes.
	@return	Number of bytes retrieved.
*/
static
size_t
prqdbe_bdb_fetch DK_P4(PJ *,pj, char *,k, char *,v, size_t,sz)
{
  size_t back = 0;
  DBT kd, vd;
  DK_MEMRES(v,sz);
  if((pj->dbbe).c.bdb.dbp) {
    DK_MEMRES(&kd,sizeof(DBT)); DK_MEMRES(&vd,sizeof(DBT));
    kd.data = k; kd.size = 1 + strlen(k);
    if(((pj->dbbe).c.bdb.dbp)->get((pj->dbbe).c.bdb.dbp,NULL,&kd,&vd,0) == 0) {
      if(vd.data) {
        if(vd.size > 0) {
	  back = vd.size;
	  if(back >= sz) { back = sz - 1; }
	  DK_MEMCPY(v,vd.data,back);
	  v[back] = '\0';
	  if(v[back - 1] == '\0') { back--; }
	  
	}
      }
    }
  }
  return back;
}
#endif



#if USE_GDBM_H
/**	Retrieve entry from GDBM database.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key string.
	@param	v	Pointer to buffer for value.
	@param	sz	Size of v in bytes.
	@return	Number of bytes retrieved.
*/
static
size_t
prqdbe_gdbm_fetch DK_P4(PJ *,pj, char *,k, char *,v, size_t,sz)
{
  size_t back = 0;
  datum kd, vd;
  GDBM_FILE gdbmf;
  
  if((pj->dbbe).c.gdbm.myfn) {
    gdbmf = gdbm_open((pj->dbbe).c.gdbm.myfn, 512, GDBM_READER, 0660, NULL);
    if(gdbmf) {
      kd.dptr = k; kd.dsize = 1 + strlen(k);
      vd = gdbm_fetch(gdbmf, kd);
      if(vd.dptr) {
        if(vd.dsize > 0) {
	  back = vd.dsize;
	  if(back >= sz) back = sz - 1;
	  DK_MEMCPY(v,vd.dptr,back);
	  v[back] = '\0';
	  if(v[back - 1] == '\0') { back--; }
	  
	}
        free(vd.dptr);
      }
      gdbm_close(gdbmf);
    } else {
      /* ERROR: Failed to open gdbm database */
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(70), (pj->dbbe).c.gdbm.myfn);
    }
  } else {
    /* ERROR: No file name */
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(73));
  }
  return back;
}
#endif



#if USE_NDBM_H
/**	Retrieve entry from NDBM database.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key string.
	@param	v	Pointer to buffer for value.
	@param	sz	Size of v in bytes.
	@return	Number of bytes retrieved.
*/
static
size_t
prqdbe_ndbm_fetch DK_P4(PJ *,pj, char *,k, char *,v, size_t,sz)
{
  size_t back = 0;
  int i;
  datum kd, vd;
  
  DK_MEMRES(v,sz);
  if( (pj->dbbe).c.ndbm.db ) {
    DK_MEMRES(&kd,sizeof(datum)); DK_MEMRES(&vd, sizeof(datum));
    kd.dptr = k; kd.dsize = 1 + strlen(k);
    vd = dbm_fetch( (pj->dbbe).c.ndbm.db, kd);
    if(vd.dptr) {		
      if(vd.dsize > 0) {	
	back = vd.dsize;
	if(back >= sz) { back = sz - 1; }
	DK_MEMCPY(v,vd.dptr,back);
	v[back] = '\0';
	if(v[back - 1] == '\0') { back--; }
	
      }
    }
  }
  
  return back;
}
#endif



/**	Retrieve entry from database.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key string.
	@param	v	Pointer to buffer for value.
	@param	sz	Size of v in bytes.
	@return	Number of bytes retrieved.
*/
size_t
prqdbe_fetch DK_P4(PJ *,pj, char *,k, char *,v, size_t,sz)
{
  int back = 0;
  if((pj) && (k) && (v)) { 
    switch((pj->dbbe).tp) {
      case DATABASE_TYPE_BDB: {
#if USE_DB_H
        back = prqdbe_bdb_fetch(pj, k, v, sz);
#else
        prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68)); pj->e1 = 1;
#endif
      } break;
      case DATABASE_TYPE_GDBM: {
#if USE_GDBM_H
        back = prqdbe_gdbm_fetch(pj, k, v, sz);
#else
        prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68)); pj->e1 = 1;
#endif
      } break;
      case DATABASE_TYPE_NDBM: {
#if USE_NDBM_H
        back = prqdbe_ndbm_fetch(pj, k, v, sz);
#else
        prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68)); pj->e1 = 1;
#endif
      } break;
    }
    if(back) {
      prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(28), k, v);
    } else {
      prqdlog(pj, PRQD_PRIO_DEBUG, prqd_get_kw(29), k);
    }
  }
  return back;
}




#if USE_DB_H
/**	Close Berkeley database.
	@param	pj	Pointer to prqd job structure.
*/
void
prqdbe_bdb_close DK_P1(PJ *,pj)
{
  
  if((pj->dbbe).c.bdb.dbp) {
    
    ((pj->dbbe).c.bdb.dbp)->close((pj->dbbe).c.bdb.dbp, 0);
    (pj->dbbe).c.bdb.dbp = NULL;
  }
  
}
#endif



#if USE_GDBM_H
/**	Close GDBM database.
	@param	pj	Pointer to prqd job structure.
*/
void
prqdbe_gdbm_close DK_P1(PJ *,pj)
{
  char *x;
  
  if((pj->dbbe).c.gdbm.myfn) {
    x = (pj->dbbe).c.gdbm.myfn;
    dk_delete(x);
    (pj->dbbe).c.gdbm.myfn = NULL;
  }
  
}
#endif



#if USE_NDBM_H
/**	Close NDBM database.
	@param	pj	Pointer to prqd job structure.
*/
void
prqdbe_ndbm_close DK_P1(PJ *,pj)
{
  
  if((pj->dbbe).c.ndbm.db) {
    dbm_close((pj->dbbe).c.ndbm.db);
    (pj->dbbe).c.ndbm.db = NULL;
  }
  
}
#endif



/**	Close database.
	@param pj	Pointer to prqd job structure.
*/
void
prqdbe_close DK_P1(PJ *,pj)
{
  
  switch((pj->dbbe).tp) {
    case DATABASE_TYPE_BDB: {	
#if USE_DB_H
      prqdbe_bdb_close(pj);
#else
      pj->e1 = 1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68));
#endif
    } break;
    case DATABASE_TYPE_GDBM: {	
#if USE_GDBM_H
      prqdbe_gdbm_close(pj);
#else
      pj->e1 = 1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68));
#endif
    } break;
    case DATABASE_TYPE_NDBM: {	
#if USE_NDBM_H
      prqdbe_ndbm_close(pj);
#else
      pj->e1 = 1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68));
#endif
    } break;
  } 
}



/**	Change file ownership.
	@param	pj	Pointer to prqd job structure.
	@param	fn	File name.
	@param	u	New owners UID.
	@param	g	New group ID for file.
*/
static
void
change_file_ownership DK_P4(PJ *,pj, char *,fn, uid_t,u, gid_t,g)
{
  
  if(fn) {
    if(chown(fn, u, g)) {
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(75), fn, (unsigned long)u, (unsigned long)g);
    }
    if(chmod(fn, 0660)) {
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(76), fn);
    }
  } 
}



/** File name suffixes for NDBM files. */
static
char
*ndbm_file_suffixes[] = { ".dir", ".pag", NULL };



/**	Change ownership for database file.
	@param	pj	Pointer to prqd job structure.
	@param	u	New owners UID.
	@param	g	New file group ID.
*/
void
prqdbe_change_ownership DK_P3(PJ *,pj, uid_t,u, gid_t,g)
{
  char *p2; size_t sz;
  
  switch((pj->dbbe).tp) {
    case DATABASE_TYPE_BDB: {	
      change_file_ownership(pj, (pj->dbbe).c.bdb.fn, u, g);
    } break;
    case DATABASE_TYPE_GDBM: {	
      change_file_ownership(pj, (pj->dbbe).c.gdbm.fn, u, g);
    } break;
    case DATABASE_TYPE_NDBM: {	
     sz = 5 + strlen((pj->dbbe).c.ndbm.fn);
     p2 = dk_new(char,sz);
     if(p2) {
       strcpy(p2, (pj->dbbe).c.ndbm.fn);
       strcat(p2, ndbm_file_suffixes[1]);
       change_file_ownership(pj, p2, u, g);
       strcpy(p2, (pj->dbbe).c.ndbm.fn);
       strcat(p2, ndbm_file_suffixes[0]);
       change_file_ownership(pj, p2, u, g);
       dk_delete(p2);
     } else {
       prqdlog(pj,PRQD_PRIO_ERROR,prqd_get_kw(42));
     }
    } break;
  } 
}




#if USE_DB_H
/**	Traverse Berkeley database.
	@param	pj	Pointer to prqd job structure.
	@param	f	Traversal function.
	@return	1 on success, 0 on error.
*/
static
int
prqdbe_bdb_traverse DK_P2(PJ *,pj, PRQD_TRAVERSE_FCT *,f)
{
  int back = 0, cc = 0;
  DB *db;
  DBT kd, vd;
  DBC *cursorp = NULL;
  int ret, act;
  char keybuffer[PRQD_DBENTRY_SIZE], valbuffer[PRQD_DBENTRY_SIZE];
  char kb2[PRQD_DBENTRY_SIZE], vb2[PRQD_DBENTRY_SIZE];
  size_t sz;
  
  db = (pj->dbbe).c.bdb.dbp;
  if(db) {
    DK_MEMRES(&kd,sizeof(DBT)); DK_MEMRES(&vd,sizeof(DBT));
    ret = db->cursor(db, NULL, &cursorp, 0);
    if(ret == 0) {
      if(cursorp) {
        ret = cursorp->c_get(cursorp, &kd, &vd, DB_FIRST);
	if(ret == 0) {
	  cc = back = 1;
	  if((kd.data) && (kd.size > 0) && (vd.data) && (vd.size > 0)) {
	    if(f) {
	      sz = kd.size;
	      if(sz >= sizeof(keybuffer)) { sz = sizeof(keybuffer) - 1; }
	      DK_MEMCPY(keybuffer,kd.data,sz);
	      keybuffer[sz] = '\0';
	      sz = vd.size;
	      if(sz >= sizeof(valbuffer)) { sz = sizeof(valbuffer) - 1 ; }
	      DK_MEMCPY(valbuffer,vd.data,sz);
	      valbuffer[sz] = '\0';
	      act = (*f)(pj, keybuffer, valbuffer);
	      if(act == 0) {
	        back = 0;
		prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(77), kb2, vb2);
	      } else {
	        if(act < 0) {
		  back = cc = 0;
		  prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(78), kb2, vb2);
		}
	      }
	    }
	  }
	}
	while(cc) {
	  ret = cursorp->c_get(cursorp, &kd, &vd, DB_NEXT);
	  if(ret == 0) {
	    cc = 1;
	    if((kd.data) && (kd.size > 0) && (vd.data) && (vd.size > 0)) {
	      if(f) {
	        sz = kd.size;
	        if(sz >= sizeof(keybuffer)) { sz = sizeof(keybuffer) - 1; }
	        DK_MEMCPY(keybuffer,kd.data,sz);
	        keybuffer[sz] = '\0';
	        sz = vd.size;
	        if(sz >= sizeof(valbuffer)) { sz = sizeof(valbuffer) - 1 ; }
	        DK_MEMCPY(valbuffer,vd.data,sz);
	        valbuffer[sz] = '\0';
	        act = (*f)(pj, keybuffer, valbuffer);
	        if(act == 0) {
	          back = 0;
		  prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(77), kb2, vb2);
	        } else {
	          if(act < 0) {
		    back = cc = 0;
		    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(78), kb2, vb2);
		  }
	        }
	      }
	    }
	  } else {
	    cc = 0;
	  }
	}
        cursorp->c_close(cursorp);
	cursorp = NULL;
      }
    }
  }
  
  return back;
}
#endif



#if USE_GDBM_H
/**	Traverse GDBM database.
	@param	pj	Pointer to prqd job structure.
	@param	f	Traversal function.
	@return	1 on success, 0 on error.
*/
static
int
prqdbe_gdbm_traverse DK_P2(PJ *,pj, PRQD_TRAVERSE_FCT *,f)
{
  int back = 0, can_run = 0, is_first = 0, cc = 1, act = 1;
  datum kd, vd, nk;
  char keybuffer[PRQD_DBENTRY_SIZE], valbuffer[PRQD_DBENTRY_SIZE];
  char kb2[PRQD_DBENTRY_SIZE], vb2[PRQD_DBENTRY_SIZE];
  size_t sz;
  GDBM_FILE gdbmf;
  
  if((pj->dbbe).c.gdbm.myfn) {
    gdbmf = gdbm_open((pj->dbbe).c.gdbm.myfn, 512, GDBM_READER, 0660, NULL);
    if(gdbmf) {
      cc = 1; is_first = 1;
      back = 1;
      while(cc) {
        can_run = 0;
        if(is_first) {
          is_first = 0;
	  DK_MEMRES(&kd,sizeof(datum));
          kd = gdbm_firstkey(gdbmf);
	  if(kd.dptr) {
	    can_run = 1;
	  } else {
	    cc = 0;
	  }
        } else {
	  DK_MEMRES(&nk,sizeof(datum));
          nk = gdbm_nextkey(gdbmf, kd);
	  if(kd.dptr) { free(kd.dptr); kd.dptr = NULL; }
	  kd = nk;
	  DK_MEMRES(&nk,sizeof(datum));
	  if(kd.dptr) {
	    can_run = 1;
	  } else {
	    cc = 0;
	  }
        }
        if(can_run) {
          vd = gdbm_fetch(gdbmf, kd);
	  if((kd.dptr) && (kd.dsize > 0) && (vd.dptr) && (vd.dsize > 0)) {
	    sz = kd.dsize;
	    if(sz >= sizeof(keybuffer)) { sz = sizeof(keybuffer) - 1; }
	    DK_MEMCPY(keybuffer,kd.dptr,sz);
	    keybuffer[sz] = '\0';
	    sz = vd.dsize;
	    if(sz >= sizeof(valbuffer)) { sz = sizeof(valbuffer) - 1; }
	    DK_MEMCPY(valbuffer,vd.dptr,sz);
	    valbuffer[sz] = '\0';
	    act = 1;
	    if(f) {
	      act = (*f)(pj, keybuffer, valbuffer);
	    }
	    if(act == 0) {
	      back = 0;
	      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(77), kb2, vb2);
	    } else {
	      if(act < 0) {
	        back = cc = 0;
		prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(78), kb2, vb2);
	      }
	    }
	  }
	  if(vd.dptr) { free(vd.dptr); vd.dptr = NULL; }
	}
      }
      if(kd.dptr) { free(kd.dptr); kd.dptr = NULL; }
      gdbm_close(gdbmf);
    } else {
      /* ERROR: Failed to open gdbm database */
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(70), (pj->dbbe).c.gdbm.myfn);
    }
  } else {
    /* ERROR: No name */
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(73));
  }
  
  return back;
}
#endif



#if USE_NDBM_H
/**	Traverse NDBM database.
	@param	pj	Pointer to prqd job structure.
	@param	f	Traversal function.
	@return	1 on success, 0 on error.
*/
static
int
prqdbe_ndbm_traverse DK_P2(PJ *,pj, PRQD_TRAVERSE_FCT *,f)
{
  int back = 0, cc = 0, act = 0;
  datum kd, vd;
  char keybuffer[PRQD_DBENTRY_SIZE], valbuffer[PRQD_DBENTRY_SIZE];
  char kb2[PRQD_DBENTRY_SIZE], vb2[PRQD_DBENTRY_SIZE];
  size_t sz;
  
  cc = 1; back = 1;
  for(
    kd = dbm_firstkey((pj->dbbe).c.ndbm.db);
    (kd.dptr != NULL) && (cc != 0);
    kd = dbm_nextkey((pj->dbbe).c.ndbm.db)
  )
  {
    
    vd = dbm_fetch((pj->dbbe).c.ndbm.db, kd);
    if((kd.dptr) && (kd.dsize > 0) && (vd.dptr) && (vd.dsize > 0)) {
      sz = kd.dsize;
      if(sz >= sizeof(keybuffer)) { sz = sizeof(keybuffer) - 1; }
      DK_MEMCPY(keybuffer,kd.dptr,sz);
      keybuffer[sz] = '\0';
      sz = vd.dsize;
      if(sz >= sizeof(valbuffer)) { sz = sizeof(valbuffer) - 1; }
      DK_MEMCPY(valbuffer,vd.dptr,sz);
      valbuffer[sz] = '\0';
      act = 1;
      if(f) {
        act = (*f)(pj, keybuffer, valbuffer);
      }
      if(act == 0) {
        back = 0;
	prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(77), kb2, vb2);
      } else {
        if(act < 0) {
	  back = cc = 0;
	  prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(78), kb2, vb2);
	}
      }
    }
  }
  
  return back;
}
#endif



/**	Traverse database.
	@param	pj	Pointer to prqd job structure.
	@param	f	Traversal function.
	@return	1 on success, 0 on error.
*/
int
prqdbe_traverse DK_P2(PJ *,pj, PRQD_TRAVERSE_FCT *,f)
{
  int back = 0;
  
  switch((pj->dbbe).tp) {
    case DATABASE_TYPE_BDB: {	
#if USE_DB_H
      back = prqdbe_bdb_traverse(pj, f);
#else
      pj->e1 = 1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68));
#endif
    } break;
    case DATABASE_TYPE_GDBM: {	
#if USE_GDBM_H
      back = prqdbe_gdbm_traverse(pj, f);
#else
      pj->e1 = 1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68));
#endif
    } break;
    case DATABASE_TYPE_NDBM: {	
#if USE_NDBM_H
      back = prqdbe_ndbm_traverse(pj, f);
#else
      pj->e1 = 1; prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68));
#endif
    } break;
  } 
  return back;
}



#if USE_DB_H
/**	Delete entry from Berkeley database.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key of entry to delete.
	@return	1 on success, 0 on error.
*/
static
int
prqdbe_bdb_delete  DK_P2(PJ *,pj, char *,k)
{
  int back = 0, ret = 0;
  DB *db;
  DBT kd;
  
  if((pj->dbbe).c.bdb.dbp) {
    db = (pj->dbbe).c.bdb.dbp;
    kd.data = k; kd.size = 1 + strlen(k);
    ret = db->del(db, NULL, &kd, 0);
    if(ret == 0) { back = 1; }
  }
  
  return back;
}
#endif



#if USE_NDBM_H
/**	Delete entry from NDBM database.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key of entry to delete.
	@return	1 on success, 0 on error.
*/
static
int
prqdbe_ndbm_delete  DK_P2(PJ *,pj, char *,k)
{
  int back = 0;
  datum kd;
  
  kd.dptr = k; kd.dsize = 1 + strlen(k);
  if(dbm_delete((pj->dbbe).c.ndbm.db, kd) == 0) {
    back = 1;
  }
  
  return back;
}
#endif



#if USE_GDBM_H
/**	Delete entry from GDBM database.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key of entry to delete.
	@return	1 on success, 0 on error.
*/
static
int
prqdbe_gdbm_delete  DK_P2(PJ *,pj, char *,k)
{
  int back = 0;
  datum kd;
  GDBM_FILE gdbmf;
  
  if((pj->dbbe).c.gdbm.myfn) {
    gdbmf = gdbm_open((pj->dbbe).c.gdbm.myfn, 512, GDBM_WRCREAT, 0660, NULL);
    if(gdbmf) {
      kd.dptr = k; kd.dsize = 1 + strlen(k);
      if(gdbm_delete(gdbmf, kd) == 0) {
        back = 1;
      }
      gdbm_close(gdbmf);
    } else {
      /* ERROR: Failed to open gdbm database */
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(70), (pj->dbbe).c.gdbm.myfn);
    }
  } else {
    /* ERROR: No filename */
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(73));
  }
  
  return back;
}
#endif



/**	Delete entry from database.
	@param	pj	Pointer to prqd job structure.
	@param	k	Key of entry to delete.
	@return	1 on success, 0 on error.
*/
int
prqdbe_delete DK_P2(PJ *,pj, char *,k)
{
  int back = 0;
  
  prqdlog(pj, PRQD_PRIO_INFO, prqd_get_kw(84), k);
  switch((pj->dbbe).tp) {
    case DATABASE_TYPE_BDB: {	
#if USE_DB_H
      back = prqdbe_bdb_delete(pj, k);
#else
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68)); pj->e1 = 1;
#endif
    } break;
    case DATABASE_TYPE_GDBM: {	
#if USE_GDBM_H
      back = prqdbe_gdbm_delete(pj, k);
#else
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68)); pj->e1 = 1;
#endif
    } break;
    case DATABASE_TYPE_NDBM: {	
#if USE_NDBM_H
      back = prqdbe_ndbm_delete(pj, k);
#else
      prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(68)); pj->e1 = 1;
#endif
    } break;
  }
  if(!back) {
    prqdlog(pj, PRQD_PRIO_ERROR, prqd_get_kw(67), k);
  }
  
  return back;
}



