/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1999, 2013 Oracle and/or its affiliates.  All rights reserved.
 *
 * $Id$
 */

#include "db_config.h"

#include "db_int.h"
#ifdef HAVE_SYSTEM_INCLUDE_FILES
#include <tcl.h>
#endif
#include "dbinc/db_page.h"
#include "dbinc/hash.h"
#include "dbinc/tcl_db.h"

/* XXX we must declare global data in just one place */
DBTCL_GLOBAL __dbtcl_global;

/*
 * Prototypes for procedures defined later in this file:
 */
static int	berkdb_Cmd __P((ClientData, Tcl_Interp *, int,
    Tcl_Obj * CONST*));
static int	bdb_EnvOpen __P((Tcl_Interp *, int, Tcl_Obj * CONST*,
    DBTCL_INFO *, DB_ENV **));
static int	bdb_DbOpen __P((Tcl_Interp *, int, Tcl_Obj * CONST*,
    DBTCL_INFO *, DB **));
static int	bdb_DbRemove __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
static int	bdb_DbRename __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
static int	bdb_Version __P((Tcl_Interp *, int, Tcl_Obj * CONST*));

#ifdef HAVE_64BIT_TYPES
static int	bdb_SeqOpen __P((Tcl_Interp *, int, Tcl_Obj * CONST*,
    DBTCL_INFO *, DB_SEQUENCE **));
#endif

static int	heap_callback __P((DB *dbp, const DBT *, const DBT *, DBT *));

#ifdef CONFIG_TEST
static int	bdb_DbUpgrade __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
static int	bdb_DbVerify __P((Tcl_Interp *, int, Tcl_Obj * CONST*,
    DBTCL_INFO *));
static int	bdb_GetConfig __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
static int	bdb_Handles __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
static int	bdb_MsgType __P((Tcl_Interp *, int, Tcl_Obj * CONST*));

static int	tcl_bt_compare __P((DB *, const DBT *, const DBT *));
static int	tcl_compare_callback __P((DB *, const DBT *, const DBT *,
    Tcl_Obj *, char *));
static void	tcl_db_free __P((void *));
static void *	tcl_db_malloc __P((size_t));
static void *	tcl_db_realloc __P((void *, size_t));
static int	tcl_dup_compare __P((DB *, const DBT *, const DBT *));
static u_int32_t tcl_h_hash __P((DB *, const void *, u_int32_t));
static int	tcl_isalive __P((DB_ENV *, pid_t, db_threadid_t, u_int32_t));
static u_int32_t tcl_part_callback __P((DB *, DBT *));
static int	 tcl_set_partition_dirs
			__P((Tcl_Interp *, DB *, Tcl_Obj *));
static int	 tcl_set_partition_keys
			__P((Tcl_Interp *, DB *, Tcl_Obj *, DBT **));
#endif

int Db_tcl_Init __P((Tcl_Interp *));

/*
 * Db_tcl_Init --
 *
 * This is a package initialization procedure, which is called by Tcl when
 * this package is to be added to an interpreter.  The name is based on the
 * name of the shared library, currently libdb_tcl-X.Y.so, which Tcl uses
 * to determine the name of this function.
 */
int
Db_tcl_Init(interp)
	Tcl_Interp *interp;		/* Interpreter in which the package is
					 * to be made available. */
{
	int code;
	char pkg[12];

	snprintf(pkg, sizeof(pkg), "%d.%d", DB_VERSION_MAJOR, DB_VERSION_MINOR);
	code = Tcl_PkgProvide(interp, "Db_tcl", pkg);
	if (code != TCL_OK)
		return (code);

	/*
	 * Don't allow setuid/setgid scripts for the Tcl API because some Tcl
	 * functions evaluate the arguments and could otherwise allow a user
	 * to inject Tcl commands.
	 */
#if defined(HAVE_SETUID) && defined(HAVE_GETUID)
	(void)setuid(getuid());
#endif
#if defined(HAVE_SETGID) && defined(HAVE_GETGID)
	(void)setgid(getgid());
#endif

	(void)Tcl_CreateObjCommand(interp,
	    "berkdb", (Tcl_ObjCmdProc *)berkdb_Cmd, (ClientData)0, NULL);
	/*
	 * Create shared global debugging variables
	 */
	(void)Tcl_LinkVar(
	    interp, "__debug_on", (char *)&__debug_on, TCL_LINK_INT);
	(void)Tcl_LinkVar(
	    interp, "__debug_print", (char *)&__debug_print, TCL_LINK_INT);
	(void)Tcl_LinkVar(
	    interp, "__debug_stop", (char *)&__debug_stop, TCL_LINK_INT);
	(void)Tcl_LinkVar(
	    interp, "__debug_test", (char *)&__debug_test,
	    TCL_LINK_INT);
	LIST_INIT(&__db_infohead);
	return (TCL_OK);
}

/*
 * berkdb_cmd --
 *	Implements the "berkdb" command.
 *	This command supports three sub commands:
 *	berkdb version - Returns a list {major minor patch}
 *	berkdb env - Creates a new DB_ENV and returns a binding
 *	  to a new command of the form dbenvX, where X is an
 *	  integer starting at 0 (dbenv0, dbenv1, ...)
 *	berkdb open - Creates a new DB (optionally within
 *	  the given environment.  Returns a binding to a new
 *	  command of the form dbX, where X is an integer
 *	  starting at 0 (db0, db1, ...)
 */
