%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% header #ifdef __cplusplus extern "C" { #endif /** Check whether a database type is supported. @param tp Database type, see @ref databasetypes. @return 1 on success (type supported), 0 on error (not supported). */ int dk3dbi_type_supported(int tp); /** Open a database. @param fn Database file name. @param tp Database type, see @ref databasetypes. @param acc Access type, see @ref databaseaccess. @param pec Pointer to error code variable, may be NULL. @param app Application structure for diagnostics, may be NULL. @return Pointer to opened database on success, NULL on error. */ dk3_dbi_t * dk3dbi_open_app(dkChar const *fn, int tp, int acc, int *pec, dk3_app_t *app); /** Open a database. @param fn Database file name. @param tp Database type, see @ref databasetypes. @param acc Access type, see @ref databaseaccess. @param pec Pointer to error code variable, may be NULL. @return Pointer to opened database on success, NULL on error. */ dk3_dbi_t * dk3dbi_open(dkChar const *fn, int tp, int acc, int *pec); /** Open a database, discard all existing contents. @param fn Database file name. @param tp Database type, see @ref databasetypes. @param acc Access type, see @ref databaseaccess. @param pec Pointer to error code variable, may be NULL. @param app Application structure for diagnostics, may be NULL. @return Pointer to opened database on success, NULL on error. */ dk3_dbi_t * dk3dbi_open_truncate_app( dkChar const *fn, int tp, int acc, int *pec, dk3_app_t *app ); /** Open a database, discard all existing contents. @param fn Database file name. @param tp Database type, see @ref databasetypes. @param acc Access type, see @ref databaseaccess. @param pec Pointer to error code variable, may be NULL. @return Pointer to opened database on success, NULL on error. */ dk3_dbi_t * dk3dbi_open_truncate(dkChar const *fn, int tp, int acc, int *pec); /** Set an entry in the database. @param dbp Database. @param key Key data. @param val Value data. @return 1 on success, 0 on error. */ int dk3dbi_set(dk3_dbi_t *dbp, dk3_datum_t *key, dk3_datum_t *val); /** Get an entry from the database. @param dbp Database. @param key Key data. @param val Value data, set up for a result buffer. On input it contains the buffer size, on successful return it contains the number of bytes. @return 1 on success, 0 on error. */ int dk3dbi_get(dk3_dbi_t *dbp, dk3_datum_t *key, dk3_datum_t *val); /** Delete an entry from the database. @param dbp Database. @param key Key data. @return 1 on success, 0 on error. */ int dk3dbi_delete(dk3_dbi_t *dbp, dk3_datum_t *key); /** Save 8-bit character string to database. @param dbp Database. @param key Key string. @param val Value string. @return 1 on success, 0 on error. */ int dk3dbi_set_c8_string( dk3_dbi_t *dbp, char const *key, char const *val ); /** Retrieve 8-bit character string from database. @param dbp Database. @param key Key string. @param vb Value buffer. @param vbsz Value buffer size in bytes. @return 1 on success, 0 on error. */ int dk3dbi_get_c8_string( dk3_dbi_t *dbp, char const *key, char *vb, size_t vbsz ); /** Delete 8-bit character string from database. @param dbp Database. @param key Key string. @return 1 on success, 0 on error. */ int dk3dbi_delete_c8_string( dk3_dbi_t *dbp, char const *key ); /** Save 16-bit character string to database. @param dbp Database. @param key Key string. @param val Value string. @return 1 on success, 0 on error. */ int dk3dbi_set_c16_string( dk3_dbi_t *dbp, dk3_c16_t const *key, dk3_c16_t const *val ); /** Retrieve 16-bit character string from database. @param dbp Database. @param key Key string. @param vb Value buffer. @param vbsz Value buffer size (number of 16-bit characters). @return 1 on success, 0 on error. */ int dk3dbi_get_c16_string( dk3_dbi_t *dbp, dk3_c16_t const *key, dk3_c16_t *vb, size_t vbsz ); /** Delete 16-bit character string from database. @param dbp Database. @param key Key string. @return 1 on success, 0 on error. */ int dk3dbi_delete_c16_string( dk3_dbi_t *dbp, dk3_c16_t const *key ); /** Save 32-bit character string to database. @param dbp Database. @param key Key string. @param val Value string. @return 1 on success, 0 on error. */ int dk3dbi_set_c32_string( dk3_dbi_t *dbp, dk3_c32_t const *key, dk3_c32_t const *val ); /** Retrieve 32-bit character string from database. @param dbp Database. @param key Key string. @param vb Value buffer. @param vbsz Value buffer size (number of 32-bit characters). @return 1 on success, 0 on error. */ int dk3dbi_get_c32_string( dk3_dbi_t *dbp, dk3_c32_t const *key, dk3_c32_t *vb, size_t vbsz ); /** Delete 32-bit character string from database. @param dbp Database. @param key Key string. @return 1 on success, 0 on error. */ int dk3dbi_delete_c32_string( dk3_dbi_t *dbp, dk3_c32_t const *key ); /** Save a dkChar string to the database. @param dbp Database. @param key Key string. @param val Value string. @return 1 on success, 0 on error. */ int dk3dbi_set_string( dk3_dbi_t *dbp, dkChar const *key, dkChar const *val ); /** Retrieve dkChar string from the database. @param dbp Database. @param key Key string. @param vb Value buffer. @param vbsz Size of value buffer (Number of dkChar). @return 1 on success, 0 on error. */ int dk3dbi_get_string( dk3_dbi_t *dbp, dkChar const *key, dkChar *vb, size_t vbsz ); /** Delete dkChar string from database. @param dbp Database. @param key Key string. @return 1 on success, 0 on error. */ int dk3dbi_delete_string( dk3_dbi_t *dbp, dkChar const *key ); /** Synchronize to disk. @param dbp Database. @return 1 on success, 0 on error. */ int dk3dbi_sync(dk3_dbi_t *dbp); /** Traverse database. @param dbp Database. @param obj Object to modify while traversing the database. @param fct Callback function to use for each key/value pair. @return 1 on success, 0 on error. */ int dk3dbi_traverse(dk3_dbi_t *dbp, void *obj, dk3_db_traverse_fct_t *fct); /** Get last error code occured. @param dbp Database. @param res Flag: Reset error code. @return Error code for last error. */ int dk3dbi_get_error(dk3_dbi_t *dbp, int res); /** Close database. @param dbp Database to close. @return 1 on success (sync ok), 0 on error. */ int dk3dbi_close(dk3_dbi_t *dbp); /** Delete database file(s). @param fn Database file name. @param tp Database type. @param app Application structure for diagnostics, may be NULL. @return 1 on success, 0 on error. */ int dk3dbi_dbfile_delete_app(dkChar const *fn, int tp, dk3_app_t *app); /** Truncate database (remove all contents). @param fn Database file name. @param tp Database type. @param app Application structure for diagnostics, may be NULL. @return 1 on success, 0 on error. */ int dk3dbi_dbfile_truncate_app(dkChar const *fn, int tp, dk3_app_t *app); /** Delete database file(s). @param fn Database file name. @param tp Database type. @return 1 on success, 0 on error. */ int dk3dbi_dbfile_delete(dkChar const *fn, int tp); /** Truncate database (remove all contents). @param fn Database file name. @param tp Database type. @return 1 on success, 0 on error. */ int dk3dbi_dbfile_truncate(dkChar const *fn, int tp); /** Split a given name into database type and real file name. The given name may be either a file name with tp set to a database type or "mem::file", "bdb::file", or "ndbm::file" to specify a file name and database type with tp set to DK3_DB_TYPE_UNKNOWN. @param myfn Pointer to a pointer to receive real file name. @param mytp Pointer to type output variable. @param fn File name or type::filename. @param tp Type as specified. @param pec Pointer to error code variable. @param app Application structure for diagnostics, may be NULL. @return 1 on success, 0 on error. */ int dk3dbi_find_type_and_name( dkChar const **myfn, int *mytp, dkChar const *fn, int tp, int *pec, dk3_app_t *app ); #if !DK3_ON_WINDOWS #if DK3_CHAR_SIZE == 1 #if DK3_HAVE_CHOWN && DK3_HAVE_CHMOD /** Change user name and permissions for a database. This function is intended for daemons on *x systems where the daemon is started by user root but gives up privileges before starting processing. @param db Database to change. @param uid New user ID. @param gid New group ID. @param mode Unix/Linux permissions. @return 1 on success, 0 on error. */ int dk3dbi_change_user_and_permissions( dk3_dbi_t *db, uid_t uid, gid_t gid, mode_t mode ); #endif #endif #endif #ifdef __cplusplus } #endif %% module #include "dk3all.h" #include "dk3dbi.h" #ifdef DK3_USE_DB #undef DK3_USE_DB #endif #ifdef DK3_USE_NDBM #undef DK3_USE_NDBM #endif #if DK3_HAVE_DB_H #include /** Use Berkeley DB. */ #define DK3_USE_DB 1 #endif #if DK3_HAVE_NDBM_H #if !DK3_HAVE_GDBM_H #include /** Use NDBM. */ #define DK3_USE_NDBM 1 #endif #endif $!trace-include /** Data type used in storage. */ typedef struct { /** Key data. */ void *keydata; /** value data. */ void *valdata; /** Key size (number of bytes in key data). */ size_t keysize; /** Value size (number of bytes in value data). */ size_t valsize; } dk3_dbi_node_t; /** Names of database types. Order must match the definitions in @ref databasetypes. */ static dkChar const * const dk3dbi_type_names[] = { $!string-table macro=dkT # # 0: in-memory database # mem # # 1: Berkeley DB # bdb # # 2: NDBM # ndbm $!end }; /** Modes to open file. */ static dkChar const * const dk3dbi_file_open_modes[] = { /* 0 */ dkT("rb"), /* 1 */ dkT("wb") }; /** File name suffixes for NDBM files. */ static char const * const dk3dbi_ndbm_suffixes[] = { ".dir", ".pag" }; /** Delete one storage node, release memory. @param np Node to delete. */ static void dk3dbi_node_delete(dk3_dbi_node_t *np) { if(np) { dk3_release(np->keydata); dk3_release(np->valdata); np->keydata = NULL; np->valdata = NULL; np->keysize = 0; np->valsize = 0; dk3_delete(np); } } /** Create a new node with specified buffer sizes. @param ks Keys size. @param vs Value size. @param app Application structure for diagnostics. @return Pointer to new node on success, NULL on error. */ static dk3_dbi_node_t * dk3dbi_node_new(size_t ks, size_t vs, dk3_app_t *app) { dk3_dbi_node_t *back = NULL; char *cpk = NULL; /* Key buffer pointer. */ char *cpv = NULL; /* Value buffer pointer. */ int ok = 0; /* Flag: Success. */ $? "+ dk3dbi_node_new %u %u", (unsigned)ks, (unsigned)vs back = dk3_new_app(dk3_dbi_node_t,1,app); if(back) { back->keysize = 0; back->valsize = 0; back->keydata = NULL; back->valdata = NULL; cpk = dk3_new_app(char,ks,app); if(cpk) { cpv = dk3_new_app(char,vs,app); if(cpv) { ok = 1; back->keydata = (void *)cpk; back->valdata = (void *)cpv; back->keysize = ks; back->valsize = vs; } } if(!(ok)) { dk3_delete(cpk); dk3_delete(cpv); dk3dbi_node_delete(back); back = NULL; } } $? "- dk3dbi_node_new %s", TR_PTR(back) return back; } /** Comparison of storage nodes. @param l Left node. @param r Right node. @param cr Comparison criteria (0=node/node, 1=node/datum). @return Comparison result. */ static int dk3dbi_node_compare(void const *l, void const *r, int cr) { int back = 0; dk3_dbi_node_t const *pl; /* Left object pointer. */ dk3_dbi_node_t const *pr; /* Right object pointer. */ dk3_datum_t const *pd; /* Right object pointer (datum). */ if(l) { if(r) { pl = (dk3_dbi_node_t const *)l; switch(cr) { case 1: { pd = (dk3_datum_t const *)r; if(pl->keydata) { if(pd->dt) { if(pl->keysize > pd->sz) { back = 1; } else { if(pl->keysize < pd->sz) { back = -1; } else { back = dk3mem_cmp(pl->keydata, pd->dt, pl->keysize); if(back < -1) back = -1; if(back > 1) back = 1; } } } else { back = 1; } } else { if(pd->dt) { back = -1; } } } break; default: { pr = (dk3_dbi_node_t const *)r; if(pl->keydata) { if(pr->keydata) { if(pl->keysize > pr->keysize) { back = 1; } else { if(pl->keysize < pr->keysize) { back = -1; } else { back = dk3mem_cmp(pl->keydata, pr->keydata, pl->keysize); if(back < -1) back = -1; if(back > 1) back = 1; } } } else { back = 1; } } else { if(pr->keydata) { back = -1; } } } break; } } else { back = 1; } } else { if(r) { back = -1; } } return back; } /** Read in-memory database from file. @param dbp Database to initialize. @return 1 on success (even if there is not db file yet), 0 on error. */ static int dk3dbi_mem_read_file(dk3_dbi_t *dbp) { unsigned char uc[8]; /* Buffer for key and value size. */ FILE *fipo; /* Input file. */ dk3_dbi_node_t *np; /* Node for record just read. */ unsigned char *kd; /* Key data buffer pointer. */ unsigned char *vd; /* Value data buffer pointer. */ dkChar const *oldsourcename; /* Previous source file name. */ unsigned long oldsourceline; /* Previous source file line. */ unsigned long ul1; /* Key size as unsigned long. */ unsigned long ul2; /* Value size as unsigned long. */ size_t nread; /* Number of bytes read from file. */ size_t ks; /* Key size. */ size_t vs; /* Value size. */ int ok = 0; /* Flag: Success. */ int back = 1; int cc = 1; /* Flag: Can continue. */ $? "+ dk3dbi_mem_read_file" oldsourcename = dk3app_get_source_file(dbp->app); oldsourceline = dk3app_get_source_line(dbp->app); dk3app_set_source_file(dbp->app, dbp->fn); dk3app_set_source_line(dbp->app, 0UL); fipo = dk3sf_fopen_app(dbp->fn, dk3dbi_file_open_modes[0], dbp->app); if(fipo) { while((cc) && (back)) { cc = 0; kd = vd = NULL; nread = dk3sf_fread_app(uc, 1, 8, fipo, dbp->app); if(nread > 0) { if(nread == 8) { 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); ks = (size_t)ul1; vs = (size_t)ul2; if(sizeof(size_t) < 4) { if((ul1 > 0x0000FFFFUL) || (ul2 > 0x0000FFFFUL)) { back = 0; /* ERROR: Entry size too large! */ dk3app_log_i1(dbp->app, DK3_LL_ERROR, 245); } } if(back) { kd = dk3_new_app(unsigned char,ks,dbp->app); vd = dk3_new_app(unsigned char,vs,dbp->app); if((kd) && (vd)) { ok = 0; nread = dk3sf_fread_app(kd, 1, ks, fipo, dbp->app); if(nread == ks) { nread = dk3sf_fread_app(vd, 1, vs, fipo, dbp->app); if(nread == vs) { np = dk3_new_app(dk3_dbi_node_t,1,dbp->app); if(np) { np->keydata = kd; np->valdata = vd; np->keysize = ks; np->valsize = vs; if(dk3sto_add((dbp->details).mem.s_mem, np)) { cc = 1; ok = 1; } else { np->keydata = np->valdata = NULL; np->keysize = np->valsize = 0; dk3_delete(np); np = NULL; back = 0; } } } else { back = 0; /* ERROR: File probably damaged! */ dk3app_log_i1(dbp->app, DK3_LL_ERROR, 246); } } else { back = 0; /* ERROR: File probably damaged! */ dk3app_log_i1(dbp->app, DK3_LL_ERROR, 246); } if(!(ok)) { dk3_delete(kd); dk3_delete(vd); kd = vd = NULL; ks = vs = 0; } } else { back = 0; dk3_delete(kd); dk3_delete(vd); } } } else { back = 0; /* ERROR: File probably damaged! */ dk3app_log_i1(dbp->app, DK3_LL_ERROR, 246); } } else { /* End of file reached. */ } } fclose(fipo); } dk3app_set_source_file(dbp->app, oldsourcename); dk3app_set_source_line(dbp->app, oldsourceline); $? "- dk3dbi_mem_read_file %d", back return back; } /** Synchronize memory database to file. @param dbp Database. @return 1 on success, 0 on error. */ static int dk3dbi_mem_sync(dk3_dbi_t *dbp) { unsigned char uc[8]; /* Buffer for key and value size. */ FILE *fipo; /* Output file. */ dk3_dbi_node_t *np; /* Current node to process. */ unsigned long ul1; /* Key size as unsigned long. */ unsigned long ul2; /* Value size as unsigned long. */ size_t nwrite; /* Number of bytes written. */ int back = 0; $? "+ dk3dbi_mem_sync" if(dbp->fn) { $? ". file name ok" fipo = dk3sf_fopen_app(dbp->fn, dk3dbi_file_open_modes[1], dbp->app); if(fipo) { $? ". file opened" back = 1; dk3sto_it_reset((dbp->details).mem.i_mem); do { np = (dk3_dbi_node_t *)dk3sto_it_next((dbp->details).mem.i_mem); if(np) { $? ". yet another entry to write" ul1 = (unsigned long)(np->keysize); ul2 = (unsigned long)(np->valsize); uc[0] = (unsigned char)((ul1 >> 24) & 0x000000FFUL); uc[1] = (unsigned char)((ul1 >> 16) & 0x000000FFUL); uc[2] = (unsigned char)((ul1 >> 8) & 0x000000FFUL); uc[3] = (unsigned char)( ul1 & 0x000000FFUL); uc[4] = (unsigned char)((ul2 >> 24) & 0x000000FFUL); uc[5] = (unsigned char)((ul2 >> 16) & 0x000000FFUL); uc[6] = (unsigned char)((ul2 >> 8) & 0x000000FFUL); uc[7] = (unsigned char)( ul2 & 0x000000FFUL); nwrite = dk3sf_fwrite_app(uc, 1, 8, fipo, dbp->app); if(nwrite) { $? ". size written successfully" nwrite = dk3sf_fwrite_app(np->keydata,1,np->keysize,fipo,dbp->app); if(nwrite) { $? ". key written successfully" nwrite = dk3sf_fwrite_app(np->valdata,1,np->valsize,fipo,dbp->app); if(!(nwrite)) { $? ". value written" back = 0; dbp->ec = DK3_ERROR_DURING_WRITE; } } else { $? "! failed to write key" back = 0; dbp->ec = DK3_ERROR_DURING_WRITE; } } else { $? "! error while writing sizes" back = 0; dbp->ec = DK3_ERROR_DURING_WRITE; } } } while((np) && (back)); if(!dk3sf_fclose_fn_app(fipo, dbp->fn, dbp->app)) { back = 0; } } else { $? "! failed to open file" dbp->ec = DK3_ERROR_NOT_OPENED_FOR_WRITING; } } else { /* BUG: No file name, must not happen! */ } $? "- dk3dbi_mem_sync %d", back return back; } /** Set entry (in-memory database). @param dbp Database. @param key Key data. @param val Value data. @return 1 on success, 0 on error. */ static int dk3dbi_mem_set(dk3_dbi_t *dbp, dk3_datum_t *key, dk3_datum_t *val) { int back = 0; dk3_dbi_node_t *np; /* Current node to process. */ unsigned char *nb; /* New value buffer for node. */ $? "+ dk3dbi_mem_set" np = (dk3_dbi_node_t *)dk3sto_it_find_like( (dbp->details).mem.i_mem, (void *)key, 1 ); if(np) { $? ". modify existing entry" nb = dk3_new_app(unsigned char,val->sz,dbp->app); if(nb) { dk3mem_cpy(nb, val->dt, val->sz); dk3_release(np->valdata); np->valdata = nb; np->valsize = val->sz; back = 1; $? ". success" } else { dbp->ec = DK3_ERROR_MEMORY; } } else { $? ". create new entry" np = dk3dbi_node_new(key->sz, val->sz, dbp->app); if(np) { dk3mem_cpy(np->keydata, key->dt, key->sz); dk3mem_cpy(np->valdata, val->dt, val->sz); np->keysize = key->sz; np->valsize = val->sz; if(dk3sto_add((dbp->details).mem.s_mem, (void *)np)) { back = 1; $? ". success" } else { dk3dbi_node_delete(np); dbp->ec = DK3_ERROR_MEMORY; } } else { dbp->ec = DK3_ERROR_MEMORY; } } $? "- dk3dbi_mem_set %d", back return back; } /** Retrieve entry (in-memory database). @param dbp Database. @param key Key data. @param val Value data (input: result buffer size, output: buffer size used). @return 1 on success, 0 on error. */ static int dk3dbi_mem_get(dk3_dbi_t *dbp, dk3_datum_t *key, dk3_datum_t *val) { int back = 0; dk3_dbi_node_t *np; /* Current node to process. */ $? "+ dk3dbi_mem_get" np = (dk3_dbi_node_t *)dk3sto_it_find_like( (dbp->details).mem.i_mem, (void *)key, 1 ); if(np) { $? ". found" if(val->sz >= np->valsize) { dk3mem_cpy(val->dt, np->valdata, np->valsize); val->sz = np->valsize; back = 1; $? ". success" } else { $? "! result buffer too small" } } else { $? "! not found" if(dbp->rfk) { if(dbp->app) { /* ERROR: Entry not found! */ dk3app_log_i1(dbp->app, DK3_LL_ERROR, 249); } } } $? "- dk3dbi_mem_get %d", back return back; } /** Delete entry (in-memory database). @param dbp Database. @param key Key data. @return 1 on success, 0 on error. */ static int dk3dbi_mem_delete(dk3_dbi_t *dbp, dk3_datum_t *key) { dk3_dbi_node_t *np; /* Current node to process .*/ int back = 1; $? "+ dk3dbi_mem_delete" np = (dk3_dbi_node_t *)dk3sto_it_find_like( (dbp->details).mem.i_mem, (void *)key, 1 ); if(np) { dk3sto_remove((dbp->details).mem.s_mem, (void *)np); dk3dbi_node_delete(np); } $? "- dk3dbi_mem_delete" return back; } /** Traverse database (in-memory database). @param dbp Database. @param obj Object to modify during traversal. @param fct Function to invoke for each key/value pair. @return 1 on success, 0 on error. */ static int dk3dbi_mem_traverse(dk3_dbi_t *dbp, void *obj, dk3_db_traverse_fct_t *fct) { dk3_datum_t key; /* Key data. */ dk3_datum_t val; /* Value data. */ dk3_dbi_node_t *np; /* Current node to process. */ int back = 1; int res = 1; /* Function result for current node. */ $? "+ dk3dbi_mem_traverse" dk3sto_it_reset((dbp->details).mem.i_mem); do { np = (dk3_dbi_node_t *)dk3sto_it_next((dbp->details).mem.i_mem); if(np) { key.sz = np->keysize; key.dt = np->keydata; val.sz = np->valsize; val.dt = np->valdata; res = (*fct)(obj, &key, &val); if(res < 1) { $? "! problem for record" back = 0; } } } while((np) && (res >= 0)); $? "- dk3dbi_mem_traverse %d", back return back; } #if DK3_USE_DB /** Open Berkeley DB file. @param dbp Database. @param trunc Flag: Truncate database. @return 1 on success, 0 on error. */ static int dk3dbi_bdb_open_file(dk3_dbi_t *dbp, int trunc) { char fnb[DK3_MAX_PATH]; /* File name buffer, 8-bit chars. */ DB *db = NULL; /* BDB. */ u_int32_t flags = 0UL; /* Flags to open BDB. */ int back = 0; int ret; /* BDB operation result. */ $? "+ dk3dbi_bdb_open_file" if(dk3sf_filename_to_c8(fnb, sizeof(fnb), dbp->fn, dbp->app)) { ret = db_create(&db, NULL, 0); if(ret == 0) { flags = DB_RDONLY; if(dbp->wr) { flags = DB_CREATE; if(trunc) { flags |= DB_TRUNCATE; } } ret = db->open(db, NULL, fnb, NULL, DB_BTREE, flags, 0600); if(ret == 0) { back = 1; (dbp->details).bdb.dbptr = (void *)db; } else { db->close(db, 0); } } else { /* ERROR: Failed to create database */ dk3app_log_i1(dbp->app, DK3_LL_ERROR, 9); } } else { /* ERROR: File name conversion failed! */ dbp->ec = DK3_ERROR_INVALID_ARGS; } $? "- dk3dbi_bdb_open_file %d", back return back; } /** Set entry (Berkeley DB). @param dbp Database. @param key Key data. @param val Value data. @return 1 on success, 0 on error. */ static int dk3dbi_bdb_set(dk3_dbi_t *dbp, dk3_datum_t *key, dk3_datum_t *val) { DBT k; /* Key datum. */ DBT v; /* Value datum. */ DB *db; /* BDB structure. */ int res; /* BDB operation result. */ int back = 0; $? "+ dk3dbi_bdb_set" db = (DB *)((dbp->details).bdb.dbptr); dk3mem_res((void *)(&k), sizeof(DBT)); dk3mem_res((void *)(&v), sizeof(DBT)); k.data = key->dt; k.size = key->sz; v.data = val->dt; v.size = val->sz; db->del(db, NULL, &k, 0); dk3mem_res((void *)(&k), sizeof(DBT)); k.data = key->dt; k.size = key->sz; res = db->put(db, NULL, &k, &v, 0); if(res == 0) { back = 1; } $? "- dk3dbi_bdb_set %d", back return back; } /** Retrieve entry (Berkeley DB). @param dbp Database. @param key Key data. @param val Value data (input: result buffer size, output: buffer size used). @return 1 on success, 0 on error. */ static int dk3dbi_bdb_get(dk3_dbi_t *dbp, dk3_datum_t *key, dk3_datum_t *val) { DBT k; /* Key datum. */ DBT v; /* Value datum. */ DB *db; /* BDB structure. */ int res; /* BDB operation result. */ int back = 0; $? "+ dk3dbi_bdb_get" db = (DB *)((dbp->details).bdb.dbptr); dk3mem_res((void *)(&k), sizeof(DBT)); dk3mem_res((void *)(&v), sizeof(DBT)); k.data = key->dt; k.size = key->sz; v.data = val->dt; v.ulen = val->sz; v.flags = DB_DBT_USERMEM; res = db->get(db, NULL, &k, &v, 0); if(res == 0) { back = 1; val->sz = v.size; } else { if(dbp->rfk) { if(dbp->app) { /* ERROR: Entry not found! */ dk3app_log_i1(dbp->app, DK3_LL_ERROR, 249); } } } $? "- dk3dbi_bdb_get %d", back return back; } /** Delete entry (Berkeley DB). @param dbp Database. @param key Key data. @return 1 on success, 0 on error. */ static int dk3dbi_bdb_delete(dk3_dbi_t *dbp, dk3_datum_t *key) { DBT k; /* Key datum. */ DB *db; /* BDB structure. */ int res; /* BDB operation result. */ int back = 0; $? "+ dk3dbi_bdb_delete" db = (DB *)((dbp->details).bdb.dbptr); dk3mem_res((void *)(&k), sizeof(DBT)); k.data = key->dt; k.size = key->sz; res = db->del(db, NULL, &k, 0); if(res == 0) { back = 1; } $? "- dk3dbi_bdb_delete %d", back return back; } /** Traverse database (Berkeley DB). @param dbp Database. @param obj Object to modify during traversal. @param fct Function to invoke for each key/value pair. @return 1 on success, 0 on error. */ static int dk3dbi_bdb_traverse(dk3_dbi_t *dbp, void *obj, dk3_db_traverse_fct_t *fct) { DBT k; /* Key datum (BDB). */ DBT v; /* Value datum (BDB). */ dk3_datum_t key; /* Key datum (dk3dbi). */ dk3_datum_t val; /* Vaue datum (dk3dbi). */ DB *db; /* BDB structure. */ DBC *cp = NULL; /* BDB cursor. */ int res; /* BDB operation result. */ int cc; /* Flag: Can continue. */ int isfirst; /* Flag: No record received yet. */ int back = 0; $? "+ dk3dbi_bdb_traverse" db = (DB *)((dbp->details).bdb.dbptr); res = db->cursor(db, NULL, &cp, 0); if(res == 0) { $? ". cursor created" if(cp) { $? ". cursor ok" cc = 1; back = 1; isfirst = 1; dk3mem_res((void *)(&k), sizeof(DBT)); dk3mem_res((void *)(&v), sizeof(DBT)); do { $? ". yet another attempt" cc = 0; #if DK3_HAVE_DB_CURSOR_C_GET /* 2011-10-23: c_get() not in current documentation of BDB. */ res = cp->c_get(cp, &k, &v, ((isfirst) ? (DB_FIRST) : (DB_NEXT))); #else if(isfirst) { $? ". first attempt" res = cp->get(cp, &k, &v, DB_FIRST); } else { $? ". not first" res = cp->get(cp, &k, &v, DB_NEXT); } #endif isfirst = 0; if(res == 0) { $? ". record found" cc = 1; key.dt = k.data; key.sz = k.size; val.dt = v.data; val.sz = v.size; res = (*fct)(obj, &key, &val); switch(res) { case 0: { $? ". processing failed, continue" back = 0; } break; case -1: { $? ". processing failed, abort" back = 0; cc = 0; } break; } } else { $? ". no record" cc = 0; if(res != DB_NOTFOUND) { $? ". error" back = 0; } } } while(cc); } else { $? "! failed to create cursor" } } else { $? "! failed to create cursor" } if(cp) { cp->c_close(cp); } $? "- dk3dbi_bdb_traverse %d", back return back; } #endif #if DK3_USE_NDBM /** Open NDBM database file. @param dbp Database. @param trunc Flag: Truncate database. @return 1 on success, 0 on error. */ static int dk3dbi_ndbm_open_file(dk3_dbi_t *dbp, int trunc) { DBM *dbm = NULL; /* NDBM structure. */ int back = 0; int omode = 0; /* Output file mode. */ $? "+ dk3dbi_ndbm_open_file trunc=%d", trunc omode = O_CREAT; if(dbp->wr) { $? ". want to write" omode |= O_RDWR; if(trunc) { $? ". allow truncate" omode |= O_TRUNC; } } else { $? ". read only" omode |= O_RDONLY; } $? ". fn = \"%s\"", TR_STR(dbp->fn) dbm = dbm_open(dbp->fn, omode, 0600); if(dbm) { $? ". dbm_open" (dbp->details).ndbm.dbptr = (void *)dbm; back = 1; } else { $? "! dbm_open" } $? "- dk3dbi_ndbm_open_file %d", back return back; } /** Set entry (NDBM). @param dbp Database. @param key Key data. @param val Value data. @return 1 on success, 0 on error. */ static int dk3dbi_ndbm_set(dk3_dbi_t *dbp, dk3_datum_t *key, dk3_datum_t *val) { datum k; /* Key datum. */ datum v; /* Value datum. */ DBM *dbm; /* NDBM structure. */ int res; /* Operation result. */ int back = 0; $? "+ dk3dbi_ndbm_set" dbm = (DBM *)((dbp->details).ndbm.dbptr); dk3mem_res((void *)(&k),sizeof(datum)); dk3mem_res((void *)(&v),sizeof(datum)); k.dptr = key->dt; k.dsize = key->sz; v.dptr = val->dt; v.dsize = val->sz; res = dbm_store(dbm, k, v, DBM_INSERT); if(res == 0) { back = 1; } else { dk3mem_res((void *)(&k),sizeof(datum)); dk3mem_res((void *)(&v),sizeof(datum)); k.dptr = key->dt; k.dsize = key->sz; v.dptr = val->dt; v.dsize = val->sz; res = dbm_store(dbm, k, v, DBM_REPLACE); if(res == 0) { back = 1; } } $? "- dk3dbi_ndbm_set %d", back return back; } /** Retrieve entry (NDBM). @param dbp Database. @param key Key data. @param val Value data (input: result buffer size, output: buffer size used). @return 1 on success, 0 on error. */ static int dk3dbi_ndbm_get(dk3_dbi_t *dbp, dk3_datum_t *key, dk3_datum_t *val) { datum k; /* Key datum. */ datum v; /* Value datum. */ DBM *dbm; /* NDBM structure. */ int back = 0; $? "+ dk3dbi_ndbm_get" dbm = (DBM *)((dbp->details).ndbm.dbptr); dk3mem_res((void *)(&k), sizeof(datum)); k.dptr = key->dt; k.dsize = key->sz; v = dbm_fetch(dbm, k); if((v.dptr) && (v.dsize)) { if(v.dsize <= val->sz) { dk3mem_cpy(val->dt, v.dptr, v.dsize); val->sz = v.dsize; back = 1; } } else { if(dbp->rfk) { if(dbp->app) { /* ERROR: Entry not found! */ dk3app_log_i1(dbp->app, DK3_LL_ERROR, 249); } } } $? "- dk3dbi_ndbm_get %d", back return back; } /** Delete entry (NDBM). @param dbp Database. @param key Key data. @return 1 on success, 0 on error. */ static int dk3dbi_ndbm_delete(dk3_dbi_t *dbp, dk3_datum_t *key) { datum k; /* Key datum. */ DBM *dbm; /* NDBM structure. */ int back = 0; $? "+ dk3dbi_ndbm_delete" dbm = (DBM *)((dbp->details).ndbm.dbptr); dk3mem_res((void *)(&k),sizeof(datum)); k.dptr = key->dt; k.dsize = key->sz; if(dbm_delete(dbm, k) == 0) { back = 1; } $? "- dk3dbi_ndbm_delete %d", back return back; } /** Traverse database (NDBM). @param dbp Database. @param obj Object to modify during traversal. @param fct Function to invoke for each key/value pair. @return 1 on success, 0 on error. */ static int dk3dbi_ndbm_traverse(dk3_dbi_t *dbp, void *obj, dk3_db_traverse_fct_t *fct) { datum k; /* Key datum (NDBM). */ datum v; /* Value datum (NDBM). */ dk3_datum_t key; /* Key datum (dk3dbi). */ dk3_datum_t val; /* Value datum (dk3dbi). */ DBM *dbm; /* NDBM structure. */ int isfirst = 1; /* Flag: No record received yet. */ int cc; /* Flag: Can continue. */ int res; /* Operation result. */ int back = 1; $? "+ dk3dbi_ndbm_traverse" dbm = (DBM *)((dbp->details).ndbm.dbptr); cc = 1; do { cc = 0; if(isfirst) { k = dbm_firstkey(dbm); } else { k = dbm_nextkey(dbm); } isfirst = 0; if((k.dptr) && (k.dsize)) { v = dbm_fetch(dbm, k); if((v.dptr) && (v.dsize)) { cc = 1; key.dt = k.dptr; key.sz = k.dsize; val.dt = v.dptr; val.sz = v.dsize; res = (*fct)(obj, &key, &val); switch(res) { case 0: { back = 0; $? ". res = 0" } break; case -1: { back = 0; cc = 0; $? ". res = -1" } break; } } } } while(cc); $? "- dk3dbi_ndbm_traverse %d", back return back; } #endif int dk3dbi_type_supported(int tp) { int back = 0; $? "+ dk3dbi_type_supported %d", tp switch(tp) { case DK3_DB_TYPE_MEMORY: { back = 1; } break; case DK3_DB_TYPE_BDB: { #if DK3_USE_DB back = 1; #endif } break; case DK3_DB_TYPE_NDBM: { #if DK3_USE_NDBM back = 1; #endif } break; } $? "- dk3dbi_type_supported %d", back return back; } /** If no type is specified use best available database type. @return Best available database type. */ static int dk3dbi_choose_best_available_type(void) { int back = DK3_DB_TYPE_MEMORY; $? "+ dk3dbi_choose_best_available_type" #if DK3_USE_DB back = DK3_DB_TYPE_BDB; #else #if DK3_USE_NDBM back = DK3_DB_TYPE_NDBM; #endif #endif $? "- dk3dbi_choose_best_available_type %d", back return back; } /** Internal function to close the database. @param dbp Database to close. */ static void dk3dbi_close_internal(dk3_dbi_t *dbp) { dk3_dbi_node_t *np; /* Current node to process. */ $? "+ dk3dbi_close_internal" if(dbp) { switch(dbp->tp) { case DK3_DB_TYPE_MEMORY: { if((dbp->details).mem.s_mem) { if((dbp->details).mem.i_mem) { dk3sto_it_reset((dbp->details).mem.i_mem); do { np = (dk3_dbi_node_t *)dk3sto_it_next((dbp->details).mem.i_mem); if(np) { dk3dbi_node_delete(np); } } while(np); dk3sto_it_close((dbp->details).mem.i_mem); } dk3sto_close((dbp->details).mem.s_mem); } (dbp->details).mem.s_mem = NULL; } break; case DK3_DB_TYPE_BDB: { #if DK3_USE_DB DB *db; if((dbp->details).bdb.dbptr) { db = (DB *)((dbp->details).bdb.dbptr); db->close(db, 0); (dbp->details).bdb.dbptr = NULL; } #endif } break; case DK3_DB_TYPE_NDBM: { #if DK3_USE_NDBM DBM *dbm; if((dbp->details).ndbm.dbptr) { dbm = (DBM *)((dbp->details).ndbm.dbptr); dbm_close(dbm); (dbp->details).ndbm.dbptr = NULL; } #endif } break; } dbp->app = NULL; if(dbp->fn) { dk3_delete(dbp->fn); } dbp->fn = NULL; dk3_delete(dbp); } $? "- dk3dbi_close_internal" } /** Synchronize database (write all modifications to file). @param dbp Database. @return 1 on success, 0 on error. */ static int dk3dbi_sync_internal(dk3_dbi_t *dbp) { int back = 0; $? "+ dk3dbi_sync_internal" switch(dbp->tp) { case DK3_DB_TYPE_MEMORY: { back = dk3dbi_mem_sync(dbp); } break; case DK3_DB_TYPE_BDB: { #if DK3_USE_DB DB *db; db = (DB *)((dbp->details).bdb.dbptr); if(db->sync(db, 0) == 0) { back = 1; } #endif } break; case DK3_DB_TYPE_NDBM: { #if DK3_USE_NDBM /* There is no sync function! */ dbp->ec = DK3_ERROR_NOT_SUPPORTED; #endif } break; } $? "- dk3dbi_sync_internal %d", back return back; } /** Open a database if the type is known. This function is invoked with the real file name and a known database type. @param fn File name. @param tp Database type. @param acc Access type DK3_DB_ACCESS_xxx, see @ref databaseaccess. @param pec Pointer to variable for error codes, may be NULL. @param app Application structure for diagnostics, may be NULL. @param trunc Flag: Truncate database. @return Pointer to database structure on success, NULL on error. */ static dk3_dbi_t * dk3dbi_open_app_with_tp( dkChar const *fn, int tp, int acc, int *pec, dk3_app_t *app, int trunc ) { dk3_dbi_t *back = NULL; int ok = 0; /* Flag: Success. */ $? "+ dk3dbi_open_app_with_tp trunc=%d", trunc back = dk3_new_app(dk3_dbi_t,1,app); if(back) { back->ec = 0; back->rfk = 0; back->wr = ((acc != DK3_DB_ACCESS_READ) ? 1 : 0); back->app = app; back->fn = NULL; back->tp = tp; back->mod = 0; back->fn = dk3str_dup_app(fn, app); if(back->fn) { switch(tp) { case DK3_DB_TYPE_MEMORY: { (back->details).mem.s_mem = NULL; (back->details).mem.i_mem = NULL; (back->details).mem.s_mem = dk3sto_open_app(app); if((back->details).mem.s_mem) { dk3sto_set_comp( (back->details).mem.s_mem, dk3dbi_node_compare, 0 ); (back->details).mem.i_mem = dk3sto_it_open( (back->details).mem.s_mem ); if((back->details).mem.i_mem) { if(!(trunc)) { ok = dk3dbi_mem_read_file(back); } else { ok = 1; } } } } break; case DK3_DB_TYPE_BDB: { #if DK3_USE_DB (back->details).bdb.dbptr = NULL; $? ". use bdb" ok = dk3dbi_bdb_open_file(back, trunc); #else if(pec) { *pec = DK3_ERROR_NOT_SUPPORTED; } if(app) { /* ERROR: Database type not supported! */ dk3app_log_i3( app, DK3_LL_ERROR, 247, 248, dk3dbi_type_names[1] ); } #endif } break; case DK3_DB_TYPE_NDBM: { #if DK3_USE_NDBM (back->details).ndbm.dbptr = NULL; $? ". use ndbm" ok = dk3dbi_ndbm_open_file(back, trunc); #else if(pec) { *pec = DK3_ERROR_NOT_SUPPORTED; } $? "! no ndbm support" if(app) { /* ERROR: Database type not supported! */ dk3app_log_i3( app, DK3_LL_ERROR, 247, 248, dk3dbi_type_names[2] ); } #endif } break; } } else { if(pec) { *pec = DK3_ERROR_MEMORY; } } if(!(ok)) { dk3dbi_close_internal(back); back = NULL; } } $? "- dk3dbi_open_app_with_tp %s", TR_PTR(back) return back; } int dk3dbi_find_type_and_name( dkChar const **myfn, int *mytp, dkChar const *fn, int tp, int *pec, dk3_app_t *app ) { dkChar *p1; /* Start of real file name. */ int back = 0; $? "+ dk3dbi_find_type_and_name \"%s\"", TR_STR(fn) *mytp = tp; p1 = dk3str_chr(fn, dkT(':')); if(p1) { $? ". one colon in name" if(p1[1] == dkT(':')) { $? ". two colons in name" *(p1++) = dkT('\0'); p1++; *myfn = p1; *mytp = dk3str_array_index(dk3dbi_type_names, fn, 0); if(*mytp >= 0) { $? ". database type found" if(dk3dbi_type_supported(*mytp)) { back = 1; } else { $? "! database type not supported" if(app) { /* ERROR: Database type not supported! */ dk3app_log_i3(app, DK3_LL_ERROR, 247, 248, fn); } if(pec) { *pec = DK3_ERROR_NOT_SUPPORTED; } } } else { $? "! database type not found" if(app) { /* ERROR: Database type not found! */ dk3app_log_i3(app, DK3_LL_ERROR, 247, 248, fn); } if(pec) { *pec = DK3_ERROR_INVALID_ARGS; } } } else { $? ". one colon in name" /* Single colon */ *myfn = fn; if(tp < 0) { *mytp = dk3dbi_choose_best_available_type(); } back = 1; } } else { $? ". no colon in name" /* No colon found */ *myfn = fn; if(tp < 0) { *mytp = dk3dbi_choose_best_available_type(); } back = 1; } if(back) { if(*mytp >= 0) { if(!dk3dbi_type_supported(*mytp)) { back = 0; if(app) { /* ERROR: Database type not supported! */ } if(pec) { *pec = DK3_ERROR_NOT_SUPPORTED; } } } else { back = 0; } } $? "- dk3dbi_find_type_and_name %d", back return back; } /** Open a database. @param fn File name. @param tp Database type. @param acc Access type. @param pec Pointer to error code variable. @param app Application structure for diagnostics, may be NULL. @param trunc Flag: Truncate database. @return Pointer to new database on success, NULL on error. */ static dk3_dbi_t * dk3dbi_open_internal( dkChar const *fn, int tp, int acc, int *pec, dk3_app_t *app, int trunc ) { dkChar fnb[DK3_MAX_PATH + 16]; /* File name buffer. */ dk3_dbi_t *back = NULL; dkChar const *myfn = NULL; /* Real file name. */ int mytp = DK3_DB_TYPE_UNKNOWN; /* Database type. */ $? "+ dk3dbi_open_internal \"%s\"", TR_STR(fn) if(fn) { $? ". fn found" if(dk3str_len(fn) < DK3_SIZEOF(fnb,dkChar)) { $? ". length ok" dk3str_cpy_not_overlapped(fnb, fn); if(dk3dbi_find_type_and_name(&myfn ,&mytp, fnb, tp, pec, app)) { $? ". type found" if((myfn) && (mytp >= 0)) { $? ". results ok" back = dk3dbi_open_app_with_tp(myfn, mytp, acc, pec, app, trunc); } else { $? "! results" /* BUG: Must not happen! */ } } } else { $? "! fn too long" if(app) { /* ERROR: File name too long! */ dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn); } if(pec) { *pec = DK3_ERROR_INVALID_ARGS; } } } else { $? "! fn" } $? "- dk3dbi_open_internal %s", TR_PTR(back) return back; } dk3_dbi_t * dk3dbi_open_app( dkChar const *fn, int tp, int acc, int *pec, dk3_app_t *app ) { dk3_dbi_t *back; back = dk3dbi_open_internal(fn, tp, acc, pec, app, 0); return back; } dk3_dbi_t * dk3dbi_open_truncate_app( dkChar const *fn, int tp, int acc, int *pec, dk3_app_t *app ) { dk3_dbi_t *back; $? "+ dk3dbi_open_truncate_app \"%s\"", TR_STR(fn) back = dk3dbi_open_internal(fn, tp, acc, pec, app, 1); $? "- dk3dbi_open_truncate_app %s", TR_PTR(back) return back; } dk3_dbi_t * dk3dbi_open(dkChar const *fn, int tp, int acc, int *pec) { dk3_dbi_t *back = NULL; $? "+ dk3dbi_open" back = dk3dbi_open_app(fn, tp, acc, pec, NULL); $? "- dk3dbi_open" return back; } dk3_dbi_t * dk3dbi_open_truncate(dkChar const *fn, int tp, int acc, int *pec) { dk3_dbi_t *back; back = dk3dbi_open_truncate_app(fn, tp, acc, pec, NULL); return back; } int dk3dbi_close(dk3_dbi_t *dbp) { int back = 0; $? "+ dk3dbi_close" if(dbp) { back = 1; if(dbp->mod) { $? ". must sync" back = dk3dbi_sync_internal(dbp); } dk3dbi_close_internal(dbp); } $? "- dk3dbi_close %d", back return back; } int dk3dbi_set(dk3_dbi_t *dbp, dk3_datum_t *key, dk3_datum_t *val) { int back = 0; $? "+ dk3dbi_set" if((dbp) && (key) && (val)) { if((key->dt) && (key->sz) && (val->dt) && (val->sz)) { if(dbp->wr) { switch(dbp->tp) { case DK3_DB_TYPE_MEMORY: { back = dk3dbi_mem_set(dbp, key, val); dbp->mod = 1; } break; case DK3_DB_TYPE_BDB: { #if DK3_USE_DB back = dk3dbi_bdb_set(dbp, key, val); dbp->mod = 1; #endif } break; case DK3_DB_TYPE_NDBM: { #if DK3_USE_NDBM back = dk3dbi_ndbm_set(dbp, key, val); dbp->mod = 1; #endif } break; } } else { /* ERROR: Write operations denied! */ dbp->ec = DK3_ERROR_NOT_OPENED_FOR_WRITING; if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 251); } } } else { dbp->ec = DK3_ERROR_INVALID_ARGS; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_set %d", back return back; } int dk3dbi_get(dk3_dbi_t *dbp, dk3_datum_t *key, dk3_datum_t *val) { int back = 0; $? "+ dk3dbi_get" if((dbp) && (key) && (val)) { if((key->dt) && (key->sz) && (val->dt) && (val->sz)) { switch(dbp->tp) { case DK3_DB_TYPE_MEMORY: { back = dk3dbi_mem_get(dbp, key, val); } break; case DK3_DB_TYPE_BDB: { #if DK3_USE_DB back = dk3dbi_bdb_get(dbp, key, val); #endif } break; case DK3_DB_TYPE_NDBM: { #if DK3_USE_NDBM back = dk3dbi_ndbm_get(dbp, key, val); #endif } break; } } else { dbp->ec = DK3_ERROR_INVALID_ARGS; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_get %d", back return back; } int dk3dbi_delete(dk3_dbi_t *dbp, dk3_datum_t *key) { int back = 0; $? "+ dk3dbi_delete" if((dbp) && (key)) { if((key->dt) && (key->sz)) { switch(dbp->tp) { case DK3_DB_TYPE_MEMORY: { back = dk3dbi_mem_delete(dbp, key); dbp->mod = 1; } break; case DK3_DB_TYPE_BDB: { #if DK3_USE_DB back = dk3dbi_bdb_delete(dbp, key); dbp->mod = 1; #endif } break; case DK3_DB_TYPE_NDBM: { #if DK3_USE_NDBM back = dk3dbi_ndbm_delete(dbp, key); dbp->mod = 1; #endif } break; } } else { dbp->ec = DK3_ERROR_INVALID_ARGS; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_delete %d", back return back; } int dk3dbi_traverse(dk3_dbi_t *dbp, void *obj, dk3_db_traverse_fct_t *fct) { int back = 0; $? "+ dk3dbi_traverse" if((dbp) && (fct)) { switch(dbp->tp) { case DK3_DB_TYPE_MEMORY: { back = dk3dbi_mem_traverse(dbp, obj, fct); } break; case DK3_DB_TYPE_BDB: { #if DK3_USE_DB back = dk3dbi_bdb_traverse(dbp, obj, fct); #endif } break; case DK3_DB_TYPE_NDBM: { #if DK3_USE_NDBM back = dk3dbi_ndbm_traverse(dbp, obj, fct); #endif } break; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_traverse %d", back return back; } int dk3dbi_sync(dk3_dbi_t *dbp) { int back = 0; $? "+ dk3dbi_sync" if(dbp) { back = 1; if(dbp->mod) { back = dk3dbi_sync_internal(dbp); if(back) { dbp->mod = 0; } } } $? "- dk3dbi_sync %d", back return back; } int dk3dbi_get_error(dk3_dbi_t *dbp, int res) { int back = 0; $? "+ dk3dbi_get_error" if(dbp) { back = dbp->ec; if(res) { dbp->ec = 0; } } $? "- dk3dbi_get_error %d", back return back; } int dk3dbi_set_c8_string( dk3_dbi_t *dbp, char const *key, char const *val ) { dk3_datum_t k; /* Key datum. */ dk3_datum_t v; /* Value datum. */ size_t ks; /* Key size (number of bytes). */ size_t vs; /* Value size (number of bytes). */ int back = 0; $? "+ dk3dbi_set_c8_string \"%s\"=\"%s\"", TR_STR(key), TR_STR(val) if((dbp) && (key) && (val)) { ks = dk3str_c8_len(key); vs = dk3str_c8_len(val); ks++; vs++; if((ks) && (vs)) { k.dt = (void *)key; k.sz = ks; v.dt = (void *)val; v.sz = vs; back = dk3dbi_set(dbp, &k, &v); } else { /* ERROR: At least one numeric overflow. */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 15); } dbp->ec = DK3_ERROR_MATH_OVERFLOW; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_set_c8_string %d", back return back; } int dk3dbi_get_c8_string( dk3_dbi_t *dbp, char const *key, char *vb, size_t vbsz ) { dk3_datum_t k; /* Key datum. */ dk3_datum_t v; /* Value datum. */ size_t ks; /* Key size (number of bytes). */ int back = 0; int orfk; $? "+ dk3dbi_get_c8_string \"%s\"", TR_STR(key) if((dbp) && (key) && (vb) && (vbsz)) { ks = dk3str_c8_len(key); ks++; if(ks) { k.dt = (void *)key; k.sz = ks; v.dt = vb; v.sz = vbsz; orfk = dbp->rfk; dbp->rfk = 0; if(dk3dbi_get(dbp, &k, &v)) { back = 1; if(v.sz > 0) { if(v.sz < vbsz) { vb[v.sz] = '\0'; } else { vb[vbsz - 1] = '\0'; } } else { vb[0] = '\0'; } } dbp->rfk = orfk; } else { /* ERROR: Numeric overflow! */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 15); } dbp->ec = DK3_ERROR_MATH_OVERFLOW; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_get_c8_string %d", back return back; } int dk3dbi_delete_c8_string( dk3_dbi_t *dbp, char const *key ) { dk3_datum_t k; /* Key datum. */ size_t ks; /* Key size in bytes. */ int back = 0; $? "+ dk3dbi_delete_c8_string \"%s\"", TR_STR(key) if((dbp) && (key)) { ks = dk3str_c8_len(key); ks++; if(ks) { k.dt = (void *)key; k.sz = ks; back = dk3dbi_delete(dbp, &k); } else { /* ERROR: Numeric overflow! */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 15); } dbp->ec = DK3_ERROR_MATH_OVERFLOW; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_delete_c8_string %d", back return back; } int dk3dbi_set_c16_string( dk3_dbi_t *dbp, dk3_c16_t const *key, dk3_c16_t const *val ) { dk3_datum_t k; /* Key datum. */ dk3_datum_t v; /* Value datum. */ size_t ks; /* Key size (number of bytes). */ size_t vs; /* Value size (number of bytes). */ int back = 0; $? "+ dk3dbi_set_c16_string" if((dbp) && (key) && (val)) { ks = dk3str_c16_len(key); vs = dk3str_c16_len(val); ks = dk3mem_mul_size_t(dk3mem_add_size_t(ks, 1, NULL), 2, NULL); vs = dk3mem_mul_size_t(dk3mem_add_size_t(vs, 1, NULL), 2, NULL); if((ks) && (vs)) { k.dt = (void *)key; k.sz = ks; v.dt = (void *)val; v.sz = vs; back = dk3dbi_set(dbp, &k, &v); } else { /* ERROR: Size overflow! */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 15); } dbp->ec = DK3_ERROR_MATH_OVERFLOW; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_set_c16_string %d", back return back; } int dk3dbi_get_c16_string( dk3_dbi_t *dbp, dk3_c16_t const *key, dk3_c16_t *vb, size_t vbsz ) { dk3_datum_t k; /* Key datum. */ dk3_datum_t v; /* Value datum. */ size_t ks; /* Key size (number of bytes). */ size_t vs; /* Value size (number of bytes). */ int back = 0; int orfk; $? "+ dk3dbi_get_c16_string" if((dbp) && (key) && (vb) && (vbsz)) { ks = dk3str_c16_len(key); ks = dk3mem_mul_size_t(dk3mem_add_size_t(ks, 1, NULL), 2, NULL); vs = dk3mem_mul_size_t(vbsz, 2, NULL); if((ks) && (vs)) { k.dt = (void *)key; k.sz = ks; v.dt = vb, v.sz = vs; orfk = dbp->rfk; dbp->rfk = 0; if(dk3dbi_get(dbp, &k, &v)) { if(!((v.sz) % 2)) { back = 1; v.sz = v.sz / 2; if(v.sz > 0) { if(v.sz < vbsz) { vb[v.sz] = 0U; } else { vb[vbsz - 1] = 0U; } } else { vb[0] = 0U; } } else { /* ERROR: Damaged entry! */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 250); } dbp->ec = DK3_ERROR_DATA_DAMAGED; } } dbp->rfk = orfk; } else { /* ERROR: Numeric overflow! */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 15); } dbp->ec = DK3_ERROR_MATH_OVERFLOW; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_get_c16_string %d", back return back; } int dk3dbi_delete_c16_string( dk3_dbi_t *dbp, dk3_c16_t const *key ) { dk3_datum_t k; /* Key datum. */ size_t ks; /* Key size (number of bytes). */ int back = 0; $? "+ dk3dbi_delete_c16_string" if((dbp) && (key)) { ks = dk3str_c16_len(key); ks = dk3mem_mul_size_t(dk3mem_add_size_t(ks, 1, NULL), 2, NULL); if(ks) { k.dt = (void *)key; k.sz = ks; back = dk3dbi_delete(dbp, &k); } else { /* ERROR: Numeric overflow! */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 15); } dbp->ec = DK3_ERROR_MATH_OVERFLOW; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_delete_c16_string %d", back return back; } int dk3dbi_set_c32_string( dk3_dbi_t *dbp, dk3_c32_t const *key, dk3_c32_t const *val ) { dk3_datum_t k; /* Key datum. */ dk3_datum_t v; /* Value datum. */ size_t ks; /* Key size (number of bytes). */ size_t vs; /* Value size (number of bytes). */ int back = 0; $? "+ dk3dbi_set_c32_string" if((dbp) && (key) && (val)) { ks = dk3str_c32_len(key); ks = dk3mem_mul_size_t(dk3mem_add_size_t(ks, 1, NULL), 4, NULL); vs = dk3str_c32_len(val); vs = dk3mem_mul_size_t(dk3mem_add_size_t(vs, 1, NULL), 4, NULL); if((ks) && (vs)) { k.dt = (void *)key; k.sz = ks; v.dt = (void *)val; v.sz = vs; back = dk3dbi_set(dbp, &k, &v); } else { /* ERROR: Numeric overflow! */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 15); } dbp->ec = DK3_ERROR_MATH_OVERFLOW; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_set_c32_string %d", back return back; } int dk3dbi_get_c32_string( dk3_dbi_t *dbp, dk3_c32_t const *key, dk3_c32_t *vb, size_t vbsz ) { dk3_datum_t k; /* Key datum. */ dk3_datum_t v; /* Value datum. */ size_t ks; /* Key size (number of bytes). */ size_t vs; /* Value size (number of bytes). */ int back = 0; int orfk; $? "+ dk3dbi_get_c32_string" if((dbp) && (key) && (vb) && (vbsz)) { ks = dk3str_c32_len(key); ks = dk3mem_mul_size_t(dk3mem_add_size_t(ks, 1, NULL), 4, NULL); vs = dk3mem_mul_size_t(vbsz, 4, NULL); if((ks) && (vs)) { k.dt = (void *)key; k.sz = ks; v.dt = vb; v.sz = vs; orfk = dbp->rfk; dbp->rfk = 0; if(dk3dbi_get(dbp, &k, &v)) { if(!((v.sz) % 4)) { back = 1; v.sz = v.sz / 4; if(v.sz > 0) { if(v.sz < vbsz) { vb[v.sz] = 0UL; } else { vb[vbsz - 1] = 0UL; } } else { vb[0] = 0UL; } } else { /* ERROR: Damaged entry! */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 250); } dbp->ec = DK3_ERROR_DATA_DAMAGED; } } dbp->rfk = orfk; } else { /* ERROR: Numeric overflow! */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 15); } dbp->ec = DK3_ERROR_MATH_OVERFLOW; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_get_c32_string %d", back return back; } int dk3dbi_delete_c32_string( dk3_dbi_t *dbp, dk3_c32_t const *key ) { dk3_datum_t k; /* Key datum. */ size_t ks; /* Key size (number of bytes). */ int back = 0; $? "+ dk3dbi_delete_c32_string" if((dbp) && (key)) { ks = dk3str_c32_len(key); ks = dk3mem_mul_size_t(dk3mem_add_size_t(ks, 1, NULL), 4, NULL); if(ks) { k.dt = (void *)key; k.sz = ks; back = dk3dbi_delete(dbp, &k); } else { /* ERROR: Numeric overflow! */ if(dbp->app) { dk3app_log_i1(dbp->app, DK3_LL_ERROR, 15); } dbp->ec = DK3_ERROR_MATH_OVERFLOW; } } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_delete_c32_string %d", back return back; } int dk3dbi_set_string( dk3_dbi_t *dbp, dkChar const *key, dkChar const *val ) { int back = 0; $? "+ dk3dbi_set_string" if((dbp) && (key) && (val)) { #if DK3_CHAR_SIZE > 1 #if DK3_CHAR_SIZE > 2 back = dk3dbi_set_c32_string(dbp, key, val); #else back = dk3dbi_set_c16_string(dbp, key, val); #endif #else back = dk3dbi_set_c8_string(dbp, key, val); #endif } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_set_string %d", back return back; } int dk3dbi_get_string( dk3_dbi_t *dbp, dkChar const *key, dkChar *vb, size_t vbsz ) { int back = 0; $? "+ dk3dbi_get_string" if((dbp) && (key) && (vb) && (vbsz)) { #if DK3_CHAR_SIZE > 1 #if DK3_CHAR_SIZE > 2 back = dk3dbi_get_c32_string(dbp, key, vb, vbsz); #else back = dk3dbi_get_c16_string(dbp, key, vb, vbsz); #endif #else back = dk3dbi_get_c8_string(dbp, key, vb, vbsz); #endif } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_get_string %d", back return back; } int dk3dbi_delete_string( dk3_dbi_t *dbp, dkChar const *key ) { int back = 0; $? "+ dk3dbi_delete_string" if((dbp) && (key)) { #if DK3_CHAR_SIZE > 1 #if DK3_CHAR_SIZE > 2 back = dk3dbi_delete_c32_string(dbp, key); #else back = dk3dbi_delete_c16_string(dbp, key); #endif #else back = dk3dbi_delete_c8_string(dbp, key); #endif } else { if(dbp) { dbp->ec = DK3_ERROR_INVALID_ARGS; } } $? "- dk3dbi_delete_string %d", back return back; } /** Delete or truncate a database file. @param fn File name. @param tp Database type. @param del Flag: Delete (non-zero) or truncate (0). @param app Application structure for diagnostics, may be NULL. @return 1 on success, 0 on error. */ static int dk3dbi_dbfile_del_or_trunc(dkChar const *fn, int tp, int del, dk3_app_t *app) { dkChar fnb[DK3_MAX_PATH + 16]; /* Copy of file name. */ char c8fn[DK3_MAX_PATH]; /* File name as 8-bit. */ FILE *fipo; /* File to truncate. */ dkChar const *myfn; /* File name. */ int ec = 0; /* Error code. */ int mytp; /* DB type. */ int back = 0; $? "+ dk3dbi_dbfile_del_or_trunc" mytp = tp; if(fn) { if(dk3str_len(fn) < DK3_SIZEOF(fnb,dkChar)) { dk3str_cpy_not_overlapped(fnb, fn); if(dk3dbi_find_type_and_name(&myfn, &mytp, fnb, tp, &ec, app)) { if((myfn) && (mytp >= 0)) { switch(mytp) { case DK3_DB_TYPE_MEMORY: { $? ". mem" if(del) { dk3sf_remove_file_app(myfn, app); back = 1; } else { fipo = dk3sf_fopen_app(myfn, dk3dbi_file_open_modes[1], app); if(fipo) { $? ". file opened ok" if(dk3sf_fclose_fn_app(fipo, myfn, app)) { back = 1; } } else { $? "! failed to open file" } } } break; case DK3_DB_TYPE_BDB: { $? ". bdb" if(dk3sf_filename_to_c8(c8fn, sizeof(c8fn), myfn, app)) { if(del) { dk3sf_remove_file_app(myfn, app); back = 1; } else { #if DK3_USE_DB DB *db = NULL; int ret; u_int32_t flags; ret = db_create(&db, NULL, 0); if(ret == 0) { $? ". db created" flags = DB_CREATE | DB_TRUNCATE; ret = db->open(db, NULL, c8fn, NULL, DB_BTREE, flags, 0600); if(ret == 0) { $? ". db opend ok" back = 1; } else { $? "! failed to open db" } if(db) { db->close(db, 0); } } else { $? "! failed to create db" } #endif } } else { $? "! failed to convert file name to 8 bit" } } break; case DK3_DB_TYPE_NDBM: { $? ". ndbm" if(dk3sf_filename_to_c8(c8fn, sizeof(c8fn), myfn, app)) { if(del) { if((dk3str_c8_len(c8fn) + dk3str_c8_len(dk3dbi_ndbm_suffixes[0]) ) < sizeof(c8fn) ) { dk3str_c8_cat(c8fn, dk3dbi_ndbm_suffixes[0]); dk3sf_c8_remove_file_app(c8fn, app); dk3sf_filename_to_c8(c8fn, sizeof(c8fn), myfn, app); if((dk3str_c8_len(c8fn) + dk3str_c8_len(dk3dbi_ndbm_suffixes[1]) ) < sizeof(c8fn) ) { dk3str_c8_cat(c8fn, dk3dbi_ndbm_suffixes[1]); dk3sf_c8_remove_file_app(c8fn, app); back = 1; } } } else { #if DK3_USE_NDBM DBM *dbm = NULL; dbm = dbm_open(c8fn, (O_RDWR | O_CREAT | O_TRUNC), 0600); if(dbm) { dbm_close(dbm); } #endif } } else { $? "! failed to convert file name to 8 bit" } } break; default: { /* BUG: Illegal database type, must not happen! */ } break; } } else { $? "! name or type not found" /* BUG: Must not happen! */ } } else { $? "! name or type not found" /* Error: Name or type not found, already reported! */ } } else { $? "! file name too long" /* ERROR: File name too long! */ dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn); } } else { $? "! illegal file name" } $? "- dk3dbi_dbfile_del_or_trunc %d", back return back; } int dk3dbi_dbfile_delete_app(dkChar const *fn, int tp, dk3_app_t *app) { int back; $? "+ dk3dbi_dbfile_delete_app \"%s\"", TR_STR(fn) back = dk3dbi_dbfile_del_or_trunc(fn, tp, 1, app); $? "- dk3dbi_dbfile_delete_app %d", back return back; } int dk3dbi_dbfile_truncate_app(dkChar const *fn, int tp, dk3_app_t *app) { int back; $? "+ dk3dbi_dbfile_truncate_app \"%s\"", TR_STR(fn) back = dk3dbi_dbfile_del_or_trunc(fn, tp, 1, app); $? "- dk3dbi_dbfile_truncate_app %d", back return back; } int dk3dbi_dbfile_delete(dkChar const *fn, int tp) { int back; $? "+ dk3dbi_dbfile_delete \"%s\"", TR_STR(fn) back = dk3dbi_dbfile_del_or_trunc(fn, tp, 1, NULL); $? "- dk3dbi_dbfile_delete %d", back return back; } int dk3dbi_dbfile_truncate(dkChar const *fn, int tp) { int back; $? "+ dk3dbi_dbfile_truncate \"%s\"", TR_STR(fn) back = dk3dbi_dbfile_del_or_trunc(fn, tp, 1, NULL); $? "- dk3dbi_dbfile_truncate %d", back return back; } #if !DK3_ON_WINDOWS #if DK3_CHAR_SIZE == 1 #if DK3_HAVE_CHOWN && DK3_HAVE_CHMOD static int dk3dbi_change_file_user_and_permissions( dk3_dbi_t *db, char const *fn, uid_t uid, gid_t gid, mode_t mode ) { int back = 0; if(0 == chown(fn, uid, gid)) { if(0 == chmod(fn, mode)) { back = 1; } else { /* ERROR: Failed to change file permissions! */ if(db->app) { dk3app_log_i3(db->app, DK3_LL_ERROR, 272, 273, fn); } } } else { /* ERROR: Failed to change file ownership! */ if(db->app) { dk3app_log_i3(db->app, DK3_LL_ERROR, 270, 271, fn); } } return back; } int dk3dbi_change_user_and_permissions( dk3_dbi_t *db, uid_t uid, gid_t gid, mode_t mode ) { dkChar fnb[DK3_MAX_PATH]; int back = 0; if(db) { switch(db->tp) { case DK3_DB_TYPE_MEMORY: case DK3_DB_TYPE_BDB: { if(db->fn) { back = dk3dbi_change_file_user_and_permissions( db, db->fn, uid, gid, mode ); } else { /* ERROR: No file name for database! */ if(db->app) { dk3app_log_i1(db->app, DK3_LL_ERROR, 269); } } } break; case DK3_DB_TYPE_NDBM: { if(db->fn) { if(dk3str_len(db->fn) < DK3_SIZEOF(fnb,dkChar)) { back = 1; dk3str_cpy_not_overlapped(fnb, db->fn); if((dk3str_len(fnb) + dk3str_len(dk3dbi_ndbm_suffixes[0])) < DK3_SIZEOF(fnb,dkChar) ) { dk3str_cat(fnb, dk3dbi_ndbm_suffixes[0]); if(!dk3dbi_change_file_user_and_permissions(db,fnb,uid,gid,mode)) { back = 0; } } else { /* ERROR: File name too long! */ if(db->app) { dk3app_log_i3(db->app, DK3_LL_ERROR, 66, 67, db->fn); } back = 0; } dk3str_cpy_not_overlapped(fnb, db->fn); if((dk3str_len(fnb) + dk3str_len(dk3dbi_ndbm_suffixes[1])) < DK3_SIZEOF(fnb,dkChar) ) { dk3str_cat(fnb, dk3dbi_ndbm_suffixes[1]); if(!dk3dbi_change_file_user_and_permissions(db,fnb,uid,gid,mode)) { back = 0; } } else { /* ERROR: File name too long! */ if(db->app) { dk3app_log_i3(db->app, DK3_LL_ERROR, 66, 67, db->fn); } back = 0; } } else { /* ERROR: File name too long! */ if(db->app) { dk3app_log_i3(db->app, DK3_LL_ERROR, 66, 67, db->fn); } } } else { /* ERROR: No file name for database! */ if(db->app) { dk3app_log_i1(db->app, DK3_LL_ERROR, 269); } } } break; } } return back; } #endif #endif #endif