%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% header /** @file dk4dbi.h Interface to databases. */ #ifndef DK4CONF_H_INCLUDED #include "dk4conf.h" #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #if DK4_HAVE_UNISTD_H #ifndef UNISTD_H_INCLUDED #include #define UNISTD_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SYS_TYPES_H #ifndef SYS_TYPES_H_INCLUDED #include #define SYS_TYPES_H_INCLUDED 1 #endif #endif #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) #include #endif #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) #if DK4_HAVE_FCNTL_H #ifndef FCNTL_H_INCLUDED #include #define FCNTL_H_INCLUDED 1 #endif #endif #ifndef NDBM_H_INCLUDED #include #define NDBM_H_INCLUDED 1 #endif #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef DK4ERROR_H_INCLUDED #include "dk4error.h" #endif #ifndef DK4STO_H_INCLUDED #include "dk4sto.h" #endif /** Backend types. */ enum { /** No backend specified yet. */ DK4_DB_BE_UNKNOWN = -1 , /** In-memory database. */ DK4_DB_BE_MEMORY = 0 , /** Berkeley DB. */ DK4_DB_BE_BDB = 1 , /** NDBM. */ DK4_DB_BE_NDBM = 2 }; /** Options. */ enum { /** Reset key data before releasing memory. */ DK4_DB_OPT_RESET_KEY = 1 , /** Reset value data before releasing memory. */ DK4_DB_OPT_RESET_VALUE = 2 , }; /** Details for an in-memory database. */ typedef struct { dk4_sto_t *s; /**< Storage container. */ dk4_sto_it_t *i; /**< Storage iterator. */ } dk4_dbi_mem_t; #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) /** Details for a Berkeley DB database. */ typedef struct { DB *db; /**< DB structure. */ } dk4_dbi_bdb_t; #endif #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) /** Details for an NDBM database. */ typedef struct { DBM *dbm; /**< NDBM structure. */ } dk4_dbi_ndbm_t; #endif /** Details for one backend. */ typedef union { dk4_dbi_mem_t mem; /**< Details for in-memory database. */ #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) dk4_dbi_bdb_t bdb; /**< Details for Berkeley DB. */ #endif #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) dk4_dbi_ndbm_t ndbm; /**< Details for NDBM. */ #endif int dummy; /**< Just to have a second component. */ } dk4_dbi_det_t; /** Database structure. */ typedef struct { dk4_dbi_det_t dt; /**< Backend-specific details. */ dkChar *fn; /**< File name. */ size_t km; /**< Maximum key size. */ size_t vm; /**< Maximum value size. */ int tp; /**< Backend type. */ int op; /**< Options. */ int wa; /**< Flag: Write access. */ int uc; /**< Flag: Have unsynchronized changes. */ } dk4_dbi_t; /** Function to traverse a simple database. The function must check the kb, ks, vb and vs arguments for validity, they are passed as found by the backend functions (this means pointers may be NULL, sizes may be 0). @param Object modified during traversal, may be NULL. @param kb Key data. @param ks Key size. @param vb Value data. @param vs Value size. @return 1 on success, 0 on error (can continue), -1 on error (abort). */ typedef int dk4dbi_traverse_fct_t( void *obj, const void *kb, size_t ks, const void *vb, size_t vs ); #ifdef __cplusplus extern "C" { #endif /** Open database. @param fn File name. @param tp Backend type. @param wa Flag: Write access required. @param tr Flag: Truncate existing database (only in conjunction with wa). @param km Maximum key size (0 for unlimited). @param vm Maximum value size (0 for unlimited). The limits are used on all backends for writing, on in-memory databases also for reading. @param erp Error report, may be NULL. @return Pointer to new database structure on success, NULL on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if fn is NULL or tp contains an invalid type number, - DK4_E_NOT_SUPPORTED
if the database type is not supported, - DK4_E_MATH_OVERFLOW
on numeric overflow when calculating size, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available, - DK4_E_SYNTAX
if - the database file name does not refer to a regular file - or the file contents is not an in-memory database - or key/value sizes in the file are in conflict with restrictions, - DK4_E_SEC_CHECK
with failed check id in iDetails1 if access is denied by additional security checks, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available, - DK4_E_OPEN_READ_FAILED
if fopen() or fread() failed, - DK4_E_OPEN_WRITE_FAILED
if fopen() or fwrite() failed, - DK4_E_CLOSE_FAILED
with errno value in iDetails1 if fclose() failed. */ dk4_dbi_t * dk4dbi_open_with_type( const dkChar *fn, int tp, int wa, int tr, size_t km, size_t vm, dk4_er_t *erp ); /** Open database. @param fn Database type and file name. The database type is one from "mem", "bdb" or "ndbm". Database type and file name are separated by two colons "::". @param wa Flag: Write access required. @param tr Flag: Truncate existing database (only in conjunction with wa). @param km Maximum key size (0 for unlimited). @param vm Maximum value size (0 for unlimited). @param erp Error report, may be NULL. @return Pointer to new database structure on success, NULL on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if fn is NULL or tp contains an invalid type number, - DK4_E_NOT_SUPPORTED
if the database type is not supported, - DK4_E_MATH_OVERFLOW
on numeric overflow when calculating size, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available, - DK4_E_SYNTAX
if - the specification contains an invalid backend type - the database file name does not refer to a regular file - or the file contents is not an in-memory database - or key/value sizes in the file are in conflict with restrictions - or an invalid database type was specified, - DK4_E_SEC_CHECK
with failed check id in iDetails1 if access is denied by additional security checks, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available, - DK4_E_OPEN_READ_FAILED
if fopen() or fread() failed, - DK4_E_OPEN_WRITE_FAILED
if fopen() or fwrite() failed, - DK4_E_CLOSE_FAILED
with errno value in iDetails1 if fclose() failed. */ dk4_dbi_t * dk4dbi_open( const dkChar *fn, int wa, int tr, size_t km, size_t vm, dk4_er_t *erp ); /** Find file name part from combined type/name. @param filename Combined database type and file name. @return Valid pointer to file name part on success, NULL on error. */ const dkChar * dk4dbi_filename_part(const dkChar *filename); /** Close database, release resources. @param db Database to close. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if db is NULL, - DK4_E_NOT_SUPPORTED
if db indicates an unsupported backend type, - DK4_E_SYNTAX
if the database file name does not refer to a regular file, - DK4_E_SEC_CHECK
with failed check id in iDetails1 if access is denied by additional security checks, - DK4_E_OPEN_WRITE_FAILED
with errno value in iDetails1 if fopen() or fwrite() failed, - DK4_E_CLOSE_FAILED
with errno value in iDetails1 if fclose() failed. */ int dk4dbi_close( dk4_dbi_t *db, dk4_er_t *erp ); /** Set or reset option. @param db Database. @param opt Option. @param val New value (1 or 0). @return 1 on success, 0 on error (unknown option). */ int dk4dbi_set_option( dk4_dbi_t *db, int opt, int val ); /** Retrieve maximum key size. @param db Database to query. @param erp Error report, may be NULL. @return Value on success (0 means unlimited), 0 with erp set on error. Error code: - DK4_E_INVALID_ARGUMENTS
if db is NULL. */ size_t dk4dbi_get_key_max( const dk4_dbi_t *db, dk4_er_t *erp ); /** Retrieve maximum value size. @param db Database to query. @param erp Error report, may be NULL. @return Value on success (0 means unlimited), 0 with erp set on error. Error code: - DK4_E_INVALID_ARGUMENTS
if db is NULL. */ size_t dk4dbi_get_val_max( const dk4_dbi_t *db, dk4_er_t *erp ); /** Set or overwrite record. @param db Database. @param kp Pointer to key data. @param ks Key data size (number of bytes). @param vp Pointer to value data. Use NULL to delete a record. @param vs Value data size (number of bytes). @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if db or kp is NULL or ks is 0, - DK4_E_SYNTAX
if the database was opened read-only or ks or vs exceeds a size limit, - DK4_E_NOT_SUPPORTED
if db indicates an unsupported backend, - DK4_E_NOT_FOUND
if no record to delete was found, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available, - DK4_E_WRITE_FAILED
if modification of the database failed. */ int dk4dbi_set( dk4_dbi_t *db, const void *kp, size_t ks, const void *vp, size_t vs, dk4_er_t *erp ); /** Synchronize changes to file if supported by backend. @param db Database. @param erp error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if db or a backend component is NULL, - DK4_E_NOT_SUPPORTED
if db indicates an unsupported backend, - DK4_E_SYNTAX
if the database file name does not refer to a regular file, - DK4_E_SEC_CHECK
with failed check id in iDetails1 if access is denied by additional security checks, - DK4_E_OPEN_WRITE_FAILED
with errno value in iDetails1 if fopen() or fwrite() failed, - DK4_E_WRITE_FAILED
if the backend failed to write to a database, - DK4_E_CLOSE_FAILED
with errno value in iDetails1 if fclose() failed. */ int dk4dbi_sync( dk4_dbi_t *db, dk4_er_t *erp ); /** Retrieve record. @param db Database. @param kp Pointer to key data. @param ks Key data size (number of bytes). @param vp Address of result buffer. @param vs Pointer to buffer size variable (in: size, out: used). @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if db, kp, vp or vs is NULL or ks is 0 or *vs is 0 or ks exceeds the key size limit, - DK4_E_NOT_SUPPORTED
if db indicates an unsupported backend, - DK4_E_NOT_FOUND
if no record was found for the key, - DK4_E_BUFFER_TOO_SMALL
if the destination buffer is too small. */ int dk4dbi_get( dk4_dbi_t *db, const void *kp, size_t ks, void *vp, size_t *vs, dk4_er_t *erp ); /** Delete one record. @param db Database. @param kp Pointer to key data. @param ks Key data size (number of bytes). @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if db or kp is NULL or ks is 0, - DK4_E_SYNTAX
if the database is opened read-only or ks exceeds the key size limit, - DK4_E_NOT_SUPPORTED
if db indicates an unsupported backend, - DK4_E_WRITE_FAILED
if modification of the database failed, - DK4_E_NOT_FOUND
if no record for the specified key is in the database. */ int dk4dbi_delete( dk4_dbi_t *db, const void *kp, size_t ks, dk4_er_t *erp ); /** Traverse database. @param db Database to traverse. @param obj Object to modify during traversal, may be NULL. @param fct Function to execute for all records. @return 1 on success, 0 on error (database traversed), -1 on error (traversal aborted). */ int dk4dbi_traverse( dk4_dbi_t *db, void *obj, dk4dbi_traverse_fct_t *fct ); /** Retrieve backend type. @param db Database to check. @return Backend type. */ int dk4dbi_get_type(const dk4_dbi_t *db); #if !DK4_ON_WINDOWS #if DK4_CHAR_SIZE == 1 #if DK4_HAVE_CHOWN && DK4_HAVE_CHMOD /** Change owner and mode for a database. @param db Database to modify. @param user New owner. @param group New owner group. @param mode New file permissions. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if db is NULL, - DK4_E_NOT_SUPPORTED
if db indicates an unsupported backend, - DK4_E_SYSTEM
with errno value in iDetails1 if a chown or chmod system call failed, - DK4_E_MATH_OVERFLOW
if a mathematical overflow occured in size calculation, - DK4_E_BUFFER_TOO_SMALL
if the file name is too large for an internal buffer. */ int dk4dbi_change_owner_and_mode( dk4_dbi_t *db, uid_t user, gid_t group, mode_t mode, dk4_er_t *erp ); #endif /* if DK4_HAVE_CHOWN && DK4_HAVE_CHMOD */ #endif /* if DK4_CHAR_SIZE == 1 */ #endif /* if !DK4_ON_WINDOWS */ #ifdef __cplusplus } #endif %% module #include "dk4conf.h" #if DK4_HAVE_ERRNO_H #ifndef ERRNO_H_INCLUDED #include #define ERRNO_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SYS_STAT_H #ifndef SYS_STAT_H_INCLUDED #include #define SYS_STAT_H_INCLUDED 1 #endif #endif #include "dk4dbi.h" #include "dk4mem.h" #include "dk4memrs.h" #include "dk4strd.h" #include "dk4fopd.h" #include "dk4numco.h" #include "dk4isadm.h" #include "dk4fopt.h" #include "dk4mpl.h" #include "dk4delfile.h" #include "dk4stat.h" #include "dk4statd.h" $!trace-include /** One storage entry. */ typedef struct { void *kd; /**< Key data. */ void *vd; /**< Value data. */ size_t ks; /**< Key size. */ size_t vs; /**< Value size. */ } dk4_dbi_datum_t; /** Backend type names. */ static const dkChar *dk4dbi_type_names[] = { $!string-table macro=dkT mem bdb ndbm $!end }; /** File name suffixes for NDBM files. */ static const dkChar *dk4dbi_ndbm_suffixes[] = { dkT(".dir"), dkT(".pag") }; /** Maximum size of key or value. */ static const unsigned long dk4dbi_max_size = 4294967295UL; /** Delete one record from an in-memory database, release resources. @param ptr Record to delete @param options The database options. @return 1 on success, 0 on error. */ static int dk4dbi_datum_delete(dk4_dbi_datum_t *ptr, int options) { int back = 0; $? "+ dk4dbi_datum_delete %!8s", TR_8PTR(ptr) if (NULL != ptr) { back = 1; if (0 != (DK4_DB_OPT_RESET_KEY & options)) { if ((NULL != ptr->kd) && ((size_t)0UL < ptr->ks)) { $? ". clean up key memory" if (0 == dk4mem_reset_secure(ptr->kd, ptr->ks, NULL)) { back = 0; } } } if (0 != (DK4_DB_OPT_RESET_VALUE & options)) { if ((NULL != ptr->vd) && ((size_t)0UL < ptr->vs)) { $? ". clean up value memory" if (0 == dk4mem_reset_secure(ptr->vd, ptr->vs, NULL)) { back = 0; } } } dk4mem_release(ptr->kd); dk4mem_release(ptr->vd); ptr->ks = ptr->vs = (size_t)0UL; dk4mem_free(ptr); } $? "- dk4dbi_datum_delete %d", back return back; } /** Allocate memory for a new record. @param ks @param vs @param erp @return Pointer to new record structure on success, NULL on error. Error codes: - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available. */ static dk4_dbi_datum_t * dk4dbi_datum_new( size_t ks, size_t vs, dk4_er_t *erp ) { dk4_dbi_datum_t *back = NULL; $? "+ dk4dbi_datum_new %lu %lu", (unsigned long)ks, (unsigned long)vs back = dk4mem_new(dk4_dbi_datum_t,1,erp); if (NULL != back) { back->kd = back->vd = NULL; back->ks = back->vs = (size_t)0UL; back->kd = dk4mem_new(char,ks,erp); back->vd = dk4mem_new(char,vs,erp); if ((NULL != back->kd) && (NULL != back->vd)) { back->ks = ks; back->vs = vs; } else { dk4mem_release(back->kd); dk4mem_release(back->vd); dk4mem_free(back); back = NULL; } } $? "- dk4dbi_datum_new %d", TR_IPTR(back) return back; } /** Write in-memory database contents back to file. @param db Database to save. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_SYNTAX
if the database file name does not refer to a regular file, - DK4_E_SEC_CHECK
with failed check id in iDetails1 if access is denied by additional security checks, - DK4_E_OPEN_WRITE_FAILED
with errno value in iDetails1 if fopen() or fwrite() failed, - DK4_E_CLOSE_FAILED
with errno value in iDetails1 if fclose() failed, - DK4_E_WRITE_FAILD
if fwrite() failed. */ static int dk4dbi_mem_to_file( dk4_dbi_t *db, dk4_er_t *erp ) { FILE *fipo = NULL; /* Write text ile */ dk4_dbi_datum_t *rp = NULL; /* Current record */ unsigned char uc[8]; /* Sizes byte array */ size_t nbw = 0; /* Number of bytes */ int tests = DK4_FOPEN_SC_USER; /* Security checks */ int back = 0; $? "+ dk4dbi_mem_to_file %!8s", TR_8PTR(db) if (dk4isadmin()) { tests = DK4_FOPEN_SC_PRIVILEGED; } fipo = dk4fopen(db->fn, dkT("wb"), tests, erp); if (NULL != fipo) { $? ". fopen" back = 1; dk4sto_it_reset((db->dt).mem.i); do { rp = (dk4_dbi_datum_t *)dk4sto_it_next((db->dt).mem.i); if (NULL != rp) { $? ". record" uc[0] = (unsigned char)(((unsigned long)(rp->ks) >> 24) & 0x000000FFUL); uc[1] = (unsigned char)(((unsigned long)(rp->ks) >> 16) & 0x000000FFUL); uc[2] = (unsigned char)(((unsigned long)(rp->ks) >> 8) & 0x000000FFUL); uc[3] = (unsigned char)(((unsigned long)(rp->ks) ) & 0x000000FFUL); uc[4] = (unsigned char)(((unsigned long)(rp->vs) >> 24) & 0x000000FFUL); uc[5] = (unsigned char)(((unsigned long)(rp->vs) >> 16) & 0x000000FFUL); uc[6] = (unsigned char)(((unsigned long)(rp->vs) >> 8) & 0x000000FFUL); uc[7] = (unsigned char)(((unsigned long)(rp->vs) ) & 0x000000FFUL); nbw = fwrite(uc, 1, 8, fipo); if (8 == nbw) { $? ". sizes written" nbw = fwrite(rp->kd, 1, rp->ks, fipo); if (rp->ks == nbw) { $? ". key written" nbw = fwrite(rp->vd, 1, rp->vs, fipo); if (rp->vs == nbw) { $? ". value written" } else { back = 0; $? "! fwrite value" dk4error_set_simple_error_code(erp, DK4_E_WRITE_FAILED); } } else { $? "! fwrite key" back = 0; dk4error_set_simple_error_code(erp, DK4_E_WRITE_FAILED); } } else { $? "! fwrite sizes" back = 0; dk4error_set_simple_error_code(erp, DK4_E_WRITE_FAILED); } } } while (NULL != rp); if (0 != fclose(fipo)) { $? "! fclose" back = 0; dk4error_set_idetails(erp, DK4_E_CLOSE_FAILED, errno); } } else { $? "! fopen" } if (0 != back) { db->uc = 0; } $? "- dk4dbi_mem_to_file %d", back return back; } /** Read in-memory database contents back from file. @param db Database to restore. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_SYNTAX
if - the database file name does not refer to a regular file - or the file contents is not an in-memory database - or key/value sizes in the file are in conflict with size restrictions, - DK4_E_SEC_CHECK
with failed check id in iDetails1 if access is denied by additional security checks, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available, - DK4_E_OPEN_READ_FAILED with errno value in iDetails1 if fopen() or fread() failed. */ static int dk4dbi_mem_from_file( dk4_dbi_t *db, dk4_er_t *erp ) { dk4_stat_t stb; /* File stat buffer */ FILE *fipo = NULL; /* Input file */ dk4_dbi_datum_t *rp = NULL; /* Current record to process */ unsigned char uc[8]; /* Buffer to read sizes */ size_t nbr = 0; /* Number of bytes read */ size_t ks = 0; /* Key size */ size_t vs = 0; /* Value size */ unsigned long ul1 = 0UL; /* Key size */ unsigned long ul2 = 0UL; /* Value size */ int back = 0; /* Result */ int cc = 1; /* Flag: Can continue */ $? "+ dk4dbi_mem_from_file %!8s", TR_8PTR(db) fipo = dk4fopen(db->fn, dkT("rb"), 0, erp); if (NULL != fipo) { back = 1; do { nbr = fread(uc, 1, 8, fipo); if (8 == nbr) { $? ". sizes read" ul1 = ((((unsigned long)(uc[0])) << 24) & 0xFF000000UL) | ((((unsigned long)(uc[1])) << 16) & 0x00FF0000UL) | ((((unsigned long)(uc[2])) << 8) & 0x0000FF00UL) | ((((unsigned long)(uc[3])) ) & 0x000000FFUL); ul2 = ((((unsigned long)(uc[4])) << 24) & 0xFF000000UL) | ((((unsigned long)(uc[5])) << 16) & 0x00FF0000UL) | ((((unsigned long)(uc[6])) << 8) & 0x0000FF00UL) | ((((unsigned long)(uc[7])) ) & 0x000000FFUL); if ((dk4_um_t)(SIZE_MAX) >= (dk4_um_t)ul1) { if ((dk4_um_t)(SIZE_MAX) >= (dk4_um_t)ul2) { ks = (size_t)ul1; vs = (size_t)ul2; if ( (((size_t)0UL == db->km) || (db->km >= ks)) && (((size_t)0UL == db->vm) || (db->vm >= vs)) && ((size_t)0UL < ks) && ((size_t)0UL < vs) ) { rp = dk4dbi_datum_new(ks, vs, erp); if (NULL != rp) { $? ". memory allocated" nbr = fread(rp->kd, 1, rp->ks, fipo); if (nbr == rp->ks) { nbr = fread(rp->vd, 1, rp->vs, fipo); if (nbr == rp->vs) { if (0 == dk4sto_add((db->dt).mem.s, rp, erp)) { cc = 0; $? "! storage add" back = 0; dk4dbi_datum_delete(rp, 0); } } else { $? "! fread value data" cc = 0; back = 0; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); dk4dbi_datum_delete(rp, 0); } } else { $? "! fread key data" cc = 0; back = 0; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); dk4dbi_datum_delete(rp, 0); } } else { $? "! memory allocation" cc = 0; back = 0; } } else { $? "! size 0 or too large" cc = 0; back = 0; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { $? "! ul2 > SIZE_MAX" cc = 0; back = 0; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { $? "! ul1 > SIZE_MAX" cc = 0; back = 0; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { $? "! fread sizes" cc = 0; if (0 != nbr) { back = 0; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } } while (0 != cc); (void)fclose(fipo); } else { $? "! fopen" if (0 == dk4stat(&stb, db->fn, NULL)) { back = 1; $? ". no file found yet" } } $? "- dk4dbi_mem_from_file %d", back return back; } /** Compare two entries. @param l Left record. @param r Right record. @param cr Comparison criteria, ignored. @return 1 if l>r, 0 if l==r, -1 if lkd) { if (NULL != pr->kd) { if (pl->ks > pr->ks) { back = 1; } else { if (pl->ks < pr->ks) { back = -1; } else { if (0 < pl->ks) { back = DK4_MEMCMP(pl->kd, pr->kd, pl->ks); if (-1 > back) back = -1; if ( 1 < back) back = 1; } } } } else { back = 1; } } else { if (NULL != pr->kd) { back = -1; } } } else { back = 1; } } else { if (NULL != r) { back = -1; } } $? "- dk4dbi_datum_compare %d", back return back; } /** Delete base database structure, release resources. @param ptr Database structure to release. */ static void dk4dbi_delete_base(dk4_dbi_t *ptr) { $? "+ dk4dbi_delete_base %!8s", TR_8PTR(ptr) if (NULL != ptr) { dk4mem_release(ptr->fn); dk4mem_free(ptr); } $? "- dk4dbi_delete_base" } /** Allocate memory for a database. @param fn File name. @param tp Backend type. @param wa Flag: Write access required. @param erp Error report, may be NULL. @return Pointer to memory on success, NULL on error. Error codes: - DK4_E_MATH_OVERFLOW
on numeric overflow when calculating size, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available. */ static dk4_dbi_t * dk4dbi_new_base( const dkChar *fn, int tp, int wa, size_t km, size_t vm, dk4_er_t *erp ) { dk4_dbi_t *back = NULL; $? "+ dk4dbi_new_base \"%!ds\" %d %d", TR_STR(fn), tp, wa back = dk4mem_new(dk4_dbi_t,1,erp); if (NULL != back) { back->tp = tp; back->wa = wa; back->op = 0; back->uc = 0; back->km = km; back->vm = vm; switch (tp) { case DK4_DB_BE_MEMORY : { (back->dt).mem.s = NULL; (back->dt).mem.i = NULL; } break; case DK4_DB_BE_BDB : { #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) (back->dt).bdb.db = NULL; #endif } break; case DK4_DB_BE_NDBM : { #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) (back->dt).ndbm.dbm = NULL; #endif } break; } back->fn = dk4str_dup(fn, erp); if (NULL == back->fn) { $? "! dk4str_dup" dk4mem_free(back); back = NULL; } } $? "- dk4dbi_new_base %!8s", TR_8PTR(back) return back; } /** Close database, release resources. NOTE: Only release in-memory specific things, the dk4dbi_close() function calls dk4dbi_delete_base() to release the base components. @param db Database to close. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_SYNTAX
if the database file name does not refer to a regular file, - DK4_E_SEC_CHECK
with failed check id in iDetails1 if access is denied by additional security checks, - DK4_E_OPEN_WRITE_FAILED
with errno value in iDetails1 if fopen() or fwrite() failed, - DK4_E_CLOSE_FAILED
with errno value in iDetails1 if fclose() failed, - DK4_E_WRITE_FAILD
if fwrite() failed. */ static int dk4dbi_close_mem( dk4_dbi_t *db, dk4_er_t *erp ) { dk4_dbi_datum_t *rp; /* Current record */ int back = 1; $? "+ dk4dbi_close_mem" if ((NULL != (db->dt).mem.s) && (NULL != (db->dt).mem.i)) { /* Write records to file if there are unsaved changes. */ $? ". wa=%d uc=%d", db->wa, db->uc if ((0 != db->wa) && (0 != db->uc)) { if (0 == dk4dbi_mem_to_file(db, erp)) { back = 0; } } /* Release memory used for the records. */ dk4sto_it_reset((db->dt).mem.i); do { rp = (dk4_dbi_datum_t *)dk4sto_it_next((db->dt).mem.i); if (NULL != rp) { if (0 == dk4dbi_datum_delete(rp, db->op)) { back = 0; } } } while (NULL != rp); } if (NULL != (db->dt).mem.i) { dk4sto_it_close((db->dt).mem.i); } if (NULL != (db->dt).mem.s) { dk4sto_close((db->dt).mem.s); } $? "- dk4dbi_close_mem %d", back return back; } /** Open in-memory database. @param fn File name. @param wa Flag: Write access required. @param tr Flag: Truncate existing database. @param km Maximum key size. @param vm Maximum value size. @param erp Error report, may be NULL. @return Pointer to new database structure on success, NULL on error. Error codes: - DK4_E_MATH_OVERFLOW
on numeric overflow when calculating size, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available, - DK4_E_SYNTAX
if - the database file name does not refer to a regular file - or the file contents is not an in-memory database - or key/value sizes in the file are in conflict with size restrictions, - DK4_E_SEC_CHECK
with failed check id in iDetails1 if access is denied by additional security checks, - DK4_E_OPEN_READ_FAILED with errno value in iDetails1 if fopen() or fread() failed, - DK4_E_OPEN_WRITE_FAILED
with errno value in iDetails1 if fopen() or fwrite() failed, - DK4_E_CLOSE_FAILED
with errno value in iDetails1 if fclose() failed. */ static dk4_dbi_t * dk4dbi_open_mem( const dkChar *fn, int wa, int tr, size_t km, size_t vm, dk4_er_t *erp ) { dk4_dbi_t *back = NULL; $? "+ dk4dbi_open_mem \"%!ds\" %d %d", TR_STR(fn), wa, tr if (0 != tr) { (void)dk4delete_file(fn, NULL); } back = dk4dbi_new_base(fn, DK4_DB_BE_MEMORY, wa, km, vm, erp); if (NULL != back) { (back->dt).mem.s = NULL; (back->dt).mem.i = NULL; (back->dt).mem.s = dk4sto_open(erp); if (NULL != (back->dt).mem.s) { dk4sto_set_comp((back->dt).mem.s, dk4dbi_datum_compare, 0); (back->dt).mem.i = dk4sto_it_open((back->dt).mem.s, erp); } if ((NULL != (back->dt).mem.s) && (NULL != (back->dt).mem.i)) { if (0 == tr) { /* Do not truncate */ if (0 == dk4dbi_mem_from_file(back, erp)) { (void)dk4dbi_close_mem(back, NULL); back = NULL; } } else { /* Truncate */ if (0 == dk4dbi_mem_to_file(back, erp)) { (void)dk4dbi_close_mem(back, NULL); back = NULL; } } } else { (void)dk4dbi_close_mem(back, NULL); back = NULL; } } $? "- dk4dbi_open_mem %!8s", TR_8PTR(back) return back; } #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) /** Close database, release resources. NOTE: Only release in-memory specific things, the dk4dbi_close() function calls dk4dbi_delete_base() to release the base components. @param db Database to close. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_SYNTAX
if the database file name does not refer to a regular file, - DK4_E_SEC_CHECK
with failed check id in iDetails1 if access is denied by additional security checks, - DK4_E_OPEN_WRITE_FAILED
with errno value in iDetails1 if fopen() or fwrite() failed, - DK4_E_CLOSE_FAILED
with errno value in iDetails1 if fclose() failed, - DK4_E_WRITE_FAILD
if fwrite() failed. */ static int dk4dbi_close_bdb( dk4_dbi_t *db, dk4_er_t *erp ) { int back = 1; if (NULL != (db->dt).bdb.db) { if ((0 != db->wa) && (0 != db->uc)) { if (0 != ((db->dt).bdb.db)->sync((db->dt).bdb.db, 0)) { back = 0; } } ((db->dt).bdb.db)->close((db->dt).bdb.db, 0); (db->dt).bdb.db = NULL; } return back; } /** Open BDB database. @param fn File name. @param wa Flag: Write access required. @param tr Flag: Truncate existing database. @param km Maximum key size. @param vm Maximum value size. @param erp Error report, may be NULL. @return Pointer to new database structure on success, NULL on error. Error codes: - DK4_E_MATH_OVERFLOW
on numeric overflow when calculating size, - DK4_E_MEMORY_ALLOCATION_FAILED
with mem.elsize and mem.nelem set if there is not enough memory available, - DK4_E_OPEN_WRITE_FAILED
if the database can not be opened for writing, - DK4_E_OPEN_READ_FAILED
if the database can not be opened or reading, - DK4_E_NOT_SUPPORTED
when attempting to provide a wchar_t file name on non-Windows systems. */ static dk4_dbi_t * dk4dbi_open_bdb( const dkChar *fn, int wa, int tr, size_t km, size_t vm, dk4_er_t *erp ) { #if DK4_CHAR_SIZE > 1 #if DK4_ON_WINDOWS dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); return NULL; #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); return NULL; #endif #else dk4_dbi_t *back = NULL; int res = 0; int flags = 0; $? "+ dk4dbi_open_bdb (char) \"%!ds\" %d %d", TR_STR(fn), wa, tr back = dk4dbi_new_base(fn, DK4_DB_BE_BDB, wa, km, vm, erp); if (NULL != back) { if (0 != tr) { (void)dk4delete_file(fn, NULL); } res = db_create(&((back->dt).bdb.db), NULL, 0); if (0 == res) { ((back->dt).bdb.db)->set_errfile((back->dt).bdb.db, NULL); flags = ((0 != wa) ? ((0 != tr) ? ((DB_CREATE) || (DB_TRUNCATE)) : (DB_CREATE)) : (DB_RDONLY)); res = ((back->dt).bdb.db)->open( (back->dt).bdb.db, NULL, back->fn, NULL, DB_BTREE, flags, 0600 ); if (0 != res) { ((back->dt).bdb.db)->close((back->dt).bdb.db, 0); (back->dt).bdb.db = NULL; dk4dbi_delete_base(back); back = NULL; dk4error_set_simple_error_code( erp, ((0 != wa) ? (DK4_E_OPEN_WRITE_FAILED) : (DK4_E_OPEN_READ_FAILED)) ); } } else { (back->dt).bdb.db = NULL; dk4dbi_delete_base(back); back = NULL; dk4error_set_simple_error_code( erp, ((0 != wa) ? (DK4_E_OPEN_WRITE_FAILED) : (DK4_E_OPEN_READ_FAILED)) ); } } $? "- dk4dbi_open_bdb (char) %d", TR_IPTR(back) return back; #endif } #endif /* if DK4_HAVE_DB_H */ #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) /** Close database, release resources. NOTE: Only release in-memory specific things, the dk4dbi_close() function calls dk4dbi_delete_base() to release the base components. @param db Database to close. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_SYNTAX
if the database file name does not refer to a regular file, - DK4_E_SEC_CHECK
with failed check id in iDetails1 if access is denied by additional security checks, - DK4_E_OPEN_WRITE_FAILED
with errno value in iDetails1 if fopen() or fwrite() failed, - DK4_E_CLOSE_FAILED
with errno value in iDetails1 if fclose() failed, - DK4_E_WRITE_FAILD
if fwrite() failed. */ static int dk4dbi_close_ndbm( dk4_dbi_t *db, dk4_er_t *erp ) { int back = 1; if (NULL != (db->dt).ndbm.dbm) { dbm_close((db->dt).ndbm.dbm); (db->dt).ndbm.dbm = NULL; } return back; } static void dk4dbi_truncate_ndbm(const dkChar *fn) { dkChar buf[DK4_MAX_PATH]; const size_t szbuf = DK4_SIZEOF(buf,dkChar); if (0 != dk4str_cpy_s(buf, szbuf, fn, NULL)) { if (0 != dk4str_cat_s(buf, szbuf, dk4dbi_ndbm_suffixes[0], NULL)) { (void)dk4delete_file(buf, NULL); } } if (0 != dk4str_cpy_s(buf, szbuf, fn, NULL)) { if (0 != dk4str_cat_s(buf, szbuf, dk4dbi_ndbm_suffixes[1], NULL)) { (void)dk4delete_file(buf, NULL); } } } /** Open NDBM database. @param fn File name. @param wa Flag: Write access required. @param tr Flag: Truncate existing database. @param km Maximum key size. @param vm Maximum value size. @param erp Error report, may be NULL. @return Pointer to new database structure on success, NULL on error. */ static dk4_dbi_t * dk4dbi_open_ndbm( const dkChar *fn, int wa, int tr, size_t km, size_t vm, dk4_er_t *erp ) { dk4_dbi_t *back = NULL; int flags = 0; /* Flags for opening the DB */ $? "+ dk4dbi_open_ndbm \"%!ds\" %d %d", TR_STR(fn), wa, tr if (0 != tr) { dk4dbi_truncate_ndbm(fn); } back = dk4dbi_new_base(fn, DK4_DB_BE_NDBM, wa, km, vm, erp); if (NULL != back) { flags = O_CREAT | ((0 != wa) ? ((0 != tr) ? (O_RDWR | O_TRUNC) : (O_RDWR)) : (O_RDONLY)); (back->dt).ndbm.dbm = dbm_open(fn, flags, 0600); if (NULL == (back->dt).ndbm.dbm) { dk4dbi_delete_base(back); back = NULL; dk4error_set_simple_error_code( erp, ((0 != wa) ? (DK4_E_OPEN_WRITE_FAILED) : (DK4_E_OPEN_READ_FAILED)) ); } } $? "- dk4dbi_open_ndbm %!8s", TR_8PTR(back) return back; } #endif /* if DK4_HAVE_NDBM_H */ dk4_dbi_t * dk4dbi_open_with_type( const dkChar *fn, int tp, int wa, int tr, size_t km, size_t vm, dk4_er_t *erp ) { dk4_dbi_t *back = NULL; $? "+ dk4dbi_open_with_type \"%!ds\" tp=%d wa=%d tr=%d", TR_STR(fn), tp, wa, tr if (NULL != fn) { switch (tp) { case DK4_DB_BE_MEMORY : { back = dk4dbi_open_mem(fn, wa, tr, km, vm, erp); } break; case DK4_DB_BE_BDB : { #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) back = dk4dbi_open_bdb(fn, wa, tr, km, vm, erp); #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; case DK4_DB_BE_NDBM : { #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) back = dk4dbi_open_ndbm(fn, wa, tr, km, vm, erp); #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; default : { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } break; } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dbi_open_with_type %!8s", TR_8PTR(back) return back; } dk4_dbi_t * dk4dbi_open( const dkChar *fn, int wa, int tr, size_t km, size_t vm, dk4_er_t *erp ) { dk4_dbi_t *back = NULL; dkChar *mycopy = NULL; /* Private copy of fn */ dkChar *colon = NULL; /* First colon in mycopy */ const dkChar *fnp = NULL; /* File name pointer */ int tp = DK4_DB_BE_MEMORY ; /* Database type */ $? "+ dk4dbi_open \"%!ds\" %d %d", TR_STR(fn), wa, tr if (NULL != fn) { mycopy = dk4str_dup(fn, erp); if (NULL != mycopy) { #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) tp = DK4_DB_BE_NDBM; #endif #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) tp = DK4_DB_BE_BDB; #endif fnp = fn; colon = dk4str_chr(mycopy, dkT(':')); if (NULL != colon) { if (dkT(':') == colon[1]) { fnp = &(colon[2]); *colon = dkT('\0'); tp = dk4str_array_index(dk4dbi_type_names, mycopy, 0); if (DK4_DB_BE_UNKNOWN == tp) { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } } if (DK4_DB_BE_UNKNOWN != tp) { back = dk4dbi_open_with_type(fnp, tp, wa, tr, km, vm, erp); } dk4mem_free(mycopy); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dbi_open %!8s", TR_8PTR(back) return back; } const dkChar * dk4dbi_filename_part(const dkChar *filename) { const dkChar *back = NULL; if (NULL != filename) { back = dk4str_chr(filename, dkT(':')); if (NULL != back) { back++; if (dkT(':') == *back) { back++; } else { back = filename; } } else { back = filename; } } return back; } int dk4dbi_close( dk4_dbi_t *db, dk4_er_t *erp ) { int back = 0; $? "+ dk4dbi_close %!8s", TR_8PTR(db) if (NULL != db) { switch (db->tp) { case DK4_DB_BE_MEMORY : { back = dk4dbi_close_mem(db, erp); } break; case DK4_DB_BE_BDB : { #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) back = dk4dbi_close_bdb(db, erp); #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; case DK4_DB_BE_NDBM : { #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) back = dk4dbi_close_ndbm(db, erp); #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; } dk4dbi_delete_base(db); } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dbi_close" return back; } int dk4dbi_set_option( dk4_dbi_t *db, int opt, int val ) { int back = 0; if (NULL != db) { switch (opt) { case DK4_DB_OPT_RESET_KEY : case DK4_DB_OPT_RESET_VALUE : { if (0 != val) { db->op |= opt; } else { db->op &= (~(opt)); } back = 1; } break; } } return back; } int dk4dbi_delete( dk4_dbi_t *db, const void *kp, size_t ks, dk4_er_t *erp ) { int back = 0; #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) DBT k; /* Key */ #endif #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) datum datk; /* Key */ #endif dk4_dbi_datum_t dat; /* Key for comparison */ dk4_dbi_datum_t *rp; /* Result found in db */ $? "+ dk4dbi_delete %!8s %!8s %lu", TR_8PTR(db), TR_8PTR(kp), (unsigned long)ks if ((NULL != db) && (NULL != kp) && ((size_t)0UL < ks)) { if ((0 != db->wa) && (((size_t)0UL == db->km) || (db->km >= ks))) { switch (db->tp) { case DK4_DB_BE_MEMORY : { db->uc = 1; dat.kd = (void *)kp; dat.ks = ks; rp = (dk4_dbi_datum_t *)dk4sto_it_find_like( (db->dt).mem.i, &dat, 0 ); if (NULL != rp) { back = dk4sto_remove((db->dt).mem.s, rp, erp); if (0 != back) { if (0 == dk4dbi_datum_delete(rp, db->op)) { back = 0; } } else { dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND); } } else { dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND); } } break; case DK4_DB_BE_BDB : { #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) if (NULL != (db->dt).bdb.db) { db->uc = 1; DK4_MEMRES(&k, sizeof(DBT)); k.data = (void *)kp; k.size = ks; if (0 == ((db->dt).bdb.db)->del((db->dt).bdb.db, NULL, &k, 0)) { back = 1; } else { dk4error_set_simple_error_code(erp, DK4_E_WRITE_FAILED); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; case DK4_DB_BE_NDBM : { #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) if (NULL != (db->dt).ndbm.dbm) { db->uc = 1; DK4_MEMRES(&datk, sizeof(datum)); datk.dptr = (char *)kp; datk.dsize = ks; if (0 == dbm_delete((db->dt).ndbm.dbm, datk)) { back = 1; } else { dk4error_set_simple_error_code(erp, DK4_E_WRITE_FAILED); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; } } else { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dbi_delete %d", back return back; } int dk4dbi_set( dk4_dbi_t *db, const void *kp, size_t ks, const void *vp, size_t vs, dk4_er_t *erp ) { dk4_dbi_datum_t dat; /* Key for comparison */ #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) DBT k; /* Key */ DBT v; /* Value */ #endif #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) datum datk; /* Key */ datum datv; /* Value */ #endif dk4_dbi_datum_t *rp = NULL; /* Result record */ char *nb = NULL; /* New value buffer */ int back = 0; $? "+ dk4dbi_set %!8s %!8s %lu %!8s %lu", TR_8PTR(db), TR_8PTR(kp), (unsigned long)ks, TR_8PTR(vp), (unsigned long)vs if ((NULL != db) && (NULL != kp) && ((size_t)0UL < ks)) { $? ". key ok" if ((NULL != vp) && ((size_t)0UL < vs)) { $? ". set value" if( (((size_t)0UL == db->km) || (db->km >= ks)) && (((size_t)0UL == db->vm) || (db->vm >= vs)) && (0 != db->wa) ) { $? ". key/value size ok" switch (db->tp) { case DK4_DB_BE_MEMORY : { $? ". type memory" db->uc = 1; dat.kd = (void *)kp; dat.ks = ks; rp = (dk4_dbi_datum_t *)dk4sto_it_find_like( (db->dt).mem.i, &dat, 0 ); if (NULL != rp) { nb = dk4mem_new(char,vs,erp); if (NULL != nb) { back = 1; if (NULL != rp->vd) { if (0 != (DK4_DB_OPT_RESET_VALUE & (db->op))) { if ((size_t)0UL < rp->vs) { back = dk4mem_reset_secure(rp->vd, rp->vs, NULL); } } dk4mem_free(rp->vd); } rp->vd = nb; DK4_MEMCPY(rp->vd,vp,vs); rp->vs = vs; } } else { rp = dk4dbi_datum_new(ks, vs, erp); if (NULL != rp) { DK4_MEMCPY(rp->kd,kp,ks); DK4_MEMCPY(rp->vd,vp,vs); if (0 != dk4sto_add((db->dt).mem.s, rp, erp)) { back = 1; } else { dk4dbi_datum_delete(rp, db->op); } } } } break; case DK4_DB_BE_BDB : { $? ". type bdb" #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) $? ". BDB supported" if (NULL != (db->dt).bdb.db) { $? ". db open" db->uc = 1; DK4_MEMRES(&k, sizeof(DBT)); DK4_MEMRES(&v, sizeof(DBT)); k.data = (void *)kp; v.data = (void *)vp; k.size = ks; v.size = vs; $? ". delete existing entry, if exists" ((db->dt).bdb.db)->del((db->dt).bdb.db, NULL, &k, 0); DK4_MEMRES(&k, sizeof(DBT)); k.data = (void *)kp; k.size = ks; $? ". set new entry" if (0 == ((db->dt).bdb.db)->put((db->dt).bdb.db,NULL,&k,&v,0)) { back = 1; $? ". success" } else { $? "! put" dk4error_set_simple_error_code(erp, DK4_E_WRITE_FAILED); } } else { $? "! db not opened" dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; case DK4_DB_BE_NDBM : { $? ". type ndbm" #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) if (NULL != (db->dt).ndbm.dbm) { db->uc = 1; DK4_MEMRES(&datk, sizeof(datum)); DK4_MEMRES(&datv, sizeof(datum)); datk.dptr = (char *)kp; datk.dsize = ks; datv.dptr = (char *)vp; datv.dsize = vs; if (0 == dbm_store((db->dt).ndbm.dbm, datk, datv, DBM_INSERT)) { back = 1; } else { DK4_MEMRES(&datk, sizeof(datum)); DK4_MEMRES(&datv, sizeof(datum)); datk.dptr = (char *)kp; datk.dsize = ks; datv.dptr = (char *)vp; datv.dsize = vs; if (0 == dbm_store((db->dt).ndbm.dbm,datk,datv,DBM_REPLACE)) { back = 1; } else { dk4error_set_simple_error_code(erp, DK4_E_WRITE_FAILED); } } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; } } else { $? "! key/value size too large" dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { $? ". delete" back = dk4dbi_delete(db, kp, ks, erp); } } else { $? "! arguments" dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dbi_set %d", back return back; } int dk4dbi_sync( dk4_dbi_t *db, dk4_er_t *erp ) { int back = 0; $? "+ dk4dbi_sync %!8s", TR_8PTR(db) if (NULL != db) { switch (db->tp) { case DK4_DB_BE_MEMORY : { if ((0 != db->wa) && (0 != db->uc)) { back = dk4dbi_mem_to_file(db, erp); } else { back = 1; } } break; case DK4_DB_BE_BDB : { #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) if (NULL != (db->dt).bdb.db) { if ((0 != db->wa) && (0 != db->uc)) { if (0 == ((db->dt).bdb.db)->sync((db->dt).bdb.db, 0)) { back = 1; db->uc = 0; } else { dk4error_set_simple_error_code(erp, DK4_E_WRITE_FAILED); } } else { back = 1; } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; case DK4_DB_BE_NDBM : { #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) if (NULL != (db->dt).ndbm.dbm) { back = 1; } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dbi_sync %d", back return back; } int dk4dbi_get( dk4_dbi_t *db, const void *kp, size_t ks, void *vp, size_t *vs, dk4_er_t *erp ) { dk4_dbi_datum_t dat; /* Key for comparison */ #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) DBT k; /* Key */ DBT v; /* Value */ #endif #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) datum datk; /* Key */ datum datv; /* Value */ #endif dk4_dbi_datum_t *rp; /* Search result pointer */ int back = 0; $? "+ dk4dbi_get %!8s %!8s %lu %!8s %!8s", TR_8PTR(db), TR_8PTR(kp), (unsigned long)ks, TR_8PTR(vp), TR_8PTR(vs) if ( (NULL != db) && (NULL != kp) && ((size_t)0 < ks) && (NULL != vp) && (NULL != vs) ) { if ((size_t)0UL < *vs) { if (((size_t)0UL == db->km) || (db->km >= ks)) { switch (db->tp) { case DK4_DB_BE_MEMORY : { $? ". type memory" dat.kd = (void *)kp; dat.ks = ks; rp = (dk4_dbi_datum_t *)dk4sto_it_find_like( (db->dt).mem.i, &dat, 0 ); if (NULL != rp) { if ((NULL != rp->vd) && ((size_t)0UL < rp->vs)) { if (*vs >= rp->vs) { DK4_MEMCPY(vp,rp->vd,rp->vs); *vs = rp->vs; back = 1; } else { dk4error_set_simple_error_code(erp, DK4_E_BUFFER_TOO_SMALL); } } else { dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND); } } else { dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND); } } break; case DK4_DB_BE_BDB : { $? ". type bdb" #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) $? ". bdb supported" if (NULL != (db->dt).bdb.db) { $? ". db open" DK4_MEMRES(&k, sizeof(DBT)); DK4_MEMRES(&v, sizeof(DBT)); k.data = (void *)kp; k.size = ks; v.data = (void *)vp; v.ulen = *vs; v.flags = DB_DBT_USERMEM; if (0 == ((db->dt).bdb.db)->get((db->dt).bdb.db,NULL,&k,&v,0)) { *vs = v.size; $? ". found" back = 1; } else { $? "! not found" dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND); } } else { $? "! db not opened" dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } #else $? "! bdb not supported" dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; case DK4_DB_BE_NDBM : { $? ". type ndbm" #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) if (NULL != (db->dt).ndbm.dbm) { DK4_MEMRES(&datk, sizeof(datum)); DK4_MEMRES(&datv, sizeof(datum)); datk.dptr = (char *)kp; datk.dsize = ks; datv = dbm_fetch((db->dt).ndbm.dbm, datk); if ((NULL != datv.dptr) && (0 < datv.dsize)) { if (*vs >= datv.dsize) { DK4_MEMCPY(vp,datv.dptr,datv.dsize); *vs = datv.dsize; back = 1; } else { dk4error_set_simple_error_code(erp, DK4_E_BUFFER_TOO_SMALL); } } else { dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } #else dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); #endif } break; } } else { $? "! key size too long" dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } } else { $? "! args (2)" dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } } else { $? "! args (1)" dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dbi_get %d", back return back; } int dk4dbi_traverse( dk4_dbi_t *db, void *obj, dk4dbi_traverse_fct_t *fct ) { #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) DBT dbtk; /* Key */ DBT dbtv; /* Value */ DBC *cp = NULL; /* Cursor for traversal */ #endif #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) datum datk; /* Key */ datum datv; /* Value */ #endif dk4_dbi_datum_t *dddt = NULL; /* Pointer for traversal */ int res = 0; /* Operation result */ int first = 1; /* Flag: Retrieving first record */ int back = -1; $? "+ dk4dbi_traverse db=%d obj=%d", TR_IPTR(db), TR_IPTR(obj) if ((NULL != db) && (NULL != fct)) { switch (db->tp) { case DK4_DB_BE_MEMORY : { back = 1; dk4sto_it_reset((db->dt).mem.i); do { dddt = dk4sto_it_next((db->dt).mem.i); if (NULL != dddt) { res = (*fct)(obj, dddt->kd, dddt->ks, dddt->vd, dddt->vs); if (res < back) { back = res; } } } while((NULL != dddt) && (-1 < back)); } break; case DK4_DB_BE_BDB : { #if DK4_HAVE_DB_H && (DK4_CHAR_SIZE == 1) res = ((db->dt).bdb.db)->cursor((db->dt).bdb.db, NULL, &cp, 0); if (0 == res) { if (NULL != cp) { back = 1; DK4_MEMRES(&dbtk, sizeof(DBT)); DK4_MEMRES(&dbtv, sizeof(DBT)); do { #if DK4_HAVE_DB_CURSOR_C_GET res = cp->c_get( cp, &dbtk, &dbtv, ((0 != first) ? (DB_FIRST) : (DB_NEXT)) ); #else res = cp->get( cp, &dbtk, &dbtv, ((0 != first) ? (DB_FIRST) : (DB_NEXT)) ); #endif first = 0; if (0 == res) { res = (*fct)(obj, dbtk.data, dbtk.size, dbtv.data, dbtv.size); if (res < back) { back = res; } } else { res = -1; /* Failed to obtain a record, stop */ } } while((-1 < res) && (-1 < back)); cp->c_close(cp); } else { $? "! failed to create cursor" } } else { $? "! failed to create cursor" } #endif } break; case DK4_DB_BE_NDBM : { #if DK4_HAVE_NDBM_H && (DK4_CHAR_SIZE == 1) back = 1; do { if (0 != first) { datk = dbm_firstkey((db->dt).ndbm.dbm); first = 0; } else { datk = dbm_nextkey((db->dt).ndbm.dbm); } if ((NULL != datk.dptr) && (0 < datk.dsize)) { datv = dbm_fetch((db->dt).ndbm.dbm, datk); if ((NULL != datv.dptr) && (0 < datv.dsize)) { res = (*fct)(obj, datk.dptr, datk.dsize, datv.dptr, datv.dsize); if (res < back) { back = res; } } else { res = -1; } } else { res = -1; } } while((-1 < res) && (-1 < back)); #endif } break; } } $? "- dk4dbi_traverse %d", back return back; } #if !DK4_ON_WINDOWS #if DK4_CHAR_SIZE == 1 #if DK4_HAVE_CHOWN && DK4_HAVE_CHMOD /** Change ownership and mode for a file. @param fn File name. @param user New owner. @param group New owner group. @param mode New file permissions. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_SYSTEM
with errno value in iDetails1 if chown or chmod failed. */ static int dk4dbi_change_file_owner_and_mode( const dkChar *fn, uid_t user, gid_t group, mode_t mode, dk4_er_t *erp ) { int back = 0; $? "+ dk4dbi_change_file_owner_and_mode" errno = 0; if (0 == chown(fn, user, group)) { errno = 0; if (0 == chmod(fn, mode)) { back = 1; } else { dk4error_set_idetails(erp, DK4_E_SYSTEM, errno); } } else { dk4error_set_idetails(erp, DK4_E_SYSTEM, errno); } $? "- dk4dbi_change_file_owner_and_mode %d", back return back; } int dk4dbi_change_owner_and_mode( dk4_dbi_t *db, uid_t user, gid_t group, mode_t mode, dk4_er_t *erp ) { dkChar buf[DK4_MAX_PATH]; /* File names construction */ const size_t szbuf = DK4_SIZEOF(buf,dkChar); /* Buffer size */ int back = 0; $? "+ dk4dbi_change_owner_and_mode" if (NULL != db) { switch (db->tp) { case DK4_DB_BE_MEMORY : case DK4_DB_BE_BDB : { back = dk4dbi_change_file_owner_and_mode( db->fn, user, group, mode, erp ); } break; case DK4_DB_BE_NDBM : { if (0 != dk4str_cpy_s(buf, szbuf, db->fn, erp)) { if (0 != dk4str_cat_s(buf, szbuf, dk4dbi_ndbm_suffixes[0], erp)) { back = dk4dbi_change_file_owner_and_mode(buf,user,group,mode,erp); (void)dk4str_cpy_s(buf, szbuf, db->fn, erp); if (0 != dk4str_cat_s(buf, szbuf, dk4dbi_ndbm_suffixes[1], erp)) { if (0 == dk4dbi_change_file_owner_and_mode(buf,user,group,mode,erp) ) { back = 0; } } } } } break; default : { dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED); } break; } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dbi_change_owner_and_mode %d", back return back; } #endif /* if DK4_HAVE_CHOWN && DK4_HAVE_CHMOD */ #endif /* if DK4_CHAR_SIZE == 1 */ #endif /* if !DK4_ON_WINDOWS */ size_t dk4dbi_get_key_max( const dk4_dbi_t *db, dk4_er_t *erp ) { size_t back = 0; $? "+ dk4dbi_get_key_max" if (NULL != db) { back = db->km; } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dbi_get_key_max %u", (unsigned)back return back; } size_t dk4dbi_get_val_max( const dk4_dbi_t *db, dk4_er_t *erp ) { size_t back = 0; $? "+ dk4dbi_get_val_max" if (NULL != db) { back = db->vm; } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } $? "- dk4dbi_get_val_max %u", (unsigned)back return back; } int dk4dbi_get_type(const dk4_dbi_t *db) { int back = DK4_DB_BE_UNKNOWN; if (NULL != db) { back = db->tp; } return back; }