static int
berkdb_Cmd(notused, interp, objc, objv)
	ClientData notused;		/* Not used. */
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
{
	static const char *berkdbcmds[] = {
#ifdef CONFIG_TEST
		"dbverify",
		"getconfig",
		"handles",
		"msgtype",
		"upgrade",
#endif
		"dbremove",
		"dbrename",
		"env",
		"envremove",
		"open",
#ifdef HAVE_64BIT_TYPES
		"sequence",
#endif
		"version",
#ifdef CONFIG_TEST
		/* All below are compatibility functions */
		"hcreate",	"hsearch",	"hdestroy",
		"dbminit",	"fetch",	"store",
		"delete",	"firstkey",	"nextkey",
		"ndbm_open",	"dbmclose",
#endif
		/* All below are convenience functions */
		"rand",		"random_int",	"srand",
		"debug_check",
		NULL
	};
	/*
	 * All commands enums below ending in X are compatibility
	 */
	enum berkdbcmds {
#ifdef CONFIG_TEST
		BDB_DBVERIFY,
		BDB_GETCONFIG,
		BDB_HANDLES,
		BDB_MSGTYPE,
		BDB_UPGRADE,
#endif
		BDB_DBREMOVE,
		BDB_DBRENAME,
		BDB_ENV,
		BDB_ENVREMOVE,
		BDB_OPEN,
#ifdef HAVE_64BIT_TYPES
		BDB_SEQUENCE,
#endif
		BDB_VERSION,
#ifdef CONFIG_TEST
		BDB_HCREATEX,	BDB_HSEARCHX,	BDB_HDESTROYX,
		BDB_DBMINITX,	BDB_FETCHX,	BDB_STOREX,
		BDB_DELETEX,	BDB_FIRSTKEYX,	BDB_NEXTKEYX,
		BDB_NDBMOPENX,	BDB_DBMCLOSEX,
#endif
		BDB_RANDX,	BDB_RAND_INTX,	BDB_SRANDX,
		BDB_DBGCKX
	};
	static int env_id = 0;
	static int db_id = 0;
#ifdef HAVE_64BIT_TYPES
	static int seq_id = 0;
#endif

	DB *dbp, *hrdbp, *hsdbp;
#ifdef HAVE_64BIT_TYPES
	DB_SEQUENCE *seq;
#endif
#ifdef CONFIG_TEST
	DBM *ndbmp;
	static int ndbm_id = 0;
#endif
	DBTCL_INFO *ip, *hrip, *hsip;
	DB_ENV *dbenv;
	Tcl_Obj *res;
	int cmdindex, result;
	char newname[MSG_SIZE];

	COMPQUIET(notused, NULL);
	COMPQUIET(hrdbp, NULL);
	COMPQUIET(hrip, NULL);
	COMPQUIET(hsdbp, NULL);
	COMPQUIET(hsip, NULL);

	Tcl_ResetResult(interp);
	memset(newname, 0, MSG_SIZE);
	result = TCL_OK;
	if (objc <= 1) {
		Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs");
		return (TCL_ERROR);
	}

	/*
	 * Get the command name index from the object based on the berkdbcmds
	 * defined above.
	 */
	if (Tcl_GetIndexFromObj(interp,
	    objv[1], berkdbcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
		return (IS_HELP(objv[1]));
	res = NULL;
	switch ((enum berkdbcmds)cmdindex) {
#ifdef CONFIG_TEST
	case BDB_DBVERIFY:
		snprintf(newname, sizeof(newname), "db%d", db_id);
		ip = _NewInfo(interp, NULL, newname, I_DB);
		if (ip != NULL) {
			result = bdb_DbVerify(interp, objc, objv, ip);
			_DeleteInfo(ip);
		} else {
			Tcl_SetResult(interp, "Could not set up info",
			    TCL_STATIC);
			result = TCL_ERROR;
		}
		break;
	case BDB_GETCONFIG:
		result = bdb_GetConfig(interp, objc, objv);
		break;
	case BDB_HANDLES:
		result = bdb_Handles(interp, objc, objv);
		break;
	case BDB_MSGTYPE:
		result = bdb_MsgType(interp, objc, objv);
		break;
	case BDB_UPGRADE:
		result = bdb_DbUpgrade(interp, objc, objv);
		break;
#endif
	case BDB_VERSION:
		_debug_check();
		result = bdb_Version(interp, objc, objv);
		break;
	case BDB_ENV:
		snprintf(newname, sizeof(newname), "env%d", env_id);
		ip = _NewInfo(interp, NULL, newname, I_ENV);
		if (ip != NULL) {
			result = bdb_EnvOpen(interp, objc, objv, ip, &dbenv);
			if (result == TCL_OK && dbenv != NULL) {
				env_id++;
				(void)Tcl_CreateObjCommand(interp, newname,
				    (Tcl_ObjCmdProc *)env_Cmd,
				    (ClientData)dbenv, NULL);
				/* Use ip->i_name - newname is overwritten */
				res = NewStringObj(newname, strlen(newname));
				_SetInfoData(ip, dbenv);
			} else
				_DeleteInfo(ip);
		} else {
			Tcl_SetResult(interp, "Could not set up info",
			    TCL_STATIC);
			result = TCL_ERROR;
		}
		break;
	case BDB_DBREMOVE:
		result = bdb_DbRemove(interp, objc, objv);
		break;
	case BDB_DBRENAME:
		result = bdb_DbRename(interp, objc, objv);
		break;
	case BDB_ENVREMOVE:
		result = tcl_EnvRemove(interp, objc, objv);
		break;
	case BDB_OPEN:
		snprintf(newname, sizeof(newname), "db%d", db_id);
		ip = _NewInfo(interp, NULL, newname, I_DB);
		if (ip != NULL) {
			result = bdb_DbOpen(interp, objc, objv, ip, &dbp);
			if (result == TCL_OK && dbp != NULL) {
				db_id++;
				(void)Tcl_CreateObjCommand(interp, newname,
				    (Tcl_ObjCmdProc *)db_Cmd,
				    (ClientData)dbp, NULL);
				/* Use ip->i_name - newname is overwritten */
				res = NewStringObj(newname, strlen(newname));
				_SetInfoData(ip, dbp);
				/* 
				 * If we are a DB_HEAP, we need to finish
				 * setting up the DB_RECNO db which was
				 * started in bdb_DbOpen call.
				 */
				if (dbp->type == DB_HEAP) {
					snprintf(newname, 
					    sizeof(newname), "db%d", db_id);
					hrip = _NewInfo(interp, 
					    NULL, newname, I_AUX);	
					if (hrip != NULL) {
						db_id++;
						hrdbp = ip->hrdbp;
						_SetInfoData(hrip, hrdbp);
						hrdbp->api_internal = hrip;
						hrip->hrdbp = dbp;
						if (hrdbp->dbenv->db_errpfx 
						    == NULL)
							hrdbp->set_errpfx(
							  hrdbp, hrip->i_name);
					} else {
						Tcl_SetResult(interp, 
						    "Could not set up info",
						    TCL_STATIC);
						result = TCL_ERROR;
					}
					snprintf(newname, 
					    sizeof(newname), "db%d", db_id);
					hsip = _NewInfo(interp, 
					    NULL, newname, I_AUX);	
					if (hsip != NULL) {
						db_id++;
						hsdbp = ip->hsdbp;
						_SetInfoData(hsip, hsdbp);
						hsdbp->api_internal = hsip;
						hsip->hsdbp = dbp;
						if (hsdbp->dbenv->db_errpfx 
						    == NULL)
							hsdbp->set_errpfx(
							  hsdbp, hsip->i_name);
					} else {
						Tcl_SetResult(interp, 
						    "Could not set up info",
						    TCL_STATIC);
						result = TCL_ERROR;
					}
				}
			} else
				_DeleteInfo(ip);
		} else {
			Tcl_SetResult(interp, "Could not set up info",
			    TCL_STATIC);
			result = TCL_ERROR;
		}
		break;
#ifdef HAVE_64BIT_TYPES
	case BDB_SEQUENCE:
		snprintf(newname, sizeof(newname), "seq%d", seq_id);
		ip = _NewInfo(interp, NULL, newname, I_SEQ);
		if (ip != NULL) {
			result = bdb_SeqOpen(interp, objc, objv, ip, &seq);
			if (result == TCL_OK && seq != NULL) {
				seq_id++;
				(void)Tcl_CreateObjCommand(interp, newname,
				    (Tcl_ObjCmdProc *)seq_Cmd,
				    (ClientData)seq, NULL);
				/* Use ip->i_name - newname is overwritten */
				res = NewStringObj(newname, strlen(newname));
				_SetInfoData(ip, seq);
			} else
				_DeleteInfo(ip);
		} else {
			Tcl_SetResult(interp, "Could not set up info",
			    TCL_STATIC);
			result = TCL_ERROR;
		}
		break;
#endif
#ifdef CONFIG_TEST
	case BDB_HCREATEX:
	case BDB_HSEARCHX:
	case BDB_HDESTROYX:
		result = bdb_HCommand(interp, objc, objv);
		break;
	case BDB_DBMINITX:
	case BDB_DBMCLOSEX:
	case BDB_FETCHX:
	case BDB_STOREX:
	case BDB_DELETEX:
	case BDB_FIRSTKEYX:
	case BDB_NEXTKEYX:
		result = bdb_DbmCommand(interp, objc, objv, DBTCL_DBM, NULL);
		break;
	case BDB_NDBMOPENX:
		snprintf(newname, sizeof(newname), "ndbm%d", ndbm_id);
		ip = _NewInfo(interp, NULL, newname, I_NDBM);
		if (ip != NULL) {
			result = bdb_NdbmOpen(interp, objc, objv, &ndbmp);
			if (result == TCL_OK) {
				ndbm_id++;
				(void)Tcl_CreateObjCommand(interp, newname,
				    (Tcl_ObjCmdProc *)ndbm_Cmd,
				    (ClientData)ndbmp, NULL);
				/* Use ip->i_name - newname is overwritten */
				res = NewStringObj(newname, strlen(newname));
				_SetInfoData(ip, ndbmp);
			} else
				_DeleteInfo(ip);
		} else {
			Tcl_SetResult(interp, "Could not set up info",
			    TCL_STATIC);
			result = TCL_ERROR;
		}
		break;
#endif
	case BDB_RANDX:
	case BDB_RAND_INTX:
	case BDB_SRANDX:
		result = bdb_RandCommand(interp, objc, objv);
		break;
	case BDB_DBGCKX:
		_debug_check();
		res = Tcl_NewIntObj(0);
		break;
	}
	/*
	 * For each different arg call different function to create
	 * new commands (or if version, get/return it).
	 */
	if (result == TCL_OK && res != NULL)
		Tcl_SetObjResult(interp, res);
	return (result);
}

/*
 * bdb_EnvOpen -
 *	Implements the environment open command.
 *	There are many, many options to the open command.
 *	Here is the general flow:
 *
 *	1.  Call db_env_create to create the env handle.
 *	2.  Parse args tracking options.
 *	3.  Make any pre-open setup calls necessary.
 *	4.  Call DB_ENV->open to open the env.
 *	5.  Return env widget handle to user.
 */
static int
bdb_EnvOpen(interp, objc, objv, ip, dbenvp)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DBTCL_INFO *ip;			/* Our internal info */
	DB_ENV **dbenvp;		/* Environment pointer */
{
	static const char *envopen[] = {
#ifdef CONFIG_TEST
		"-alloc",
		"-auto_commit",
		"-cdb",
		"-cdb_alldb",
		"-event",
		"-failchk",
		"-isalive",
		"-lock",
		"-lock_conflict",
		"-lock_detect",
		"-lock_locks",
		"-lock_lockers",
		"-lock_objects",
		"-lock_max_locks",
		"-lock_max_lockers",
		"-lock_max_objects",
		"-lock_partitions",
		"-lock_tablesize",
		"-lock_timeout",
		"-log",
		"-log_filemode",
		"-log_buffer",
		"-log_inmemory",
		"-log_max",
		"-log_regionmax",
		"-log_remove",
		"-memory_max",
		"-mpool_max_openfd",
		"-mpool_max_write",
		"-mpool_mmap_size",
		"-mpool_mutex_count",
		"-mpool_nommap",
		"-multiversion",
		"-mutex_set_align",
		"-mutex_set_incr",
		"-mutex_set_init",
		"-mutex_set_max",
		"-mutex_set_tas_spins",
		"-overwrite",
		"-pagesize",
		"-register",
		"-reg_timeout",
		"-region_init",
		"-rep",
		"-rep_client",
		"-rep_inmem_files",
		"-rep_lease",
		"-rep_master",
		"-rep_nsites",
		"-rep_transport",
		"-set_intermediate_dir_mode",
		"-snapshot",
		"-tablesize",
		"-thread",
		"-thread_count",
		"-time_notgranted",
		"-txn_nowait",
		"-txn_timeout",
		"-txn_timestamp",
		"-verbose",
		"-wrnosync",
		"-zero_log",
#endif
		"-add_dir",
		"-cachesize",
		"-cache_max",
		"-create",
		"-create_dir",
		"-data_dir",
		"-encryptaes",
		"-encryptany",
		"-errfile",
		"-errpfx",
		"-home",
		"-log_dir",
		"-metadata_dir",
		"-mode",
		"-msgfile",
		"-private",
		"-recover",
		"-recover_fatal",
		"-shm_key",
		"-system_mem",
		"-tmp_dir",
		"-txn",
		"-txn_init",
		"-txn_max",
		"-use_environ",
		"-use_environ_root",
		NULL
	};
	/*
	 * !!!
	 * These have to be in the same order as the above,
	 * which is close to but not quite alphabetical.
	 */
	enum envopen {
#ifdef CONFIG_TEST
		TCL_ENV_ALLOC,
		TCL_ENV_AUTO_COMMIT,
		TCL_ENV_CDB,
		TCL_ENV_CDB_ALLDB,
		TCL_ENV_EVENT,
		TCL_ENV_FAILCHK,
		TCL_ENV_ISALIVE,
		TCL_ENV_LOCK,
		TCL_ENV_CONFLICT,
		TCL_ENV_DETECT,
		TCL_ENV_LOCK_LOCKS,
		TCL_ENV_LOCK_LOCKERS,
		TCL_ENV_LOCK_OBJECTS,
		TCL_ENV_LOCK_MAX_LOCKS,
		TCL_ENV_LOCK_MAX_LOCKERS,
		TCL_ENV_LOCK_MAX_OBJECTS,
		TCL_ENV_LOCK_PARTITIONS,
		TCL_ENV_LOCK_TABLESIZE,
		TCL_ENV_LOCK_TIMEOUT,
		TCL_ENV_LOG,
		TCL_ENV_LOG_FILEMODE,
		TCL_ENV_LOG_BUFFER,
		TCL_ENV_LOG_INMEMORY,
		TCL_ENV_LOG_MAX,
		TCL_ENV_LOG_REGIONMAX,
		TCL_ENV_LOG_REMOVE,
		TCL_ENV_MEMORY_MAX,
		TCL_ENV_MPOOL_MAX_OPENFD,
		TCL_ENV_MPOOL_MAX_WRITE,
		TCL_ENV_MPOOL_MMAP_SIZE,
		TCL_ENV_MUTEXCOUNT,
		TCL_ENV_MPOOL_NOMMAP,
		TCL_ENV_MULTIVERSION,
		TCL_ENV_MUTSETALIGN,
		TCL_ENV_MUTSETINCR,
		TCL_ENV_MUTSETINIT,
		TCL_ENV_MUTSETMAX,
		TCL_ENV_MUTSETTAS,
		TCL_ENV_OVERWRITE,
		TCL_ENV_PAGESIZE,
		TCL_ENV_REGISTER,
		TCL_ENV_REG_TIMEOUT,
		TCL_ENV_REGION_INIT,
		TCL_ENV_REP,
		TCL_ENV_REP_CLIENT,
		TCL_ENV_REP_INMEM_FILES,
		TCL_ENV_REP_LEASE,
		TCL_ENV_REP_MASTER,
		TCL_ENV_REP_NSITES,
		TCL_ENV_REP_TRANSPORT,
		TCL_ENV_SET_INTERMEDIATE_DIR,
		TCL_ENV_SNAPSHOT,
		TCL_ENV_TABLESIZE,
		TCL_ENV_THREAD,
		TCL_ENV_THREAD_COUNT,
		TCL_ENV_TIME_NOTGRANTED,
		TCL_ENV_TXN_NOWAIT,
		TCL_ENV_TXN_TIMEOUT,
		TCL_ENV_TXN_TIME,
		TCL_ENV_VERBOSE,
		TCL_ENV_WRNOSYNC,
		TCL_ENV_ZEROLOG,
#endif
		TCL_ENV_ADD_DIR,
		TCL_ENV_CACHESIZE,
		TCL_ENV_CACHE_MAX,
		TCL_ENV_CREATE,
		TCL_ENV_CREATE_DIR,
		TCL_ENV_DATA_DIR,
		TCL_ENV_ENCRYPT_AES,
		TCL_ENV_ENCRYPT_ANY,
		TCL_ENV_ERRFILE,
		TCL_ENV_ERRPFX,
		TCL_ENV_HOME,
		TCL_ENV_LOG_DIR,
		TCL_ENV_METADATA_DIR,
		TCL_ENV_MODE,
		TCL_ENV_MSGFILE,
		TCL_ENV_PRIVATE,
		TCL_ENV_RECOVER,
		TCL_ENV_RECOVER_FATAL,
		TCL_ENV_SHM_KEY,
		TCL_ENV_SYSTEM_MEM,
		TCL_ENV_TMP_DIR,
		TCL_ENV_TXN,
		TCL_ENV_TXN_INIT,
		TCL_ENV_TXN_MAX,
		TCL_ENV_USE_ENVIRON,
		TCL_ENV_USE_ENVIRON_ROOT
	};
	DB_ENV *dbenv;
	Tcl_Obj **myobjv;
	u_int32_t cr_flags, gbytes, bytes, logbufset, logmaxset;
	u_int32_t open_flags, rep_flags, set_flags, uintarg;
	int i, mode, myobjc, ncaches, optindex, result, ret;
	long shm;
	char *arg, *home, *passwd;
#ifdef CONFIG_TEST
	Tcl_Obj **myobjv1;
	time_t timestamp;
	long v;
	u_int32_t detect, time_flag;
	u_int8_t *conflicts;
	int intarg, intarg2, j, nmodes, temp;
#endif

	result = TCL_OK;
	mode = 0;
	rep_flags = set_flags = cr_flags = 0;
	home = NULL;

	/*
	 * XXX
	 * If/when our Tcl interface becomes thread-safe, we should enable
	 * DB_THREAD here in all cases.  For now, we turn it on later in this
	 * function, and only when we're in testing and we specify the
	 * -thread flag, so that we can exercise MUTEX_THREAD_LOCK cases.
	 *
	 * In order to become truly thread-safe, we need to look at making sure
	 * DBTCL_INFO structs are safe to share across threads (they're not
	 * mutex-protected) before we declare the Tcl interface thread-safe.
	 * Meanwhile, there's no strong reason to enable DB_THREAD when not
	 * testing.
	 */
	open_flags = 0;
	logmaxset = logbufset = 0;

	if (objc <= 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "?args?");
		return (TCL_ERROR);
	}

	i = 2;
	if (result != TCL_OK)
		return (TCL_ERROR);
	if ((ret = db_env_create(&dbenv, cr_flags)) != 0)
		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "db_env_create"));
	*dbenvp = dbenv;

	/*
	 * From here on we must 'goto error' in order to clean up the
	 * dbenv from db_env_create.
	 */
	dbenv->set_errpfx(dbenv, ip->i_name);
	dbenv->set_errcall(dbenv, _ErrorFunc);

	/* Hang our info pointer on the dbenv handle, so we can do callbacks. */
	dbenv->app_private = ip;

	/*
	 * Get the command name index from the object based on the bdbcmds
	 * defined above.
	 */
	i = 2;
	while (i < objc) {
		Tcl_ResetResult(interp);
		if (Tcl_GetIndexFromObj(interp, objv[i], envopen, "option",
		    TCL_EXACT, &optindex) != TCL_OK) {
			result = IS_HELP(objv[i]);
			goto error;
		}
		i++;
		switch ((enum envopen)optindex) {
#ifdef CONFIG_TEST
		case TCL_ENV_ALLOC:
			/*
			 * Use a Tcl-local alloc and free function so that
			 * we're sure to test whether we use umalloc/ufree in
			 * the right places.
			 */
			(void)dbenv->set_alloc(dbenv,
			    tcl_db_malloc, tcl_db_realloc, tcl_db_free);
			break;
		case TCL_ENV_AUTO_COMMIT:
			FLD_SET(set_flags, DB_AUTO_COMMIT);
			break;
		case TCL_ENV_CDB:
			FLD_SET(open_flags, DB_INIT_CDB | DB_INIT_MPOOL);
			break;
		case TCL_ENV_CDB_ALLDB:
			FLD_SET(set_flags, DB_CDB_ALLDB);
			break;
		case TCL_ENV_EVENT:
			if ((ret = __os_calloc(dbenv->env, 1,
			    sizeof(DBTCL_EVENT_INFO),
			    &ip->i_event_info)) == 0) {
				_debug_check();
				ret = dbenv->set_event_notify(dbenv,
				    _EventFunc);
			}
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_event_notify");
			break;
		case TCL_ENV_FAILCHK:
			FLD_SET(open_flags, DB_FAILCHK);
			break;
		case TCL_ENV_ISALIVE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-isalive aliveproc");
				result = TCL_ERROR;
				break;
			}

			ip->i_isalive = objv[i++];
			Tcl_IncrRefCount(ip->i_isalive);
			_debug_check();
			/* Choose an arbitrary thread count, for testing. */
			ret = dbenv->get_thread_count(dbenv, &uintarg);
			if ((ret == 0) && (uintarg == 0))
				ret = dbenv->set_thread_count(dbenv, 5);
			if (ret == 0)
				ret = dbenv->set_isalive(dbenv, tcl_isalive);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_isalive");
			break;
		case TCL_ENV_LOCK:
			FLD_SET(open_flags, DB_INIT_LOCK | DB_INIT_MPOOL);
			break;
		case TCL_ENV_CONFLICT:
			/*
			 * Get conflict list.  List is:
			 * {nmodes {matrix}}
			 *
			 * Where matrix must be nmodes*nmodes big.
			 * Set up conflicts array to pass.
			 */
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			if (myobjc != 2) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-lock_conflict {nmodes {matrix}}?");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, myobjv[0], &nmodes);
			if (result != TCL_OK)
				break;
			result = Tcl_ListObjGetElements(interp, myobjv[1],
			    &myobjc, &myobjv1);
			if (myobjc != (nmodes * nmodes)) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-lock_conflict {nmodes {matrix}}?");
				result = TCL_ERROR;
				break;
			}

			ret = __os_malloc(dbenv->env, sizeof(u_int8_t) *
			    (size_t)nmodes * (size_t)nmodes, &conflicts);
			if (ret != 0) {
				result = TCL_ERROR;
				break;
			}
			for (j = 0; j < myobjc; j++) {
				result = Tcl_GetIntFromObj(interp, myobjv1[j],
				    &temp);
				conflicts[j] = temp;
				if (result != TCL_OK) {
					__os_free(NULL, conflicts);
					break;
				}
			}
			_debug_check();
			ret = dbenv->set_lk_conflicts(dbenv,
			    (u_int8_t *)conflicts, nmodes);
			__os_free(NULL, conflicts);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_lk_conflicts");
			break;
		case TCL_ENV_DETECT:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-lock_detect policy?");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			if (strcmp(arg, "default") == 0)
				detect = DB_LOCK_DEFAULT;
			else if (strcmp(arg, "expire") == 0)
				detect = DB_LOCK_EXPIRE;
			else if (strcmp(arg, "maxlocks") == 0)
				detect = DB_LOCK_MAXLOCKS;
			else if (strcmp(arg, "maxwrites") == 0)
				detect = DB_LOCK_MAXWRITE;
			else if (strcmp(arg, "minlocks") == 0)
				detect = DB_LOCK_MINLOCKS;
			else if (strcmp(arg, "minwrites") == 0)
				detect = DB_LOCK_MINWRITE;
			else if (strcmp(arg, "oldest") == 0)
				detect = DB_LOCK_OLDEST;
			else if (strcmp(arg, "youngest") == 0)
				detect = DB_LOCK_YOUNGEST;
			else if (strcmp(arg, "random") == 0)
				detect = DB_LOCK_RANDOM;
			else {
				Tcl_AddErrorInfo(interp,
				    "lock_detect: illegal policy");
				result = TCL_ERROR;
				break;
			}
			_debug_check();
			ret = dbenv->set_lk_detect(dbenv, detect);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "lock_detect");
			break;
		case TCL_ENV_LOCK_LOCKS:
		case TCL_ENV_LOCK_LOCKERS:
		case TCL_ENV_LOCK_OBJECTS:
		case TCL_ENV_LOCK_MAX_LOCKS:
		case TCL_ENV_LOCK_MAX_LOCKERS:
		case TCL_ENV_LOCK_MAX_OBJECTS:
		case TCL_ENV_LOCK_PARTITIONS:
		case TCL_ENV_LOCK_TABLESIZE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-lock_max max?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				switch ((enum envopen)optindex) {
				case TCL_ENV_LOCK_LOCKS:
					ret = dbenv->set_memory_init(dbenv,
					    DB_MEM_LOCK, uintarg);
					break;
				case TCL_ENV_LOCK_LOCKERS:
					ret = dbenv->set_memory_init(dbenv,
					    DB_MEM_LOCKER, uintarg);
					break;
				case TCL_ENV_LOCK_OBJECTS:
					ret = dbenv->set_memory_init(dbenv,
					    DB_MEM_LOCKOBJECT, uintarg);
					break;
				case TCL_ENV_LOCK_MAX_LOCKS:
					ret = dbenv->set_lk_max_locks(dbenv,
					    uintarg);
					break;
				case TCL_ENV_LOCK_MAX_LOCKERS:
					ret = dbenv->set_lk_max_lockers(dbenv,
					    uintarg);
					break;
				case TCL_ENV_LOCK_MAX_OBJECTS:
					ret = dbenv->set_lk_max_objects(dbenv,
					    uintarg);
					break;
				case TCL_ENV_LOCK_PARTITIONS:
					ret = dbenv->set_lk_partitions(dbenv,
					    uintarg);
					break;
				case TCL_ENV_LOCK_TABLESIZE:
					ret = dbenv->set_lk_tablesize(dbenv,
					    uintarg);
					break;
				default:
					break;
				}
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "lock_max");
			}
			break;
		case TCL_ENV_MUTSETALIGN:
		case TCL_ENV_MUTSETINCR:
		case TCL_ENV_MUTSETINIT:
		case TCL_ENV_MUTSETMAX:
		case TCL_ENV_MUTSETTAS:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-mutex_set val");
				result = TCL_ERROR;
				break;
			}
			intarg = 0;
			switch ((enum envopen)optindex) {
			case TCL_ENV_MUTSETALIGN:
				intarg = DBTCL_MUT_ALIGN;
				break;
			case TCL_ENV_MUTSETINCR:
				intarg = DBTCL_MUT_INCR;
				break;
			case TCL_ENV_MUTSETINIT:
				intarg = DBTCL_MUT_INIT;
				break;
			case TCL_ENV_MUTSETMAX:
				intarg = DBTCL_MUT_MAX;
				break;
			case TCL_ENV_MUTSETTAS:
				intarg = DBTCL_MUT_TAS;
				break;
			default:
				break;
			}
			result = tcl_MutSet(interp, objv[i++], dbenv, intarg);
			break;
		case TCL_ENV_TXN_NOWAIT:
			FLD_SET(set_flags, DB_TXN_NOWAIT);
			break;
		case TCL_ENV_TXN_TIME:
		case TCL_ENV_TXN_TIMEOUT:
		case TCL_ENV_LOCK_TIMEOUT:
		case TCL_ENV_REG_TIMEOUT:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-xxx_timeout time?");
				result = TCL_ERROR;
				break;
			}

			if ((result = Tcl_GetLongFromObj(
			   interp, objv[i++], &v)) != TCL_OK)
				break;
			timestamp = (time_t)v;

			_debug_check();
			if ((enum envopen)optindex == TCL_ENV_TXN_TIME)
				ret =
				    dbenv->set_tx_timestamp(dbenv, &timestamp);
			else {
				if ((enum envopen)optindex ==
				    TCL_ENV_LOCK_TIMEOUT)
					time_flag = DB_SET_LOCK_TIMEOUT;
				else if ((enum envopen)optindex ==
				    TCL_ENV_REG_TIMEOUT)
					time_flag = DB_SET_REG_TIMEOUT;
				else
					time_flag = DB_SET_TXN_TIMEOUT;

				ret = dbenv->set_timeout(dbenv,
				    (db_timeout_t)timestamp, time_flag);
			}
			result = _ReturnSetup(interp, ret,
			    DB_RETOK_STD(ret), "txn_timestamp");
			break;
		case TCL_ENV_LOG:
			FLD_SET(open_flags, DB_INIT_LOG | DB_INIT_MPOOL);
			break;
		case TCL_ENV_LOG_BUFFER:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-log_buffer size?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_lg_bsize(dbenv, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "log_bsize");
				logbufset = 1;
				if (logmaxset) {
					_debug_check();
					ret = dbenv->set_lg_max(dbenv,
					    logmaxset);
					result = _ReturnSetup(interp, ret,
					    DB_RETOK_STD(ret), "log_max");
					logmaxset = 0;
					logbufset = 0;
				}
			}
			break;
		case TCL_ENV_LOG_FILEMODE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-log_filemode mode?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_lg_filemode(dbenv,
				    (int)uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "log_filemode");
			}
			break;
		case TCL_ENV_LOG_INMEMORY:
			ret =
			    dbenv->log_set_config(dbenv, DB_LOG_IN_MEMORY, 1);
			result = _ReturnSetup(interp, ret,
			    DB_RETOK_STD(ret), "log_inmemory");
			break;
		case TCL_ENV_LOG_MAX:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-log_max max?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK && logbufset) {
				_debug_check();
				ret = dbenv->set_lg_max(dbenv, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "log_max");
				logbufset = 0;
			} else
				logmaxset = uintarg;
			break;
		case TCL_ENV_LOG_REGIONMAX:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-log_regionmax size?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_lg_regionmax(dbenv, uintarg);
				result =
				    _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
					"log_regionmax");
			}
			break;
		case TCL_ENV_LOG_REMOVE:
			ret =
			    dbenv->log_set_config(dbenv, DB_LOG_AUTO_REMOVE, 1);
			result = _ReturnSetup(interp, ret,
			    DB_RETOK_STD(ret), "log_remove");
			break;
		case TCL_ENV_MEMORY_MAX:
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			if (myobjc != 2) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-memory_max {gbytes bytes}?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, myobjv[0], &gbytes);
			if (result != TCL_OK)
				break;
			result = _GetUInt32(interp, myobjv[1], &bytes);
			if (result != TCL_OK)
				break;
			_debug_check();
			ret = dbenv->set_memory_max(dbenv, gbytes, bytes);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "memory_max");
			break;
		case TCL_ENV_MPOOL_MAX_OPENFD:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-mpool_max_openfd fd_count?");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_mp_max_openfd(dbenv, intarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "mpool_max_openfd");
			}
			break;
		case TCL_ENV_MPOOL_MAX_WRITE:
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			if (myobjc != 2) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-mpool_max_write {nwrite nsleep}?");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, myobjv[0], &intarg);
			if (result != TCL_OK)
				break;
			result = Tcl_GetIntFromObj(interp, myobjv[1], &intarg2);
			if (result != TCL_OK)
				break;
			_debug_check();
			ret = dbenv->set_mp_max_write(
			    dbenv, intarg, (db_timeout_t)intarg2);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_mp_max_write");
			break;
		case TCL_ENV_MPOOL_MMAP_SIZE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-mpool_mmap_size size?");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_mp_mmapsize(dbenv,
				    (size_t)intarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "mpool_mmap_size");
			}
			break;
		case TCL_ENV_MPOOL_NOMMAP:
			FLD_SET(set_flags, DB_NOMMAP);
			break;
		case TCL_ENV_MULTIVERSION:
			FLD_SET(set_flags, DB_MULTIVERSION);
			break;
		case TCL_ENV_OVERWRITE:
			FLD_SET(set_flags, DB_OVERWRITE);
			break;
		case TCL_ENV_MUTEXCOUNT:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-mp_mutex_count count?");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_mp_mtxcount(dbenv,
				    (u_int32_t)intarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "mutex count");
			}
			break;
		case TCL_ENV_PAGESIZE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-pagesize size?");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_mp_pagesize(dbenv,
				    (u_int32_t)intarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "pagesize");
			}
			break;
		case TCL_ENV_TABLESIZE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-tablesize size?");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_mp_tablesize(dbenv,
				    (u_int32_t)intarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "tablesize");
			}
			break;
		case TCL_ENV_REGISTER:
			FLD_SET(open_flags, DB_REGISTER);
			break;
		case TCL_ENV_REGION_INIT:
			_debug_check();
			ret = dbenv->set_flags(dbenv, DB_REGION_INIT, 1);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "region_init");
			break;
		case TCL_ENV_SET_INTERMEDIATE_DIR:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-set_intermediate_dir_mode mode?");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			ret = dbenv->set_intermediate_dir_mode(dbenv, arg);
			result = _ReturnSetup(interp, ret,
			    DB_RETOK_STD(ret), "set_intermediate_dir_mode");
			break;
		case TCL_ENV_REP:
			FLD_SET(open_flags, DB_INIT_REP);
			break;
		case TCL_ENV_REP_CLIENT:
			rep_flags = DB_REP_CLIENT;
			FLD_SET(open_flags, DB_INIT_REP);
			break;
		case TCL_ENV_REP_MASTER:
			rep_flags = DB_REP_MASTER;
			FLD_SET(open_flags, DB_INIT_REP);
			break;
		case TCL_ENV_REP_NSITES:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-rep_nsites nsites?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->rep_set_nsites(dbenv, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "rep_set_nsites");
			}
			if (result == TCL_OK)
				FLD_SET(open_flags, DB_INIT_REP);
			break;
		case TCL_ENV_REP_INMEM_FILES:
			result = tcl_RepInmemFiles(interp,dbenv);
			if (result == TCL_OK)
				FLD_SET(open_flags, DB_INIT_REP);
			break;
		case TCL_ENV_REP_LEASE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-rep_lease {timeout clockskew}");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			result = tcl_RepLease(interp, myobjc, myobjv, dbenv);
			if (result == TCL_OK)
				FLD_SET(open_flags, DB_INIT_REP);
			break;
		case TCL_ENV_REP_TRANSPORT:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-rep_transport {envid sendproc}");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			result = tcl_RepTransport(
			    interp, myobjc, myobjv, dbenv, ip);
			if (result == TCL_OK)
				FLD_SET(open_flags, DB_INIT_REP);
			break;
		case TCL_ENV_SNAPSHOT:
			FLD_SET(set_flags, DB_TXN_SNAPSHOT);
			break;
		case TCL_ENV_THREAD:
			/* Enable DB_THREAD when specified in testing. */
			FLD_SET(open_flags, DB_THREAD);
			break;
		case TCL_ENV_THREAD_COUNT:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-thread_count count?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_thread_count(dbenv, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_thread_count");
			}
			break;
		case TCL_ENV_TIME_NOTGRANTED:
			FLD_SET(set_flags, DB_TIME_NOTGRANTED);
			break;
		case TCL_ENV_VERBOSE:
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			if (myobjc != 2) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-verbose {which on|off}?");
				result = TCL_ERROR;
				break;
			}
			result = tcl_EnvVerbose(
			    interp, dbenv, myobjv[0], myobjv[1]);
			break;
		case TCL_ENV_WRNOSYNC:
			FLD_SET(set_flags, DB_TXN_WRITE_NOSYNC);
			break;
		case TCL_ENV_ZEROLOG:
			if ((ret =
			    dbenv->log_set_config(dbenv, DB_LOG_ZERO, 1)) != 0)
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "set_log_config");
			break;
