/* Copyright (c) 2007-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 dksdbi.c Simple database API. */ #include "dk.h" #if DK_HAVE_STRING_H #include #endif #if DK_HAVE_FCNTL_H #include #endif #if DK_HAVE_IO_H #include #endif #include "dkmem.h" #include "dkstr.h" #include "dksfc.h" #include "dksf.h" /** Inside the dksdbi module. */ #define DKSDBI_C 1 #include "dksdbi.h" #include "dksdbii.h" #line 73 "dksdbi.ctr" /** DB type: Berkeley DB. */ static char str_bdb[] = { "bdb:" }; /** DB type: NDBM. */ static char str_ndbm[] = { "ndbm:" }; /** DB type: GDBM. */ static char str_gdbm[] = { "gdbm:" }; /** DB file suffixes: dir for NDBM files. */ static char suffix_dir[] = { ".dir" }; /** DB file suffixes: pag for NDBM files. */ static char suffix_pag[] = { ".pag" }; /** Connect prefix and database type. */ typedef struct { char *prefix; /**< Prefix. */ int db_type; /**< Database type. */ } _prefix_info_t_ ; /** Map DB type names to numeric constants. */ _prefix_info_t_ prefix_info[] = { { str_bdb, DK_SDBI_TYPE_BDB}, { str_ndbm, DK_SDBI_TYPE_NDBM}, { str_gdbm, DK_SDBI_TYPE_GDBM}, {NULL, 0} }; /** Translate file mode from DK_PERM_xxx to native constants. @param i DK_PERM_xxx file mode. @return Native file mode. */ static mode_t translate_file_mode(int i) { mode_t back = 0; if(i) { if(i & DK_PERM_SUID) back |= 04000; if(i & DK_PERM_SGID) back |= 02000; if(i & DK_PERM_VTX) back |= 01000; if(i & DK_PERM_U_READ) back |= 0400; if(i & DK_PERM_U_WRITE) back |= 0200; if(i & DK_PERM_U_EXECUTE) back |= 0100; if(i & DK_PERM_G_READ) back |= 040; if(i & DK_PERM_G_WRITE) back |= 020; if(i & DK_PERM_G_EXECUTE) back |= 010; if(i & DK_PERM_O_READ) back |= 04; if(i & DK_PERM_O_WRITE) back |= 02; if(i & DK_PERM_O_EXECUTE) back |= 01; } else { back = 0600; } return back; } /** Close the database. @param p Simple database API structure. */ static void do_close DK_P1(dk_sdbi_db *,p) { switch(p->tp) { case DK_SDBI_TYPE_BDB: { #if DK_HAVE_DB_H dksdbi_bdb_close(p); #endif } break; case DK_SDBI_TYPE_NDBM: { #if DK_HAVE_NDBM_H dksdbi_ndbm_close(p); #endif } break; case DK_SDBI_TYPE_GDBM: { #if DK_HAVE_GDBM_H dksdbi_gdbm_close(p); #endif } break; } } void dksdbi_close DK_P1(dk_sdbi_t,p) { if(p) { do_close((dk_sdbi_db *)p); } } /** Initialize a simple database API structure. @param p API structure to initialize. */ static void db_init_empty DK_P1(dk_sdbi_db *,p) { p->buffer = NULL; p->fn = NULL; p->tp = 0; p->acc = 0; p->fm = 0; p->blksize = 0; p->did_truncate = 0; p->dbptr = NULL; } /** Retrieve preferred database type. @return The preferred underlaying mechanism, DK_SDBI_TYPE_xxx. */ static int get_preferred_dk_sdbi_dbtype DK_P0() { int back = 0; #if DK_HAVE_DB_H back = DK_SDBI_TYPE_BDB; #else #if DK_HAVE_NDBM_H #if DK_HAVE_GDBM_H #if DK_PREFER_NDBM back = DK_SDBI_TYPE_NDBM; #else back = DK_SDBI_TYPE_GDBM; #endif #else back = DK_SDBI_TYPE_NDBM; #endif #else #if DK_HAVE_GDBM_H back = DK_SDBI_TYPE_GDBM; #endif #endif #endif return back; } /** Correct name and type. @param p Simple database API structure. */ static void correct_name_and_type DK_P1(dk_sdbi_db *,p) { size_t l; if(p->tp == DK_SDBI_TYPE_AUTO) { _prefix_info_t_ *pptr; pptr = prefix_info; while((pptr->prefix) && (p->tp == DK_SDBI_TYPE_AUTO)) { l = strlen(pptr->prefix); if(strlen(p->buffer) > l) { if(strncmp(pptr->prefix, p->buffer, l) == 0) { p->tp = pptr->db_type; p->fn = &((p->buffer)[l]); } } pptr++; } if(p->tp == DK_SDBI_TYPE_AUTO) { p->tp = get_preferred_dk_sdbi_dbtype(); } } } /** Attempt to open database. @param p Simple database API structure. @return 1 on success, 0 on error. */ static int attempt_to_open DK_P1(dk_sdbi_db *,p) { int back = 0; switch(p->tp) { case DK_SDBI_TYPE_BDB: { #if DK_HAVE_DB_H back = dksdbi_bdb_open(p); #endif } break; case DK_SDBI_TYPE_NDBM: { #if DK_HAVE_NDBM_H back = dksdbi_ndbm_open(p); #endif } break; case DK_SDBI_TYPE_GDBM: { #if DK_HAVE_GDBM_H back = dksdbi_gdbm_open(p); #endif } break; } return back; } dk_sdbi_t dksdbi_open DK_P5(char *,fn, int,tp, int,acc, int,fm, int,blksize) { dk_sdbi_db *back = NULL; if(fn) { back = dk_new(dk_sdbi_db,1); if(back) { db_init_empty(back); back->buffer = dkstr_dup(fn); if(back->buffer) { back->fn = back->buffer; back->tp = tp; back->acc = acc; back->fm = fm; back->tfm = translate_file_mode(fm); back->blksize = blksize; correct_name_and_type(back); if(back->tp != DK_SDBI_TYPE_AUTO) { if(attempt_to_open(back)) { } else { dksdbi_close(back); back = NULL; } } else { dksdbi_close(back); back = NULL; } } else { dksdbi_close(back); back = NULL; } } else { } } else { } return((dk_sdbi_t)back); } /** Save new DB entry. @param p Simple database API structure. @param kp Key pointer. @param kl Key length. @param vp Value pointer. @param vl value length. @param insmod Insertion mode. @return 1 on success, 0 on error. */ static int do_store DK_P6(dk_sdbi_db *,p, void *,kp, size_t,kl, void *,vp, size_t,vl, int,insmod) { int back = 0; if((p->acc) & DK_SDBI_MODE_WRITE) { switch(p->tp) { case DK_SDBI_TYPE_BDB: { #if DK_HAVE_DB_H back = dksdbi_bdb_store(p, kp, kl, vp, vl, insmod); #endif } break; case DK_SDBI_TYPE_NDBM: { #if DK_HAVE_NDBM_H back = dksdbi_ndbm_store(p, kp, kl, vp, vl, insmod); #endif } break; case DK_SDBI_TYPE_GDBM: { #if DK_HAVE_GDBM_H back = dksdbi_gdbm_store(p, kp, kl, vp, vl, insmod); #endif } break; } } return back; } int dksdbi_store DK_P6(dk_sdbi_t,p, void *,kp, size_t,kl, void *,vp, size_t,vl, int,insmod) { int back = 0; if((p) && (kp) && (kl) && (vp) && (vl)) { back = do_store((dk_sdbi_db *)p, kp, kl, vp, vl, insmod); } return back; } /** Store a string entry into the database. @param p Simple database API structure. @param k Key name. @param v Value. @param insm Insertion mode (allow overwrite or not). @return 1 on success, 0 on error. */ static int do_string_store DK_P4(dk_sdbi_db *,p, char *,k, char *,v, int,insm) { int back = 0; size_t l1, l2; if((p->acc) & DK_SDBI_MODE_WRITE) { l1 = 1 + strlen(k); l2 = 1 + strlen(v); back = do_store(p, (void *)k, l1, (void *)v, l2, insm); } return back; } /** Delete a DB entry. @param p Simple database API structure. @param k Key to delete. @param kl Size of \a k. @return 1 on success, 0 on error. */ static int do_delete DK_P3(dk_sdbi_db *,p, char *,k, size_t,kl) { int back = 0; if((p->acc) & DK_SDBI_MODE_WRITE) { switch(p->tp) { case DK_SDBI_TYPE_BDB: { #if DK_HAVE_DB_H back = dksdbi_bdb_delete(p, k, kl); #endif } break; case DK_SDBI_TYPE_NDBM: { #if DK_HAVE_NDBM_H back = dksdbi_ndbm_delete(p, k, kl); #endif } break; case DK_SDBI_TYPE_GDBM: { #if DK_HAVE_GDBM_H back = dksdbi_gdbm_delete(p, k, kl); #endif } break; } } return back; } /** Delete a string entry from the database. @param p Simple database API structure. @param k Key name. @return 1 on success, 0 on error. */ static int do_string_delete DK_P2(dk_sdbi_db *,p, char *,k) { int back = 0; size_t l1; if((p->acc) & DK_SDBI_MODE_WRITE) { l1 = 1 + strlen(k); back = do_delete(p, (void *)k, l1); } return back; } int dksdbi_string_store DK_P4(dk_sdbi_t,p, char *,k, char *,v, int,insm) { int back = 0; if((p) && (k)) { if(v) { back = do_string_store((dk_sdbi_db *)p, k, v, insm); } else { do_string_delete((dk_sdbi_db *)p, k); } } return back; } /** Fetch entry from database. @param p Database. @param kp Key pointer. @param kl Key length. @param vp Value buffer pointer. @param vl Value length. @return 1 on success, 0 on error. */ static int do_fetch DK_P5(dk_sdbi_db *,p, void *,kp, size_t,kl, void *,vp, size_t *,vl) { int back = 0; if((p->acc) & DK_SDBI_MODE_READ) { switch(p->tp) { case DK_SDBI_TYPE_BDB: { #if DK_HAVE_DB_H back = dksdbi_bdb_fetch(p, kp, kl, vp, vl); #endif } break; case DK_SDBI_TYPE_NDBM: { #if DK_HAVE_NDBM_H back = dksdbi_ndbm_fetch(p, kp, kl, vp, vl); #endif } break; case DK_SDBI_TYPE_GDBM: { #if DK_HAVE_GDBM_H back = dksdbi_gdbm_fetch(p, kp, kl, vp, vl); #endif } break; } } return back; } int dksdbi_fetch DK_P5(dk_sdbi_t,p, void *,kp, size_t,kl, void *,vp, size_t *,vl) { int back = 0; if((p) && (kp) && (kl) && (vp) && (vl)) { back = do_fetch((dk_sdbi_db *)p, kp, kl, vp, vl); } return back; } /** Fetch string from database. @param p Database. @param k Key. @param v Result buffer. @param s Length of \a s. @return 1 on success, 0 on error. */ static int do_string_fetch DK_P4(dk_sdbi_db *,p, char *,k, char *,v, size_t,s) { int back = 0; if((p->acc) & DK_SDBI_MODE_READ) { switch(p->tp) { case DK_SDBI_TYPE_BDB: { #if DK_HAVE_DB_H back = dksdbi_bdb_string_fetch(p, k, v, s); #endif } break; case DK_SDBI_TYPE_NDBM: { #if DK_HAVE_NDBM_H back = dksdbi_ndbm_string_fetch(p, k, v, s); #endif } break; case DK_SDBI_TYPE_GDBM: { #if DK_HAVE_GDBM_H back = dksdbi_gdbm_string_fetch(p, k, v, s); #endif } break; } } return back; } int dksdbi_string_fetch DK_P4(dk_sdbi_t,p, char *,k, char *,v, size_t,s) { int back = 0; if((p) && (k) && (v) && (s)) { back = do_string_fetch((dk_sdbi_db *)p, k, v, s); } return back; } int dksdbi_delete DK_P3(dk_sdbi_t,p, char *,k, size_t,kl) { int back = 0; if((p) && (k) && (kl)) { do_delete((dk_sdbi_db *)p, k, kl); } return back; } int dksdbi_string_delete DK_P2(dk_sdbi_t,p, char *,k) { int back = 0; if((p) && (k)) { do_string_delete((dk_sdbi_db *)p, k); } return back; } int dksdbi_remove_file DK_P2(char *,fn, int,tp) { int back = 0; int t = 0; char *f = NULL; size_t pl; _prefix_info_t_ *pptr; t = tp; f = fn; if(fn) { if(t == DK_SDBI_TYPE_AUTO) { pptr = prefix_info; while((pptr->prefix) && (t == DK_SDBI_TYPE_AUTO)) { pl = strlen(pptr->prefix); if(strlen(fn) > pl) { if(strncmp(pptr->prefix, fn, pl) == 0) { t = pptr->db_type; f = &((fn)[pl]); } } pptr++; } if(t == DK_SDBI_TYPE_AUTO) { t = get_preferred_dk_sdbi_dbtype(); } } switch(t) { case DK_SDBI_TYPE_NDBM: { int i1, i2; size_t sz; char *mybuffer; sz = strlen(f) + 5; mybuffer = dk_new(char,sz); if(mybuffer) { i1 = i2 = 0; strcpy(mybuffer, f); strcat(mybuffer, suffix_dir); i1 = dksf_remove_file(mybuffer); strcpy(mybuffer, f); strcat(mybuffer, suffix_pag); i2 = dksf_remove_file(mybuffer); if((i1) && (i2)) { back = 1; } dk_delete(mybuffer); } } break; default: { back = dksf_remove_file(f); } break; } } return back; } /** Traverse a database. @param p Simple database API structure. @param d Object to use during traversal. @param f Function to use for traversal. @return 1 on success, 0 on error. */ static int do_traverse DK_P3(dk_sdbi_db *,p, void *,d, dk_sdbi_fct_t *,f) { int back = 0; if((p->acc) & DK_SDBI_MODE_READ) { switch(p->tp) { case DK_SDBI_TYPE_BDB: { #if DK_HAVE_DB_H back = dksdbi_bdb_traverse(p, d, f); #endif } break; case DK_SDBI_TYPE_NDBM: { #if DK_HAVE_NDBM_H back = dksdbi_ndbm_traverse(p, d, f); #endif } break; case DK_SDBI_TYPE_GDBM: { #if DK_HAVE_GDBM_H back = dksdbi_gdbm_traverse(p, d, f); #endif } break; } } return back; } int dksdbi_traverse DK_P3(dk_sdbi_t,p, void *,d, dk_sdbi_fct_t *,f) { int back = 0; if((p) && (f)) { back = do_traverse((dk_sdbi_db *)p, d, f); } return back; } /** Synchronize DB to disk if supported by underlaying mechanisms. @param p Simple database API structure. @return 1 on success, 0 on error. */ static int do_sync DK_P1(dk_sdbi_db *,p) { int back = 0; if((p->acc) & DK_SDBI_MODE_WRITE) { switch(p->tp) { case DK_SDBI_TYPE_BDB: { #if DK_HAVE_DB_H back = dksdbi_bdb_sync(p); #endif } break; case DK_SDBI_TYPE_NDBM: { #if DK_HAVE_NDBM_H back = dksdbi_ndbm_sync(p); #endif } break; case DK_SDBI_TYPE_GDBM: { #if DK_HAVE_GDBM_H back = dksdbi_gdbm_sync(p); #endif } break; } } return back; } int dksdbi_sync DK_P1(dk_sdbi_t,p) { int back = 0; if(p) { back = do_sync((dk_sdbi_db *)p); } return back; }