7 /* dictionary manager interface to DB files
9 /* #include <dict_db.h>
11 /* int dict_db_cache_size;
13 /* DICT *dict_hash_open(path, open_flags, dict_flags)
18 /* DICT *dict_btree_open(path, open_flags, dict_flags)
23 /* dict_XXX_open() opens the specified DB database. The result is
24 /* a pointer to a structure that can be used to access the dictionary
25 /* using the generic methods documented in dict_open(3).
27 /* The dict_db_cache_size variable specifies a non-default per-table
28 /* I/O buffer size. The default buffer size is adequate for reading.
29 /* For better performance while creating a large table, specify a large
30 /* buffer size before opening the file.
34 /* The database pathname, not including the ".db" suffix.
36 /* Flags passed to dbopen().
38 /* Flags used by the dictionary interface.
40 /* dict(3) generic dictionary manager
42 /* Fatal errors: cannot open file, write error, out of memory.
46 /* The Secure Mailer license must be distributed with this software.
49 /* IBM T.J. Watson Research
51 /* Yorktown Heights, NY 10598, USA
71 #if defined(_DB_185_H_) && defined(USE_FCNTL_LOCK)
72 #error "Error: this system must not use the db 1.85 compatibility interface"
75 #ifndef DB_VERSION_MAJOR
76 #define DB_VERSION_MAJOR 1
77 #define DICT_DB_GET(db, key, val, flag) db->get(db, key, val, flag)
78 #define DICT_DB_PUT(db, key, val, flag) db->put(db, key, val, flag)
79 #define DICT_DB_DEL(db, key, flag) db->del(db, key, flag)
80 #define DICT_DB_SYNC(db, flag) db->sync(db, flag)
81 #define DICT_DB_CLOSE(db) db->close(db)
82 #define DONT_CLOBBER R_NOOVERWRITE
85 #if DB_VERSION_MAJOR > 1
86 #define DICT_DB_GET(db, key, val, flag) sanitize(db->get(db, 0, key, val, flag))
87 #define DICT_DB_PUT(db, key, val, flag) sanitize(db->put(db, 0, key, val, flag))
88 #define DICT_DB_DEL(db, key, flag) sanitize(db->del(db, 0, key, flag))
89 #define DICT_DB_SYNC(db, flag) ((errno = db->sync(db, flag)) ? -1 : 0)
90 #define DICT_DB_CLOSE(db) ((errno = db->close(db, 0)) ? -1 : 0)
91 #define DONT_CLOBBER DB_NOOVERWRITE
94 #if (DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR < 6)
95 #define DICT_DB_CURSOR(db, curs) (db)->cursor((db), NULL, (curs))
97 #define DICT_DB_CURSOR(db, curs) (db)->cursor((db), NULL, (curs), 0)
100 #ifndef DB_FCNTL_LOCKING
101 #define DB_FCNTL_LOCKING 0
104 /* Utility library. */
107 #include "mymalloc.h"
109 #include "stringops.h"
115 /* Application-specific. */
118 DICT dict
; /* generic members */
119 DB
*db
; /* open db file */
120 #if DB_VERSION_MAJOR > 1
121 DBC
*cursor
; /* dict_db_sequence() */
123 VSTRING
*key_buf
; /* key result */
124 VSTRING
*val_buf
; /* value result */
127 #define SCOPY(buf, data, size) \
128 vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), data, size))
131 * You can override the default dict_db_cache_size setting before calling
132 * dict_hash_open() or dict_btree_open(). This is done in mkmap_db_open() to
133 * set a larger memory pool for database (re)builds.
135 * XXX This should be specified via the DICT interface so that it becomes an
136 * object property, instead of being specified by poking a global variable
137 * so that it becomes a class property.
139 int dict_db_cache_size
= (128 * 1024); /* 128K default memory pool */
141 #define DICT_DB_NELM 4096
143 #if DB_VERSION_MAJOR > 1
145 /* sanitize - sanitize db_get/put/del result */
147 static int sanitize(int status
)
151 * XXX This is unclean but avoids a lot of clutter elsewhere. Categorize
152 * results into non-fatal errors (i.e., errors that we can deal with),
153 * success, or fatal error (i.e., all other errors).
157 case DB_NOTFOUND
: /* get, del */
158 case DB_KEYEXIST
: /* put */
159 return (1); /* non-fatal */
162 return (0); /* success */
164 case DB_KEYEMPTY
: /* get, others? */
169 return (-1); /* fatal */
175 /* dict_db_lookup - find database entry */
177 static const char *dict_db_lookup(DICT
*dict
, const char *name
)
179 DICT_DB
*dict_db
= (DICT_DB
*) dict
;
180 DB
*db
= dict_db
->db
;
184 const char *result
= 0;
189 if ((dict
->flags
& (DICT_FLAG_TRY1NULL
| DICT_FLAG_TRY0NULL
)) == 0)
190 msg_panic("dict_db_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
193 memset(&db_key
, 0, sizeof(db_key
));
194 memset(&db_value
, 0, sizeof(db_value
));
197 * Optionally fold the key.
199 if (dict
->flags
& DICT_FLAG_FOLD_FIX
) {
200 if (dict
->fold_buf
== 0)
201 dict
->fold_buf
= vstring_alloc(10);
202 vstring_strcpy(dict
->fold_buf
, name
);
203 name
= lowercase(vstring_str(dict
->fold_buf
));
207 * Acquire a shared lock.
209 if ((dict
->flags
& DICT_FLAG_LOCK
)
210 && myflock(dict
->lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_SHARED
) < 0)
211 msg_fatal("%s: lock dictionary: %m", dict_db
->dict
.name
);
214 * See if this DB file was written with one null byte appended to key and
217 if (dict
->flags
& DICT_FLAG_TRY1NULL
) {
218 db_key
.data
= (void *) name
;
219 db_key
.size
= strlen(name
) + 1;
220 if ((status
= DICT_DB_GET(db
, &db_key
, &db_value
, 0)) < 0)
221 msg_fatal("error reading %s: %m", dict_db
->dict
.name
);
223 dict
->flags
&= ~DICT_FLAG_TRY0NULL
;
224 result
= SCOPY(dict_db
->val_buf
, db_value
.data
, db_value
.size
);
229 * See if this DB file was written with no null byte appended to key and
232 if (result
== 0 && (dict
->flags
& DICT_FLAG_TRY0NULL
)) {
233 db_key
.data
= (void *) name
;
234 db_key
.size
= strlen(name
);
235 if ((status
= DICT_DB_GET(db
, &db_key
, &db_value
, 0)) < 0)
236 msg_fatal("error reading %s: %m", dict_db
->dict
.name
);
238 dict
->flags
&= ~DICT_FLAG_TRY1NULL
;
239 result
= SCOPY(dict_db
->val_buf
, db_value
.data
, db_value
.size
);
244 * Release the shared lock.
246 if ((dict
->flags
& DICT_FLAG_LOCK
)
247 && myflock(dict
->lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_NONE
) < 0)
248 msg_fatal("%s: unlock dictionary: %m", dict_db
->dict
.name
);
253 /* dict_db_update - add or update database entry */
255 static void dict_db_update(DICT
*dict
, const char *name
, const char *value
)
257 DICT_DB
*dict_db
= (DICT_DB
*) dict
;
258 DB
*db
= dict_db
->db
;
266 if ((dict
->flags
& (DICT_FLAG_TRY1NULL
| DICT_FLAG_TRY0NULL
)) == 0)
267 msg_panic("dict_db_update: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
270 * Optionally fold the key.
272 if (dict
->flags
& DICT_FLAG_FOLD_FIX
) {
273 if (dict
->fold_buf
== 0)
274 dict
->fold_buf
= vstring_alloc(10);
275 vstring_strcpy(dict
->fold_buf
, name
);
276 name
= lowercase(vstring_str(dict
->fold_buf
));
278 memset(&db_key
, 0, sizeof(db_key
));
279 memset(&db_value
, 0, sizeof(db_value
));
280 db_key
.data
= (void *) name
;
281 db_value
.data
= (void *) value
;
282 db_key
.size
= strlen(name
);
283 db_value
.size
= strlen(value
);
286 * If undecided about appending a null byte to key and value, choose a
287 * default depending on the platform.
289 if ((dict
->flags
& DICT_FLAG_TRY1NULL
)
290 && (dict
->flags
& DICT_FLAG_TRY0NULL
)) {
291 #ifdef DB_NO_TRAILING_NULL
292 dict
->flags
&= ~DICT_FLAG_TRY1NULL
;
294 dict
->flags
&= ~DICT_FLAG_TRY0NULL
;
299 * Optionally append a null byte to key and value.
301 if (dict
->flags
& DICT_FLAG_TRY1NULL
) {
307 * Acquire an exclusive lock.
309 if ((dict
->flags
& DICT_FLAG_LOCK
)
310 && myflock(dict
->lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_EXCLUSIVE
) < 0)
311 msg_fatal("%s: lock dictionary: %m", dict_db
->dict
.name
);
316 if ((status
= DICT_DB_PUT(db
, &db_key
, &db_value
,
317 (dict
->flags
& DICT_FLAG_DUP_REPLACE
) ? 0 : DONT_CLOBBER
)) < 0)
318 msg_fatal("error writing %s: %m", dict_db
->dict
.name
);
320 if (dict
->flags
& DICT_FLAG_DUP_IGNORE
)
322 else if (dict
->flags
& DICT_FLAG_DUP_WARN
)
323 msg_warn("%s: duplicate entry: \"%s\"", dict_db
->dict
.name
, name
);
325 msg_fatal("%s: duplicate entry: \"%s\"", dict_db
->dict
.name
, name
);
327 if (dict
->flags
& DICT_FLAG_SYNC_UPDATE
)
328 if (DICT_DB_SYNC(db
, 0) < 0)
329 msg_fatal("%s: flush dictionary: %m", dict_db
->dict
.name
);
332 * Release the exclusive lock.
334 if ((dict
->flags
& DICT_FLAG_LOCK
)
335 && myflock(dict
->lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_NONE
) < 0)
336 msg_fatal("%s: unlock dictionary: %m", dict_db
->dict
.name
);
339 /* delete one entry from the dictionary */
341 static int dict_db_delete(DICT
*dict
, const char *name
)
343 DICT_DB
*dict_db
= (DICT_DB
*) dict
;
344 DB
*db
= dict_db
->db
;
352 if ((dict
->flags
& (DICT_FLAG_TRY1NULL
| DICT_FLAG_TRY0NULL
)) == 0)
353 msg_panic("dict_db_delete: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
356 * Optionally fold the key.
358 if (dict
->flags
& DICT_FLAG_FOLD_FIX
) {
359 if (dict
->fold_buf
== 0)
360 dict
->fold_buf
= vstring_alloc(10);
361 vstring_strcpy(dict
->fold_buf
, name
);
362 name
= lowercase(vstring_str(dict
->fold_buf
));
364 memset(&db_key
, 0, sizeof(db_key
));
367 * Acquire an exclusive lock.
369 if ((dict
->flags
& DICT_FLAG_LOCK
)
370 && myflock(dict
->lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_EXCLUSIVE
) < 0)
371 msg_fatal("%s: lock dictionary: %m", dict_db
->dict
.name
);
374 * See if this DB file was written with one null byte appended to key and
377 if (dict
->flags
& DICT_FLAG_TRY1NULL
) {
378 db_key
.data
= (void *) name
;
379 db_key
.size
= strlen(name
) + 1;
380 if ((status
= DICT_DB_DEL(db
, &db_key
, flags
)) < 0)
381 msg_fatal("error deleting from %s: %m", dict_db
->dict
.name
);
383 dict
->flags
&= ~DICT_FLAG_TRY0NULL
;
387 * See if this DB file was written with no null byte appended to key and
390 if (status
> 0 && (dict
->flags
& DICT_FLAG_TRY0NULL
)) {
391 db_key
.data
= (void *) name
;
392 db_key
.size
= strlen(name
);
393 if ((status
= DICT_DB_DEL(db
, &db_key
, flags
)) < 0)
394 msg_fatal("error deleting from %s: %m", dict_db
->dict
.name
);
396 dict
->flags
&= ~DICT_FLAG_TRY1NULL
;
398 if (dict
->flags
& DICT_FLAG_SYNC_UPDATE
)
399 if (DICT_DB_SYNC(db
, 0) < 0)
400 msg_fatal("%s: flush dictionary: %m", dict_db
->dict
.name
);
403 * Release the exclusive lock.
405 if ((dict
->flags
& DICT_FLAG_LOCK
)
406 && myflock(dict
->lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_NONE
) < 0)
407 msg_fatal("%s: unlock dictionary: %m", dict_db
->dict
.name
);
412 /* dict_db_sequence - traverse the dictionary */
414 static int dict_db_sequence(DICT
*dict
, int function
,
415 const char **key
, const char **value
)
417 const char *myname
= "dict_db_sequence";
418 DICT_DB
*dict_db
= (DICT_DB
*) dict
;
419 DB
*db
= dict_db
->db
;
425 #if DB_VERSION_MAJOR > 1
431 memset(&db_key
, 0, sizeof(db_key
));
432 memset(&db_value
, 0, sizeof(db_value
));
435 * Determine the function.
438 case DICT_SEQ_FUN_FIRST
:
439 if (dict_db
->cursor
== 0)
440 DICT_DB_CURSOR(db
, &(dict_db
->cursor
));
441 db_function
= DB_FIRST
;
443 case DICT_SEQ_FUN_NEXT
:
444 if (dict_db
->cursor
== 0)
445 msg_panic("%s: no cursor", myname
);
446 db_function
= DB_NEXT
;
449 msg_panic("%s: invalid function %d", myname
, function
);
453 * Acquire a shared lock.
455 if ((dict
->flags
& DICT_FLAG_LOCK
)
456 && myflock(dict
->lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_SHARED
) < 0)
457 msg_fatal("%s: lock dictionary: %m", dict_db
->dict
.name
);
463 dict_db
->cursor
->c_get(dict_db
->cursor
, &db_key
, &db_value
, db_function
);
464 if (status
!= 0 && status
!= DB_NOTFOUND
)
465 msg_fatal("error [%d] seeking %s: %m", status
, dict_db
->dict
.name
);
468 * Release the shared lock.
470 if ((dict
->flags
& DICT_FLAG_LOCK
)
471 && myflock(dict
->lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_NONE
) < 0)
472 msg_fatal("%s: unlock dictionary: %m", dict_db
->dict
.name
);
477 * Copy the result so it is guaranteed null terminated.
479 *key
= SCOPY(dict_db
->key_buf
, db_key
.data
, db_key
.size
);
480 *value
= SCOPY(dict_db
->val_buf
, db_value
.data
, db_value
.size
);
486 * determine the function
489 case DICT_SEQ_FUN_FIRST
:
490 db_function
= R_FIRST
;
492 case DICT_SEQ_FUN_NEXT
:
493 db_function
= R_NEXT
;
496 msg_panic("%s: invalid function %d", myname
, function
);
500 * Acquire a shared lock.
502 if ((dict
->flags
& DICT_FLAG_LOCK
)
503 && myflock(dict
->lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_SHARED
) < 0)
504 msg_fatal("%s: lock dictionary: %m", dict_db
->dict
.name
);
506 if ((status
= db
->seq(db
, &db_key
, &db_value
, db_function
)) < 0)
507 msg_fatal("error seeking %s: %m", dict_db
->dict
.name
);
510 * Release the shared lock.
512 if ((dict
->flags
& DICT_FLAG_LOCK
)
513 && myflock(dict
->lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_NONE
) < 0)
514 msg_fatal("%s: unlock dictionary: %m", dict_db
->dict
.name
);
519 * Copy the result so that it is guaranteed null terminated.
521 *key
= SCOPY(dict_db
->key_buf
, db_key
.data
, db_key
.size
);
522 *value
= SCOPY(dict_db
->val_buf
, db_value
.data
, db_value
.size
);
528 /* dict_db_close - close data base */
530 static void dict_db_close(DICT
*dict
)
532 DICT_DB
*dict_db
= (DICT_DB
*) dict
;
534 #if DB_VERSION_MAJOR > 1
536 dict_db
->cursor
->c_close(dict_db
->cursor
);
538 if (DICT_DB_SYNC(dict_db
->db
, 0) < 0)
539 msg_fatal("flush database %s: %m", dict_db
->dict
.name
);
540 if (DICT_DB_CLOSE(dict_db
->db
) < 0)
541 msg_fatal("close database %s: %m", dict_db
->dict
.name
);
542 if (dict_db
->key_buf
)
543 vstring_free(dict_db
->key_buf
);
544 if (dict_db
->val_buf
)
545 vstring_free(dict_db
->val_buf
);
547 vstring_free(dict
->fold_buf
);
551 /* dict_db_open - open data base */
553 static DICT
*dict_db_open(const char *class, const char *path
, int open_flags
,
554 int type
, void *tweak
, int dict_flags
)
563 #if DB_VERSION_MAJOR > 1
569 * Mismatches between #include file and library are a common cause for
572 #if DB_VERSION_MAJOR > 1
577 (void) db_version(&major_version
, &minor_version
, &patch_version
);
578 if (major_version
!= DB_VERSION_MAJOR
|| minor_version
!= DB_VERSION_MINOR
)
579 msg_fatal("incorrect version of Berkeley DB: "
580 "compiled against %d.%d.%d, run-time linked against %d.%d.%d",
581 DB_VERSION_MAJOR
, DB_VERSION_MINOR
, DB_VERSION_PATCH
,
582 major_version
, minor_version
, patch_version
);
584 msg_info("Compiled against Berkeley DB: %d.%d.%d\n",
585 DB_VERSION_MAJOR
, DB_VERSION_MINOR
, DB_VERSION_PATCH
);
586 msg_info("Run-time linked against Berkeley DB: %d.%d.%d\n",
587 major_version
, minor_version
, patch_version
);
591 msg_info("Compiled against Berkeley DB version 1");
594 db_path
= concatenate(path
, ".db", (char *) 0);
597 * Note: DICT_FLAG_LOCK is used only by programs that do fine-grained (in
598 * the time domain) locking while accessing individual database records.
600 * Programs such as postmap/postalias use their own large-grained (in the
601 * time domain) locks while rewriting the entire file.
603 * XXX DB version 4.1 will not open a zero-length file. This means we must
604 * open an existing file without O_CREAT|O_TRUNC, and that we must let
605 * db_open() create a non-existent file for us.
607 #define LOCK_OPEN_FLAGS(f) ((f) & ~(O_CREAT|O_TRUNC))
609 if (dict_flags
& DICT_FLAG_LOCK
) {
610 if ((lock_fd
= open(db_path
, LOCK_OPEN_FLAGS(open_flags
), 0644)) < 0) {
612 msg_fatal("open database %s: %m", db_path
);
614 if (myflock(lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_SHARED
) < 0)
615 msg_fatal("shared-lock database %s for open: %m", db_path
);
620 * Use the DB 1.x programming interface. This is the default interface
621 * with 4.4BSD systems. It is also available via the db_185 compatibility
622 * interface, but that interface does not have the undocumented feature
623 * that we need to make file locking safe with POSIX fcntl() locking.
625 #if DB_VERSION_MAJOR < 2
626 if ((db
= dbopen(db_path
, open_flags
, 0644, type
, tweak
)) == 0)
627 msg_fatal("open database %s: %m", db_path
);
632 * Use the DB 2.x programming interface. Jump a couple extra hoops.
634 #if DB_VERSION_MAJOR == 2
635 db_flags
= DB_FCNTL_LOCKING
;
636 if (open_flags
== O_RDONLY
)
637 db_flags
|= DB_RDONLY
;
638 if (open_flags
& O_CREAT
)
639 db_flags
|= DB_CREATE
;
640 if (open_flags
& O_TRUNC
)
641 db_flags
|= DB_TRUNCATE
;
642 if ((errno
= db_open(db_path
, type
, db_flags
, 0644, 0, tweak
, &db
)) != 0)
643 msg_fatal("open database %s: %m", db_path
);
645 msg_panic("db_open null result");
646 if ((errno
= db
->fd(db
, &dbfd
)) != 0)
647 msg_fatal("get database file descriptor: %m");
651 * Use the DB 3.x programming interface. Jump even more hoops.
653 #if DB_VERSION_MAJOR > 2
654 db_flags
= DB_FCNTL_LOCKING
;
655 if (open_flags
== O_RDONLY
)
656 db_flags
|= DB_RDONLY
;
657 if (open_flags
& O_CREAT
)
658 db_flags
|= DB_CREATE
;
659 if (open_flags
& O_TRUNC
)
660 db_flags
|= DB_TRUNCATE
;
661 if ((errno
= db_create(&db
, 0, 0)) != 0)
662 msg_fatal("create DB database: %m");
664 msg_panic("db_create null result");
665 if ((errno
= db
->set_cachesize(db
, 0, dict_db_cache_size
, 0)) != 0)
666 msg_fatal("set DB cache size %d: %m", dict_db_cache_size
);
667 if (type
== DB_HASH
&& db
->set_h_nelem(db
, DICT_DB_NELM
) != 0)
668 msg_fatal("set DB hash element count %d: %m", DICT_DB_NELM
);
669 #if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 0)
670 if ((errno
= db
->open(db
, 0, db_path
, 0, type
, db_flags
, 0644)) != 0)
671 msg_fatal("open database %s: %m", db_path
);
672 #elif (DB_VERSION_MAJOR == 3 || DB_VERSION_MAJOR == 4)
673 if ((errno
= db
->open(db
, db_path
, 0, type
, db_flags
, 0644)) != 0)
674 msg_fatal("open database %s: %m", db_path
);
676 #error "Unsupported Berkeley DB version"
678 if ((errno
= db
->fd(db
, &dbfd
)) != 0)
679 msg_fatal("get database file descriptor: %m");
681 if ((dict_flags
& DICT_FLAG_LOCK
) && lock_fd
>= 0) {
682 if (myflock(lock_fd
, INTERNAL_LOCK
, MYFLOCK_OP_NONE
) < 0)
683 msg_fatal("unlock database %s for open: %m", db_path
);
684 if (close(lock_fd
) < 0)
685 msg_fatal("close database %s: %m", db_path
);
687 dict_db
= (DICT_DB
*) dict_alloc(class, db_path
, sizeof(*dict_db
));
688 dict_db
->dict
.lookup
= dict_db_lookup
;
689 dict_db
->dict
.update
= dict_db_update
;
690 dict_db
->dict
.delete = dict_db_delete
;
691 dict_db
->dict
.sequence
= dict_db_sequence
;
692 dict_db
->dict
.close
= dict_db_close
;
693 dict_db
->dict
.lock_fd
= dbfd
;
694 dict_db
->dict
.stat_fd
= dbfd
;
695 if (fstat(dict_db
->dict
.stat_fd
, &st
) < 0)
696 msg_fatal("dict_db_open: fstat: %m");
697 dict_db
->dict
.mtime
= st
.st_mtime
;
700 * Warn if the source file is newer than the indexed file, except when
701 * the source file changed only seconds ago.
703 if ((dict_flags
& DICT_FLAG_LOCK
) != 0
704 && stat(path
, &st
) == 0
705 && st
.st_mtime
> dict_db
->dict
.mtime
706 && st
.st_mtime
< time((time_t *) 0) - 100)
707 msg_warn("database %s is older than source file %s", db_path
, path
);
709 close_on_exec(dict_db
->dict
.lock_fd
, CLOSE_ON_EXEC
);
710 close_on_exec(dict_db
->dict
.stat_fd
, CLOSE_ON_EXEC
);
711 dict_db
->dict
.flags
= dict_flags
| DICT_FLAG_FIXED
;
712 if ((dict_flags
& (DICT_FLAG_TRY1NULL
| DICT_FLAG_TRY0NULL
)) == 0)
713 dict_db
->dict
.flags
|= (DICT_FLAG_TRY1NULL
| DICT_FLAG_TRY0NULL
);
714 if (dict_flags
& DICT_FLAG_FOLD_FIX
)
715 dict_db
->dict
.fold_buf
= vstring_alloc(10);
717 #if DB_VERSION_MAJOR > 1
720 dict_db
->key_buf
= 0;
721 dict_db
->val_buf
= 0;
724 return (DICT_DEBUG (&dict_db
->dict
));
727 /* dict_hash_open - create association with data base */
729 DICT
*dict_hash_open(const char *path
, int open_flags
, int dict_flags
)
731 #if DB_VERSION_MAJOR < 2
734 memset((char *) &tweak
, 0, sizeof(tweak
));
735 tweak
.nelem
= DICT_DB_NELM
;
736 tweak
.cachesize
= dict_db_cache_size
;
738 #if DB_VERSION_MAJOR == 2
741 memset((char *) &tweak
, 0, sizeof(tweak
));
742 tweak
.h_nelem
= DICT_DB_NELM
;
743 tweak
.db_cachesize
= dict_db_cache_size
;
745 #if DB_VERSION_MAJOR > 2
750 return (dict_db_open(DICT_TYPE_HASH
, path
, open_flags
, DB_HASH
,
751 (void *) &tweak
, dict_flags
));
754 /* dict_btree_open - create association with data base */
756 DICT
*dict_btree_open(const char *path
, int open_flags
, int dict_flags
)
758 #if DB_VERSION_MAJOR < 2
761 memset((char *) &tweak
, 0, sizeof(tweak
));
762 tweak
.cachesize
= dict_db_cache_size
;
764 #if DB_VERSION_MAJOR == 2
767 memset((char *) &tweak
, 0, sizeof(tweak
));
768 tweak
.db_cachesize
= dict_db_cache_size
;
770 #if DB_VERSION_MAJOR > 2
776 return (dict_db_open(DICT_TYPE_BTREE
, path
, open_flags
, DB_BTREE
,
777 (void *) &tweak
, dict_flags
));