#endif
		case TCL_ENV_TXN:
			FLD_SET(open_flags, DB_INIT_LOCK |
			    DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN);
			/* Make sure we have an arg to check against! */
			while (i < objc) {
				arg = Tcl_GetStringFromObj(objv[i], NULL);
				if (strcmp(arg, "nosync") == 0) {
					FLD_SET(set_flags, DB_TXN_NOSYNC);
					i++;
				} else if (strcmp(arg, "snapshot") == 0) {
					FLD_SET(set_flags, DB_TXN_SNAPSHOT);
					i++;
				} else if (strcmp(arg, "wrnosync") == 0) {
					FLD_SET(set_flags, DB_TXN_WRITE_NOSYNC);
					i++;
				} else
					break;
			}
			break;
		case TCL_ENV_CREATE:
			FLD_SET(open_flags, DB_CREATE | DB_INIT_MPOOL);
			break;
		case TCL_ENV_ENCRYPT_AES:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-encryptaes passwd?");
				result = TCL_ERROR;
				break;
			}
			passwd = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			ret = dbenv->set_encrypt(dbenv, passwd, DB_ENCRYPT_AES);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_encrypt");
			break;
		case TCL_ENV_ENCRYPT_ANY:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-encryptany passwd?");
				result = TCL_ERROR;
				break;
			}
			passwd = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			ret = dbenv->set_encrypt(dbenv, passwd, 0);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_encrypt");
			break;
		case TCL_ENV_HOME:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-home dir?");
				result = TCL_ERROR;
				break;
			}
			home = Tcl_GetStringFromObj(objv[i++], NULL);
			break;
		case TCL_ENV_MODE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-mode mode?");
				result = TCL_ERROR;
				break;
			}
			/*
			 * Don't need to check result here because
			 * if TCL_ERROR, the error message is already
			 * set up, and we'll bail out below.  If ok,
			 * the mode is set and we go on.
			 */
			result = Tcl_GetIntFromObj(interp, objv[i++], &mode);
			break;
		case TCL_ENV_PRIVATE:
			FLD_SET(open_flags, DB_PRIVATE | DB_INIT_MPOOL);
			break;
		case TCL_ENV_RECOVER:
			FLD_SET(open_flags, DB_RECOVER);
			break;
		case TCL_ENV_RECOVER_FATAL:
			FLD_SET(open_flags, DB_RECOVER_FATAL);
			break;
		case TCL_ENV_SYSTEM_MEM:
			FLD_SET(open_flags, DB_SYSTEM_MEM);
			break;
		case TCL_ENV_USE_ENVIRON_ROOT:
			FLD_SET(open_flags, DB_USE_ENVIRON_ROOT);
			break;
		case TCL_ENV_USE_ENVIRON:
			FLD_SET(open_flags, DB_USE_ENVIRON);
			break;
		case TCL_ENV_CACHESIZE:
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			if (myobjc != 3) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-cachesize {gbytes bytes ncaches}?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, myobjv[0], &gbytes);
			if (result != TCL_OK)
				break;
			result = _GetUInt32(interp, myobjv[1], &bytes);
			if (result != TCL_OK)
				break;
			result = Tcl_GetIntFromObj(interp, myobjv[2], &ncaches);
			if (result != TCL_OK)
				break;
			_debug_check();
			ret = dbenv->set_cachesize(dbenv, gbytes, bytes,
			    ncaches);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_cachesize");
			break;
		case TCL_ENV_CACHE_MAX:
			result = Tcl_ListObjGetElements(interp, objv[i],
			    &myobjc, &myobjv);
			if (result == TCL_OK)
				i++;
			else
				break;
			if (myobjc != 2) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-cache_max {gbytes bytes}?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, myobjv[0], &gbytes);
			if (result != TCL_OK)
				break;
			result = _GetUInt32(interp, myobjv[1], &bytes);
			if (result != TCL_OK)
				break;
			_debug_check();
			ret = dbenv->set_cache_max(dbenv, gbytes, bytes);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_cache_max");
			break;
		case TCL_ENV_SHM_KEY:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-shm_key key?");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetLongFromObj(interp, objv[i++], &shm);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_shm_key(dbenv, shm);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "shm_key");
			}
			break;
		case TCL_ENV_TXN_INIT:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-txn_init init?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_memory_init(dbenv,
				    DB_MEM_TRANSACTION, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "txn_init");
			}
			break;
		case TCL_ENV_TXN_MAX:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-txn_max max?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = dbenv->set_tx_max(dbenv, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "txn_max");
			}
			break;
		case TCL_ENV_ERRFILE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-errfile file");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			tcl_EnvSetErrfile(interp, dbenv, ip, arg);
			break;
		case TCL_ENV_MSGFILE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-msgfile file");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			tcl_EnvSetMsgfile(interp, dbenv, ip, arg);
			break;			
		case TCL_ENV_ERRPFX:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-errpfx prefix");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			result = tcl_EnvSetErrpfx(interp, dbenv, ip, arg);
			break;
		case TCL_ENV_DATA_DIR:
		case TCL_ENV_ADD_DIR:
		case TCL_ENV_CREATE_DIR:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-xxx_dir dir");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			switch ((enum envopen)optindex) {
			case TCL_ENV_DATA_DIR:
				ret = dbenv->set_data_dir(dbenv, arg);
				break;
			case TCL_ENV_ADD_DIR:
				ret = dbenv->add_data_dir(dbenv, arg);
				break;
			case TCL_ENV_CREATE_DIR:
				ret = dbenv->set_create_dir(dbenv, arg);
				break;
			default:
				break;
			}
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "xxx_dir");
			break;
		case TCL_ENV_LOG_DIR:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-log_dir dir");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			ret = dbenv->set_lg_dir(dbenv, arg);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_lg_dir");
			break;
		case TCL_ENV_METADATA_DIR:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-metadata_dir dir");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			ret = dbenv->set_metadata_dir(dbenv, arg);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_metadata_dir");
			break;
		case TCL_ENV_TMP_DIR:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-tmp_dir dir");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			ret = dbenv->set_tmp_dir(dbenv, arg);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_tmp_dir");
			break;
		}
		/*
		 * If, at any time, parsing the args we get an error,
		 * bail out and return.
		 */
		if (result != TCL_OK)
			goto error;
	}

	/*
	 * We have to check this here.  We want to set the log buffer
	 * size first, if it is specified.  So if the user did so,
	 * then we took care of it above.  But, if we get out here and
	 * logmaxset is non-zero, then they set the log_max without
	 * resetting the log buffer size, so we now have to do the
	 * call to set_lg_max, since we didn't do it above.
	 */
	if (logmaxset) {
		_debug_check();
		ret = dbenv->set_lg_max(dbenv, (u_int32_t)logmaxset);
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "log_max");
	}

	if (result != TCL_OK)
		goto error;

	if (set_flags) {
		ret = dbenv->set_flags(dbenv, set_flags, 1);
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "set_flags");
		if (result == TCL_ERROR)
			goto error;
		/*
		 * If we are successful, clear the result so that the
		 * return from set_flags isn't part of the result.
		 */
		Tcl_ResetResult(interp);
	}
	/*
	 * When we get here, we have already parsed all of our args
	 * and made all our calls to set up the environment.  Everything
	 * is okay so far, no errors, if we get here.
	 *
	 * Now open the environment.
	 */
	_debug_check();
	ret = dbenv->open(dbenv, home, open_flags, mode);
	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "dbenv open");

	if (ip->i_event_info != NULL && result == TCL_OK) {
		_debug_check();
		ret = __mutex_alloc(dbenv->env, MTX_TCL_EVENTS,
		    DB_MUTEX_PROCESS_ONLY, &ip->i_mutex);
		result = _ReturnSetup(interp,
		    ret, DB_RETOK_STD(ret), "__mutex_alloc");
	}

	if (rep_flags != 0 && result == TCL_OK) {
		_debug_check();
		ret = dbenv->rep_start(dbenv, NULL, rep_flags);
		result = _ReturnSetup(interp,
		    ret, DB_RETOK_STD(ret), "rep_start");
	}

error:	if (result == TCL_ERROR) {
		if (ip->i_err && ip->i_err != stdout && ip->i_err != stderr) {
			(void)fclose(ip->i_err);
			ip->i_err = NULL;
		}
		if (ip->i_msg && ip->i_msg != stdout && ip->i_msg != stderr) {
			(void)fclose(ip->i_msg);
			ip->i_msg = NULL;
		}
		(void)__mutex_free(dbenv->env, &ip->i_mutex);
		(void)dbenv->close(dbenv, 0);
	}
	return (result);
}

/*
 * bdb_DbOpen --
 *	Implements the "db_create/db_open" command.
 *	There are many, many options to the open command.
 *	Here is the general flow:
 *
 *	0.  Preparse args to determine if we have -env.
 *	1.  Call db_create to create the db handle.
 *	2.  Parse args tracking options.
 *	3.  Make any pre-open setup calls necessary.
 *	4.  Call DB->open to open the database.
 *	5.  Return db widget handle to user.
 */
static int
bdb_DbOpen(interp, objc, objv, ip, dbp)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DBTCL_INFO *ip;			/* Our internal info */
	DB **dbp;			/* DB handle */
{
	static const char *bdbenvopen[] = {
		"-env",	NULL
	};
	enum bdbenvopen {
		TCL_DB_ENV0
	};
	static const char *bdbopen[] = {
#ifdef CONFIG_TEST
		"-btcompare",
		"-dupcompare",
		"-hashcompare",
		"-hashproc",
		"-lorder",
		"-minkey",
		"-nommap",
		"-notdurable",
		"-partition",
		"-partition_dirs",
		"-partition_callback",
		"-read_uncommitted",
		"-revsplitoff",
		"-test",
		"-thread",
#endif
		"-auto_commit",
		"-btree",
		"-cachesize",
		"-chksum",
		"-compress",
		"-create",
		"-create_dir",
		"-delim",
		"-dup",
		"-dupsort",
		"-encrypt",
		"-encryptaes",
		"-encryptany",
		"-env",
		"-errfile",
		"-errpfx",
		"-excl",
		"-extent",
		"-ffactor",
		"-hash",
		"-heap",
		"-heap_regionsize",
		"-inorder",
		"-len",
		"-lk_exclusive",
		"-maxsize",
		"-mode",
		"-msgfile",
		"-multiversion",
		"-nelem",
		"-pad",
		"-pagesize",
		"-queue",
		"-rdonly",
		"-recno",
		"-recnum",
		"-renumber",
		"-snapshot",
		"-source",
		"-truncate",
		"-txn",
		"-unknown",
		"--",
		NULL
	};
	enum bdbopen {
#ifdef CONFIG_TEST
		TCL_DB_BTCOMPARE,
		TCL_DB_DUPCOMPARE,
		TCL_DB_HASHCOMPARE,
		TCL_DB_HASHPROC,
		TCL_DB_LORDER,
		TCL_DB_MINKEY,
		TCL_DB_NOMMAP,
		TCL_DB_NOTDURABLE,
		TCL_DB_PARTITION,
		TCL_DB_PART_DIRS,
		TCL_DB_PART_CALLBACK,
		TCL_DB_READ_UNCOMMITTED,
		TCL_DB_REVSPLIT,
		TCL_DB_TEST,
		TCL_DB_THREAD,
#endif
		TCL_DB_AUTO_COMMIT,
		TCL_DB_BTREE,
		TCL_DB_CACHESIZE,
		TCL_DB_CHKSUM,
		TCL_DB_COMPRESS,
		TCL_DB_CREATE,
		TCL_DB_CREATE_DIR,
		TCL_DB_DELIM,
		TCL_DB_DUP,
		TCL_DB_DUPSORT,
		TCL_DB_ENCRYPT,
		TCL_DB_ENCRYPT_AES,
		TCL_DB_ENCRYPT_ANY,
		TCL_DB_ENV,
		TCL_DB_ERRFILE,
		TCL_DB_ERRPFX,
		TCL_DB_EXCL,
		TCL_DB_EXTENT,
		TCL_DB_FFACTOR,
		TCL_DB_HASH,
		TCL_DB_HEAP,
		TCL_DB_HEAP_REGIONSIZE,
		TCL_DB_INORDER,
		TCL_DB_LEN,
		TCL_DB_LK_EXCLUSIVE,
		TCL_DB_MAXSIZE,
		TCL_DB_MODE,
		TCL_DB_MSGFILE,
		TCL_DB_MULTIVERSION,
		TCL_DB_NELEM,
		TCL_DB_PAD,
		TCL_DB_PAGESIZE,
		TCL_DB_QUEUE,
		TCL_DB_RDONLY,
		TCL_DB_RECNO,
		TCL_DB_RECNUM,
		TCL_DB_RENUMBER,
		TCL_DB_SNAPSHOT,
		TCL_DB_SOURCE,
		TCL_DB_TRUNCATE,
		TCL_DB_TXN,
		TCL_DB_UNKNOWN,
		TCL_DB_ENDARG
	};
	DBT *keys;
	DBTCL_INFO *envip, *errip;
	DBTYPE type;
	DB_ENV *dbenv;
	DB_TXN *txn;
	DB *hrdbp, *hsdbp;
	ENV *env;

	Tcl_Obj **myobjv;
	u_int32_t gbytes, bytes, open_flags, set_flags, uintarg;
	int endarg, encenble, i, intarg, mode, myobjc, ncaches, excl, nowait;
	int optindex, result, ret, set_err, set_msg, set_pfx, subdblen;
	u_char *subdbtmp;
	char *arg, *db, *dbr, *passwd, *subdb, *subdbr, msg[MSG_SIZE];
	size_t nlen;

	type = DB_UNKNOWN;
	endarg = encenble = mode = nlen = set_err = set_msg = set_flags = 0;
	set_pfx = 0;
	result = TCL_OK;
	subdbtmp = NULL;
	keys = NULL;
	db = dbr = passwd = subdb = subdbr = NULL;
	hrdbp = hsdbp = NULL;

	/*
	 * XXX
	 * If/when our Tcl interface becomes thread-safe, we should enable
	 * DB_THREAD here in all cases.  For now, we turn it on later in this
	 * function, and only when we're in testing and we specify the
	 * -thread flag, so that we can exercise MUTEX_THREAD_LOCK cases.
	 *
	 * In order to become truly thread-safe, we need to look at making sure
	 * DBTCL_INFO structs are safe to share across threads (they're not
	 * mutex-protected) before we declare the Tcl interface thread-safe.
	 * Meanwhile, there's no strong reason to enable DB_THREAD when not
	 * testing.
	 */
	open_flags = 0;

	dbenv = NULL;
	txn = NULL;
	env = NULL;

	if (objc < 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "?args?");
		return (TCL_ERROR);
	}

	/*
	 * We must first parse for the environment flag, since that
	 * is needed for db_create.  Then create the db handle.
	 */
	i = 2;
	while (i < objc) {
		if (Tcl_GetIndexFromObj(interp, objv[i++], bdbenvopen,
		    "option", TCL_EXACT, &optindex) != TCL_OK) {
			/*
			 * Reset the result so we don't get
			 * an errant error message if there is another error.
			 */
			Tcl_ResetResult(interp);
			continue;
		}
		switch ((enum bdbenvopen)optindex) {
		case TCL_DB_ENV0:
			arg = Tcl_GetStringFromObj(objv[i], NULL);
			dbenv = NAME_TO_ENV(arg);
			if (dbenv == NULL) {
				Tcl_SetResult(interp,
				    "db open: illegal environment", TCL_STATIC);
				return (TCL_ERROR);
			}
		}
		break;
	}

	/*
	 * Create the db handle before parsing the args
	 * since we'll be modifying the database options as we parse.
	 */
	ret = db_create(dbp, dbenv, 0);
	if (ret)
		return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "db_create"));

	/* Hang our info pointer on the DB handle, so we can do callbacks. */
	(*dbp)->api_internal = ip;

	/*
	 * XXX
	 * Remove restriction if error handling not tied to env.
	 *
	 * The DB->set_err* functions overwrite the environment.  So, if
	 * we are using an env, don't overwrite it; if not using an env,
	 * then configure error handling.
	 */
	if (dbenv == NULL) {
		env = NULL;
		(*dbp)->set_errpfx((*dbp), ip->i_name);
		(*dbp)->set_errcall((*dbp), _ErrorFunc);
	} else
		env = dbenv->env;

	/*
	 * If we are using an env, we keep track of err info in the env's ip.
	 * Otherwise use the DB's ip.
	 */
	envip = _PtrToInfo(dbenv); /* XXX */
	if (envip)
		errip = envip;
	else
		errip = ip;

	/*
	 * Get the option name index from the object based on the args
	 * defined above.
	 */
	i = 2;
	while (i < objc) {
		Tcl_ResetResult(interp);
		if (Tcl_GetIndexFromObj(interp, objv[i], bdbopen, "option",
		    TCL_EXACT, &optindex) != TCL_OK) {
			arg = Tcl_GetStringFromObj(objv[i], NULL);
			if (arg[0] == '-') {
				result = IS_HELP(objv[i]);
				goto error;
			} else
				Tcl_ResetResult(interp);
			break;
		}
		i++;
		switch ((enum bdbopen)optindex) {
#ifdef CONFIG_TEST
		case TCL_DB_BTCOMPARE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-btcompare compareproc");
				result = TCL_ERROR;
				break;
			}

			/*
			 * Store the object containing the procedure name.
			 * We don't need to crack it out now--we'll want
			 * to bundle it up to pass into Tcl_EvalObjv anyway.
			 * Tcl's object refcounting will--I hope--take care
			 * of the memory management here.
			 */
			ip->i_compare = objv[i++];
			Tcl_IncrRefCount(ip->i_compare);
			_debug_check();
			ret = (*dbp)->set_bt_compare(*dbp, tcl_bt_compare);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_bt_compare");
			break;
		case TCL_DB_DUPCOMPARE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-dupcompare compareproc");
				result = TCL_ERROR;
				break;
			}

			/*
			 * Store the object containing the procedure name.
			 * See TCL_DB_BTCOMPARE.
			 */
			ip->i_dupcompare = objv[i++];
			Tcl_IncrRefCount(ip->i_dupcompare);
			_debug_check();
			ret = (*dbp)->set_dup_compare(*dbp, tcl_dup_compare);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_dup_compare");
			break;
		case TCL_DB_HASHCOMPARE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-hashcompare compareproc");
				result = TCL_ERROR;
				break;
			}

			/*
			 * Store the object containing the procedure name.
			 * We don't need to crack it out now--we'll want
			 * to bundle it up to pass into Tcl_EvalObjv anyway.
			 * Tcl's object refcounting will--I hope--take care
			 * of the memory management here.
			 */
			ip->i_compare = objv[i++];
			Tcl_IncrRefCount(ip->i_compare);
			_debug_check();
			ret = (*dbp)->set_h_compare(*dbp, tcl_bt_compare);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_h_compare");
			break;
		case TCL_DB_HASHPROC:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-hashproc hashproc");
				result = TCL_ERROR;
				break;
			}

			/*
			 * Store the object containing the procedure name.
			 * See TCL_DB_BTCOMPARE.
			 */
			ip->i_hashproc = objv[i++];
			Tcl_IncrRefCount(ip->i_hashproc);
			_debug_check();
			ret = (*dbp)->set_h_hash(*dbp, tcl_h_hash);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_h_hash");
			break;
		case TCL_DB_HEAP_REGIONSIZE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-heap_regionsize npages");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_heap_regionsize(*dbp, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_heap_regionsize");
			}
			break;
		case TCL_DB_LORDER:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-lorder 1234|4321");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_lorder(*dbp, intarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_lorder");
			}
			break;
		case TCL_DB_MINKEY:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-minkey minkey");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_bt_minkey(*dbp, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_bt_minkey");
			}
			break;
		case TCL_DB_NOMMAP:
			open_flags |= DB_NOMMAP;
			break;
		case TCL_DB_NOTDURABLE:
			set_flags |= DB_TXN_NOT_DURABLE;
			break;
		case TCL_DB_PART_CALLBACK:
			if (i + 1 >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-partition_callback  numparts callback");
				result = TCL_ERROR;
				break;
			}

			/*
			 * Store the object containing the procedure name.
			 * See TCL_DB_BTCOMPARE.
			 */
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result != TCL_OK)
				break;
			ip->i_part_callback = objv[i++];
			Tcl_IncrRefCount(ip->i_part_callback);
			_debug_check();
			ret = (*dbp)->set_partition(
			     *dbp, uintarg, NULL, tcl_part_callback);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_partition_callback");
			break;
		case TCL_DB_PART_DIRS:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-partition {dir list}");
				result = TCL_ERROR;
				break;
			}
			ret = tcl_set_partition_dirs(interp, *dbp, objv[i++]);
			result = _ReturnSetup(interp, ret,
			    DB_RETOK_STD(ret), "set_partition_dirs");
			break;
		case TCL_DB_PARTITION:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-partition {key list}");
				result = TCL_ERROR;
				break;
			}
			_debug_check();
			ret = tcl_set_partition_keys(interp,
			    *dbp, objv[i++], &keys);
			result = _ReturnSetup(interp, ret,
			    DB_RETOK_STD(ret), "set_partition_keys");
			break;
		case TCL_DB_READ_UNCOMMITTED:
			open_flags |= DB_READ_UNCOMMITTED;
			break;
		case TCL_DB_REVSPLIT:
			set_flags |= DB_REVSPLITOFF;
			break;
		case TCL_DB_TEST:
			ret = (*dbp)->set_h_hash(*dbp, __ham_test);
			result = _ReturnSetup(interp, ret,
			    DB_RETOK_STD(ret), "set_h_hash");
			break;
		case TCL_DB_THREAD:
			/* Enable DB_THREAD when specified in testing. */
			open_flags |= DB_THREAD;
			break;
#endif
		case TCL_DB_AUTO_COMMIT:
			open_flags |= DB_AUTO_COMMIT;
			break;
		case TCL_DB_ENV:
			/*
			 * Already parsed this, skip it and the env pointer.
			 */
			i++;
			continue;
		case TCL_DB_TXN:
			if (i > (objc - 1)) {
				Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			txn = NAME_TO_TXN(arg);
			if (txn == NULL) {
				snprintf(msg, MSG_SIZE,
				    "Open: Invalid txn: %s\n", arg);
				Tcl_SetResult(interp, msg, TCL_VOLATILE);
				result = TCL_ERROR;
			}
			break;
		case TCL_DB_BTREE:
			if (type != DB_UNKNOWN) {
				Tcl_SetResult(interp,
				    "Too many DB types specified", TCL_STATIC);
				result = TCL_ERROR;
				goto error;
			}
			type = DB_BTREE;
			break;
		case TCL_DB_HASH:
			if (type != DB_UNKNOWN) {
				Tcl_SetResult(interp,
				    "Too many DB types specified", TCL_STATIC);
				result = TCL_ERROR;
				goto error;
			}
			type = DB_HASH;
			break;
		case TCL_DB_HEAP:
			if (type != DB_UNKNOWN) {
				Tcl_SetResult(interp,
				    "Too many DB types specified", TCL_STATIC);
				result = TCL_ERROR;
				goto error;
			}
			type = DB_HEAP;
			break;
		case TCL_DB_RECNO:
			if (type != DB_UNKNOWN) {
				Tcl_SetResult(interp,
				    "Too many DB types specified", TCL_STATIC);
				result = TCL_ERROR;
				goto error;
			}
			type = DB_RECNO;
			break;
		case TCL_DB_QUEUE:
			if (type != DB_UNKNOWN) {
				Tcl_SetResult(interp,
				    "Too many DB types specified", TCL_STATIC);
				result = TCL_ERROR;
				goto error;
			}
			type = DB_QUEUE;
			break;
		case TCL_DB_UNKNOWN:
			if (type != DB_UNKNOWN) {
				Tcl_SetResult(interp,
				    "Too many DB types specified", TCL_STATIC);
				result = TCL_ERROR;
				goto error;
			}
			break;
		case TCL_DB_CREATE:
			open_flags |= DB_CREATE;
			break;
		case TCL_DB_CREATE_DIR:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-create_dir dir");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			ret = (*dbp)->set_create_dir(*dbp, arg);
			result = _ReturnSetup(interp, ret,
			    DB_RETOK_STD(ret), "set_create_dir");
			break;
		case TCL_DB_EXCL:
			open_flags |= DB_EXCL;
			break;
		case TCL_DB_RDONLY:
			open_flags |= DB_RDONLY;
			break;
		case TCL_DB_TRUNCATE:
			open_flags |= DB_TRUNCATE;
			break;
		case TCL_DB_MODE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-mode mode?");
				result = TCL_ERROR;
				break;
			}
			/*
			 * Don't need to check result here because
			 * if TCL_ERROR, the error message is already
			 * set up, and we'll bail out below.  If ok,
			 * the mode is set and we go on.
			 */
			result = Tcl_GetIntFromObj(interp, objv[i++], &mode);
			break;
		case TCL_DB_DUP:
			set_flags |= DB_DUP;
			break;
		case TCL_DB_DUPSORT:
			set_flags |= DB_DUPSORT;
			break;
		case TCL_DB_INORDER:
			set_flags |= DB_INORDER;
			break;
		case TCL_DB_RECNUM:
			set_flags |= DB_RECNUM;
			break;
		case TCL_DB_RENUMBER:
			set_flags |= DB_RENUMBER;
			break;
		case TCL_DB_SNAPSHOT:
			set_flags |= DB_SNAPSHOT;
			break;
		case TCL_DB_CHKSUM:
			set_flags |= DB_CHKSUM;
			break;
		case TCL_DB_ENCRYPT:
			set_flags |= DB_ENCRYPT;
			break;
		case TCL_DB_ENCRYPT_AES:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-encryptaes passwd?");
				result = TCL_ERROR;
				break;
			}
			passwd = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			ret = (*dbp)->set_encrypt(*dbp, passwd, DB_ENCRYPT_AES);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_encrypt");
			encenble = 1;
			break;
		case TCL_DB_ENCRYPT_ANY:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-encryptany passwd?");
				result = TCL_ERROR;
				break;
			}
			passwd = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			ret = (*dbp)->set_encrypt(*dbp, passwd, 0);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_encrypt");
			encenble = 2;
			break;
		case TCL_DB_COMPRESS:
			ret = (*dbp)->set_bt_compress(*dbp, 0, 0);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_bt_compress");
			break;
		case TCL_DB_FFACTOR:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-ffactor density");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_h_ffactor(*dbp, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_h_ffactor");
			}
			break;
		case TCL_DB_MULTIVERSION:
			open_flags |= DB_MULTIVERSION;
			break;
		case TCL_DB_NELEM:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-nelem nelem");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_h_nelem(*dbp, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_h_nelem");
			}
			break;
		case TCL_DB_DELIM:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-delim delim");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_re_delim(*dbp, intarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_re_delim");
			}
			break;
		case TCL_DB_LEN:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-len length");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_re_len(*dbp, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_re_len");
			}
			break;
		case TCL_DB_LK_EXCLUSIVE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-lk_exclusive nowait");
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_lk_exclusive(*dbp, intarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_lk_exclusive");
			}
			break;
		case TCL_DB_MAXSIZE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-len length");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->mpf->set_maxsize(
				    (*dbp)->mpf, 0, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_maxsize");
			}
			break;
		case TCL_DB_PAD:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-pad pad");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_re_pad(*dbp, intarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_re_pad");
			}
			break;
		case TCL_DB_SOURCE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-source file");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			_debug_check();
			ret = (*dbp)->set_re_source(*dbp, arg);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_re_source");
			break;
		case TCL_DB_EXTENT:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-extent size");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, objv[i++], &uintarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_q_extentsize(*dbp, uintarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set_q_extentsize");
			}
			break;
		case TCL_DB_CACHESIZE:
			result = Tcl_ListObjGetElements(interp, objv[i++],
			    &myobjc, &myobjv);
			if (result != TCL_OK)
				break;
			if (myobjc != 3) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-cachesize {gbytes bytes ncaches}?");
				result = TCL_ERROR;
				break;
			}
			result = _GetUInt32(interp, myobjv[0], &gbytes);
			if (result != TCL_OK)
				break;
			result = _GetUInt32(interp, myobjv[1], &bytes);
			if (result != TCL_OK)
				break;
			result = Tcl_GetIntFromObj(interp, myobjv[2], &ncaches);
			if (result != TCL_OK)
				break;
			_debug_check();
			ret = (*dbp)->set_cachesize(*dbp, gbytes, bytes,
			    ncaches);
			result = _ReturnSetup(interp, ret,
			    DB_RETOK_STD(ret), "set_cachesize");
			break;
		case TCL_DB_PAGESIZE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-pagesize size?");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
			if (result == TCL_OK) {
				_debug_check();
				ret = (*dbp)->set_pagesize(*dbp,
				    (size_t)intarg);
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "set pagesize");
			}
			break;
		case TCL_DB_ERRFILE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-errfile file");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			/*
			 * If the user already set one, close it.
			 */
			if (errip->i_err != NULL &&
			    errip->i_err != stdout && errip->i_err != stderr)
				(void)fclose(errip->i_err);
			if (strcmp(arg, "/dev/stdout") == 0)
				errip->i_err = stdout;
			else if (strcmp(arg, "/dev/stderr") == 0)
				errip->i_err = stderr;
			else
				errip->i_err = fopen(arg, "a");
			if (errip->i_err != NULL) {
				_debug_check();
				(*dbp)->set_errfile(*dbp, errip->i_err);
				set_err = 1;
			}
			break;
		case TCL_DB_MSGFILE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-msgfile file");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			/*
			 * If the user already set one, close it.
			 */
			if (errip->i_msg != NULL &&
			    errip->i_msg != stdout && errip->i_msg != stderr)
				(void)fclose(errip->i_msg);
			if (strcmp(arg, "/dev/stdout") == 0)
				errip->i_msg = stdout;
			else if (strcmp(arg, "/dev/stderr") == 0)
				errip->i_msg = stderr;
			else
				errip->i_msg = fopen(arg, "a");
			if (errip->i_msg != NULL) {
				_debug_check();
				(*dbp)->set_msgfile(*dbp, errip->i_msg);
				set_msg = 1;
			}
			break;			
		case TCL_DB_ERRPFX:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-errpfx prefix");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			/*
			 * If the user already set one, free it.
			 */
			if (errip->i_errpfx != NULL) {
				(*dbp)->set_errpfx(*dbp, NULL);
				__os_free(NULL, errip->i_errpfx);
			}
			if ((ret = __os_strdup((*dbp)->env,
			    arg, &errip->i_errpfx)) != 0) {
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "__os_strdup");
				break;
			}
			if (errip->i_errpfx != NULL) {
				_debug_check();
				(*dbp)->set_errpfx(*dbp, errip->i_errpfx);
				set_pfx = 1;
			}
			break;
		case TCL_DB_ENDARG:
			endarg = 1;
			break;
		} /* switch */

		/*
		 * If, at any time, parsing the args we get an error,
		 * bail out and return.
		 */
		if (result != TCL_OK)
			goto error;
		if (endarg)
			break;
	}
	if (result != TCL_OK)
		goto error;

	/*
	 * Any args we have left, (better be 0, 1 or 2 left) are
	 * file names.  If we have 0, then an in-memory db.  If
	 * there is 1, a db name, if 2 a db and subdb name.
	 */
	if (i != objc) {
		/*
		 * Dbs must be NULL terminated file names, but subdbs can
		 * be anything.  Use Strings for the db name and byte
		 * arrays for the subdb.
		 */
		db = Tcl_GetStringFromObj(objv[i++], NULL);
		if (strcmp(db, "") == 0)
			db = NULL;
		if (i != objc) {
			subdbtmp =
			    Tcl_GetByteArrayFromObj(objv[i++], &subdblen);
			if ((ret = __os_malloc(env,
			   (size_t)subdblen + 1, &subdb)) != 0) {
				Tcl_SetResult(interp, db_strerror(ret),
				    TCL_STATIC);
				return (0);
			}
			memcpy(subdb, subdbtmp, (size_t)subdblen);
			subdb[subdblen] = '\0';
		}
	}
	if (set_flags) {
		ret = (*dbp)->set_flags(*dbp, set_flags);
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "set_flags");
		if (result == TCL_ERROR)
			goto error;
		/*
		 * If we are successful, clear the result so that the
		 * return from set_flags isn't part of the result.
		 */
		Tcl_ResetResult(interp);
	}

	/*
	 * When we get here, we have already parsed all of our args and made
	 * all our calls to set up the database.  Everything is okay so far,
	 * no errors, if we get here.
	 */
	_debug_check();

	/* Open the database. */
	ret = (*dbp)->open(*dbp, txn, db, subdb, type, open_flags, mode);
	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db open");
	/* 
	 * We may have been cleanly opened with DB_UNKNOWN, and we need to know
	 * if we opened a heap.
	 */
	if (ret == 0)
		(void)(*dbp)->get_type(*dbp, &type);
	
	/* 
	 * If we cleanly created a heap database, then we need to initiate the
	 * db handle for the auxiliary recno db.   To have heap work like
	 * the other record based am, we pass along record numbers.  The
	 * recno db maps the record number to the actual record id used
	 * by the heap am.   This is all done underneath the covers.  
	 */
	if (ret == 0 && type == DB_HEAP) {
		/* Setup recno db mapping recno to RID. */
		ret = db_create(&hrdbp, dbenv, 0);
		if (ret)
			return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "db_create heap recno db"));
		/* Set up file name by appending a 1 and open db */
		if (db != NULL) {
			nlen = strlen(db);
			if ((ret = __os_malloc(env, nlen + 2, &dbr)) != 0) {
				Tcl_SetResult(interp, db_strerror(ret),
				    TCL_STATIC);
				return (0);
			}
			memcpy(dbr, db, nlen);
			dbr[nlen] = '1';
			dbr[nlen+1] = '\0';
		}
		if (subdb != NULL) {
			nlen = strlen(subdb);
			if ((ret = __os_malloc(env, nlen + 2, &subdbr)) != 0) {
				Tcl_SetResult(interp, db_strerror(ret),
				    TCL_STATIC);
				return (0);
			}
			memcpy(subdbr, subdb, nlen);
			subdbr[nlen] = '1';
			subdbr[nlen+1] = '\0';
		}
			
		/* 
		 * Use same flags as heap, note: heap does not use of 
		 * DB_AFTER/DB_BEFORE on cursor puts, but recno can.
		 * Since we use the same set flags, use of DB_RENUMBER gets
		 * caught above when heap db is created and heap create
		 * fails.  So we never get this far.
		 */
		if (set_flags) {
			ret = hrdbp->set_flags(hrdbp, set_flags);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    	    "set_flags");
			if (result == TCL_ERROR)
				goto error;
			/*
		 	 * If we are successful, clear the result so that the
		 	 * return from set_flags isn't part of the result.
		 	 */
			Tcl_ResetResult(interp);
		}
		/* set up encryption if needed */
		if (encenble && passwd != NULL) {
			if (encenble == 1)
				ret = hrdbp->set_encrypt(hrdbp, 
				    passwd, DB_ENCRYPT_AES);
			else
				ret = hrdbp->set_encrypt(hrdbp, passwd, 0);
		}
		(void)(*dbp)->get_lk_exclusive((*dbp), &excl, &nowait);
		if (excl) 
			(void)hrdbp->set_lk_exclusive(hrdbp, nowait);
		ret = hrdbp->open(hrdbp, 
		    txn, dbr, subdbr, DB_RECNO, open_flags, mode);
		if (ret) {
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), 
		   	    "db open");
			goto error;
		}

		/* Point heap's ip to recno db and do errcall (see above) */
		ip->hrdbp = hrdbp;
		if (dbenv == NULL)
			hrdbp->set_errcall(hrdbp, _ErrorFunc);

		/* Set up secondary db mapping RID to recno. */
		ret = db_create(&hsdbp, dbenv, 0);
		if (ret)
			return (_ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "db_create heap secondary db"));
		/* Set up file name by appending a 2 and open db */
		if (dbr != NULL)
			dbr[nlen] = '2';
		if (subdbr != NULL)
			subdbr[nlen] = '2';
			
		/* 
		 * Use same flags as heap, note: heap does not use of 
		 * DB_AFTER/DB_BEFORE on cursor puts, but recno can.
		 * Since we use the same set flags, use of DB_RENUMBER gets
		 * caught above when heap db is created and heap create
		 * fails.  So we never get this far.
		 */
		if (set_flags) {
			ret = hsdbp->set_flags(hsdbp, set_flags);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    	    "set_flags");
			if (result == TCL_ERROR)
				goto error;
			/*
		 	 * If we are successful, clear the result so that the
		 	 * return from set_flags isn't part of the result.
		 	 */
			Tcl_ResetResult(interp);
		}
		/* set up encryption if needed */
		if (encenble) {
			if (encenble == 1)
				ret = hsdbp->set_encrypt(hsdbp, 
				    passwd, DB_ENCRYPT_AES);
			else
				ret = hsdbp->set_encrypt(hsdbp, passwd, 0);
		}
		if (excl) 
			(void)hsdbp->set_lk_exclusive(hsdbp, nowait);
		ret = hsdbp->open(hsdbp, 
		    txn, dbr, subdbr, DB_BTREE, open_flags, mode);
		if (ret) {
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), 
		   	    "db open");
			goto error;
		}

		ret = hrdbp->associate(hrdbp, txn, hsdbp, heap_callback, 0);
		if (ret) {
			result = _ReturnSetup(
			    interp, ret, DB_RETOK_STD(ret), "db associate");
			goto error;
		}

		/* Point heap's ip to recno db and do errcall (see above) */
		ip->hsdbp = hsdbp;
		if (dbenv == NULL)
			hrdbp->set_errcall(hsdbp, _ErrorFunc);
	}

error:
	if (keys != NULL)
		__os_free(NULL, keys);
	if (subdb)
		__os_free(env, subdb);
	if (subdbr != NULL)
		__os_free(env, subdbr);
	if (dbr)
		__os_free(env, dbr);
	if (result == TCL_ERROR) {
		if (set_pfx && errip && errip->i_errpfx != NULL) {
			(*dbp)->set_errpfx(*dbp, NULL);
			__os_free(env, errip->i_errpfx);
			errip->i_errpfx = NULL;
		}
		(void)(*dbp)->close(*dbp, 0);
		if (type == DB_HEAP) {
			if (hrdbp != NULL) {
				(void)hrdbp->close(hrdbp, 0);
				hrdbp = NULL;
			}
			if (hsdbp != NULL) {
				(void)hsdbp->close(hsdbp, 0);
				hsdbp = NULL;
			}
		}
		/*
		 * If we opened and set up the error file in the environment
		 * on this open, but we failed for some other reason, clean
		 * up and close the file.
		 *
		 * XXX when err stuff isn't tied to env, change to use ip,
		 * instead of envip.  Also, set_err is irrelevant when that
		 * happens.  It will just read:
		 * if (ip->i_err)
		 *	fclose(ip->i_err);
		 */
		if (set_err && errip && errip->i_err != NULL &&
		    errip->i_err != stdout && errip->i_err != stderr) {
			(void)fclose(errip->i_err);
			errip->i_err = NULL;
		}
		if (set_msg && errip && errip->i_msg != NULL &&
		    errip->i_msg != stdout && errip->i_msg != stderr) {
			(void)fclose(errip->i_msg);
			errip->i_msg = NULL;
		}		
		*dbp = NULL;
	}
	return (result);
}

#ifdef HAVE_64BIT_TYPES
/*
 * bdb_SeqOpen --
 *	Implements the "Seq_create/Seq_open" command.
 */
static int
bdb_SeqOpen(interp, objc, objv, ip, seqp)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DBTCL_INFO *ip;			/* Our internal info */
	DB_SEQUENCE **seqp;		/* DB_SEQUENCE handle */
{
	static const char *seqopen[] = {
		"-cachesize",
		"-create",
		"-inc",
		"-init",
		"-dec",
		"-max",
		"-min",
		"-thread",
		"-txn",
		"-wrap",
		"--",
		NULL
	} ;
	enum seqopen {
		TCL_SEQ_CACHESIZE,
		TCL_SEQ_CREATE,
		TCL_SEQ_INC,
		TCL_SEQ_INIT,
		TCL_SEQ_DEC,
		TCL_SEQ_MAX,
		TCL_SEQ_MIN,
		TCL_SEQ_THREAD,
		TCL_SEQ_TXN,
		TCL_SEQ_WRAP,
		TCL_SEQ_ENDARG
	};
	DB *dbp;
	DBT key;
	DBTYPE type;
	DB_TXN *txn;
	db_recno_t recno;
	db_seq_t min, max, value;
	Tcl_WideInt tcl_value;
	u_int32_t flags, oflags;
	int cache, endarg, i, optindex, result, ret, setrange, setvalue, v;
	char *arg, *db, msg[MSG_SIZE];

	COMPQUIET(ip, NULL);
	COMPQUIET(value, 0);
	*seqp = NULL;

	if (objc < 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "?args?");
		return (TCL_ERROR);
	}

	txn = NULL;
	endarg = 0;
	flags = oflags = 0;
	setrange = setvalue = 0;
	min = INT64_MIN;
	max = INT64_MAX;
	cache = 0;

	for (i = 2; i < objc;) {
		Tcl_ResetResult(interp);
		if (Tcl_GetIndexFromObj(interp, objv[i], seqopen, "option",
		    TCL_EXACT, &optindex) != TCL_OK) {
			arg = Tcl_GetStringFromObj(objv[i], NULL);
			if (arg[0] == '-') {
				result = IS_HELP(objv[i]);
				goto error;
			} else
				Tcl_ResetResult(interp);
			break;
		}
		i++;
		result = TCL_OK;
		switch ((enum seqopen)optindex) {
		case TCL_SEQ_CREATE:
			oflags |= DB_CREATE;
			break;
		case TCL_SEQ_INC:
			LF_SET(DB_SEQ_INC);
			break;
		case TCL_SEQ_CACHESIZE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-cachesize value?");
				result = TCL_ERROR;
				break;
			}
			result = Tcl_GetIntFromObj(interp, objv[i++], &cache);
			break;
		case TCL_SEQ_INIT:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-init value?");
				result = TCL_ERROR;
				break;
			}
			result =
			     Tcl_GetWideIntFromObj(
				 interp, objv[i++], &tcl_value);
			value = tcl_value;
			setvalue = 1;
			break;
		case TCL_SEQ_DEC:
			LF_SET(DB_SEQ_DEC);
			break;
		case TCL_SEQ_MAX:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-max value?");
				result = TCL_ERROR;
				break;
			}
			if ((result =
			     Tcl_GetWideIntFromObj(interp,
			     objv[i++], &tcl_value)) != TCL_OK)
				goto error;
			max = tcl_value;
			setrange = 1;
			break;
		case TCL_SEQ_MIN:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-min value?");
				result = TCL_ERROR;
				break;
			}
			if ((result =
			     Tcl_GetWideIntFromObj(interp,
			     objv[i++], &tcl_value)) != TCL_OK)
				goto error;
			min = tcl_value;
			setrange = 1;
			break;
		case TCL_SEQ_THREAD:
			oflags |= DB_THREAD;
			break;
		case TCL_SEQ_TXN:
			if (i > (objc - 1)) {
				Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			txn = NAME_TO_TXN(arg);
			if (txn == NULL) {
				snprintf(msg, MSG_SIZE,
				    "Sequence: Invalid txn: %s\n", arg);
				Tcl_SetResult(interp, msg, TCL_VOLATILE);
				result = TCL_ERROR;
			}
			break;
		case TCL_SEQ_WRAP:
			LF_SET(DB_SEQ_WRAP);
			break;
		case TCL_SEQ_ENDARG:
			endarg = 1;
			break;
		}
		/*
		 * If, at any time, parsing the args we get an error,
		 * bail out and return.
		 */
		if (result != TCL_OK)
			goto error;
		if (endarg)
			break;
	}

	if (objc - i != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "?args?");
		return (TCL_ERROR);
	}
	/*
	 * The db must be a string but the sequence key may
	 * be anything.
	 */
	db = Tcl_GetStringFromObj(objv[i++], NULL);
	if ((dbp = NAME_TO_DB(db)) == NULL) {
		Tcl_SetResult(interp, "No such dbp", TCL_STATIC);
		return (TCL_ERROR);
	}
	(void)dbp->get_type(dbp, &type);

	if (type == DB_QUEUE || type == DB_RECNO) {
		result = _GetUInt32(interp, objv[i++], &recno);
		if (result != TCL_OK)
			return (result);
		DB_INIT_DBT(key, &recno, sizeof(recno));
	} else
		DB_INIT_DBT(key, Tcl_GetByteArrayFromObj(objv[i++], &v), v);
	ret = db_sequence_create(seqp, dbp, 0);
	if ((result = _ReturnSetup(interp,
	    ret, DB_RETOK_STD(ret), "sequence create")) != TCL_OK) {
		*seqp = NULL;
		return (result);
	}

	ret = (*seqp)->set_flags(*seqp, flags);
	if ((result = _ReturnSetup(interp,
	    ret, DB_RETOK_STD(ret), "sequence set_flags")) != TCL_OK)
		goto error;
	if (setrange) {
		ret = (*seqp)->set_range(*seqp, min, max);
		if ((result = _ReturnSetup(interp,
		    ret, DB_RETOK_STD(ret), "sequence set_range")) != TCL_OK)
			goto error;
	}
	if (cache) {
		ret = (*seqp)->set_cachesize(*seqp, cache);
		if ((result = _ReturnSetup(interp,
		    ret, DB_RETOK_STD(ret), "sequence cachesize")) != TCL_OK)
			goto error;
	}
	if (setvalue) {
		ret = (*seqp)->initial_value(*seqp, value);
		if ((result = _ReturnSetup(interp,
		    ret, DB_RETOK_STD(ret), "sequence init")) != TCL_OK)
			goto error;
	}
	ret = (*seqp)->open(*seqp, txn, &key, oflags);
	if ((result = _ReturnSetup(interp,
	    ret, DB_RETOK_STD(ret), "sequence open")) != TCL_OK)
		goto error;

	if (0) {
error:		if (*seqp != NULL)
			(void)(*seqp)->close(*seqp, 0);
		*seqp = NULL;
	}
	return (result);
}
#endif

/*
 * bdb_DbRemove --
 *	Implements the DB_ENV->remove and DB->remove command.
 */
static int
bdb_DbRemove(interp, objc, objv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
{
	static const char *bdbrem[] = {
		"-auto_commit",
		"-encrypt",
		"-encryptaes",
		"-encryptany",
		"-env",
		"-fast",
		"-txn",
		"--",
		NULL
	};
	enum bdbrem {
		TCL_DBREM_AUTOCOMMIT,
		TCL_DBREM_ENCRYPT,
		TCL_DBREM_ENCRYPT_AES,
		TCL_DBREM_ENCRYPT_ANY,
		TCL_DBREM_ENV,
		TCL_DBREM_FAST,
		TCL_DBREM_TXN,
		TCL_DBREM_ENDARG
	};
	DB *dbp;
	DB_ENV *dbenv;
	DB_TXN *txn;
	ENV *env;
	u_int32_t enc_flag, iflags, set_flags;
	int endarg, i, optindex, result, ret, subdblen;
	u_char *subdbtmp;
	char *arg, *db, *dbr, msg[MSG_SIZE], *passwd, *subdb, *subdbr;
	size_t nlen;

	dbp = NULL;
	dbenv = NULL;
	txn = NULL;
	env = NULL;
	enc_flag = iflags = set_flags = subdblen = 0;
	endarg = nlen = 0;
	result = TCL_OK;
	subdbtmp = NULL;
	db = dbr = passwd = subdb = subdbr = NULL;

	if (objc < 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "?args? filename ?database?");
		return (TCL_ERROR);
	}

	/*
	 * We must first parse for the environment flag, since that
	 * is needed for db_create.  Then create the db handle.
	 */
	i = 2;
	while (i < objc) {
		if (Tcl_GetIndexFromObj(interp, objv[i], bdbrem,
		    "option", TCL_EXACT, &optindex) != TCL_OK) {
			arg = Tcl_GetStringFromObj(objv[i], NULL);
			if (arg[0] == '-') {
				result = IS_HELP(objv[i]);
				goto error;
			} else
				Tcl_ResetResult(interp);
			break;
		}
		i++;
		switch ((enum bdbrem)optindex) {
		case TCL_DBREM_AUTOCOMMIT:
			iflags |= DB_AUTO_COMMIT;
			_debug_check();
			break;
		case TCL_DBREM_ENCRYPT:
			set_flags |= DB_ENCRYPT;
			_debug_check();
			break;
		case TCL_DBREM_ENCRYPT_AES:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-encryptaes passwd?");
				result = TCL_ERROR;
				break;
			}
			passwd = Tcl_GetStringFromObj(objv[i++], NULL);
			enc_flag = DB_ENCRYPT_AES;
			break;
		case TCL_DBREM_ENCRYPT_ANY:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-encryptany passwd?");
				result = TCL_ERROR;
				break;
			}
			passwd = Tcl_GetStringFromObj(objv[i++], NULL);
			enc_flag = 0;
			break;
		case TCL_DBREM_ENV:
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			dbenv = NAME_TO_ENV(arg);
			if (dbenv == NULL) {
				Tcl_SetResult(interp,
				    "db remove: illegal environment",
				    TCL_STATIC);
				return (TCL_ERROR);
			}
			env = dbenv->env;
			break;
		case TCL_DBREM_ENDARG:
			endarg = 1;
			break;
		case TCL_DBREM_FAST:
			iflags |= DB_LOG_NO_DATA;
			_debug_check();
			break;
		case TCL_DBREM_TXN:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			txn = NAME_TO_TXN(arg);
			if (txn == NULL) {
				snprintf(msg, MSG_SIZE,
				    "Put: Invalid txn: %s\n", arg);
				Tcl_SetResult(interp, msg, TCL_VOLATILE);
				result = TCL_ERROR;
			}
			break;
		}
		/*
		 * If, at any time, parsing the args we get an error,
		 * bail out and return.
		 */
		if (result != TCL_OK)
			goto error;
		if (endarg)
			break;
	}
	if (result != TCL_OK)
		goto error;
	/*
	 * Any args we have left, (better be 1 or 2 left) are
	 * file names. If there is 1, a db name, if 2 a db and subdb name.
	 */
	if ((i != (objc - 1)) || (i != (objc - 2))) {
		/*
		 * Dbs must be NULL terminated file names, but subdbs can
		 * be anything.  Use Strings for the db name and byte
		 * arrays for the subdb.
		 */
		db = Tcl_GetStringFromObj(objv[i++], NULL);
		if (strcmp(db, "") == 0)
			db = NULL;
		if (i != objc) {
			subdbtmp =
			    Tcl_GetByteArrayFromObj(objv[i++], &subdblen);
			if ((ret = __os_malloc(env, (size_t)subdblen + 1,
			    &subdb)) != 0) { Tcl_SetResult(interp,
				    db_strerror(ret), TCL_STATIC);
				return (0);
			}
			memcpy(subdb, subdbtmp, (size_t)subdblen);
			subdb[subdblen] = '\0';
		}
	} else {
		Tcl_WrongNumArgs(interp, 2, objv, "?args? filename ?database?");
		result = TCL_ERROR;
		goto error;
	}


        if (dbenv == NULL) {
		ret = db_create(&dbp, dbenv, 0);
		if (ret) {
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    	    "db_create");
			goto error;
		}

		/*
		 * XXX
		 * Remove restriction if error handling not tied to env.
		 *
		 * The DB->set_err* functions overwrite the environment.  So, if
		 * we are using an env, don't overwrite it; if not using an env,
		 * then configure error handling.
		 */
		dbp->set_errpfx(dbp, "DbRemove");
		dbp->set_errcall(dbp, _ErrorFunc);

		if (passwd != NULL) {
			ret = dbp->set_encrypt(dbp, passwd, enc_flag);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_encrypt");
		}
		if (set_flags != 0) {
			ret = dbp->set_flags(dbp, set_flags);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_flags");
		}
	}

	/*
	 * The dbremove method is a destructor, NULL out the dbp.
	 */
	_debug_check();
	if (dbenv != NULL)
		ret = dbenv->dbremove(dbenv, txn, db, subdb, iflags);
	else
		ret = dbp->remove(dbp, db, subdb, 0);

	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db remove");

	/* 
	 * If the heap auxiliary databases exist, remove them too. If we have an
	 * environment, just try the dbremove and ignore ENOENT, instead of
	 * trying to create a path to the file.  Without an environment, we can
	 * easily create the path.
	 */

	if ((db != NULL || subdb != NULL) && ret == 0) {
		/* set up file name for associated recno db */
		if (db != NULL) {
			nlen = strlen(db);
			if ((ret = __os_malloc(env, nlen + 2, &dbr)) != 0) {
				Tcl_SetResult(interp,
				    db_strerror(ret), TCL_STATIC);
				return (0);
			}
			memcpy(dbr, db, nlen);
			dbr[nlen] = '1';
			dbr[nlen+1] = '\0';
		}
		if (subdb != NULL) {
			if ((ret = __os_malloc(
			    env, (size_t)subdblen + 2, &subdbr)) != 0) {
				Tcl_SetResult(
				    interp, db_strerror(ret), TCL_STATIC);
				return (0);
			}
			memcpy(subdbr, subdb, (size_t)subdblen);
			subdbr[subdblen] = '1';
			subdbr[subdblen+1] = '\0';
		}

		if (dbenv != NULL) {
			ret = dbenv->dbremove(dbenv, txn, dbr, subdbr, iflags);
			if (ret == ENOENT)
				ret = 0;
		} else if (__os_exists(NULL, dbr, NULL) == 0) {
			/* additional files not set up as subdbs */
			subdb = NULL;
			
			ret = db_create(&dbp, dbenv, 0);
			if (ret) {
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "db_create");
				goto error;
			}

			/*
			 * XXX
			 * Remove restriction if error handling not tied to env. 
			 *
			 * The DB->set_err* functions overwrite the
			 * environment. So, if we are using an env, don't
			 * overwrite it; if not using an env, then configure
			 * error handling.  
			 */
			dbp->set_errpfx(dbp, "DbRemove");
			dbp->set_errcall(dbp, _ErrorFunc);
			
			if (passwd != NULL) {
				ret = dbp->set_encrypt(dbp, passwd, enc_flag);
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "set_encrypt");
			}
			if (set_flags != 0) {
				ret = dbp->set_flags(dbp, set_flags);
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "set_flags");
			}

			ret = dbp->remove(dbp, dbr, subdbr, 0);
			/* if file doesnt exist, ignore error */
			if (ret == ENOENT)
				ret = 0;
		
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), 
			    "db remove associated recno");
		}

		/* remove the secondary db mapping RID to recno */
		if (dbr != NULL)
			dbr[nlen] = '2';
		if (subdbr != NULL)
			subdbr[subdblen] = '2';
		if (dbenv != NULL) {
			ret = dbenv->dbremove(dbenv, txn, dbr, subdbr, iflags);
			if (ret == ENOENT)
				ret = 0;
		} else if (__os_exists(NULL, dbr, NULL) == 0) {
			ret = db_create(&dbp, dbenv, 0);
			if (ret) {
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "db_create");
				goto error;
			}

			/*
			 * XXX
			 * Remove restriction if error handling not tied to env. 
			 *
			 * The DB->set_err* functions overwrite the
			 * environment. So, if we are using an env, don't
			 * overwrite it; if not using an env, then configure
			 * error handling.  
			 */
			dbp->set_errpfx(dbp, "DbRemove");
			dbp->set_errcall(dbp, _ErrorFunc);

			if (passwd != NULL) {
				ret = dbp->set_encrypt(dbp, passwd, enc_flag);
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "set_encrypt");
			}
			if (set_flags != 0) {
				ret = dbp->set_flags(dbp, set_flags);
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "set_flags");
			}

			ret = dbp->remove(dbp, dbr, subdbr, 0);
			/* if file doesnt exist, ignore error */
			if (ret == ENOENT)
				ret = 0;

			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "db remove associated btree");
		}
	}
	dbp = NULL;
error:
	if (subdb)
		__os_free(env, subdb);
	if (dbr)
		__os_free(env, dbr);
	if (subdbr)
		__os_free(env, subdbr);
	if (result == TCL_ERROR && dbp != NULL)
		(void)dbp->close(dbp, 0);
	return (result);
}

/*
 * bdb_DbRename --
 *	Implements the DB_ENV->dbrename and DB->rename commands.
 */
static int
bdb_DbRename(interp, objc, objv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
{
	static const char *bdbmv[] = {
		"-auto_commit",
		"-encrypt",
		"-encryptaes",
		"-encryptany",
		"-env",
		"-txn",
		"--",
		NULL
	};
	enum bdbmv {
		TCL_DBMV_AUTOCOMMIT,
		TCL_DBMV_ENCRYPT,
		TCL_DBMV_ENCRYPT_AES,
		TCL_DBMV_ENCRYPT_ANY,
		TCL_DBMV_ENV,
		TCL_DBMV_TXN,
		TCL_DBMV_ENDARG
	};
	DB *dbp;
	DB_ENV *dbenv;
	DB_TXN *txn;
	ENV *env;
	u_int32_t enc_flag, iflags, set_flags;
	int endarg, i, newlen, optindex, result, ret, subdblen;
	u_char *subdbtmp;
	char *arg, *db, *dbr, msg[MSG_SIZE], *newname, *newnamer, *passwd;
	char *subdb, *subdbr;
	size_t nlen;

	dbp = NULL;
	dbenv = NULL;
	txn = NULL;
	env = NULL;
	enc_flag = iflags = set_flags = subdblen = 0;
	nlen = 0;
	result = TCL_OK;
	endarg = 0;
	db = dbr = newname = newnamer = passwd = subdb = subdbr = NULL;
	subdbtmp = NULL;

	if (objc < 2) {
		Tcl_WrongNumArgs(interp,
			3, objv, "?args? filename ?database? ?newname?");
		return (TCL_ERROR);
	}

	/*
	 * We must first parse for the environment flag, since that
	 * is needed for db_create.  Then create the db handle.
	 */
	i = 2;
	while (i < objc) {
		if (Tcl_GetIndexFromObj(interp, objv[i], bdbmv,
		    "option", TCL_EXACT, &optindex) != TCL_OK) {
			arg = Tcl_GetStringFromObj(objv[i], NULL);
			if (arg[0] == '-') {
				result = IS_HELP(objv[i]);
				goto error;
			} else
				Tcl_ResetResult(interp);
			break;
		}
		i++;
		switch ((enum bdbmv)optindex) {
		 case TCL_DBMV_AUTOCOMMIT:
			 iflags |= DB_AUTO_COMMIT;
			 _debug_check();
			 break;
		case TCL_DBMV_ENCRYPT:
			set_flags |= DB_ENCRYPT;
			_debug_check();
			break;
		case TCL_DBMV_ENCRYPT_AES:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-encryptaes passwd?");
				result = TCL_ERROR;
				break;
			}
			passwd = Tcl_GetStringFromObj(objv[i++], NULL);
			enc_flag = DB_ENCRYPT_AES;
			break;
		case TCL_DBMV_ENCRYPT_ANY:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-encryptany passwd?");
				result = TCL_ERROR;
				break;
			}
			passwd = Tcl_GetStringFromObj(objv[i++], NULL);
			enc_flag = 0;
			break;
		case TCL_DBMV_ENV:
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			dbenv = NAME_TO_ENV(arg);
			if (dbenv == NULL) {
				Tcl_SetResult(interp,
				    "db rename: illegal environment",
				    TCL_STATIC);
				return (TCL_ERROR);
			}
			env = dbenv->env;
			break;
		case TCL_DBMV_ENDARG:
			endarg = 1;
			break;
		case TCL_DBMV_TXN:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			txn = NAME_TO_TXN(arg);
			if (txn == NULL) {
				snprintf(msg, MSG_SIZE,
				    "Put: Invalid txn: %s\n", arg);
				Tcl_SetResult(interp, msg, TCL_VOLATILE);
				result = TCL_ERROR;
			}
			break;
		}
		/*
		 * If, at any time, parsing the args we get an error,
		 * bail out and return.
		 */
		if (result != TCL_OK)
			goto error;
		if (endarg)
			break;
	}
	if (result != TCL_OK)
		goto error;
	/*
	 * Any args we have left, (better be 2 or 3 left) are
	 * file names. If there is 2, a file name, if 3 a file and db name.
	 */
	if ((i != (objc - 2)) || (i != (objc - 3))) {
		/*
		 * Dbs must be NULL terminated file names, but subdbs can
		 * be anything.  Use Strings for the db name and byte
		 * arrays for the subdb.
		 */
		db = Tcl_GetStringFromObj(objv[i++], NULL);
		if (strcmp(db, "") == 0)
			db = NULL;
		if (i == objc - 2) {
			subdbtmp =
			    Tcl_GetByteArrayFromObj(objv[i++], &subdblen);
			if ((ret = __os_malloc(env,
			    (size_t)subdblen + 1, &subdb)) != 0) {
				Tcl_SetResult(interp,
				    db_strerror(ret), TCL_STATIC);
				return (0);
			}
			memcpy(subdb, subdbtmp, (size_t)subdblen);
			subdb[subdblen] = '\0';
		}
		subdbtmp =
		    Tcl_GetByteArrayFromObj(objv[i++], &newlen);
		if ((ret = __os_malloc(
		    env, (size_t)newlen + 1, &newname)) != 0) {
			Tcl_SetResult(interp,
			    db_strerror(ret), TCL_STATIC);
			return (0);
		}
		memcpy(newname, subdbtmp, (size_t)newlen);
		newname[newlen] = '\0';
	} else {
		Tcl_WrongNumArgs(
		    interp, 3, objv, "?args? filename ?database? ?newname?");
		result = TCL_ERROR;
		goto error;
	}
	if (dbenv == NULL) {
		ret = db_create(&dbp, dbenv, 0);
		if (ret) {
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "db_create");
			goto error;
		}
		/*
		 * XXX
		 * Remove restriction if error handling not tied to env.
		 *
		 * The DB->set_err* functions overwrite the environment.  So, if
		 * we are using an env, don't overwrite it; if not using an env,
		 * then configure error handling.
		 */
		dbp->set_errpfx(dbp, "DbRename");
		dbp->set_errcall(dbp, _ErrorFunc);

		if (passwd != NULL) {
			ret = dbp->set_encrypt(dbp, passwd, enc_flag);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_encrypt");
		}
		if (set_flags != 0) {
			ret = dbp->set_flags(dbp, set_flags);
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "set_flags");
		}
	}

	/*
	 * The dbrename method is a destructor, NULL out the dbp.
	 */
	_debug_check();
	if (dbp == NULL)
		ret = dbenv->dbrename(dbenv, txn, db, subdb, newname, iflags);
	else
		ret = dbp->rename(dbp, db, subdb, newname, 0);
	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db rename");
	/* 
	 * If the heap auxiliary databases exist, rename them too. If we have an
	 * environment, just try the dbrename and ignore ENOENT, instead of
	 * trying to create a path to the file.  Without an environment, we can
	 * easily create the path.
	 */

	if ((db != NULL || subdb != NULL) && ret == 0) {
		/* set up file name for associated recno db */
		if (db != NULL) {
			nlen = strlen(db);
			if ((ret = __os_malloc(env, nlen + 2, &dbr)) != 0) {
				Tcl_SetResult(interp,
				    db_strerror(ret), TCL_STATIC);
				return (0);
			}
			memcpy(dbr, db, nlen);
			dbr[nlen] = '1';
			dbr[nlen+1] = '\0';
		}
		if (subdb != NULL) {
			if ((ret = __os_malloc(
			    env, (size_t)subdblen + 2, &subdbr)) != 0) {
				Tcl_SetResult(
				    interp, db_strerror(ret), TCL_STATIC);
				return (0);
			}
			memcpy(subdbr, subdb, (size_t)subdblen);
			subdbr[subdblen] = '1';
			subdbr[subdblen+1] = '\0';
		}

		if ((ret = __os_malloc(env, 
		    (size_t)newlen + 2, &newnamer)) != 0) {
			Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
			return (0);
		}
		memcpy(newnamer, newname, (size_t)newlen);
		newnamer[newlen] = '1';
		newnamer[newlen+1] = '\0';
	
		if (dbenv != NULL) {
			ret = dbenv->dbrename(dbenv,
			    txn, dbr, subdbr, newnamer, iflags);
			if (ret == ENOENT)
				ret = 0;
		} else if (__os_exists(NULL, dbr, NULL) == 0) {
			/* additional files not set up as subdbs */
			subdb = NULL;
			
			ret = db_create(&dbp, dbenv, 0);
			if (ret) {
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "db_create");
				goto error;
			}

			/*
			 * XXX
			 * Remove restriction if error handling not tied to env. 
			 *
			 * The DB->set_err* functions overwrite the
			 * environment. So, if we are using an env, don't
			 * overwrite it; if not using an env, then configure
			 * error handling.  
			 */
			dbp->set_errpfx(dbp, "DbRename");
			dbp->set_errcall(dbp, _ErrorFunc);
			
			if (passwd != NULL) {
				ret = dbp->set_encrypt(dbp, passwd, enc_flag);
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "set_encrypt");
			}
			if (set_flags != 0) {
				ret = dbp->set_flags(dbp, set_flags);
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "set_flags");
			}

			ret = dbp->rename(dbp, dbr, subdbr, newnamer, 0);
			/* if file doesnt exist, ignore error */
			if (ret == ENOENT)
				ret = 0;
		
			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), 
			    "db rename associated recno");
		}

		/* remove the secondary db mapping RID to recno */
		if (dbr != NULL)
			dbr[nlen] = '2';
		if (subdbr != NULL)
			subdbr[subdblen] = '2';
		newnamer[newlen] = '2';
		if (dbenv != NULL) {
			ret = dbenv->dbrename(dbenv,
			    txn, dbr, subdbr, newnamer, iflags);
			if (ret == ENOENT)
				ret = 0;
		} else if (__os_exists(NULL, dbr, NULL) == 0) {
			ret = db_create(&dbp, dbenv, 0);
			if (ret) {
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "db_create");
				goto error;
			}

			/*
			 * XXX
			 * Remove restriction if error handling not tied to env. 
			 *
			 * The DB->set_err* functions overwrite the
			 * environment. So, if we are using an env, don't
			 * overwrite it; if not using an env, then configure
			 * error handling.  
			 */
			dbp->set_errpfx(dbp, "DbRename");
			dbp->set_errcall(dbp, _ErrorFunc);

			if (passwd != NULL) {
				ret = dbp->set_encrypt(dbp, passwd, enc_flag);
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "set_encrypt");
			}
			if (set_flags != 0) {
				ret = dbp->set_flags(dbp, set_flags);
				result = _ReturnSetup(interp, ret, 
				    DB_RETOK_STD(ret), "set_flags");
			}

			ret = dbp->rename(dbp, dbr, subdbr, newnamer, 0);
			/* if file doesnt exist, ignore error */
			if (ret == ENOENT)
				ret = 0;

			result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
			    "db rename associated btree");
		}
	}
	dbp = NULL;
error:
	if (subdb)
		__os_free(env, subdb);
	if (newname)
		__os_free(env, newname);
	if (dbr)
		__os_free(env, dbr);
	if (subdbr)
		__os_free(env, subdbr);
	if (newnamer)
		__os_free(env, newnamer);
	if (result == TCL_ERROR && dbp != NULL)
		(void)dbp->close(dbp, 0);
	return (result);
}

#ifdef CONFIG_TEST
/*
 * bdb_DbVerify --
 *	Implements the DB->verify command.
 */
static int
bdb_DbVerify(interp, objc, objv, ip)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
	DBTCL_INFO *ip;			/* Our internal info */
{
	static const char *bdbverify[] = {
		"-btcompare",
		"-dupcompare",
		"-hashcompare",
		"-hashproc",

		"-encrypt",
		"-encryptaes",
		"-encryptany",
		"-env",
		"-errfile",
		"-errpfx",
		"-noorderchk",
		"-orderchkonly",
		"-unref",
		"--",
		NULL
	};
	enum bdbvrfy {
		TCL_DBVRFY_BTCOMPARE,
		TCL_DBVRFY_DUPCOMPARE,
		TCL_DBVRFY_HASHCOMPARE,
		TCL_DBVRFY_HASHPROC,

		TCL_DBVRFY_ENCRYPT,
		TCL_DBVRFY_ENCRYPT_AES,
		TCL_DBVRFY_ENCRYPT_ANY,
		TCL_DBVRFY_ENV,
		TCL_DBVRFY_ERRFILE,
		TCL_DBVRFY_ERRPFX,
		TCL_DBVRFY_NOORDERCHK,
		TCL_DBVRFY_ORDERCHKONLY,
		TCL_DBVRFY_UNREF,
		TCL_DBVRFY_ENDARG
	};
	DB_ENV *dbenv;
	DB *dbp;
	FILE *errf;
	int (*bt_compare) __P((DB *, const DBT *, const DBT *));
	int (*dup_compare) __P((DB *, const DBT *, const DBT *));
	int (*h_compare) __P((DB *, const DBT *, const DBT *));
	u_int32_t (*h_hash)__P((DB *, const void *, u_int32_t));
	u_int32_t enc_flag, flags, set_flags;
	int endarg, i, optindex, result, ret, subdblen;
	char *arg, *db, *errpfx, *passwd, *subdb;
	u_char *subdbtmp;

	dbenv = NULL;
	dbp = NULL;
	passwd = NULL;
	result = TCL_OK;
	db = errpfx = subdb = NULL;
	errf = NULL;
	bt_compare = NULL;
	dup_compare = NULL;
	h_compare = NULL;
	h_hash = NULL;
	flags = endarg = 0;
	enc_flag = set_flags = 0;

	if (objc < 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "?args? filename");
		return (TCL_ERROR);
	}

	/*
	 * We must first parse for the environment flag, since that
	 * is needed for db_create.  Then create the db handle.
	 */
	i = 2;
	while (i < objc) {
		if (Tcl_GetIndexFromObj(interp, objv[i], bdbverify,
		    "option", TCL_EXACT, &optindex) != TCL_OK) {
			arg = Tcl_GetStringFromObj(objv[i], NULL);
			if (arg[0] == '-') {
				result = IS_HELP(objv[i]);
				goto error;
			} else
				Tcl_ResetResult(interp);
			break;
		}
		i++;
		switch ((enum bdbvrfy)optindex) {
		case TCL_DBVRFY_BTCOMPARE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-btcompare compareproc");
				result = TCL_ERROR;
				break;
			}

			/*
			 * Store the object containing the procedure name.
			 * We don't need to crack it out now--we'll want
			 * to bundle it up to pass into Tcl_EvalObjv anyway.
			 * Tcl's object refcounting will--I hope--take care
			 * of the memory management here.
			 */
			ip->i_compare = objv[i++];
			Tcl_IncrRefCount(ip->i_compare);
			_debug_check();
			bt_compare = tcl_bt_compare;
			break;
		case TCL_DBVRFY_DUPCOMPARE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-dupcompare compareproc");
				result = TCL_ERROR;
				break;
			}

			/*
			 * Store the object containing the procedure name.
			 * See TCL_DBVRFY_BTCOMPARE.
			 */
			ip->i_dupcompare = objv[i++];
			Tcl_IncrRefCount(ip->i_dupcompare);
			_debug_check();
			dup_compare = tcl_dup_compare;
			break;
		case TCL_DBVRFY_HASHCOMPARE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-hashcompare compareproc");
				result = TCL_ERROR;
				break;
			}

			/*
			 * Store the object containing the procedure name.
			 * We don't need to crack it out now--we'll want
			 * to bundle it up to pass into Tcl_EvalObjv anyway.
			 * Tcl's object refcounting will--I hope--take care
			 * of the memory management here.
			 */
			ip->i_compare = objv[i++];
			Tcl_IncrRefCount(ip->i_compare);
			_debug_check();
			h_compare = tcl_bt_compare;
			break;
		case TCL_DBVRFY_HASHPROC:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-hashproc hashproc");
				result = TCL_ERROR;
				break;
			}

			/*
			 * Store the object containing the procedure name.
			 * See TCL_DBVRFY_BTCOMPARE.
			 */
			ip->i_hashproc = objv[i++];
			Tcl_IncrRefCount(ip->i_hashproc);
			_debug_check();
			h_hash = tcl_h_hash;
			break;
		case TCL_DBVRFY_ENCRYPT:
			set_flags |= DB_ENCRYPT;
			_debug_check();
			break;
		case TCL_DBVRFY_ENCRYPT_AES:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-encryptaes passwd?");
				result = TCL_ERROR;
				break;
			}
			passwd = Tcl_GetStringFromObj(objv[i++], NULL);
			enc_flag = DB_ENCRYPT_AES;
			break;
		case TCL_DBVRFY_ENCRYPT_ANY:
			/* Make sure we have an arg to check against! */
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "?-encryptany passwd?");
				result = TCL_ERROR;
				break;
			}
			passwd = Tcl_GetStringFromObj(objv[i++], NULL);
			enc_flag = 0;
			break;
		case TCL_DBVRFY_ENV:
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			dbenv = NAME_TO_ENV(arg);
			if (dbenv == NULL) {
				Tcl_SetResult(interp,
				    "db verify: illegal environment",
				    TCL_STATIC);
				result = TCL_ERROR;
				break;
			}
			break;
		case TCL_DBVRFY_ERRFILE:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-errfile file");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			/*
			 * If the user already set one, close it.
			 */
			if (errf != NULL && errf != stdout && errf != stderr)
				(void)fclose(errf);
			if (strcmp(arg, "/dev/stdout") == 0)
				errf = stdout;
			else if (strcmp(arg, "/dev/stderr") == 0)
				errf = stderr;
			else
				errf = fopen(arg, "a");
			break;
		case TCL_DBVRFY_ERRPFX:
			if (i >= objc) {
				Tcl_WrongNumArgs(interp, 2, objv,
				    "-errpfx prefix");
				result = TCL_ERROR;
				break;
			}
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			/*
			 * If the user already set one, free it.
			 */
			if (errpfx != NULL)
				__os_free(dbenv->env, errpfx);
			if ((ret = __os_strdup(NULL, arg, &errpfx)) != 0) {
				result = _ReturnSetup(interp, ret,
				    DB_RETOK_STD(ret), "__os_strdup");
				break;
			}
			break;
		case TCL_DBVRFY_NOORDERCHK:
			flags |= DB_NOORDERCHK;
			break;
		case TCL_DBVRFY_ORDERCHKONLY:
			flags |= DB_ORDERCHKONLY;
			break;
		case TCL_DBVRFY_UNREF:
			flags |= DB_UNREF;
			break;
		case TCL_DBVRFY_ENDARG:
			endarg = 1;
			break;
		}
		/*
		 * If, at any time, parsing the args we get an error,
		 * bail out and return.
		 */
		if (result != TCL_OK)
			goto error;
		if (endarg)
			break;
	}
	if (result != TCL_OK)
		goto error;
	/*
	 * The remaining arg is the db filename.
	 */
	/*
	 * Any args we have left, (better be 1 or 2 left) are
	 * file names.  If there is 1, a db name, if 2 a db and subdb name.
	 */
	if (i != objc) {
		/*
		 * Dbs must be NULL terminated file names, but subdbs can
		 * be anything.  Use Strings for the db name and byte
		 * arrays for the subdb.
		 */
		db = Tcl_GetStringFromObj(objv[i++], NULL);
		if (strcmp(db, "") == 0)
			db = NULL;
		if (i != objc) {
			subdbtmp =
			    Tcl_GetByteArrayFromObj(objv[i++], &subdblen);
			if ((ret = __os_malloc(dbenv->env,
			   (size_t)subdblen + 1, &subdb)) != 0) {
				Tcl_SetResult(interp, db_strerror(ret),
				    TCL_STATIC);
				return (0);
			}
			memcpy(subdb, subdbtmp, (size_t)subdblen);
			subdb[subdblen] = '\0';
		}
	} else {
		Tcl_WrongNumArgs(interp, 2, objv, "?args? filename");
		result = TCL_ERROR;
		goto error;
	}

	ret = db_create(&dbp, dbenv, 0);
	if (ret) {
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "db_create");
		goto error;
	}

	/* Hang our info pointer on the DB handle, so we can do callbacks. */
	dbp->api_internal = ip;

	if (errf != NULL)
		dbp->set_errfile(dbp, errf);
	if (errpfx != NULL)
		dbp->set_errpfx(dbp, errpfx);

	if (passwd != NULL &&
	    (ret = dbp->set_encrypt(dbp, passwd, enc_flag)) != 0) {
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "set_encrypt");
		goto error;
	}

	if (set_flags != 0 &&
	    (ret = dbp->set_flags(dbp, set_flags)) != 0) {
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "set_flags");
		goto error;
	}
	if (bt_compare != NULL &&
	    (ret = dbp->set_bt_compare(dbp, bt_compare)) != 0) {
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "set_bt_compare");
		goto error;
	}
	if (dup_compare != NULL &&
	    (ret = dbp->set_dup_compare(dbp, dup_compare)) != 0) {
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "set_dup_compare");
		goto error;
	}
	if (h_compare != NULL &&
	    (ret = dbp->set_h_compare(dbp, h_compare)) != 0) {
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "set_h_compare");
		goto error;
	}
	if (h_hash != NULL &&
	    (ret = dbp->set_h_hash(dbp, h_hash)) != 0) {
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "set_h_hash");
		goto error;
	}

	/*
	 * The verify method is a destructor, NULL out the dbp.
	 */
	ret = dbp->verify(dbp, db, subdb, NULL, flags);
	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db verify");
	dbp = NULL;
error:
	if (errf != NULL && errf != stdout && errf != stderr)
		(void)fclose(errf);
	if (errpfx != NULL)
		__os_free(dbenv->env, errpfx);
	if (subdb != NULL)
		__os_free(dbenv->env, subdb);
	if (dbp)
		(void)dbp->close(dbp, 0);
	return (result);
}
#endif

/*
 * bdb_Version --
 *	Implements the version command.
 */
static int
bdb_Version(interp, objc, objv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
{
	static const char *bdbver[] = {
		"-string", NULL
	};
	enum bdbver {
		TCL_VERSTRING
	};
	int i, optindex, maj, min, patch, result, string, verobjc;
	char *arg, *v;
	Tcl_Obj *res, *verobjv[3];

	result = TCL_OK;
	string = 0;

	if (objc < 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "?args?");
		return (TCL_ERROR);
	}

	/*
	 * We must first parse for the environment flag, since that
	 * is needed for db_create.  Then create the db handle.
	 */
	i = 2;
	while (i < objc) {
		if (Tcl_GetIndexFromObj(interp, objv[i], bdbver,
		    "option", TCL_EXACT, &optindex) != TCL_OK) {
			arg = Tcl_GetStringFromObj(objv[i], NULL);
			if (arg[0] == '-') {
				result = IS_HELP(objv[i]);
				goto error;
			} else
				Tcl_ResetResult(interp);
			break;
		}
		i++;
		switch ((enum bdbver)optindex) {
		case TCL_VERSTRING:
			string = 1;
			break;
		}
		/*
		 * If, at any time, parsing the args we get an error,
		 * bail out and return.
		 */
		if (result != TCL_OK)
			goto error;
	}
	if (result != TCL_OK)
		goto error;

	v = db_version(&maj, &min, &patch);
	if (string)
		res = NewStringObj(v, strlen(v));
	else {
		verobjc = 3;
		verobjv[0] = Tcl_NewIntObj(maj);
		verobjv[1] = Tcl_NewIntObj(min);
		verobjv[2] = Tcl_NewIntObj(patch);
		res = Tcl_NewListObj(verobjc, verobjv);
	}
	Tcl_SetObjResult(interp, res);
error:
	return (result);
}

#ifdef CONFIG_TEST
/*
 * bdb_GetConfig --
 *	Implements the getconfig command.
 */
#define	ADD_CONFIG_NAME(name)						\
	conf = NewStringObj(name, strlen(name));			\
	if (Tcl_ListObjAppendElement(interp, res, conf) != TCL_OK)	\
		return (TCL_ERROR);

static int
bdb_GetConfig(interp, objc, objv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
{
	Tcl_Obj *res, *conf;

	/*
	 * No args.  Error if we have some
	 */
	if (objc != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "");
		return (TCL_ERROR);
	}
	res = Tcl_NewListObj(0, NULL);
	conf = NULL;

	/*
	 * This command conditionally adds strings in based on
	 * how DB is configured so that the test suite can make
	 * decisions based on that.  For now only implement the
	 * configuration pieces we need.
	 */
#ifdef DEBUG
	ADD_CONFIG_NAME("debug");
#endif
#ifdef DEBUG_ROP
	ADD_CONFIG_NAME("debug_rop");
#endif
#ifdef DEBUG_WOP
	ADD_CONFIG_NAME("debug_wop");
#endif
#ifdef DIAGNOSTIC
	ADD_CONFIG_NAME("diagnostic");
#endif
#ifdef HAVE_PARTITION
	ADD_CONFIG_NAME("partition");
#endif
#ifdef HAVE_HASH
	ADD_CONFIG_NAME("hash");
#endif
#ifdef HAVE_HEAP
	ADD_CONFIG_NAME("heap");
#endif
#ifdef HAVE_QUEUE
	ADD_CONFIG_NAME("queue");
#endif
#ifdef HAVE_REPLICATION
	ADD_CONFIG_NAME("rep");
#endif
#ifdef HAVE_REPLICATION_THREADS
	ADD_CONFIG_NAME("repmgr");
#endif
#ifdef HAVE_VERIFY
	ADD_CONFIG_NAME("verify");
#endif
	Tcl_SetObjResult(interp, res);
	return (TCL_OK);
}

/*
 * bdb_Handles --
 *	Implements the handles command.
 */
static int
bdb_Handles(interp, objc, objv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
{
	DBTCL_INFO *p;
	Tcl_Obj *res, *handle;

	/*
	 * No args.  Error if we have some
	 */
	if (objc != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "");
		return (TCL_ERROR);
	}
	res = Tcl_NewListObj(0, NULL);

	LIST_FOREACH(p, &__db_infohead, entries) {
		if (p->i_type == I_AUX)
			continue;
		handle = NewStringObj(p->i_name, strlen(p->i_name));
		if (Tcl_ListObjAppendElement(interp, res, handle) != TCL_OK)
			return (TCL_ERROR);
	}
	Tcl_SetObjResult(interp, res);
	return (TCL_OK);
}

/*
 * bdb_MsgType -
 *	Implements the msgtype command.
 *	Given a replication message return its message type name.
 */
static int
bdb_MsgType(interp, objc, objv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
{
	__rep_control_args *rp;
	Tcl_Obj *msgname;
	u_int32_t len, msgtype, swaptype;
	int freerp, ret;

	/*
	 * If the messages in rep.h change, this must change too!
	 * Add "no_type" for 0 so that we directly index.
	 */
	static const char *msgnames[] = {
		"no_type", "alive", "alive_req", "all_req",
		"bulk_log", "bulk_page",
		"dupmaster", "file", "file_fail", "file_req", "lease_grant",
		"log", "log_more", "log_req", "master_req", "newclient",
		"newfile", "newmaster", "newsite", "page",
		"page_fail", "page_more", "page_req",
		"rerequest", "startsync", "update", "update_req",
		"verify", "verify_fail", "verify_req",
		"vote1", "vote2", NULL
	};

	/*
	 * 1 arg, the message.  Error if different.
	 */
	if (objc != 3) {
		Tcl_WrongNumArgs(interp, 3, objv, "msgtype msg");
		return (TCL_ERROR);
	}

	ret = _CopyObjBytes(interp, objv[2], &rp, &len, &freerp);
	if (ret != TCL_OK) {
		Tcl_SetResult(interp,
		    "msgtype: bad control message", TCL_STATIC);
		return (TCL_ERROR);
	}
	swaptype = msgtype = rp->rectype;
	/*
	 * We have no DB_ENV or ENV here.  The message type may be
	 * swapped.  Get both and use the one that is in the message range.
	 */
	M_32_SWAP(swaptype);
	if (msgtype > REP_MAX_MSG && swaptype <= REP_MAX_MSG)
		msgtype = swaptype;
	msgname = NewStringObj(msgnames[msgtype], strlen(msgnames[msgtype]));
	Tcl_SetObjResult(interp, msgname);
	if (rp != NULL && freerp)
		__os_free(NULL, rp);
	return (TCL_OK);
}

/*
 * bdb_DbUpgrade --
 *	Implements the DB->upgrade command.
 */
static int
bdb_DbUpgrade(interp, objc, objv)
	Tcl_Interp *interp;		/* Interpreter */
	int objc;			/* How many arguments? */
	Tcl_Obj *CONST objv[];		/* The argument objects */
{
	static const char *bdbupg[] = {
		"-dupsort", "-env", "--", NULL
	};
	enum bdbupg {
		TCL_DBUPG_DUPSORT,
		TCL_DBUPG_ENV,
		TCL_DBUPG_ENDARG
	};
	DB_ENV *dbenv;
	DB *dbp;
	u_int32_t flags;
	int endarg, i, optindex, result, ret;
	char *arg, *db;

	dbenv = NULL;
	dbp = NULL;
	result = TCL_OK;
	db = NULL;
	flags = endarg = 0;

	if (objc < 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "?args? filename");
		return (TCL_ERROR);
	}

	i = 2;
	while (i < objc) {
		if (Tcl_GetIndexFromObj(interp, objv[i], bdbupg,
		    "option", TCL_EXACT, &optindex) != TCL_OK) {
			arg = Tcl_GetStringFromObj(objv[i], NULL);
			if (arg[0] == '-') {
				result = IS_HELP(objv[i]);
				goto error;
			} else
				Tcl_ResetResult(interp);
			break;
		}
		i++;
		switch ((enum bdbupg)optindex) {
		case TCL_DBUPG_DUPSORT:
			flags |= DB_DUPSORT;
			break;
		case TCL_DBUPG_ENV:
			arg = Tcl_GetStringFromObj(objv[i++], NULL);
			dbenv = NAME_TO_ENV(arg);
			if (dbenv == NULL) {
				Tcl_SetResult(interp,
				    "db upgrade: illegal environment",
				    TCL_STATIC);
				return (TCL_ERROR);
			}
			break;
		case TCL_DBUPG_ENDARG:
			endarg = 1;
			break;
		}
		/*
		 * If, at any time, parsing the args we get an error,
		 * bail out and return.
		 */
		if (result != TCL_OK)
			goto error;
		if (endarg)
			break;
	}
	if (result != TCL_OK)
		goto error;
	/*
	 * The remaining arg is the db filename.
	 */
	if (i == (objc - 1))
		db = Tcl_GetStringFromObj(objv[i++], NULL);
	else {
		Tcl_WrongNumArgs(interp, 2, objv, "?args? filename");
		result = TCL_ERROR;
		goto error;
	}
	ret = db_create(&dbp, dbenv, 0);
	if (ret) {
		result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
		    "db_create");
		goto error;
	}

	/*
	 * XXX
	 * Remove restriction if error handling not tied to env.
	 *
	 * The DB->set_err* functions overwrite the environment.  So, if
	 * we are using an env, don't overwrite it; if not using an env,
	 * then configure error handling.
	 */
	if (dbenv == NULL) {
		dbp->set_errpfx(dbp, "DbUpgrade");
		dbp->set_errcall(dbp, _ErrorFunc);
	}
	ret = dbp->upgrade(dbp, db, flags);
	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db upgrade");
error:
	if (dbp)
		(void)dbp->close(dbp, 0);
	return (result);
}

/*
 * tcl_bt_compare and tcl_dup_compare --
 *	These two are basically identical internally, so may as well
 * share code.  The only differences are the name used in error
 * reporting and the Tcl_Obj representing their respective procs.
 */
static int
tcl_bt_compare(dbp, dbta, dbtb)
	DB *dbp;
	const DBT *dbta, *dbtb;
{
	return (tcl_compare_callback(dbp, dbta, dbtb,
	    ((DBTCL_INFO *)dbp->api_internal)->i_compare, "bt_compare"));
}

static int
tcl_dup_compare(dbp, dbta, dbtb)
	DB *dbp;
	const DBT *dbta, *dbtb;
{
	return (tcl_compare_callback(dbp, dbta, dbtb,
	    ((DBTCL_INFO *)dbp->api_internal)->i_dupcompare, "dup_compare"));
}

/*
 * tcl_compare_callback --
 *	Tcl callback for set_bt_compare and set_dup_compare. What this
 * function does is stuff the data fields of the two DBTs into Tcl ByteArray
 * objects, then call the procedure stored in ip->i_compare on the two
 * objects.  Then we return that procedure's result as the comparison.
 */
static int
tcl_compare_callback(dbp, dbta, dbtb, procobj, errname)
	DB *dbp;
	const DBT *dbta, *dbtb;
	Tcl_Obj *procobj;
	char *errname;
{
	DBTCL_INFO *ip;
	Tcl_Interp *interp;
	Tcl_Obj *a, *b, *resobj, *objv[3];
	int result, cmp;

	ip = (DBTCL_INFO *)dbp->api_internal;
	interp = ip->i_interp;
	objv[0] = procobj;

	/*
	 * Create two ByteArray objects, with the two data we've been passed.
	 * This will involve a copy, which is unpleasantly slow, but there's
	 * little we can do to avoid this (I think).
	 */
	a = Tcl_NewByteArrayObj(dbta->data, (int)dbta->size);
	Tcl_IncrRefCount(a);
	b = Tcl_NewByteArrayObj(dbtb->data, (int)dbtb->size);
	Tcl_IncrRefCount(b);

	objv[1] = a;
	objv[2] = b;

	result = Tcl_EvalObjv(interp, 3, objv, 0);
	if (result != TCL_OK) {
		/*
		 * XXX
		 * If this or the next Tcl call fails, we're doomed.
		 * There's no way to return an error from comparison functions,
		 * no way to determine what the correct sort order is, and
		 * so no way to avoid corrupting the database if we proceed.
		 * We could play some games stashing return values on the
		 * DB handle, but it's not worth the trouble--no one with
		 * any sense is going to be using this other than for testing,
		 * and failure typically means that the bt_compare proc
		 * had a syntax error in it or something similarly dumb.
		 *
		 * So, drop core.  If we're not running with diagnostic
		 * mode, panic--and always return a negative number. :-)
		 */
err:		__db_errx(dbp->env, "Tcl %s callback failed", errname);
		return (__env_panic(dbp->env, DB_RUNRECOVERY));
	}

	resobj = Tcl_GetObjResult(interp);
	result = Tcl_GetIntFromObj(interp, resobj, &cmp);
	if (result != TCL_OK)
		goto err;

	Tcl_DecrRefCount(a);
	Tcl_DecrRefCount(b);
	return (cmp);
}

/*
 * tcl_h_hash --
 *	Tcl callback for the hashing function.  See tcl_compare_callback--
 * this works much the same way, only we're given a buffer and a length
 * instead of two DBTs.
 */
static u_int32_t
tcl_h_hash(dbp, buf, len)
	DB *dbp;
	const void *buf;
	u_int32_t len;
{
	DBTCL_INFO *ip;
	Tcl_Interp *interp;
	Tcl_Obj *objv[2];
	int result, hval;

	ip = (DBTCL_INFO *)dbp->api_internal;
	interp = ip->i_interp;
	objv[0] = ip->i_hashproc;

	/*
	 * Create a ByteArray for the buffer.
	 */
	objv[1] = Tcl_NewByteArrayObj((void *)buf, (int)len);
	Tcl_IncrRefCount(objv[1]);
	result = Tcl_EvalObjv(interp, 2, objv, 0);
	if (result != TCL_OK)
		goto err;

	result = Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &hval);
	if (result != TCL_OK)
		goto err;

	Tcl_DecrRefCount(objv[1]);
	return ((u_int32_t)hval);

err:	__db_errx(dbp->env, "Tcl h_hash callback failed");
	(void)__env_panic(dbp->env, DB_RUNRECOVERY);
	return (0);
}

static int
tcl_isalive(dbenv, pid, tid, flags)
	DB_ENV *dbenv;
	pid_t pid;
	db_threadid_t tid;
	u_int32_t flags;
{
	ENV *env;
	DBTCL_INFO *ip;
	Tcl_Interp *interp;
	Tcl_Obj *objv[2];
	pid_t mypid;
	db_threadid_t mytid;
	int answer, result;

	__os_id(dbenv, &mypid, &mytid);
	if (mypid == pid && (LF_ISSET(DB_MUTEX_PROCESS_ONLY) ||
	    mytid == tid))
		return (1);
	/*
	 * We only support the PROCESS_ONLY case for now, because that seems
	 * easiest, and that's all we need for our tests for the moment.
	 */
	if (!LF_ISSET(DB_MUTEX_PROCESS_ONLY))
		return (1);

	ip = (DBTCL_INFO *)dbenv->app_private;
	interp = ip->i_interp;
	objv[0] = ip->i_isalive;

	objv[1] = Tcl_NewLongObj((long)pid);
	Tcl_IncrRefCount(objv[1]);

	result = Tcl_EvalObjv(interp, 2, objv, 0);
	if (result != TCL_OK)
		goto err;
	Tcl_DecrRefCount(objv[1]);
	result = Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &answer);
	if (result != TCL_OK)
		goto err;

	return (answer);

err:	env = dbenv->env;
	__db_errx(env, "Tcl isalive callback failed: %s",
	    Tcl_GetStringResult(interp));

	(void)__env_panic(env, DB_RUNRECOVERY);
	return (0);
}

/*
 * tcl_part_callback --
 */
static u_int32_t
tcl_part_callback(dbp, data)
	DB *dbp;
	DBT *data;
{
	DBTCL_INFO *ip;
	Tcl_Interp *interp;
	Tcl_Obj *objv[2];
	int result, hval;

	ip = (DBTCL_INFO *)dbp->api_internal;
	interp = ip->i_interp;
	objv[0] = ip->i_part_callback;

	objv[1] = Tcl_NewByteArrayObj(data->data, (int)data->size);
	Tcl_IncrRefCount(objv[1]);

	result = Tcl_EvalObjv(interp, 2, objv, 0);
	if (result != TCL_OK)
		goto err;

	result = Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &hval);
	if (result != TCL_OK)
		goto err;

	Tcl_DecrRefCount(objv[1]);
	return ((u_int32_t)hval);

err:	__db_errx(dbp->env, "Tcl part_callback callback failed");
	(void)__env_panic(dbp->env, DB_RUNRECOVERY);
	return (0);
}

/*
 * tcl_rep_send --
 *	Replication send callback.
 *
 * PUBLIC: int tcl_rep_send __P((DB_ENV *,
 * PUBLIC:      const DBT *, const DBT *, const DB_LSN *, int, u_int32_t));
 */
int
tcl_rep_send(dbenv, control, rec, lsnp, eid, flags)
	DB_ENV *dbenv;
	const DBT *control, *rec;
	const DB_LSN *lsnp;
	int eid;
	u_int32_t flags;
{
#define	TCLDB_SENDITEMS	7
#define	TCLDB_MAXREPFLAGS 32
	DBTCL_INFO *ip;
	Tcl_Interp *interp;
	Tcl_Obj *control_o, *eid_o, *flags_o, *lsn_o, *origobj, *rec_o;
	Tcl_Obj *lsnobj[2], *myobjv[TCLDB_MAXREPFLAGS], *objv[TCLDB_SENDITEMS];
	Tcl_Obj *resobj;
	int i, myobjc, result, ret;

	ip = (DBTCL_INFO *)dbenv->app_private;
	interp = ip->i_interp;
	objv[0] = ip->i_rep_send;

	control_o = Tcl_NewByteArrayObj(control->data, (int)control->size);
	Tcl_IncrRefCount(control_o);

	rec_o = Tcl_NewByteArrayObj(rec->data, (int)rec->size);
	Tcl_IncrRefCount(rec_o);

	eid_o = Tcl_NewIntObj(eid);
	Tcl_IncrRefCount(eid_o);

	myobjv[myobjc = 0] = NULL;
	if (flags == 0)
		myobjv[myobjc++] = NewStringObj("none", strlen("none"));
	if (LF_ISSET(DB_REP_ANYWHERE))
		myobjv[myobjc++] = NewStringObj("any", strlen("any"));
	if (LF_ISSET(DB_REP_NOBUFFER))
		myobjv[myobjc++] = NewStringObj("nobuffer", strlen("nobuffer"));
	if (LF_ISSET(DB_REP_PERMANENT))
		myobjv[myobjc++] = NewStringObj("perm", strlen("perm"));
	if (LF_ISSET(DB_REP_REREQUEST))
		myobjv[myobjc++] =
		    NewStringObj("rerequest", strlen("rerequest"));
	/*
	 * If we're given an unrecognized flag send "unknown".
	 */
	if (myobjc == 0)
		myobjv[myobjc++] = NewStringObj("unknown", strlen("unknown"));
	for (i = 0; i < myobjc; i++)
		Tcl_IncrRefCount(myobjv[i]);
	flags_o = Tcl_NewListObj(myobjc, myobjv);
	Tcl_IncrRefCount(flags_o);

	lsnobj[0] = Tcl_NewLongObj((long)lsnp->file);
	Tcl_IncrRefCount(lsnobj[0]);
	lsnobj[1] = Tcl_NewLongObj((long)lsnp->offset);
	Tcl_IncrRefCount(lsnobj[1]);
	lsn_o = Tcl_NewListObj(2, lsnobj);
	Tcl_IncrRefCount(lsn_o);

	objv[1] = control_o;
	objv[2] = rec_o;
	objv[3] = ip->i_rep_eid;	/* From ID */
	objv[4] = eid_o;		/* To ID */
	objv[5] = flags_o;		/* Flags */
	objv[6] = lsn_o;		/* LSN */

	/*
	 * We really want to return the original result to the
	 * user.  So, save the result obj here, and then after
	 * we've taken care of the Tcl_EvalObjv, set the result
	 * back to this original result.
	 */
	origobj = Tcl_GetObjResult(interp);
	Tcl_IncrRefCount(origobj);
	result = Tcl_EvalObjv(interp, TCLDB_SENDITEMS, objv, 0);
	if (result != TCL_OK) {
		/*
		 * XXX
		 * This probably isn't the right error behavior, but
		 * this error should only happen if the Tcl callback is
		 * somehow invalid, which is a fatal scripting bug.
		 */
err:		__db_errx(dbenv->env,
		    "Tcl rep_send failure: %s", Tcl_GetStringResult(interp));
		return (EINVAL);
	}

	resobj = Tcl_GetObjResult(interp);
	result = Tcl_GetIntFromObj(interp, resobj, &ret);
	if (result != TCL_OK)
		goto err;

	Tcl_SetObjResult(interp, origobj);
	Tcl_DecrRefCount(origobj);
	Tcl_DecrRefCount(control_o);
	Tcl_DecrRefCount(rec_o);
	Tcl_DecrRefCount(eid_o);
	for (i = 0; i < myobjc; i++)
		Tcl_DecrRefCount(myobjv[i]);
	Tcl_DecrRefCount(flags_o);
	Tcl_DecrRefCount(lsnobj[0]);
	Tcl_DecrRefCount(lsnobj[1]);
	Tcl_DecrRefCount(lsn_o);

	return (ret);
}
#endif

#ifdef CONFIG_TEST
/*
 * tcl_db_malloc, tcl_db_realloc, tcl_db_free --
 *	Tcl-local malloc, realloc, and free functions to use for user data
 * to exercise umalloc/urealloc/ufree.  Allocate the memory as a Tcl object
 * so we're sure to exacerbate and catch any shared-library issues.
 */
static void *
tcl_db_malloc(size)
	size_t size;
{
	Tcl_Obj *obj;
	void *buf;

	obj = Tcl_NewObj();
	if (obj == NULL)
		return (NULL);
	Tcl_IncrRefCount(obj);

	Tcl_SetObjLength(obj, (int)(size + sizeof(Tcl_Obj *)));
	buf = Tcl_GetString(obj);
	memcpy(buf, &obj, sizeof(&obj));

	buf = (Tcl_Obj **)buf + 1;
	return (buf);
}

static void *
tcl_db_realloc(ptr, size)
	void *ptr;
	size_t size;
{
	Tcl_Obj *obj;

	if (ptr == NULL)
		return (tcl_db_malloc(size));

	obj = *(Tcl_Obj **)((Tcl_Obj **)ptr - 1);
	Tcl_SetObjLength(obj, (int)(size + sizeof(Tcl_Obj *)));

	ptr = Tcl_GetString(obj);
	memcpy(ptr, &obj, sizeof(&obj));

	ptr = (Tcl_Obj **)ptr + 1;
	return (ptr);
}

static void
tcl_db_free(ptr)
	void *ptr;
{
	Tcl_Obj *obj;

	obj = *(Tcl_Obj **)((Tcl_Obj **)ptr - 1);
	Tcl_DecrRefCount(obj);
}

static int
tcl_set_partition_keys(interp, dbp, obj, keyp)
	Tcl_Interp *interp;
	DB *dbp;
	Tcl_Obj *obj;
	DBT **keyp;
{
	DBT *keys, *kp;
	Tcl_Obj **obj_list;
	u_int32_t i, count;
	int ret;

	*keyp = NULL;
	if ((ret = Tcl_ListObjGetElements(interp,
	    obj, (int *)&count, &obj_list)) != TCL_OK)
		return (EINVAL);

	if ((ret = __os_calloc(NULL, count, sizeof(DBT), &keys)) != 0)
		return (ret);

	*keyp = keys;

	kp = keys;
	for (i = 0; i < count; i++, kp++)
		kp->data = Tcl_GetStringFromObj(obj_list[i], (int*)&kp->size);

	if ((ret = dbp->set_partition(dbp,
	     (u_int32_t)count + 1, keys, NULL)) != 0)
		return (ret);

	return (0);
}

static int
tcl_set_partition_dirs(interp, dbp, obj)
	Tcl_Interp *interp;
	DB *dbp;
	Tcl_Obj *obj;
{
	char **dp, **dirs;
	Tcl_Obj **obj_list;
	u_int32_t i, count;
	int ret;

	if ((ret = Tcl_ListObjGetElements(interp,
	    obj, (int*)&count, &obj_list)) != TCL_OK)
		return (EINVAL);

	if ((ret = __os_calloc(NULL, count + 1, sizeof(char *), &dirs)) != 0)
		return (ret);

	dp = dirs;
	for (i = 0; i < count; i++, dp++)
		*dp = Tcl_GetStringFromObj(obj_list[i], NULL);

	if ((ret = dbp->set_partition_dirs(dbp, (const char **)dirs)) != 0)
		return (ret);

	__os_free(NULL, dirs);

	return (0);
}
#endif

static int
heap_callback(dbp, key, data, seckey)
     DB *dbp;
     const DBT *key, *data;
     DBT *seckey;
{
	COMPQUIET(dbp, NULL);
	COMPQUIET(key, NULL);
	memset(seckey, 0, sizeof(DBT));
	seckey->data = data->data;
	seckey->size = data->size;
	return 0;
}
