summaryrefslogtreecommitdiff
path: root/db/tcl
diff options
context:
space:
mode:
Diffstat (limited to 'db/tcl')
-rw-r--r--db/tcl/docs/db.html266
-rw-r--r--db/tcl/docs/env.html303
-rw-r--r--db/tcl/docs/historic.html168
-rw-r--r--db/tcl/docs/index.html47
-rw-r--r--db/tcl/docs/library.html26
-rw-r--r--db/tcl/docs/lock.html187
-rw-r--r--db/tcl/docs/log.html142
-rw-r--r--db/tcl/docs/mpool.html189
-rw-r--r--db/tcl/docs/test.html149
-rw-r--r--db/tcl/docs/txn.html56
-rw-r--r--db/tcl/tcl_compat.c1055
-rw-r--r--db/tcl/tcl_db.c1771
-rw-r--r--db/tcl/tcl_db_pkg.c2246
-rw-r--r--db/tcl/tcl_dbcursor.c744
-rw-r--r--db/tcl/tcl_env.c678
-rw-r--r--db/tcl/tcl_internal.c440
-rw-r--r--db/tcl/tcl_lock.c655
-rw-r--r--db/tcl/tcl_log.c581
-rw-r--r--db/tcl/tcl_mp.c822
-rw-r--r--db/tcl/tcl_txn.c473
20 files changed, 10998 insertions, 0 deletions
diff --git a/db/tcl/docs/db.html b/db/tcl/docs/db.html
new file mode 100644
index 000000000..c75ab6ecf
--- /dev/null
+++ b/db/tcl/docs/db.html
@@ -0,0 +1,266 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.08 [en] (X11; I; FreeBSD 3.3-RELEASE i386) [Netscape]">
+</HEAD>
+<BODY>
+
+<H2>
+<A NAME="Database Commands"></A>Database Commands</H2>
+The database commands provide a conduit into the DB method functions.&nbsp;
+They are all fairly straightforward and I describe them in terms of their
+DB functions briefly here, with a link to the DB page where appropriate.&nbsp;
+The first set of commands are those I believe will be the primary functions
+used by most databases.&nbsp; Some are directly related to their DB counterparts,
+and some are higher level functions that are useful to provide the user.
+<P><B>> berkdb open [-env <I>env</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-btree|-hash|-recno|-queue|-unknown]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-create] [-excl] [-nommap] [-rdonly] [-truncate]
+[-mode
+<I>mode</I>] [-errfile <I>filename</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-dup] [-dupsort] [-recnum] [-renumber] [-revsplitoff]
+[-snapshot]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-extent <I>size</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-ffactor <I>density</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-nelem <I>size</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-lorder <I>order</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-delim <I>delim</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-len <I>len</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-pad <I>pad</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-source <I>file</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-minkey <I>minkey</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-cachesize {<I>gbytes bytes ncaches</I>}]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-pagesize <I>pagesize</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [--]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [<I>filename </I>[<I>subdbname</I>]]</B>
+<P>This command will invoke the <A HREF="../../docs/api_c/db_create.html">db_create</A>
+function.&nbsp; If the command is given the <B>-env</B> option, then we
+will accordingly creating the database within the context of that environment.&nbsp;
+After it successfully gets a handle to a database, we bind it to a new
+Tcl command of the form <B><I>dbX, </I></B>where X is an integer starting
+at 0 (e.g. <B>db0, db1, </B>etc).&nbsp; We use the <I>Tcl_CreateObjCommand()&nbsp;</I>
+to create the top level database function.&nbsp; It is through this handle
+that the user can access all of the commands described in the <A HREF="#Database Commands">Database
+Commands</A> section.&nbsp; Internally, the database handle is sent as
+the <I>ClientData</I> portion of the new command set so that all future
+database calls access the appropriate handle.
+<P>After parsing all of the optional arguments affecting the setup of the
+database and making the appropriate calls to DB to manipulate those values,
+we open the database for the user. It&nbsp; translates to the
+<A HREF="../../docs/api_c/db_open.html">DB->open</A>
+method call after parsing all of the various optional arguments.&nbsp;
+We automatically set the DB_THREAD flag.&nbsp; The arguments are:
+<UL>
+<LI>
+<B>-- </B>- Terminate the list of options and use remaining arguments as
+the file or subdb names (thus allowing the use of filenames beginning with
+a dash '-')</LI>
+
+<LI>
+<B>-btree</B> - DB_BTREE database</LI>
+
+<LI>
+<B>-hash</B> -&nbsp; DB_HASH database</LI>
+
+<LI>
+<B>-recno&nbsp;</B> - DB_RECNO database</LI>
+
+<LI>
+<B>-queue</B> - DB_QUEUE database</LI>
+
+<LI>
+<B>-create</B> selects the DB_CREATE flag&nbsp; to create underlying files</LI>
+
+<LI>
+<B>-excl</B> selects the DB_EXCL flag&nbsp; to exclusively create underlying
+files</LI>
+
+<LI>
+<B>-nommap</B> selects the DB_NOMMAP flag to forbid mmaping of files</LI>
+
+<LI>
+<B>-rdonly</B> selects the DB_RDONLY flag for opening in read-only mode</LI>
+
+<LI>
+<B>-truncate</B> selects the DB_TRUNCATE flag to truncate the database</LI>
+
+<LI>
+<B>-mode<I> mode</I></B> specifies the mode for created files</LI>
+
+<LI>
+<B>-errfile </B>specifies the error file to use for this environment to
+<B><I>filename</I></B>
+by calling <A HREF="../../docs/api_c/db_set_errfile.html">DB->set_errfile</A><B><I>.
+</I></B>If
+the file already exists then we will append to the end of the file</LI>
+
+<LI>
+<B>-dup </B>selects the DB_DUP flag to permit duplicates in the database</LI>
+
+<LI>
+<B>-dupsort</B> selects the DB_DUPSORT flag to support sorted duplicates</LI>
+
+<LI>
+<B>-recnum</B> selects the DB_RECNUM flag to support record numbers in
+btrees</LI>
+
+<LI>
+<B>-renumber </B>selects the DB_RENUMBER flag to support mutable record
+numbers</LI>
+
+<LI>
+<B>-revsplitoff </B>selects the DB_REVSPLITOFF flag to suppress reverse
+splitting of pages on deletion</LI>
+
+<LI>
+<B>-snapshot </B>selects the DB_SNAPSHOT flag to support database snapshots</LI>
+
+<LI>
+<B>-extent </B>sets the size of a Queue database extent to the given <B><I>size
+</I></B>using
+the <A HREF="../../docs/api_c/db_set_q_extentsize.html">DB->set_q_extentsize</A>
+method</LI>
+
+<LI>
+<B>-ffactor</B> sets the hash table key density to the given <B><I>density
+</I></B>using
+the <A HREF="../../docs/api_c/db_set_h_ffactor.html">DB->set_h_ffactor</A>
+method</LI>
+
+<LI>
+<B>-nelem </B>sets the hash table size estimate to the given <B><I>size
+</I></B>using
+the <A HREF="../../docs/api_c/db_set_h_nelem.html">DB->set_h_nelem</A>
+method</LI>
+
+<LI>
+<B>-lorder </B>sets the byte order for integers stored in the database
+meta-data to the given <B><I>order</I></B> using the <A HREF="../../docs/api_c/db_set_lorder.html">DB->set_lorder</A>
+method</LI>
+
+<LI>
+<B>-delim </B>sets the delimiting byte for variable length records to
+<B><I>delim</I></B>
+using the <A HREF="../../docs/api_c/db_set_re_delim.html">DB->set_re_delim</A>
+method</LI>
+
+<LI>
+<B>-len </B>sets the length of fixed-length records to <B><I>len</I></B>
+using the <A HREF="../../docs/api_c/db_set_re_len.html">DB->set_re_len</A>
+method</LI>
+
+<LI>
+<B>-pad </B>sets the pad character used for fixed length records to
+<B><I>pad</I></B>&nbsp;
+using the <A HREF="../../docs/db_set_re_pad.html">DB->set_re_pad</A> method</LI>
+
+<LI>
+<B>-source </B>sets the backing source file name to <B><I>file</I></B>
+using the <A HREF="../../docs/api_c/db_set_re_source.html">DB->set_re_source</A>
+method</LI>
+
+<LI>
+<B>-minkey </B>sets the minimum number of keys per Btree page to <B><I>minkey</I></B>
+using the <A HREF="../../docs/api_c/db_set_bt_minkey.html">DB->set_bt_minkey</A>
+method</LI>
+
+<LI>
+<B>-cachesize </B>sets the size of the database cache to the size&nbsp;
+specified by <B><I>gbytes </I></B>and <B><I>bytes, </I></B>broken up into
+<B><I>ncaches</I></B>
+number of caches using the <A HREF="../../docs/api_c/db_set_cachesize.html">DB->set_cachesize</A>
+method</LI>
+
+<LI>
+<B>-pagesize </B>sets the size of the database page to <B><I>pagesize </I></B>using
+the <A HREF="../../docs/api_c/db_set_pagesize.html">DB->set_pagesize</A>
+method</LI>
+
+<LI>
+<B><I>filename</I></B> indicates the name of the database</LI>
+
+<LI>
+<B><I>subdbname</I></B> indicate the name of the sub-database</LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><B>&nbsp;berkdb upgrade [-dupsort] [-env <I>env</I>] [--] [<I>filename</I>]</B>
+<P>This command will invoke the <A HREF="../../docs/api_c/db_upgrade.html">DB->upgrade</A>
+function.&nbsp; If the command is given the <B>-env</B> option, then we
+will accordingly upgrade the database filename within the context of that
+environment. The <B>-dupsort</B> option selects the DB_DUPSORT flag for
+upgrading. The use of --<B> </B>terminates the list of options, thus allowing
+filenames beginning with a dash.
+<P>
+<HR WIDTH="100%"><B>> berkdb verify [-env <I>env</I>] [--] [<I>filename</I>]</B>
+<P>This command will invoke the <A HREF="../../docs/api_c/db_verify.html">DB->verify</A>
+function.&nbsp; If the command is given the <B>-env</B> option, then we
+will accordingly verify the database filename within the context of that
+environment.&nbsp; The use of --<B> </B>terminates the list of options,
+thus allowing filenames beginning with a dash.
+<P>
+<HR WIDTH="100%"><B>> <I>db</I> join [-nosort] <I>db0.c0 db1.c0</I> ...</B>
+<P>This command will invoke the <A HREF="../../docs/api_c/db_join.html">db_join</A>
+function.&nbsp; After it successfully joins a database, we bind it to a
+new Tcl command of the form <B><I>dbN.cX, </I></B>where X is an integer
+starting at 0 (e.g. <B>db2.c0, db3.c0, </B>etc).&nbsp; We use the <I>Tcl_CreateObjCommand()&nbsp;</I>
+to create the top level database function.&nbsp; It is through this cursor
+handle that the user can access the joined data items.
+<P>The options are:
+<UL>
+<LI>
+<B>-nosort -</B> This flag causes DB not to sort the cursors based on the
+number of data items they reference.&nbsp; It results in the DB_JOIN_NOSORT
+flag being set.</LI>
+</UL>
+
+<HR WIDTH="100%"><B>> <I>db</I> get_join [-nosort] {db key} {db key} ...</B>
+<P>This command performs a join operation on the keys specified and returns
+a list of the joined {key data} pairs.
+<P>The options are:
+<UL>
+<LI>
+<B>-nosort</B> This flag causes DB not to sort the cursors based on the
+number of data items they reference.&nbsp; It results in the DB_JOIN_NOSORT
+flag being set.</LI>
+</UL>
+
+<HR WIDTH="100%"><B>> <I>db</I> keyrange [-txn <I>id</I>] key</B>
+<P>This command returns the range for the given <B>key</B>.&nbsp; It returns
+a list of 3 double elements of the form {<B><I>less equal greater</I></B>}
+where <B><I>less</I></B> is the percentage of keys less than the given
+key, <B><I>equal</I></B> is the percentage equal to the given key and <B><I>greater</I></B>
+is the percentage greater than the given key.&nbsp; If the -txn option
+is specified it performs this operation under transaction protection.
+<BR>
+<HR WIDTH="100%"><B>> <I>db</I> put</B>
+<P>The <B>undocumented</B> options are:
+<UL>
+<LI>
+<B>-nodupdata</B> This flag causes DB not to insert the key/data pair if
+it already exists, that is, both the key and data items are already in
+the database. The -nodupdata flag may only be specified if the underlying
+database has been configured to support sorted duplicates.</LI>
+</UL>
+
+<HR WIDTH="100%"><B>> <I>db</I> stat</B>
+<P>The <B>undocumented</B> options are:
+<UL>
+<LI>
+<B>-cachedcounts</B> This flag causes DB to return the cached key/record
+counts, similar to the DB_CACHED_COUNTS flags to DB->stat.</LI>
+</UL>
+
+<HR WIDTH="100%"><B>> <I>dbc</I> put</B>
+<P>The <B>undocumented</B> options are:
+<UL>
+<LI>
+<B>-nodupdata</B> This flag causes DB not to insert the key/data pair if
+it already exists, that is, both the key and data items are already in
+the database. The -nodupdata flag may only be specified if the underlying
+database has been configured to support sorted duplicates.</LI>
+</UL>
+
+</BODY>
+</HTML>
diff --git a/db/tcl/docs/env.html b/db/tcl/docs/env.html
new file mode 100644
index 000000000..a1bd08fd1
--- /dev/null
+++ b/db/tcl/docs/env.html
@@ -0,0 +1,303 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.08 [en] (X11; I; FreeBSD 3.3-RELEASE i386) [Netscape]">
+</HEAD>
+<BODY>
+
+<H2>
+Environment Commands</H2>
+Environments provide a structure for creating a consistent environment
+for processes using one or more of the features of Berkeley DB.&nbsp; Unlike
+some of the database commands, the environment commands are very low level.
+<BR>
+<HR WIDTH="100%">
+<P>The user may create and open a new DB environment&nbsp; by invoking:
+<P><B>> berkdb env</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-cdb] [-cdb_alldb] [-lock] [-log] [-txn [nosync]]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-create] [-home<I> directory</I>] [-mode <I>mode</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-data_dir <I>directory</I>] [-log_dir <I>directory</I>]
+[-tmp_dir <I>directory</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-nommap] [-private] [-recover] [-recover_fatal]
+[-system_mem] [-errfile <I>filename</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-use_environ] [-use_environ_root] [-verbose
+{<I>which </I>on|off}]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-region_init]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-cachesize {<I>gbytes bytes ncaches</I>}]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-mmapsize<I> size</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-log_max <I>max</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-log_buffer <I>size</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-lock_conflict {<I>nmodes </I>{<I>matrix</I>}}]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-lock_detect default|oldest|random|youngest]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-lock_max <I>max</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-lock_max_locks <I>max</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-lock_max_lockers <I>max</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-lock_max_objects <I>max</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-txn_max <I>max</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-client_timeout <I>seconds</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-server_timeout <I>seconds</I>]</B>
+<BR><B>&nbsp;&nbsp;&nbsp; [-server <I>hostname</I>]</B>
+<BR>&nbsp;
+<P>This command opens up an environment.&nbsp;&nbsp; We automatically set
+the DB_THREAD and the DB_INIT_MPOOL flags.&nbsp; The arguments are:
+<UL>
+<LI>
+<B>-cdb</B> selects the DB_INIT_CDB flag for Concurrent Data Store</LI>
+
+<LI>
+<B>-cdb_alldb</B> selects the DB_CDB_ALLDB flag for Concurrent Data Store</LI>
+
+<LI>
+<B>-lock</B> selects the DB_INIT_LOCK flag for the locking subsystem</LI>
+
+<LI>
+<B>-log</B> selects the DB_INIT_LOG flag for the logging subsystem</LI>
+
+<LI>
+<B>-txn</B> selects the DB_INIT_TXN, DB_INIT_LOCK and DB_INIT_LOG flags
+for the transaction subsystem.&nbsp; If <B>nosync</B> is specified, then
+it will also select DB_TXN_NOSYNC to indicate no flushes of log on commits</LI>
+
+<LI>
+<B>-create </B>selects the DB_CREATE flag to create underlying files</LI>
+
+<LI>
+<B>-home <I>directory </I></B>selects the home directory of the environment</LI>
+
+<LI>
+<B>-data_dir <I>directory </I></B>selects the data file directory of the
+environment by calling <A HREF="../../docs/api_c/env_set_data_dir.html">DBENV->set_data_dir</A>.</LI>
+
+<LI>
+<B>-log_dir <I>directory </I></B>selects the log file directory of the
+environment&nbsp; by calling <A HREF="../../docs/api_c/env_set_lg_dir.html">DBENV->set_lg_dir</A>.</LI>
+
+<LI>
+<B>-tmp_dir <I>directory </I></B>selects the temporary file directory of
+the environment&nbsp; by calling <A HREF="../../docs/api_c/env_set_tmp_dir.so">DBENV->set_tmp_dir</A>.</LI>
+
+<LI>
+<B>-mode <I>mode </I></B>sets the permissions of created files to <B><I>mode</I></B></LI>
+
+<LI>
+<B>-nommap</B> selects the DB_NOMMAP flag to disallow using mmap'ed files</LI>
+
+<LI>
+<B>-private</B> selects the DB_PRIVATE flag for a private environment</LI>
+
+<LI>
+<B>-recover</B> selects the DB_RECOVER flag for recovery</LI>
+
+<LI>
+<B>-recover_fatal</B> selects the DB_RECOVER_FATAL flag for catastrophic
+recovery</LI>
+
+<LI>
+<B>-system_mem</B> selects the DB_SYSTEM_MEM flag to use system memory</LI>
+
+<LI>
+<B>-errfile </B>specifies the error file to use for this environment to
+<B><I>filename</I></B>
+by calling <A HREF="../../docs/api_c/env_set_errfile.html">DBENV->set_errfile</A><B><I>.
+</I></B>If
+the file already exists then we will append to the end of the file</LI>
+
+<LI>
+<B>-use_environ</B> selects the DB_USE_ENVIRON flag to affect file naming</LI>
+
+<LI>
+<B>-use_environ_root</B> selects the DB_USE_ENVIRON_ROOT flag to have the
+root environment affect file naming</LI>
+
+<LI>
+<B>-verbose</B> produces verbose error output for the given which subsystem,
+using the <A HREF="../../docs/api_c/dbenv_set_verbose.html">DBENV->set_verbose</A>
+method.&nbsp;&nbsp; See the description of <A HREF="#> <env> verbose which on|off">verbose</A>
+below for valid <B><I>which </I></B>values</LI>
+
+<LI>
+<B>-region_init </B>specifies that the user wants to page fault the region
+in on startup using the <A HREF="../../docs/api_c/env_set_region_init.html">DBENV->set_region_init</A>
+method call</LI>
+
+<LI>
+<B>-cachesize </B>sets the size of the database cache to the size&nbsp;
+specified by <B><I>gbytes </I></B>and <B><I>bytes, </I></B>broken up into
+<B><I>ncaches</I></B>
+number of caches using the <A HREF="../../docs/api_c/env_set_cachesize.html">DBENV->set_cachesize</A>
+method</LI>
+
+<LI>
+<B>-mmapsize </B>sets the size of the database page to <B><I>size </I></B>using
+the <A HREF="../../docs/api_c/env_set_mp_mmapsize.html">DBENV->set_mp_mmapsize</A>
+method</LI>
+
+<LI>
+<B>-log_max </B>sets the maximum size of the log file to <B><I>max</I></B>
+using the <A HREF="../../docs/api_c/env_set_lg_max.html">DBENV->set_lg_max</A>
+call</LI>
+
+<LI>
+<B>-log_buffer </B>sets the size of the log file in bytes to <B><I>size</I></B>
+using the <A HREF="../../docs/api_c/env_set_lg_bsize.html">DBENV->set_lg_bsize</A>
+call</LI>
+
+<LI>
+<B>-lock_conflict </B>sets the number of lock modes to <B><I>nmodes</I></B>
+and sets the locking policy for those modes to the <B><I>conflict_matrix</I></B>
+given using the <A HREF="../../docs/api_c/env_set_lk_conflict.html">DBENV->set_lk_conflict</A>
+method call</LI>
+
+<LI>
+<B>-lock_detect </B>sets the deadlock detection policy to the given policy
+using the <A HREF="../../docs/env_set_lk_detect.html">DBENV->set_lk_detect</A>
+method call.&nbsp; The policy choices are:</LI>
+
+<UL>
+<LI>
+<B>default</B> selects the DB_LOCK_DEFAULT policy for default detection</LI>
+
+<LI>
+<B>oldest </B>selects DB_LOCK_OLDEST to abort the oldest locker on a deadlock</LI>
+
+<LI>
+<B>random</B> selects DB_LOCK_RANDOM to abort a random locker on a deadlock</LI>
+
+<LI>
+<B>youngest</B> selects DB_LOCK_YOUNGEST to abort the youngest locker on
+a deadlock</LI>
+</UL>
+
+<LI>
+<B>-lock_max </B>sets the maximum size of the lock table to <B><I>max </I></B>using
+the <A HREF="../../docs/api_c/env_set_lk_max.html">DBENV->set_lk_max</A>
+method call</LI>
+
+<LI>
+<B>-lock_max_locks </B>sets the maximum number of locks to <B><I>max </I></B>using
+the <A HREF="../../docs/api_c/env_set_lk_max_locks.html">DBENV->set_lk_max_locks</A>
+method call</LI>
+
+<LI>
+<B>-lock_max_lockers </B>sets the maximum number of locking entities to
+<B><I>max </I></B>using the <A HREF="../../docs/api_c/env_set_lk_max_lockers.html">DBENV->set_lk_max_lockers</A>
+method call</LI>
+
+<LI>
+<B>-lock_max_objects </B>sets the maximum number of simultaneously locked
+objects to <B><I>max </I></B>using the <A HREF="../../docs/api_c/env_set_lk_max_objects.html">DBENV->set_lk_max_objects</A>
+method call</LI>
+
+<LI>
+<B>-txn_max </B>sets the maximum size of the transaction table to <B><I>max</I></B>
+using the <A HREF="../../docs/api_c/env_set_txn_max.html">DBENV->set_txn_max</A>
+method call</LI>
+
+<LI>
+<B>-client_timeout</B> sets the timeout value for the client waiting for
+a reply from the server for RPC operations to <B><I>seconds</I></B>.</LI>
+
+<LI>
+<B>-server_timeout</B> sets the timeout value for the server to determine
+an idle client is gone to <B><I>seconds</I></B>.</LI>
+
+<LI>
+<B>&nbsp;-server </B>specifies the <B><I>hostname</I></B> of the server
+to connect to in the <A HREF="../../docs/api_c/env_set_server.html">DBENV->set_server</A>
+call.</LI>
+</UL>
+This command will invoke the <A HREF="../../docs/api_c/env_create.html">db_env_create</A>
+function.&nbsp; After it successfully gets a handle to an environment,
+we bind it to a new Tcl command of the form <B><I>envX</I></B>, where X
+is an integer starting at&nbsp; 0 (e.g. <B>env0, env1, </B>etc).&nbsp;
+We use the <I>Tcl_CreateObjCommand()</I> to create the top level environment
+command function.&nbsp; It is through this handle that the user can access
+all the commands described in the <A HREF="#Environment Commands">Environment
+Commands</A> section.&nbsp; Internally, the handle we get back from DB
+will be stored as the <I>ClientData</I> portion of the new command set
+so that all future environment calls will have that handle readily available.&nbsp;
+Then we call the <A HREF="../../docs/api_c/env_open.html">DBENV->open</A>
+method call and possibly some number of setup calls as described above.
+<P>
+<HR WIDTH="100%">
+<BR><A NAME="> <env> verbose which on|off"></A><B>> &lt;env> verbose <I>which</I>
+on|off</B>
+<P>This command controls the use of debugging output for the environment.&nbsp;
+This command directly translates to a call to the <A HREF="../../docs/api_c/dbenv_set_verbose.html">DBENV->set_verbose</A>
+method call.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message.&nbsp; The user specifies
+<B><I>which</I></B>
+subsystem to control, and indicates whether debug messages should be turned
+<B>on</B>
+or <B>off</B> for that subsystem.&nbsp; The value of <B><I>which</I></B>
+must be one of the following:
+<UL>
+<LI>
+<B>chkpt</B> - Chooses the checkpointing code by using the DB_VERB_CHKPOINT
+value</LI>
+
+<LI>
+<B>deadlock </B>- Chooses the deadlocking code by using the DB_VERB_DEADLOCK
+value</LI>
+
+<LI>
+<B>recovery </B>- Chooses the recovery code by using the DB_VERB_RECOVERY
+value</LI>
+
+<LI>
+<B>wait </B>- Chooses the waitsfor code by using the DB_VERB_WAITSFOR value</LI>
+</UL>
+
+<HR WIDTH="100%">
+<P><A NAME="> <env> close"></A><B>> &lt;env> close</B>
+<P>This command closes an environment and deletes the handle.&nbsp; This
+command directly translates to a call to the <A HREF="../../docs/api_c/env_close.html">DBENV->close</A>
+method call.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message.
+<P>Additionally, since the handle is no longer valid, we will call <I>Tcl_DeleteCommand()
+</I>so
+that further uses of the handle will be dealt with properly by Tcl itself.
+<P>Also, the close command will automatically abort any <A HREF="txn.html">transactions</A>
+and close any <A HREF="mpool.html">mpool</A> memory files.&nbsp; As such
+we must maintain a list of open transaction and mpool handles so that we
+can call <I>Tcl_DeleteCommand</I> on those as well.
+<P>
+<HR WIDTH="100%">
+<BR><B>> berkdb envremove [-data_dir <I>directory</I>] [-force] [-home
+<I>directory</I>]
+-log_dir <I>directory</I>] [-tmp_dir <I>directory</I>] [-use_environ] [-use_environ_root]</B>
+<P>This command removes the environment if it is not in use and deletes
+the handle.&nbsp; This command directly translates to a call to the <A HREF="../../docs/api_c/env_remove.html">DBENV->remove</A>
+method call.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message.&nbsp; The arguments are:
+<UL>
+<LI>
+<B>-force</B> selects the DB_FORCE flag to remove even if other processes
+have the environment open</LI>
+
+<LI>
+<B>-home <I>directory</I> </B>specifies the home directory of the environment</LI>
+
+<LI>
+<B>-data_dir <I>directory </I></B>selects the data file directory of the
+environment by calling <A HREF="../../docs/api_c/env_set_data_dir.html">DBENV->set_data_dir</A>.</LI>
+
+<LI>
+<B>-log_dir <I>directory </I></B>selects the log file directory of the
+environment&nbsp; by calling <A HREF="../../docs/api_c/env_set_lg_dir.html">DBENV->set_lg_dir</A>.</LI>
+
+<LI>
+<B>-tmp_dir <I>directory </I></B>selects the temporary file directory of
+the environment&nbsp; by calling <A HREF="../../docs/api_c/env_set_tmp_dir.so">DBENV->set_tmp_dir</A>.</LI>
+
+<LI>
+<B>-use_environ </B>selects the DB_USE_ENVIRON flag to affect file naming</LI>
+
+<LI>
+<B>-use_environ_root</B> selects the DB_USE_ENVIRON_ROOT flag to affect
+file naming</LI>
+</UL>
+
+</BODY>
+</HTML>
diff --git a/db/tcl/docs/historic.html b/db/tcl/docs/historic.html
new file mode 100644
index 000000000..216dc456b
--- /dev/null
+++ b/db/tcl/docs/historic.html
@@ -0,0 +1,168 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.08 [en] (X11; I; FreeBSD 2.2.8-19990120-SNAP i386) [Netscape]">
+</HEAD>
+<BODY>
+
+<H2>
+<A NAME="Compatibility Commands"></A>Compatibility Commands</H2>
+The compatibility commands for old Dbm and Ndbm are described in the <A HREF="../../docs/api_c/dbm.html">dbm</A>
+manpage.
+<P><B>> berkdb dbminit <I>filename</I></B>
+<P>This command will invoke the dbminit function.&nbsp;&nbsp; <B><I>Filename</I></B>
+is used as the name of the database.
+<P>
+<HR WIDTH="100%"><B>> berkdb dbmclose</B>
+<P>This command will invoke the dbmclose function.
+<P>
+<HR WIDTH="100%"><B>> berkdb fetch <I>key</I></B>
+<P>This command will invoke the fetch function.&nbsp;&nbsp; It will return
+the data associated with the given <B><I>key </I></B>or a Tcl error.
+<P>
+<HR WIDTH="100%"><B>> berkdb store <I>key data</I></B>
+<P>This command will invoke the store function.&nbsp;&nbsp; It will store
+the <B><I>key/data</I></B> pair.&nbsp; It will return a 0 on success or
+throw a Tcl error.
+<P>
+<HR WIDTH="100%"><B>> berkdb delete <I>key</I></B>
+<P>This command will invoke the deletet function.&nbsp;&nbsp; It will delete
+the <B><I>key</I></B> from the database.&nbsp; It will return a 0 on success
+or throw a Tcl error.
+<P>
+<HR WIDTH="100%"><B>> berkdb firstkey</B>
+<P>This command will invoke the firstkey function.&nbsp;&nbsp; It will
+return the first key in the database or a Tcl error.
+<P>
+<HR WIDTH="100%"><B>> berkdb nextkey <I>key</I></B>
+<P>This command will invoke the nextkey function.&nbsp;&nbsp; It will return
+the next key after the given <B><I>key</I></B> or a Tcl error.
+<P>
+<HR WIDTH="100%"><B>> berkdb hcreate <I>nelem</I></B>
+<P>This command will invoke the hcreate function with <B><I>nelem</I></B>
+elements.&nbsp; It will return a 0 on success or a Tcl error.
+<P>
+<HR WIDTH="100%"><B>> berkdb hsearch <I>key data action</I></B>
+<P>This command will invoke the hsearch function with <B><I>key</I></B>
+and <B><I>data</I></B>.&nbsp; The <B><I>action</I></B> must be either <B>find</B>
+or <B>enter</B>.&nbsp; If it is <B>find</B>, it will return the resultant
+data.&nbsp; If it is <B>enter</B>, it will return a 0 on success or a Tcl
+error.
+<P>
+<HR WIDTH="100%"><B>> berkdb hdestroy</B>
+<P>This command will invoke the hdestroy function.&nbsp; It will return
+a 0.
+<HR WIDTH="100%"><B>> berkdb ndbm_open [-create] [-rdonly] [-truncate]
+[-mode
+<I>mode</I>] [--] <I>filename</I></B>
+<P>This command will invoke the dbm_open function.&nbsp;&nbsp;&nbsp; After
+it successfully gets a handle to a database, we bind it to a new Tcl command
+of the form <B><I>ndbmX, </I></B>where X is an integer starting at 0 (e.g.
+<B>ndbm0,
+ndbm1, </B>etc).&nbsp; We use the <I>Tcl_CreateObjCommand()&nbsp;</I> to
+create the top level database function.&nbsp; It is through this handle
+that the user can access all of the commands described below.&nbsp; Internally,
+the database handle is sent as the <I>ClientData</I> portion of the new
+command set so that all future database calls access the appropriate handle.
+<P>The arguments are:
+<UL>
+<LI>
+<B>-- </B>- Terminate the list of options and use remaining arguments as
+the file or subdb names (thus allowing the use of filenames beginning with
+a dash '-')</LI>
+
+<LI>
+<B>-create</B> selects the O_CREAT flag&nbsp; to create underlying files</LI>
+
+<LI>
+<B>-rdonly</B> selects the O_RDONLY flag for opening in read-only mode</LI>
+
+<LI>
+<B>-truncate</B> selects the O_TRUNC flag to truncate the database</LI>
+
+<LI>
+<B>-mode<I> mode</I></B> specifies the mode for created files</LI>
+
+<LI>
+<B><I>filename</I></B> indicates the name of the database</LI>
+</UL>
+
+<P><BR>
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> close</B>
+<P>This command closes the database and renders the handle invalid.&nbsp;&nbsp;
+This command directly translates to the dbm_close function call.&nbsp;
+It returns either a 0 (for success),&nbsp; or it throws a Tcl error with
+a system message.
+<P>Additionally, since the handle is no longer valid, we will call <I>Tcl_DeleteCommand()
+</I>so
+that further uses of the handle will be dealt with properly by Tcl itself.&nbsp;
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> clearerr</B>
+<P>This command clears errors&nbsp; the database.&nbsp;&nbsp; This command
+directly translates to the dbm_clearerr function call.&nbsp; It returns
+either a 0 (for success),&nbsp; or it throws a Tcl error with a system
+message.
+<P>
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> delete <I>key</I></B>
+<P>This command deletes the <B><I>key</I></B> from thedatabase.&nbsp;&nbsp;
+This command directly translates to the dbm_delete function call.&nbsp;
+It returns either a 0 (for success),&nbsp; or it throws a Tcl error with
+a system message.
+<P>
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> dirfno</B>
+<P>This command directly translates to the dbm_dirfno function call.&nbsp;
+It returns either resultts,&nbsp; or it throws a Tcl error with a system
+message.
+<P>
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> error</B>
+<P>This command returns the last error.&nbsp;&nbsp; This command directly
+translates to the dbm_error function call.&nbsp; It returns an error string..
+<P>
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> fetch <I>key</I></B>
+<P>This command gets the given <B><I>key</I></B> from the database.&nbsp;&nbsp;
+This command directly translates to the dbm_fetch function call.&nbsp;
+It returns either the data,&nbsp; or it throws a Tcl error with a system
+message.
+<P>
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> firstkey</B>
+<P>This command returns the first key in the database.&nbsp;&nbsp; This
+command directly translates to the dbm_firstkey function call.&nbsp; It
+returns either the key,&nbsp; or it throws a Tcl error with a system message.
+<P>
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> nextkey</B>
+<P>This command returns the next key in the database.&nbsp;&nbsp; This
+command directly translates to the dbm_nextkey function call.&nbsp; It
+returns either the key,&nbsp; or it throws a Tcl error with a system message.
+<P>
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> pagfno</B>
+<P>This command directly translates to the dbm_pagfno function call.&nbsp;
+It returns either resultts,&nbsp; or it throws a Tcl error with a system
+message.
+<BR>
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> rdonly</B>
+<P>This command changes the database to readonly.&nbsp;&nbsp; This command
+directly translates to the dbm_rdonly function call.&nbsp; It returns either
+a 0 (for success),&nbsp; or it throws a Tcl error with a system message.
+<P>
+<HR WIDTH="100%">
+<BR><B>> &lt;ndbm> store <I>key data </I>insert|replace</B>
+<P>This command puts the given <B><I>key</I></B> and <B><I>data</I></B>
+pair into the database.&nbsp;&nbsp; This command directly translates to
+the dbm_store function call.&nbsp; It will either <B>insert</B> or <B>replace</B>
+the data based on the action given in the third argument.&nbsp; It returns
+either a 0 (for success),&nbsp; or it throws a Tcl error with a system
+message.
+<BR>
+<HR WIDTH="100%">
+</BODY>
+</HTML>
diff --git a/db/tcl/docs/index.html b/db/tcl/docs/index.html
new file mode 100644
index 000000000..2866c1e23
--- /dev/null
+++ b/db/tcl/docs/index.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.08 [en] (X11; I; FreeBSD 2.2.8-19990120-SNAP i386) [Netscape]">
+</HEAD>
+<BODY>
+
+<CENTER>
+<H1>
+Complete Tcl Interface for Berkeley DB</H1></CENTER>
+
+<UL type=disc>
+<LI>
+<A HREF="../../docs/api_tcl/tcl_index.html">General use Berkeley DB commands</A></LI>
+</UL>
+
+<UL type=disc>
+<LI>
+<A HREF="./env.html">Environment commands</A></LI>
+
+<LI>
+<A HREF="./lock.html">Locking commands</A></LI>
+
+<LI>
+<A HREF="./log.html">Logging commands</A></LI>
+
+<LI>
+<A HREF="./mpool.html">Memory Pool commands</A></LI>
+
+<LI>
+<A HREF="./txn.html">Transaction commands</A></LI>
+</UL>
+
+<UL>
+<LI>
+<A HREF="./db.html">Access Method commands</A></LI>
+
+<LI>
+<A HREF="./test.html">Debugging and Testing</A></LI>
+
+<LI>
+<A HREF="./historic.html">Compatibility commands</A></LI>
+
+<LI>
+<A HREF="./library.html">Convenience commands</A></LI>
+</UL>
diff --git a/db/tcl/docs/library.html b/db/tcl/docs/library.html
new file mode 100644
index 000000000..abd656d8e
--- /dev/null
+++ b/db/tcl/docs/library.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.08 [en] (X11; I; FreeBSD 2.2.8-19990120-SNAP i386) [Netscape]">
+</HEAD>
+<BODY>
+<HR WIDTH="100%">
+<H2>
+<A NAME="Convenience Commands"></A>Convenience Commands</H2>
+The convenience commands are provided for ease of use with the DB test
+suite.
+<P><B>> berkdb rand</B>
+<P>This command will invoke the rand function and return the random number.
+<P>
+<HR WIDTH="100%"><B>> berkdb random_int <I>low high</I></B>
+<P>This command will invoke the rand function and return a number between
+<B><I>low</I></B>
+and <B><I>high</I></B>.
+<P>
+<HR WIDTH="100%">
+<P><B>> berkdb srand <I>seed</I></B>
+<P>This command will invoke the srand function with the given <B><I>seed</I></B>
+and return 0.
+<P>
+<HR WIDTH="100%">
diff --git a/db/tcl/docs/lock.html b/db/tcl/docs/lock.html
new file mode 100644
index 000000000..87a20e9a6
--- /dev/null
+++ b/db/tcl/docs/lock.html
@@ -0,0 +1,187 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.08 [en] (X11; I; FreeBSD 2.2.8-19990120-SNAP i386) [Netscape]">
+</HEAD>
+<BODY>
+
+<H2>
+<A NAME="Locking Commands"></A>Locking Commands</H2>
+Most locking commands work with the environment handle.&nbsp; However,
+when a user gets a lock we create a new lock handle that they then use
+with in a similar manner to all the other handles to release the lock.&nbsp;
+We present the general locking functions first, and then those that manipulate
+locks.
+<P><B>> &lt;env> lock_detect [-lock_conflict] [default|oldest|youngest|random]</B>
+<P>This command runs the deadlock detector.&nbsp; It directly translates
+to the <A HREF="../../docs/api_c/lock_detect.html">lock_detect</A> DB call.&nbsp;
+It returns either a 0 (for success), a DB error message or it throws a
+Tcl error with a system message.&nbsp; The first argument sets the policy
+for deadlock as follows:
+<UL>
+<LI>
+<B>default</B> selects the DB_LOCK_DEFAULT policy for default detection
+(default if not specified)</LI>
+
+<LI>
+<B>oldest </B>selects DB_LOCK_OLDEST to abort the oldest locker on a deadlock</LI>
+
+<LI>
+<B>random</B> selects DB_LOCK_RANDOM to abort a random locker on a deadlock</LI>
+
+<LI>
+<B>youngest</B> selects DB_LOCK_YOUNGEST to abort the youngest locker on
+a deadlock</LI>
+</UL>
+The second argument, <B>-lock_conflict</B>, selects the DB_LOCK_CONFLICT
+flag to only run the detector if a lock conflict has occurred since the
+last time the detector was run.
+<HR WIDTH="100%">
+<BR><B>> &lt;env> lock_stat</B>
+<P>This command returns a list of name/value pairs where the names correspond
+to the C-structure field names of DB_LOCK_STAT and the values are the data
+returned.&nbsp; This command is a direct translation of the <A HREF="../../docs/api_c/lock_stat.html">lock_stat</A>
+DB call.
+<HR WIDTH="100%">
+<BR><A NAME="> <env> lock_id"></A><B>> &lt;env> lock_id</B>
+<P>This command returns a unique locker ID value.&nbsp; It directly translates
+to the <A HREF="../../docs/api_c/lock_id.html">lock_id</A> DB call.
+<HR WIDTH="100%">
+<BR><A NAME="> <env> lock_get"></A><B>> &lt;env> lock_get [-nowait]<I>lockmode
+locker obj</I></B>
+<P>This command gets a lock. It will invoke the <A HREF="../../docs/api_c/lock_get.html">lock_get</A>
+function.&nbsp; After it successfully gets a handle to a lock, we bind
+it to a new Tcl command of the form <B><I>$env.lockX</I></B>, where X is
+an integer starting at&nbsp; 0 (e.g. <B>$env.lock0, $env.lock1, </B>etc).&nbsp;
+We use the <I>Tcl_CreateObjCommand()</I> to create the top level locking
+command function.&nbsp; It is through this handle that the user can release
+the lock.&nbsp; Internally, the handle we get back from DB will be stored
+as the <I>ClientData</I> portion of the new command set so that future
+locking calls will have that handle readily available.
+<P>The arguments are:
+<UL>
+<LI>
+<B><I>locker</I></B> specifies the locker ID returned from the <A HREF="#> <env> lock_id">lock_id</A>
+command</LI>
+
+<LI>
+<B><I>obj</I></B> specifies an object to lock</LI>
+
+<LI>
+the <B><I>lock mode</I></B> is specified as one of the following:</LI>
+
+<UL>
+<LI>
+<B>ng </B>specifies DB_LOCK_NG for not granted (always 0)</LI>
+
+<LI>
+<B>read</B> specifies DB_LOCK_READ for a read (shared) lock</LI>
+
+<LI>
+<B>write</B> specifies DB_LOCK_WRITE for an exclusive write lock</LI>
+
+<LI>
+<B>iwrite </B>specifies DB_LOCK_IWRITE for intent for exclusive write lock</LI>
+
+<LI>
+<B>iread </B>specifies DB_LOCK_IREAD for intent for shared read lock</LI>
+
+<LI>
+<B>iwr </B>specifies DB_LOCK_IWR for intent for eread and write lock</LI>
+</UL>
+
+<LI>
+<B>-nowait</B> selects the DB_LOCK_NOWAIT to indicate that we do not want
+to wait on the lock</LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><B>> &lt;lock> put</B>
+<P>This command releases the lock referenced by the command.&nbsp; It is
+a direct translation of the <A HREF="../../docs/api_c/lock_put.html">lock_put</A>
+function.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message.&nbsp; Additionally, since
+the handle is no longer valid, we will call
+<I>Tcl_DeleteCommand()
+</I>so
+that further uses of the handle will be dealt with properly by Tcl itself.
+<BR>
+<HR WIDTH="100%">
+<BR><A NAME="> <env> lock_vec"></A><B>> &lt;env> lock_vec [-nowait] <I>locker
+</I>{get|put|put_all|put_obj
+[<I>obj</I>] [<I>lockmode</I>] [<I>lock</I>]} ...</B>
+<P>This command performs a series of lock calls.&nbsp; It is a direct translation
+of the <A HREF="../../docs/api_c/lock_vec.html">lock_vec</A> function.&nbsp;
+This command will return a list of the return values from each operation
+specified in the argument list.&nbsp; For the 'put' operations the entry
+in the return value list is either a 0 (for success) or an error.&nbsp;
+For the 'get' operation, the entry is the lock widget handle, <B>$env.lockN</B>
+(as described above in <A HREF="#> <env> lock_get">&lt;env> lock_get</A>)
+or an error.&nbsp; If an error occurs, the return list will contain the
+return values for all the successful operations up the erroneous one and
+the error code for that operation.&nbsp; Subsequent operations will be
+ignored.
+<P>As for the other operations, if we are doing a 'get' we will create
+the commands and if we are doing a 'put' we will have to delete the commands.&nbsp;
+Additionally, we will have to do this after the call to the DB lock_vec
+and iterate over the results, creating and/or deleting Tcl commands.&nbsp;
+It is possible that we may return a lock widget from a get operation that
+is considered invalid, if, for instance, there was a <B>put_all</B> operation
+performed later in the vector of operations.&nbsp; The arguments are:
+<UL>
+<LI>
+<B><I>locker</I></B> specifies the locker ID returned from the <A HREF="#> <env> lock_id">lock_id</A>
+command</LI>
+
+<LI>
+<B>-nowait</B> selects the DB_LOCK_NOWAIT to indicate that we do not want
+to wait on the lock</LI>
+
+<LI>
+the lock vectors are tuple consisting of {an operation, lock object, lock
+mode, lock handle} where what is required is based on the operation desired:</LI>
+
+<UL>
+<LI>
+<B>get</B> specifes DB_LOCK_GET to get a lock.&nbsp; Requires a tuple <B>{get
+<I>obj</I>
+<I>mode</I>}
+</B>where
+<B><I>mode</I></B>
+is:</LI>
+
+<UL>
+<LI>
+<B>ng </B>specifies DB_LOCK_NG for not granted (always 0)</LI>
+
+<LI>
+<B>read</B> specifies DB_LOCK_READ for a read (shared) lock</LI>
+
+<LI>
+<B>write</B> specifies DB_LOCK_WRITE for an exclusive write lock</LI>
+
+<LI>
+<B>iwrite </B>specifies DB_LOCK_IWRITE for intent for exclusive write lock</LI>
+
+<LI>
+<B>iread </B>specifies DB_LOCK_IREAD for intent for shared read lock</LI>
+
+<LI>
+<B>iwr </B>specifies DB_LOCK_IWR for intent for eread and write lock</LI>
+</UL>
+
+<LI>
+<B>put</B> specifies DB_LOCK_PUT to release a <B><I>lock</I></B>.&nbsp;
+Requires a tuple <B>{put <I>lock}</I></B></LI>
+
+<LI>
+<B>put_all </B>specifies DB_LOCK_PUT_ALL to release all locks held by <B><I>locker</I></B>.&nbsp;
+Requires a tuple <B>{put_all}</B></LI>
+
+<LI>
+<B>put_obj</B> specifies DB_LOCK_PUT_OBJ to release all locks held by <B><I>locker</I></B>
+associated with the given <B><I>obj</I></B>.&nbsp; Requires a tuple <B>{put_obj
+<I>obj</I>}</B></LI>
+</UL>
+</UL>
diff --git a/db/tcl/docs/log.html b/db/tcl/docs/log.html
new file mode 100644
index 000000000..35ecfc2f5
--- /dev/null
+++ b/db/tcl/docs/log.html
@@ -0,0 +1,142 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.08 [en] (X11; I; FreeBSD 3.3-RELEASE i386) [Netscape]">
+</HEAD>
+<BODY>
+
+<H2>
+<A NAME="Logging Commands"></A>Logging Commands</H2>
+Logging commands work from the environment handle to control the use of
+the log files.&nbsp; Log files are opened when the environment is opened
+and closed when the environment is closed.&nbsp; In all of the commands
+in the logging subsystem that take or return a log sequence number, it
+is of the form:
+<BR><B>{<I>fileid offset</I>}</B>
+<BR>where the <B><I>fileid</I></B> is an identifier of the log file, as
+returned from the <A HREF="#> <env> log_get">log_get</A> call.
+<P><B>> &lt;env> log_archive [-arch_abs] [-arch_data] [-arch_log]</B>
+<P>This command returns&nbsp; a list of log files that are no longer in
+use.&nbsp; It is a direct call to the <A HREF="../../docs/api_c/log_archive.html">log_archive</A>
+function. The arguments are:
+<UL>
+<LI>
+<B>-arch_abs </B>selects DB_ARCH_ABS to return all pathnames as absolute
+pathnames</LI>
+
+<LI>
+<B>-arch_data </B>selects DB_ARCH_DATA to return a list of database files</LI>
+
+<LI>
+<B>-arch_log </B>selects DB_ARCH_LOG to return a list of log files</LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><B>> &lt;env> log_compare <I>lsn1 lsn2</I></B>
+<P>This command compares two log sequence numbers, given as <B><I>lsn1</I></B>
+and <B><I>lsn2</I></B>.&nbsp; It is a direct call to the <A HREF="../../docs/api_c/log_compare.html">log_compare</A>
+function.&nbsp; It will return a -1, 0, 1 to indicate if <B><I>lsn1</I></B>
+is less than, equal to or greater than <B><I>lsn2</I></B> respectively.
+<BR>
+<HR WIDTH="100%">
+<BR><B>> &lt;env> log_file <I>lsn</I></B>
+<P>This command returns&nbsp; the file name associated with the given <B><I>lsn</I></B>.&nbsp;
+It is a direct call to the <A HREF="../../docs/api_c/log_file.html">log_file</A>
+function.
+<BR>
+<HR WIDTH="100%">
+<BR><B>> &lt;env> log_flush [<I>lsn</I>]</B>
+<P>This command&nbsp; flushes the log up to the specified <B><I>lsn</I></B>
+or flushes all records if none is given&nbsp; It is a direct call to the
+<A HREF="../../docs/api_c/log_flush.html">log_flush</A>
+function.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message.
+<BR>
+<HR WIDTH="100%">
+<BR><A NAME="<env> log_get"></A><B>> &lt;env> log_get<I> </I>[-checkpoint]
+[-current] [-first] [-last] [-next] [-prev] [-set <I>lsn</I>]</B>
+<P>This command retrieves a record from the log according to the <B><I>lsn</I></B>
+given and returns it and the data.&nbsp; It is a direct call to the <A HREF="../../docs/api_c/log_get.html">log_get</A>
+function.&nbsp; It is a way of implementing a manner of log iteration similar
+to <A HREF="../../docs/api_tcl/db_cursor.html">cursors</A>.&nbsp;&nbsp;
+The information we return is similar to database information.&nbsp; We
+return a list where the first item is the LSN (which is a list itself)
+and the second item is the data.&nbsp; So it looks like, fully expanded,
+<B>{{<I>fileid</I>
+<I>offset</I>}
+<I>data</I>}.</B>&nbsp;
+In the case where DB_NOTFOUND is returned, we return an empty list <B>{}</B>.&nbsp;
+All other errors return a Tcl error.&nbsp; The arguments are:
+<UL>
+<LI>
+<B>-checkpoint </B>selects the DB_CHECKPOINT flag to return the LSN/data
+pair of the last record written through <A HREF="#> <env> log_put">log_put</A>
+with DB_CHECKPOINT specified</LI>
+
+<LI>
+<B>-current</B> selects the DB_CURRENT flag to return the current record</LI>
+
+<LI>
+<B>-first</B> selects the DB_FIRST flag to return the first record in the
+log.</LI>
+
+<LI>
+<B>-last </B>selects the DB_LAST flag to return the last record in the
+log.</LI>
+
+<LI>
+<B>-next</B> selects the DB_NEXT flag to return the next record in the
+log.</LI>
+
+<LI>
+<B>-prev </B>selects the DB_PREV flag to return the&nbsp; previous record
+in the log.</LI>
+
+<LI>
+<B>-set</B> selects the DB_SET flag to return the record specified by the
+given <B><I>lsn</I></B></LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><A NAME="> <env> log_put"></A><B>> &lt;env> log_put<I> </I>[-checkpoint]
+[-curlsn] [-flush] <I>record</I></B>
+<P>This command stores a <B><I>record</I></B> into the log and returns
+the LSN of the log record.&nbsp; It is a direct call to the <A HREF="../../docs/api_c/log_put.html">log_put</A>
+function.&nbsp; It returns either an LSN or it throws a Tcl error with
+a system message.&nbsp;<B> </B>The arguments are:
+<UL>
+<LI>
+<B>-checkpoint </B>selects the DB_CHECKPOINT flag</LI>
+
+<LI>
+<B>-curlsn</B> selects the DB_CURLSN flag to return the LSN of the next
+record</LI>
+
+<LI>
+<B>-flush </B>selects the DB_FLUSH flag to flush the log to disk.</LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><A NAME="> <env> log_register"></A><B>> &lt;env> log_register <I>db</I>
+<I>file</I></B>
+<P>This command registers a <B><I>file</I></B> and <B><I>db</I></B> with
+the log manager.&nbsp; It is a direct call to the <A HREF="../../docs/api_c/log_register.html">log_register</A>
+function.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message.
+<BR>
+<HR WIDTH="100%">
+<BR><A NAME="> <env> log_unregister"></A><B>> &lt;env> log_unregister <I>db</I></B>
+<P>This command unregisters the file specified by the database handle <B><I>db
+</I></B>from the log manager.&nbsp; It is a direct call to the <A HREF="../../docs/api_c/log_unregister.html">log_unregister</A>
+function.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message.
+<BR>
+<HR WIDTH="100%">
+<BR><B>> &lt;env> log_stat</B>
+<P>This command returns&nbsp; the statistics associated with the logging
+subsystem.&nbsp; It is a direct call to the <A HREF="../../docs/api_c/log_stat.html">log_stat</A>
+function.&nbsp; It returns a list of name/value pairs of the DB_LOG_STAT
+structure.
+</BODY>
+</HTML>
diff --git a/db/tcl/docs/mpool.html b/db/tcl/docs/mpool.html
new file mode 100644
index 000000000..666219306
--- /dev/null
+++ b/db/tcl/docs/mpool.html
@@ -0,0 +1,189 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.08 [en] (X11; I; FreeBSD 2.2.8-19990120-SNAP i386) [Netscape]">
+</HEAD>
+<BODY>
+
+<H2>
+<A NAME="Memory Pool Commands"></A>Memory Pool Commands</H2>
+Memory pools are used in a manner similar to the other subsystems.&nbsp;
+We create a handle to the pool and&nbsp; then use it for a variety of operations.&nbsp;
+Some of the memory pool commands use the environment instead. Those are
+presented first.
+<P><B>> &lt;env> mpool_stat</B>
+<P>This command returns&nbsp; the statistics associated with the memory
+pool subsystem.&nbsp; It is a direct call to the <A HREF="../../docs/api_c/memp_stat.html">memp_stat</A>
+function.&nbsp; It returns a list of name/value pairs of the DB_MPOOL_STAT
+structure.
+<BR>
+<HR WIDTH="100%">
+<BR><B>> &lt;env> mpool_sync <I>lsn</I></B>
+<P>This command flushes the memory pool for all pages with a log sequence
+number less than <B><I>lsn</I></B>.&nbsp; It is a direct call to the <A HREF="../../docs/api_c/memp_sync.html">memp_sync&nbsp;</A>
+function.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message.
+<BR>
+<HR WIDTH="100%">
+<BR><B>> &lt;env> mpool_trickle <I>percent</I></B>
+<P>This command tells DB to ensure that at least <B><I>percent</I></B>
+percent of the pages are clean by writing out enough to dirty pages to
+achieve that percentage.&nbsp; It is a direct call to the <A HREF="../../docs/api_c/memp_trickle.html">memp_trickle</A>
+function.&nbsp; The command will return the number of pages actually written.&nbsp;
+It returns either the number of pages on success, or it throws a Tcl error
+with a system message.
+<BR>
+<HR WIDTH="100%">
+<P><B>> &lt;env> mpool [-create] [-nommap] [-rdonly] [-mode <I>mode</I>]
+-pagesize <I>size</I> [<I>file</I>]</B>
+<P>This command creates a new memory pool.&nbsp; It invokes the <A HREF="../../docs/api_c/memp_fopen.html">memp_fopen</A>
+function.&nbsp; After it successfully gets a handle to a memory pool, we
+bind it to a new Tcl command of the form <B><I>$env.mpX</I></B>, where
+X is an integer starting at&nbsp; 0 (e.g. <B>$env.mp0, $env.mp1, </B>etc).&nbsp;
+We use the <I>Tcl_CreateObjCommand()</I> to create the top level memory
+pool functions.&nbsp; It is through this handle that the user can manipulate
+the pool.&nbsp; Internally, the handle we get back from DB will be stored
+as the <I>ClientData</I> portion of the new command set so that future
+memory pool calls will have that handle readily available.&nbsp; Additionally,
+we need to maintain this handle in relation to the environment so that
+if the user calls <A HREF="../../docs/api_tcl/env_close.html">&lt;env> close</A> without closing
+the memory pool we can properly clean up.&nbsp; The arguments are:
+<UL>
+<LI>
+<B><I>file</I></B> is the name of the file to open</LI>
+
+<LI>
+<B>-create </B>selects the DB_CREATE flag to create underlying file</LI>
+
+<LI>
+<B>-mode <I>mode </I></B>sets the permissions of created file to <B><I>mode</I></B></LI>
+
+<LI>
+<B>-nommap</B> selects the DB_NOMMAP flag to disallow using mmap'ed files</LI>
+
+<LI>
+<B>-pagesize</B> sets the underlying file page size to <B><I>size</I></B></LI>
+
+<LI>
+<B>-rdonly </B>selects the DB_RDONLY flag for read only access</LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><B>> &lt;mp> close</B>
+<P>This command closes the memory pool.&nbsp; It is a direct call to the
+<A HREF="../../docs/api_c/memp_fclose.html">memp_close</A>
+function.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message.
+<P>Additionally, since the handle is no longer valid, we will call
+<I>Tcl_DeleteCommand()
+</I>so
+that further uses of the handle will be dealt with properly by Tcl itself.&nbsp;
+We must also remove the reference to this handle from the environment.&nbsp;
+We will go through the list of pinned pages that were acquired by the <A HREF="#> <mp> get">get</A>
+command and
+<A HREF="#> <pg> put">put</A> them back.
+<HR WIDTH="100%">
+<BR><B>> &lt;mp> fsync</B>
+<P>This command flushes all of the file's dirty pages to disk.&nbsp; It
+is a direct call to the <A HREF="../../docs/api_c/memp_fsync.html">memp_fsync</A>
+function.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message.
+<HR WIDTH="100%">
+<BR><A NAME="> <mp> get"></A><B>> &lt;mp> get [-create] [-last] [-new]
+[<I>pgno</I>]</B>
+<P>This command gets the&nbsp; <B><I>pgno </I></B>page from the memory
+pool.&nbsp; It invokes the <A HREF="../../docs/api_c/memp_fget.html">memp_fget</A>
+function and possibly the <A HREF="../../docs/api_c/memp_fset.html">memp_fset</A>
+function if any options are chosen to set the page characteristics.&nbsp;
+After it successfully gets a handle to a page,&nbsp; we bind it to and
+return a new Tcl command of the form <B><I>$env.mpN.pX</I></B>, where X
+is an integer starting at&nbsp; 0 (e.g. <B>$env.mp0.p0, $env.mp1.p0, </B>etc).&nbsp;
+We use the <I>Tcl_CreateObjCommand()</I> to create the top level page functions.&nbsp;
+It is through this handle that the user can manipulate the page.&nbsp;
+Internally, the handle we get back from DB will be stored as the <I>ClientData</I>
+portion of the new command set.&nbsp; We need to store this handle in&nbsp;
+relation to the memory pool handle so that if the memory pool is closed,
+we will <A HREF="#> <pg> put">put</A> back the pages (setting the discard
+flag) and delete that set of commands.
+<P>The arguments are:
+<UL>
+<LI>
+<B>-create </B>selects the DB_MPOOL_CREATE flag&nbsp; to create the page
+if it does not exist.</LI>
+
+<LI>
+<B>-last</B> selects the DB_MPOOL_LAST flag to return the last page in
+the file</LI>
+
+<LI>
+<B>-new</B> selects the DB_MPOOL_NEW flag to create a new page</LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><B>> &lt;pg> pgnum</B>
+<P>This command returns the page number associated with this memory pool
+page.&nbsp; Primarily it will be used after an <A HREF="#> <mp> get">&lt;mp>
+get</A> call.
+<BR>
+<HR WIDTH="100%"><B>> &lt;pg> pgsize</B>
+<P>This command returns the page size associated with this memory pool
+page.&nbsp; Primarily it will be used after an <A HREF="#> <mp> get">&lt;mp>
+get</A> call.
+<BR>
+<HR WIDTH="100%"><B>> &lt;pg> set [-clean] [-dirty] [-discard]</B>
+<P>This command sets the characteristics of the page.&nbsp; It is a direct
+call to the <A HREF="../../docs/api_c/memp_fset.html">memp_fset</A> function.&nbsp;
+It returns either a 0 (for success), a DB error message or it throws a
+Tcl error with a system message.&nbsp; The arguments are:
+<UL>
+<LI>
+<B>-clean</B> selects the DB_MPOOL_CLEAN flag to indicate this is a clean
+page</LI>
+
+<LI>
+<B>-dirty</B> selects the DB_MPOOL_DIRTY flag to indicate this page should
+be flushed before eviction</LI>
+
+<LI>
+<B>-discard</B> selects the DB_MPOOL_DISCARD flag to indicate this page
+is unimportant</LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><A NAME="> <pg> put"></A><B>> &lt;pg> put [-clean] [-dirty] [-discard]</B>
+<P>This command will put back the page to the memory pool.&nbsp; It is
+a direct call to the <A HREF="../../docs/api_c/memp_fput.html">memp_fput</A>
+function.&nbsp; It returns either a 0 (for success), a DB error message
+or it throws a Tcl error with a system message. Additionally, since the
+handle is no longer valid, we will call
+<I>Tcl_DeleteCommand()
+</I>so that
+further uses of the handle will be dealt with properly by Tcl itself.&nbsp;
+We must also remove the reference to this handle from the memory pool.
+<P>The arguments are:
+<UL>
+<LI>
+<B>-clean</B> selects the DB_MPOOL_CLEAN flag to indicate this is a clean
+page</LI>
+
+<LI>
+<B>-dirty</B> selects the DB_MPOOL_DIRTY flag to indicate this page should
+be flushed before eviction</LI>
+
+<LI>
+<B>-discard</B> selects the DB_MPOOL_DISCARD flag to indicate this page
+is unimportant</LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><B>> &lt;pg> init <I>val|string</I></B>
+<P>This command initializes the page to the <B><I>val</I></B> given or
+places the <B><I>string</I></B> given at the beginning of the page.&nbsp;
+It returns a 0 for success or it throws a Tcl error with an error message.
+<P>
+<HR WIDTH="100%">
+<BR><B>> &lt;pg> is_setto <I>val|string</I></B>
+<P>This command verifies the page contains the <B><I>val</I></B> given
+or checks that the <B>string</B> given is at the beginning of the page.&nbsp;
+It returns a 1 if the page is correctly set to the value and a 0 otherwise.
diff --git a/db/tcl/docs/test.html b/db/tcl/docs/test.html
new file mode 100644
index 000000000..10cf09efb
--- /dev/null
+++ b/db/tcl/docs/test.html
@@ -0,0 +1,149 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.08 [en] (X11; I; FreeBSD 2.2.8-19990120-SNAP i386) [Netscape]">
+</HEAD>
+<BODY>
+
+<H2>
+<A NAME="Debugging"></A>Debugging and Testing</H2>
+We have imported the debugging system from the old test suite into the
+new interface to aid in debugging problems.&nbsp; There are several variables
+that are available both in gdb as globals to the C code, and variables
+in Tcl that the user can set.&nbsp; These variables are linked together
+so that changes in one venue are reflected in the other.&nbsp; The names
+of the variables have been modified a bit to reduce the likelihood
+<BR>of namespace trampling.&nbsp; We have added a double underscore to
+all the names.
+<P>The variables are all initialized to zero (0) thus resulting in debugging
+being turned off.&nbsp; The purpose of the debugging, fundamentally, is
+to allow the user to set a breakpoint prior to making a DB call.&nbsp;
+This breakpoint is set in the <I>__db_loadme() </I>function.&nbsp; The
+user may selectively turn on various debugging areas each controlled by
+a separate variable (note they all have two (2) underscores prepended to
+the name):
+<UL>
+<LI>
+<B>__debug_on</B> - Turns on the debugging system.&nbsp; This must be on
+for any debugging to occur</LI>
+
+<LI>
+<B>__debug_print - </B>Turns on printing a debug count statement on each
+call</LI>
+
+<LI>
+<B>__debug_test -</B> Hits the breakpoint in <I>__db_loadme</I> on the
+specific iteration</LI>
+
+<LI>
+<B>__debug_stop </B>- Hits the breakpoint in <I>__db_loadme</I> on every
+(or the next) iteration</LI>
+</UL>
+<B>Note to developers:</B>&nbsp; Anyone extending this interface must place
+a call to <B>_debug_check()</B> (no arguments) before every call into the
+DB library.
+<P>There is also a command available that will force a call to the _debug_check
+function.
+<P><B>> berkdb debug_check</B>
+<P>
+<HR WIDTH="100%">
+<BR>For testing purposes we have added several hooks into the DB library
+and a small interface into the environment and/or database commands to
+manipulate the hooks.&nbsp; This command interface and the hooks and everything
+that goes with it is only enabled when the test option is configured into
+DB.
+<P><B>> &lt;env> test copy <I>location</I></B>
+<BR><B>> &lt;db> test copy <I>location</I></B>
+<BR><B>> &lt;env> test abort <I>location</I></B>
+<BR><B>> &lt;db> test abort <I>location</I></B>
+<P>In order to test recovery we need to be able to abort the creation or
+deletion process at various points.&nbsp; Also we want to invoke a copy
+function to copy the database file(s)&nbsp; at various points as well so
+that we can obtain before/after snapshots of the databases.&nbsp; The interface
+provides the test command to specify a <B><I>location</I></B> where we
+wish to invoke a <B>copy</B> or an <B>abort</B>.&nbsp; The command is available
+from either the environment or the database for convenience.&nbsp; The
+<B><I>location</I></B>
+can be one of the following:
+<UL>
+<LI>
+<B>none -</B> Clears the location</LI>
+
+<LI>
+<B>preopen -</B> Sets the location prior to the __os_open call in the creation
+process</LI>
+
+<LI>
+<B>postopen</B> - Sets the location to immediately following the __os_open
+call in creation</LI>
+
+<LI>
+<B>postlogmeta</B> - Sets the location to immediately following the __db_log_page
+call to log the meta data in creation.&nbsp; Only valid for Btree.</LI>
+
+<LI>
+<B>postlog</B> - Sets the location to immediately following the last (or
+only) __db_log_page call in creation.</LI>
+
+<LI>
+<B>postsync</B> - Sets the location to immediately following the sync of
+the log page in creation.</LI>
+
+<LI>
+<B>prerename</B> - Sets the location prior to the __os_rename call in the
+deletion process.</LI>
+
+<LI>
+<B>postrename</B> - Sets the location to immediately following the __os_rename
+call in deletion</LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><B>> &lt;env> mutex <I>mode nitems</I></B>
+<P>This command creates a mutex region for testing.&nbsp; It sets the mode
+of the region to <B><I>mode</I></B> and sets up for <B><I>nitems</I></B>
+number of mutex entries.&nbsp; After we successfully get a handle to a
+mutex we create a command of the form <B><I>$env.mutexX</I></B>, where
+X is an integer starting at&nbsp; 0 (e.g. <B>$env.mutex0, $env.mutex1,
+</B>etc).&nbsp;&nbsp;
+We use the <I>Tcl_CreateObjCommand()&nbsp;</I> to create the top level
+mutex function.&nbsp; It is through this handle that the user can access
+all of the commands described below.&nbsp; Internally, the mutex handle
+is sent as the <I>ClientData</I> portion of the new command set so that
+all future mutex calls access the appropriate handle.
+<P>
+<HR WIDTH="100%"><B>> &lt;mutex> close</B>
+<P>This command closes the mutex and renders the handle invalid.&nbsp;&nbsp;
+This command directly translates to the __db_r_detach function call.&nbsp;
+It returns either a 0 (for success),&nbsp; or it throws a Tcl error with
+a system message.
+<P>Additionally, since the handle is no longer valid, we will call <I>Tcl_DeleteCommand()
+</I>so
+that further uses of the handle will be dealt with properly by Tcl itself.&nbsp;
+<HR WIDTH="100%"><B>> &lt;mutex> get <I>id</I></B>
+<P>This command locks the mutex identified by <B><I>id</I></B>.&nbsp; It
+returns either a 0 (for success),&nbsp; or it throws a Tcl error with a
+system message.
+<BR>
+<HR WIDTH="100%"><B>> &lt;mutex> release <I>id</I></B>
+<P>This command releases the mutex identified by <B><I>id</I></B>.&nbsp;
+It returns either a 0 (for success),&nbsp; or it throws a Tcl error with
+a system message.
+<BR>
+<HR WIDTH="100%"><B>> &lt;mutex> getval <I>id</I></B>
+<P>This command gets the value stored for the mutex identified by <B><I>id</I></B>.&nbsp;
+It returns either the value,&nbsp; or it throws a Tcl error with a system
+message.
+<BR>
+<HR WIDTH="100%"><B>> &lt;mutex> setval <I>id val</I></B>
+<P>This command sets the value stored for the mutex identified by <B><I>id
+</I></B>to
+<B><I>val</I></B>.&nbsp;
+It returns either a 0 (for success),&nbsp; or it throws a Tcl error with
+a system message.
+<BR>
+<HR WIDTH="100%">
+<BR>&nbsp;
+</BODY>
+</HTML>
diff --git a/db/tcl/docs/txn.html b/db/tcl/docs/txn.html
new file mode 100644
index 000000000..863c9a875
--- /dev/null
+++ b/db/tcl/docs/txn.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Mozilla/4.08 [en] (X11; I; FreeBSD 2.2.8-19990120-SNAP i386) [Netscape]">
+</HEAD>
+<BODY>
+
+<H2>
+<A NAME="Transaction Commands"></A>Transaction Commands</H2>
+Transactions are used in a manner similar to the other subsystems.&nbsp;
+We create a handle to the transaction and&nbsp; then use it for a variety
+of operations.&nbsp; Some of the transaction commands use the environment
+instead.&nbsp; Those are presented first.&nbsp; The transaction command
+handle returned is the handle used by the various commands that can be
+transaction protected, such as <A HREF="../../docs/api_tcl/db_cursor.html">cursors</A>.<BR>
+
+<HR WIDTH="100%">
+<P><B>> &lt;env> txn_checkpoint [-kbyte <I>kb</I>] [-min <I>min</I>]</B>
+<P>This command causes a checkpoint of the transaction region.&nbsp; It
+is a direct translation of the <A HREF="../../docs/api_c/txn_checkpoint.html">txn_checkpoint
+</A>function.&nbsp;
+It returns either a 0 (for success), a DB error message or it throws a
+Tcl error with a system message.&nbsp; The arguments are:
+<UL>
+<LI>
+<B>-kbyte </B>causes the checkpoint to occur only if <B><I>kb</I></B> kilobytes
+of log data has been written since the last checkpoint</LI>
+
+<LI>
+<B>-min</B> causes the checkpoint to occur only if <B><I>min</I></B> minutes
+have passed since the last checkpoint</LI>
+</UL>
+
+<HR WIDTH="100%">
+<BR><B>> &lt;env> txn_stat</B>
+<P>This command returns transaction statistics.&nbsp; It is a direct translation
+of the <A HREF="../../docs/api_c/txn_stat.html">txn_stat</A> function.&nbsp;
+It will return a list of name/value pairs that correspond to the DB_TXN_STAT
+structure.
+<HR WIDTH="100%">
+<BR><B>>&nbsp; &lt;txn> id</B>
+<P>This command returns the transaction id.&nbsp; It is a direct call to
+the <A HREF="../../docs/api_c/txn_id.html">txn_id</A> function.&nbsp; The
+typical use of this identifier is as the <B><I>locker</I></B> value for
+the <A HREF="lock.html">lock_get</A> and <A HREF="lock.html">lock_vec</A>
+calls.
+<HR WIDTH="100%">
+<BR><B>> &lt;txn> prepare</B>
+<P>This command initiates a two-phase commit.&nbsp; It is a direct call
+to the <A HREF="../../docs/api_c/txn_prepare.html">txn_prepare</A> function.&nbsp;
+It returns either a 0 (for success), a DB error message or it throws a
+Tcl error with a system message.
+<HR WIDTH="100%">
+</BODY>
+</HTML>
diff --git a/db/tcl/tcl_compat.c b/db/tcl/tcl_compat.c
new file mode 100644
index 000000000..41caee95c
--- /dev/null
+++ b/db/tcl/tcl_compat.c
@@ -0,0 +1,1055 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2000
+ * Sleepycat Software. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#ifndef lint
+static const char revid[] = "$Id: tcl_compat.c,v 11.22 2001/01/11 18:19:55 bostic Exp $";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+#endif
+
+#define DB_DBM_HSEARCH 1
+
+#include "db_int.h"
+#include "tcl_db.h"
+
+/*
+ * Prototypes for procedures defined later in this file:
+ */
+static int mutex_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
+
+/*
+ * bdb_HCommand --
+ * Implements h* functions.
+ *
+ * PUBLIC: int bdb_HCommand __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
+ */
+int
+bdb_HCommand(interp, objc, objv)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *hcmds[] = {
+ "hcreate",
+ "hdestroy",
+ "hsearch",
+ NULL
+ };
+ enum hcmds {
+ HHCREATE,
+ HHDESTROY,
+ HHSEARCH
+ };
+ static char *srchacts[] = {
+ "enter",
+ "find",
+ NULL
+ };
+ enum srchacts {
+ ACT_ENTER,
+ ACT_FIND
+ };
+ ENTRY item, *hres;
+ ACTION action;
+ int actindex, cmdindex, nelem, result, ret;
+ Tcl_Obj *res;
+
+ result = TCL_OK;
+ /*
+ * Get the command name index from the object based on the cmds
+ * defined above. This SHOULD NOT fail because we already checked
+ * in the 'berkdb' command.
+ */
+ if (Tcl_GetIndexFromObj(interp,
+ objv[1], hcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+
+ res = NULL;
+ switch ((enum hcmds)cmdindex) {
+ case HHCREATE:
+ /*
+ * Must be 1 arg, nelem. Error if not.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "nelem");
+ return (TCL_ERROR);
+ }
+ result = Tcl_GetIntFromObj(interp, objv[2], &nelem);
+ if (result == TCL_OK) {
+ _debug_check();
+ ret = hcreate(nelem) == 0 ? 1: 0;
+ _ReturnSetup(interp, ret, "hcreate");
+ }
+ break;
+ case HHSEARCH:
+ /*
+ * 3 args for this. Error if different.
+ */
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 2, objv, "key data action");
+ return (TCL_ERROR);
+ }
+ item.key = Tcl_GetStringFromObj(objv[2], NULL);
+ item.data = Tcl_GetStringFromObj(objv[3], NULL);
+ action = 0;
+ if (Tcl_GetIndexFromObj(interp, objv[4], srchacts,
+ "action", TCL_EXACT, &actindex) != TCL_OK)
+ return (IS_HELP(objv[4]));
+ switch ((enum srchacts)actindex) {
+ case ACT_FIND:
+ action = FIND;
+ break;
+ case ACT_ENTER:
+ action = ENTER;
+ break;
+ }
+ _debug_check();
+ hres = hsearch(item, action);
+ if (hres == NULL)
+ Tcl_SetResult(interp, "-1", TCL_STATIC);
+ else if (action == FIND)
+ Tcl_SetResult(interp, (char *)hres->data, TCL_STATIC);
+ else
+ /* action is ENTER */
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+
+ break;
+ case HHDESTROY:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ (void)hdestroy();
+ res = Tcl_NewIntObj(0);
+ break;
+ }
+ /*
+ * Only set result if we have a res. Otherwise, lower
+ * functions have already done so.
+ */
+ if (result == TCL_OK && res)
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+/*
+ *
+ * bdb_NdbmOpen --
+ * Opens an ndbm database.
+ *
+ * PUBLIC: #if DB_DBM_HSEARCH != 0
+ * PUBLIC: int bdb_NdbmOpen __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DBM **));
+ * PUBLIC: #endif
+ */
+int
+bdb_NdbmOpen(interp, objc, objv, dbpp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DBM **dbpp; /* Dbm pointer */
+{
+ static char *ndbopen[] = {
+ "-create",
+ "-mode",
+ "-rdonly",
+ "-truncate",
+ "--",
+ NULL
+ };
+ enum ndbopen {
+ NDB_CREATE,
+ NDB_MODE,
+ NDB_RDONLY,
+ NDB_TRUNC,
+ NDB_ENDARG
+ };
+
+ u_int32_t open_flags;
+ int endarg, i, mode, optindex, read_only, result;
+ char *arg, *db;
+
+ result = TCL_OK;
+ open_flags = 0;
+ endarg = mode = 0;
+ read_only = 0;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?args?");
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the option name index from the object based on the args
+ * defined above.
+ */
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], ndbopen, "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 ndbopen)optindex) {
+ case NDB_CREATE:
+ open_flags |= O_CREAT;
+ break;
+ case NDB_RDONLY:
+ read_only = 1;
+ break;
+ case NDB_TRUNC:
+ open_flags |= O_TRUNC;
+ break;
+ case NDB_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 NDB_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, or 1 left) is a
+ * file name. If we have 0, then an in-memory db. If
+ * there is 1, a db name.
+ */
+ db = NULL;
+ if (i != objc && i != objc - 1) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?args? ?file?");
+ result = TCL_ERROR;
+ goto error;
+ }
+ if (i != objc)
+ db = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+
+ /*
+ * 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.
+ *
+ * Now open the database.
+ */
+ if (read_only)
+ open_flags |= O_RDONLY;
+ else
+ open_flags |= O_RDWR;
+ _debug_check();
+ if ((*dbpp = dbm_open(db, open_flags, mode)) == NULL) {
+ result = _ReturnSetup(interp, Tcl_GetErrno(), "db open");
+ goto error;
+ }
+ return (TCL_OK);
+
+error:
+ *dbpp = NULL;
+ return (result);
+}
+
+/*
+ * bdb_DbmCommand --
+ * Implements "dbm" commands.
+ *
+ * PUBLIC: #if DB_DBM_HSEARCH != 0
+ * PUBLIC: int bdb_DbmCommand
+ * PUBLIC: __P((Tcl_Interp *, int, Tcl_Obj * CONST*, int, DBM *));
+ * PUBLIC: #endif
+ */
+int
+bdb_DbmCommand(interp, objc, objv, flag, dbm)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ int flag; /* Which db interface */
+ DBM *dbm; /* DBM pointer */
+{
+ static char *dbmcmds[] = {
+ "dbmclose",
+ "dbminit",
+ "delete",
+ "fetch",
+ "firstkey",
+ "nextkey",
+ "store",
+ NULL
+ };
+ enum dbmcmds {
+ DBMCLOSE,
+ DBMINIT,
+ DBMDELETE,
+ DBMFETCH,
+ DBMFIRST,
+ DBMNEXT,
+ DBMSTORE
+ };
+ static char *stflag[] = {
+ "insert", "replace",
+ NULL
+ };
+ enum stflag {
+ STINSERT, STREPLACE
+ };
+ datum key, data;
+ int cmdindex, stindex, result, ret;
+ char *name, *t;
+
+ result = TCL_OK;
+ /*
+ * Get the command name index from the object based on the cmds
+ * defined above. This SHOULD NOT fail because we already checked
+ * in the 'berkdb' command.
+ */
+ if (Tcl_GetIndexFromObj(interp,
+ objv[1], dbmcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+
+ switch ((enum dbmcmds)cmdindex) {
+ case DBMCLOSE:
+ /*
+ * No arg for this. Error if different.
+ */
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ if (flag == DBTCL_DBM)
+ ret = dbmclose();
+ else {
+ Tcl_SetResult(interp,
+ "Bad interface flag for command", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ _ReturnSetup(interp, ret, "dbmclose");
+ break;
+ case DBMINIT:
+ /*
+ * Must be 1 arg - file.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "file");
+ return (TCL_ERROR);
+ }
+ name = Tcl_GetStringFromObj(objv[2], NULL);
+ if (flag == DBTCL_DBM)
+ ret = dbminit(name);
+ else {
+ Tcl_SetResult(interp, "Bad interface flag for command",
+ TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ _ReturnSetup(interp, ret, "dbminit");
+ break;
+ case DBMFETCH:
+ /*
+ * 1 arg for this. Error if different.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "key");
+ return (TCL_ERROR);
+ }
+ key.dptr = (char *)Tcl_GetByteArrayFromObj(objv[2], &key.dsize);
+ _debug_check();
+ if (flag == DBTCL_DBM)
+ data = fetch(key);
+ else if (flag == DBTCL_NDBM)
+ data = dbm_fetch(dbm, key);
+ else {
+ Tcl_SetResult(interp,
+ "Bad interface flag for command", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (data.dptr == NULL ||
+ (ret = __os_malloc(NULL, data.dsize + 1, NULL, &t)) != 0)
+ Tcl_SetResult(interp, "-1", TCL_STATIC);
+ else {
+ memcpy(t, data.dptr, data.dsize);
+ t[data.dsize] = '\0';
+ Tcl_SetResult(interp, t, TCL_VOLATILE);
+ __os_free(t, data.dsize + 1);
+ }
+ break;
+ case DBMSTORE:
+ /*
+ * 2 args for this. Error if different.
+ */
+ if (objc != 4 && flag == DBTCL_DBM) {
+ Tcl_WrongNumArgs(interp, 2, objv, "key data");
+ return (TCL_ERROR);
+ }
+ if (objc != 5 && flag == DBTCL_NDBM) {
+ Tcl_WrongNumArgs(interp, 2, objv, "key data action");
+ return (TCL_ERROR);
+ }
+ key.dptr = (char *)Tcl_GetByteArrayFromObj(objv[2], &key.dsize);
+ data.dptr =
+ (char *)Tcl_GetByteArrayFromObj(objv[3], &data.dsize);
+ _debug_check();
+ if (flag == DBTCL_DBM)
+ ret = store(key, data);
+ else if (flag == DBTCL_NDBM) {
+ if (Tcl_GetIndexFromObj(interp, objv[4], stflag,
+ "flag", TCL_EXACT, &stindex) != TCL_OK)
+ return (IS_HELP(objv[4]));
+ switch ((enum stflag)stindex) {
+ case STINSERT:
+ flag = DBM_INSERT;
+ break;
+ case STREPLACE:
+ flag = DBM_REPLACE;
+ break;
+ }
+ ret = dbm_store(dbm, key, data, flag);
+ } else {
+ Tcl_SetResult(interp,
+ "Bad interface flag for command", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ _ReturnSetup(interp, ret, "store");
+ break;
+ case DBMDELETE:
+ /*
+ * 1 arg for this. Error if different.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "key");
+ return (TCL_ERROR);
+ }
+ key.dptr = (char *)Tcl_GetByteArrayFromObj(objv[2], &key.dsize);
+ _debug_check();
+ if (flag == DBTCL_DBM)
+ ret = delete(key);
+ else if (flag == DBTCL_NDBM)
+ ret = dbm_delete(dbm, key);
+ else {
+ Tcl_SetResult(interp,
+ "Bad interface flag for command", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ _ReturnSetup(interp, ret, "delete");
+ break;
+ case DBMFIRST:
+ /*
+ * No arg for this. Error if different.
+ */
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ if (flag == DBTCL_DBM)
+ key = firstkey();
+ else if (flag == DBTCL_NDBM)
+ key = dbm_firstkey(dbm);
+ else {
+ Tcl_SetResult(interp,
+ "Bad interface flag for command", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (key.dptr == NULL ||
+ (ret = __os_malloc(NULL, key.dsize + 1, NULL, &t)) != 0)
+ Tcl_SetResult(interp, "-1", TCL_STATIC);
+ else {
+ memcpy(t, key.dptr, key.dsize);
+ t[key.dsize] = '\0';
+ Tcl_SetResult(interp, t, TCL_VOLATILE);
+ __os_free(t, key.dsize + 1);
+ }
+ break;
+ case DBMNEXT:
+ /*
+ * 0 or 1 arg for this. Error if different.
+ */
+ _debug_check();
+ if (flag == DBTCL_DBM) {
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ key.dptr = (char *)
+ Tcl_GetByteArrayFromObj(objv[2], &key.dsize);
+ data = nextkey(key);
+ } else if (flag == DBTCL_NDBM) {
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ data = dbm_nextkey(dbm);
+ } else {
+ Tcl_SetResult(interp,
+ "Bad interface flag for command", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (data.dptr == NULL ||
+ (ret = __os_malloc(NULL, data.dsize + 1, NULL, &t)) != 0)
+ Tcl_SetResult(interp, "-1", TCL_STATIC);
+ else {
+ memcpy(t, data.dptr, data.dsize);
+ t[data.dsize] = '\0';
+ Tcl_SetResult(interp, t, TCL_VOLATILE);
+ __os_free(t, data.dsize + 1);
+ }
+ break;
+ }
+ return (result);
+}
+
+/*
+ * ndbm_Cmd --
+ * Implements the "ndbm" widget.
+ *
+ * PUBLIC: int ndbm_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
+ */
+int
+ndbm_Cmd(clientData, interp, objc, objv)
+ ClientData clientData; /* DB handle */
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *ndbcmds[] = {
+ "clearerr",
+ "close",
+ "delete",
+ "dirfno",
+ "error",
+ "fetch",
+ "firstkey",
+ "nextkey",
+ "pagfno",
+ "rdonly",
+ "store",
+ NULL
+ };
+ enum ndbcmds {
+ NDBCLRERR,
+ NDBCLOSE,
+ NDBDELETE,
+ NDBDIRFNO,
+ NDBERR,
+ NDBFETCH,
+ NDBFIRST,
+ NDBNEXT,
+ NDBPAGFNO,
+ NDBRDONLY,
+ NDBSTORE
+ };
+ DBM *dbp;
+ DBTCL_INFO *dbip;
+ Tcl_Obj *res;
+ int cmdindex, result, ret;
+
+ Tcl_ResetResult(interp);
+ dbp = (DBM *)clientData;
+ dbip = _PtrToInfo((void *)dbp);
+ result = TCL_OK;
+ if (objc <= 1) {
+ Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs");
+ return (TCL_ERROR);
+ }
+ if (dbp == NULL) {
+ Tcl_SetResult(interp, "NULL db pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (dbip == NULL) {
+ Tcl_SetResult(interp, "NULL db info pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the command name index from the object based on the dbcmds
+ * defined above.
+ */
+ if (Tcl_GetIndexFromObj(interp,
+ objv[1], ndbcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+
+ res = NULL;
+ switch ((enum ndbcmds)cmdindex) {
+ case NDBCLOSE:
+ _debug_check();
+ dbm_close(dbp);
+ (void)Tcl_DeleteCommand(interp, dbip->i_name);
+ _DeleteInfo(dbip);
+ res = Tcl_NewIntObj(0);
+ break;
+ case NDBDELETE:
+ case NDBFETCH:
+ case NDBFIRST:
+ case NDBNEXT:
+ case NDBSTORE:
+ result = bdb_DbmCommand(interp, objc, objv, DBTCL_NDBM, dbp);
+ break;
+ case NDBCLRERR:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = dbm_clearerr(dbp);
+ if (ret)
+ _ReturnSetup(interp, ret, "clearerr");
+ else
+ res = Tcl_NewIntObj(ret);
+ break;
+ case NDBDIRFNO:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = dbm_dirfno(dbp);
+ res = Tcl_NewIntObj(ret);
+ break;
+ case NDBPAGFNO:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = dbm_pagfno(dbp);
+ res = Tcl_NewIntObj(ret);
+ break;
+ case NDBERR:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = dbm_error(dbp);
+ Tcl_SetErrno(ret);
+ Tcl_SetResult(interp, Tcl_PosixError(interp), TCL_STATIC);
+ break;
+ case NDBRDONLY:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = dbm_rdonly(dbp);
+ if (ret)
+ _ReturnSetup(interp, ret, "rdonly");
+ else
+ res = Tcl_NewIntObj(ret);
+ break;
+ }
+ /*
+ * Only set result if we have a res. Otherwise, lower
+ * functions have already done so.
+ */
+ if (result == TCL_OK && res)
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+/*
+ * bdb_RandCommand --
+ * Implements rand* functions.
+ *
+ * PUBLIC: int bdb_RandCommand __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
+ */
+int
+bdb_RandCommand(interp, objc, objv)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *rcmds[] = {
+ "rand", "random_int", "srand",
+ NULL
+ };
+ enum rcmds {
+ RRAND, RRAND_INT, RSRAND
+ };
+ long t;
+ int cmdindex, hi, lo, result, ret;
+ Tcl_Obj *res;
+ char msg[MSG_SIZE];
+
+ result = TCL_OK;
+ /*
+ * Get the command name index from the object based on the cmds
+ * defined above. This SHOULD NOT fail because we already checked
+ * in the 'berkdb' command.
+ */
+ if (Tcl_GetIndexFromObj(interp,
+ objv[1], rcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+
+ res = NULL;
+ switch ((enum rcmds)cmdindex) {
+ case RRAND:
+ /*
+ * Must be 0 args. Error if different.
+ */
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ ret = rand();
+ res = Tcl_NewIntObj(ret);
+ break;
+ case RRAND_INT:
+ /*
+ * Must be 4 args. Error if different.
+ */
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "lo hi");
+ return (TCL_ERROR);
+ }
+ result = Tcl_GetIntFromObj(interp, objv[2], &lo);
+ if (result != TCL_OK)
+ break;
+ result = Tcl_GetIntFromObj(interp, objv[3], &hi);
+ if (result == TCL_OK) {
+#ifndef RAND_MAX
+#define RAND_MAX 0x7fffffff
+#endif
+ t = rand();
+ if (t > RAND_MAX) {
+ snprintf(msg, MSG_SIZE,
+ "Max random is higher than %ld\n",
+ (long)RAND_MAX);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ result = TCL_ERROR;
+ break;
+ }
+ _debug_check();
+ ret = (int)(((double)t / ((double)(RAND_MAX) + 1)) *
+ (hi - lo + 1));
+ ret += lo;
+ res = Tcl_NewIntObj(ret);
+ }
+ break;
+ case RSRAND:
+ /*
+ * Must be 1 arg. Error if different.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "seed");
+ return (TCL_ERROR);
+ }
+ result = Tcl_GetIntFromObj(interp, objv[2], &lo);
+ if (result == TCL_OK) {
+ srand((u_int)lo);
+ res = Tcl_NewIntObj(0);
+ }
+ break;
+ }
+ /*
+ * Only set result if we have a res. Otherwise, lower
+ * functions have already done so.
+ */
+ if (result == TCL_OK && res)
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+/*
+ *
+ * tcl_Mutex --
+ * Opens an env mutex.
+ *
+ * PUBLIC: int tcl_Mutex __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB_ENV *,
+ * PUBLIC: DBTCL_INFO *));
+ */
+int
+tcl_Mutex(interp, objc, objv, envp, envip)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+ DBTCL_INFO *envip; /* Info pointer */
+{
+ DBTCL_INFO *ip;
+ Tcl_Obj *res;
+ _MUTEX_DATA *md;
+ int i, mode, nitems, result, ret;
+ char newname[MSG_SIZE];
+
+ md = NULL;
+ result = TCL_OK;
+ mode = nitems = ret = 0;
+ memset(newname, 0, MSG_SIZE);
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "mode nitems");
+ return (TCL_ERROR);
+ }
+ result = Tcl_GetIntFromObj(interp, objv[2], &mode);
+ if (result != TCL_OK)
+ return (TCL_ERROR);
+ result = Tcl_GetIntFromObj(interp, objv[3], &nitems);
+ if (result != TCL_OK)
+ return (TCL_ERROR);
+
+ snprintf(newname, sizeof(newname),
+ "%s.mutex%d", envip->i_name, envip->i_envmutexid);
+ ip = _NewInfo(interp, NULL, newname, I_MUTEX);
+ if (ip == NULL) {
+ Tcl_SetResult(interp, "Could not set up info",
+ TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ /*
+ * Set up mutex.
+ */
+ /*
+ * Map in the region.
+ *
+ * XXX
+ * We don't bother doing this "right", i.e., using the shalloc
+ * functions, just grab some memory knowing that it's correctly
+ * aligned.
+ */
+ _debug_check();
+ if (__os_calloc(NULL, 1, sizeof(_MUTEX_DATA), &md) != 0)
+ goto posixout;
+ md->env = envp;
+ md->n_mutex = nitems;
+ md->size = sizeof(_MUTEX_ENTRY) * nitems;
+
+ md->reginfo.type = REGION_TYPE_MUTEX;
+ md->reginfo.id = INVALID_REGION_TYPE;
+ md->reginfo.mode = mode;
+ md->reginfo.flags = REGION_CREATE_OK | REGION_JOIN_OK;
+ if ((ret = __db_r_attach(envp, &md->reginfo, md->size)) != 0)
+ goto posixout;
+ md->marray = md->reginfo.addr;
+
+ /* Initialize a created region. */
+ if (F_ISSET(&md->reginfo, REGION_CREATE))
+ for (i = 0; i < nitems; i++) {
+ md->marray[i].val = 0;
+ if ((ret =
+ __db_mutex_init(envp, &md->marray[i].m, i, 0)) != 0)
+ goto posixout;
+ }
+ R_UNLOCK(envp, &md->reginfo);
+
+ /*
+ * Success. Set up return. Set up new info
+ * and command widget for this mutex.
+ */
+ envip->i_envmutexid++;
+ ip->i_parent = envip;
+ _SetInfoData(ip, md);
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)mutex_Cmd, (ClientData)md, NULL);
+ res = Tcl_NewStringObj(newname, strlen(newname));
+ Tcl_SetObjResult(interp, res);
+
+ return (TCL_OK);
+
+posixout:
+ if (ret > 0)
+ Tcl_PosixError(interp);
+ result = _ReturnSetup(interp, ret, "mutex");
+ _DeleteInfo(ip);
+
+ if (md != NULL) {
+ if (md->reginfo.addr != NULL)
+ (void)__db_r_detach(md->env,
+ &md->reginfo, F_ISSET(&md->reginfo, REGION_CREATE));
+ __os_free(md, sizeof(*md));
+ }
+ return (result);
+}
+
+/*
+ * mutex_Cmd --
+ * Implements the "mutex" widget.
+ */
+static int
+mutex_Cmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Mutex handle */
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *mxcmds[] = {
+ "close",
+ "get",
+ "getval",
+ "release",
+ "setval",
+ NULL
+ };
+ enum mxcmds {
+ MXCLOSE,
+ MXGET,
+ MXGETVAL,
+ MXRELE,
+ MXSETVAL
+ };
+ DB_ENV *dbenv;
+ DBTCL_INFO *envip, *mpip;
+ _MUTEX_DATA *mp;
+ Tcl_Obj *res;
+ int cmdindex, id, result, newval;
+
+ Tcl_ResetResult(interp);
+ mp = (_MUTEX_DATA *)clientData;
+ mpip = _PtrToInfo((void *)mp);
+ envip = mpip->i_parent;
+ dbenv = envip->i_envp;
+ result = TCL_OK;
+
+ if (mp == NULL) {
+ Tcl_SetResult(interp, "NULL mp pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (mpip == NULL) {
+ Tcl_SetResult(interp, "NULL mp info pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the command name index from the object based on the dbcmds
+ * defined above.
+ */
+ if (Tcl_GetIndexFromObj(interp,
+ objv[1], mxcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+
+ res = NULL;
+ switch ((enum mxcmds)cmdindex) {
+ case MXCLOSE:
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ (void)__db_r_detach(mp->env, &mp->reginfo, 0);
+ res = Tcl_NewIntObj(0);
+ (void)Tcl_DeleteCommand(interp, mpip->i_name);
+ _DeleteInfo(mpip);
+ __os_free(mp, sizeof(*mp));
+ break;
+ case MXRELE:
+ /*
+ * Check for 1 arg. Error if different.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "id");
+ return (TCL_ERROR);
+ }
+ result = Tcl_GetIntFromObj(interp, objv[2], &id);
+ if (result != TCL_OK)
+ break;
+ MUTEX_UNLOCK(dbenv, &mp->marray[id].m);
+ res = Tcl_NewIntObj(0);
+ break;
+ case MXGET:
+ /*
+ * Check for 1 arg. Error if different.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "id");
+ return (TCL_ERROR);
+ }
+ result = Tcl_GetIntFromObj(interp, objv[2], &id);
+ if (result != TCL_OK)
+ break;
+ MUTEX_LOCK(dbenv, &mp->marray[id].m, mp->env->lockfhp);
+ res = Tcl_NewIntObj(0);
+ break;
+ case MXGETVAL:
+ /*
+ * Check for 1 arg. Error if different.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "id");
+ return (TCL_ERROR);
+ }
+ result = Tcl_GetIntFromObj(interp, objv[2], &id);
+ if (result != TCL_OK)
+ break;
+ res = Tcl_NewIntObj(mp->marray[id].val);
+ break;
+ case MXSETVAL:
+ /*
+ * Check for 2 args. Error if different.
+ */
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "id val");
+ return (TCL_ERROR);
+ }
+ result = Tcl_GetIntFromObj(interp, objv[2], &id);
+ if (result != TCL_OK)
+ break;
+ result = Tcl_GetIntFromObj(interp, objv[3], &newval);
+ if (result != TCL_OK)
+ break;
+ mp->marray[id].val = newval;
+ res = Tcl_NewIntObj(0);
+ break;
+ }
+ /*
+ * Only set result if we have a res. Otherwise, lower
+ * functions have already done so.
+ */
+ if (result == TCL_OK && res)
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
diff --git a/db/tcl/tcl_db.c b/db/tcl/tcl_db.c
new file mode 100644
index 000000000..8e7215a27
--- /dev/null
+++ b/db/tcl/tcl_db.c
@@ -0,0 +1,1771 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2000
+ * Sleepycat Software. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#ifndef lint
+static const char revid[] = "$Id: tcl_db.c,v 11.55 2000/11/28 20:12:31 bostic Exp $";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+#endif
+
+#include "db_int.h"
+#include "tcl_db.h"
+
+/*
+ * Prototypes for procedures defined later in this file:
+ */
+static int tcl_DbClose __P((Tcl_Interp *,
+ int, Tcl_Obj * CONST*, DB *, DBTCL_INFO *));
+static int tcl_DbDelete __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
+static int tcl_DbGet __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
+static int tcl_DbKeyRange __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
+static int tcl_DbPut __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
+static int tcl_DbStat __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
+static int tcl_DbCursor __P((Tcl_Interp *,
+ int, Tcl_Obj * CONST*, DB *, DBC **));
+static int tcl_DbJoin __P((Tcl_Interp *,
+ int, Tcl_Obj * CONST*, DB *, DBC **));
+static int tcl_DbGetjoin __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
+static int tcl_DbCount __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
+
+/*
+ *
+ * PUBLIC: int db_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
+ *
+ * db_Cmd --
+ * Implements the "db" widget.
+ */
+int
+db_Cmd(clientData, interp, objc, objv)
+ ClientData clientData; /* DB handle */
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *dbcmds[] = {
+ "close",
+ "count",
+ "cursor",
+ "del",
+ "get",
+ "get_join",
+ "get_type",
+ "is_byteswapped",
+ "join",
+ "keyrange",
+ "put",
+ "stat",
+ "sync",
+#if CONFIG_TEST
+ "test",
+#endif
+ NULL
+ };
+ enum dbcmds {
+ DBCLOSE,
+ DBCOUNT,
+ DBCURSOR,
+ DBDELETE,
+ DBGET,
+ DBGETJOIN,
+ DBGETTYPE,
+ DBSWAPPED,
+ DBJOIN,
+ DBKEYRANGE,
+ DBPUT,
+ DBSTAT,
+ DBSYNC
+#if CONFIG_TEST
+ , DBTEST
+#endif
+ };
+ DB *dbp;
+ DBC *dbc;
+ DBTCL_INFO *dbip;
+ DBTCL_INFO *ip;
+ Tcl_Obj *res;
+ int cmdindex, result, ret;
+ char newname[MSG_SIZE];
+
+ Tcl_ResetResult(interp);
+ dbp = (DB *)clientData;
+ dbip = _PtrToInfo((void *)dbp);
+ memset(newname, 0, MSG_SIZE);
+ result = TCL_OK;
+ if (objc <= 1) {
+ Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs");
+ return (TCL_ERROR);
+ }
+ if (dbp == NULL) {
+ Tcl_SetResult(interp, "NULL db pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (dbip == NULL) {
+ Tcl_SetResult(interp, "NULL db info pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the command name index from the object based on the dbcmds
+ * defined above.
+ */
+ if (Tcl_GetIndexFromObj(interp,
+ objv[1], dbcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+
+ res = NULL;
+ switch ((enum dbcmds)cmdindex) {
+ case DBCLOSE:
+ result = tcl_DbClose(interp, objc, objv, dbp, dbip);
+ break;
+ case DBDELETE:
+ result = tcl_DbDelete(interp, objc, objv, dbp);
+ break;
+ case DBGET:
+ result = tcl_DbGet(interp, objc, objv, dbp);
+ break;
+ case DBKEYRANGE:
+ result = tcl_DbKeyRange(interp, objc, objv, dbp);
+ break;
+ case DBPUT:
+ result = tcl_DbPut(interp, objc, objv, dbp);
+ break;
+ case DBCOUNT:
+ result = tcl_DbCount(interp, objc, objv, dbp);
+ break;
+ case DBSWAPPED:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = dbp->get_byteswapped(dbp);
+ res = Tcl_NewIntObj(ret);
+ break;
+ case DBGETTYPE:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = dbp->get_type(dbp);
+ if (ret == DB_BTREE)
+ res = Tcl_NewStringObj("btree", strlen("btree"));
+ else if (ret == DB_HASH)
+ res = Tcl_NewStringObj("hash", strlen("hash"));
+ else if (ret == DB_RECNO)
+ res = Tcl_NewStringObj("recno", strlen("recno"));
+ else if (ret == DB_QUEUE)
+ res = Tcl_NewStringObj("queue", strlen("queue"));
+ else {
+ Tcl_SetResult(interp,
+ "db gettype: Returned unknown type\n", TCL_STATIC);
+ result = TCL_ERROR;
+ }
+ break;
+ case DBSTAT:
+ result = tcl_DbStat(interp, objc, objv, dbp);
+ break;
+ case DBSYNC:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = dbp->sync(dbp, 0);
+ res = Tcl_NewIntObj(ret);
+ if (ret != 0) {
+ Tcl_SetObjResult(interp, res);
+ result = TCL_ERROR;
+ }
+ break;
+ case DBCURSOR:
+ snprintf(newname, sizeof(newname),
+ "%s.c%d", dbip->i_name, dbip->i_dbdbcid);
+ ip = _NewInfo(interp, NULL, newname, I_DBC);
+ if (ip != NULL) {
+ result = tcl_DbCursor(interp, objc, objv, dbp, &dbc);
+ if (result == TCL_OK) {
+ dbip->i_dbdbcid++;
+ ip->i_parent = dbip;
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)dbc_Cmd,
+ (ClientData)dbc, NULL);
+ res =
+ Tcl_NewStringObj(newname, strlen(newname));
+ _SetInfoData(ip, dbc);
+ } else
+ _DeleteInfo(ip);
+ } else {
+ Tcl_SetResult(interp,
+ "Could not set up info", TCL_STATIC);
+ result = TCL_ERROR;
+ }
+ break;
+ case DBJOIN:
+ snprintf(newname, sizeof(newname),
+ "%s.c%d", dbip->i_name, dbip->i_dbdbcid);
+ ip = _NewInfo(interp, NULL, newname, I_DBC);
+ if (ip != NULL) {
+ result = tcl_DbJoin(interp, objc, objv, dbp, &dbc);
+ if (result == TCL_OK) {
+ dbip->i_dbdbcid++;
+ ip->i_parent = dbip;
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)dbc_Cmd,
+ (ClientData)dbc, NULL);
+ res =
+ Tcl_NewStringObj(newname, strlen(newname));
+ _SetInfoData(ip, dbc);
+ } else
+ _DeleteInfo(ip);
+ } else {
+ Tcl_SetResult(interp,
+ "Could not set up info", TCL_STATIC);
+ result = TCL_ERROR;
+ }
+ break;
+ case DBGETJOIN:
+ result = tcl_DbGetjoin(interp, objc, objv, dbp);
+ break;
+#if CONFIG_TEST
+ case DBTEST:
+ result = tcl_EnvTest(interp, objc, objv, dbp->dbenv);
+ break;
+#endif
+ }
+ /*
+ * Only set result if we have a res. Otherwise, lower
+ * functions have already done so.
+ */
+ if (result == TCL_OK && res)
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+/*
+ * tcl_db_stat --
+ */
+static int
+tcl_DbStat(interp, objc, objv, dbp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB *dbp; /* Database pointer */
+{
+ DB_BTREE_STAT *bsp;
+ DB_HASH_STAT *hsp;
+ DB_QUEUE_STAT *qsp;
+ void *sp;
+ Tcl_Obj *res;
+ DBTYPE type;
+ u_int32_t flag;
+ int result, ret;
+ char *arg;
+
+ result = TCL_OK;
+ flag = 0;
+
+ if (objc > 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-recordcount?");
+ return (TCL_ERROR);
+ }
+
+ if (objc == 3) {
+ arg = Tcl_GetStringFromObj(objv[2], NULL);
+ if (strcmp(arg, "-recordcount") == 0)
+ flag = DB_RECORDCOUNT;
+ else if (strcmp(arg, "-cachedcounts") == 0)
+ flag = DB_CACHED_COUNTS;
+ else {
+ Tcl_SetResult(interp,
+ "db stat: unknown arg", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ }
+
+ _debug_check();
+ ret = dbp->stat(dbp, &sp, NULL, flag);
+ result = _ReturnSetup(interp, ret, "db stat");
+ if (result == TCL_ERROR)
+ return (result);
+
+ type = dbp->get_type(dbp);
+ /*
+ * Have our stats, now construct the name value
+ * list pairs and free up the memory.
+ */
+ res = Tcl_NewObj();
+ /*
+ * MAKE_STAT_LIST assumes 'res' and 'error' label.
+ */
+ if (type == DB_HASH) {
+ hsp = (DB_HASH_STAT *)sp;
+ MAKE_STAT_LIST("Magic", hsp->hash_magic);
+ MAKE_STAT_LIST("Version", hsp->hash_version);
+ MAKE_STAT_LIST("Page size", hsp->hash_pagesize);
+ MAKE_STAT_LIST("Number of keys", hsp->hash_nkeys);
+ MAKE_STAT_LIST("Number of records", hsp->hash_ndata);
+ MAKE_STAT_LIST("Estim. number of elements", hsp->hash_nelem);
+ MAKE_STAT_LIST("Fill factor", hsp->hash_ffactor);
+ MAKE_STAT_LIST("Buckets", hsp->hash_buckets);
+ MAKE_STAT_LIST("Free pages", hsp->hash_free);
+ MAKE_STAT_LIST("Bytes free", hsp->hash_bfree);
+ MAKE_STAT_LIST("Number of big pages", hsp->hash_bigpages);
+ MAKE_STAT_LIST("Big pages bytes free", hsp->hash_big_bfree);
+ MAKE_STAT_LIST("Overflow pages", hsp->hash_overflows);
+ MAKE_STAT_LIST("Overflow bytes free", hsp->hash_ovfl_free);
+ MAKE_STAT_LIST("Duplicate pages", hsp->hash_dup);
+ MAKE_STAT_LIST("Duplicate pages bytes free",
+ hsp->hash_dup_free);
+ } else if (type == DB_QUEUE) {
+ qsp = (DB_QUEUE_STAT *)sp;
+ MAKE_STAT_LIST("Magic", qsp->qs_magic);
+ MAKE_STAT_LIST("Version", qsp->qs_version);
+ MAKE_STAT_LIST("Page size", qsp->qs_pagesize);
+ MAKE_STAT_LIST("Number of records", qsp->qs_ndata);
+ MAKE_STAT_LIST("Number of pages", qsp->qs_pages);
+ MAKE_STAT_LIST("Bytes free", qsp->qs_pgfree);
+ MAKE_STAT_LIST("Record length", qsp->qs_re_len);
+ MAKE_STAT_LIST("Record pad", qsp->qs_re_pad);
+ MAKE_STAT_LIST("First record number", qsp->qs_first_recno);
+ MAKE_STAT_LIST("Last record number", qsp->qs_cur_recno);
+ } else { /* BTREE and RECNO are same stats */
+ bsp = (DB_BTREE_STAT *)sp;
+ MAKE_STAT_LIST("Number of keys", bsp->bt_nkeys);
+ MAKE_STAT_LIST("Number of records", bsp->bt_ndata);
+ if (flag != DB_RECORDCOUNT) {
+ MAKE_STAT_LIST("Magic", bsp->bt_magic);
+ MAKE_STAT_LIST("Version", bsp->bt_version);
+ MAKE_STAT_LIST("Flags", bsp->bt_metaflags);
+ MAKE_STAT_LIST("Minimum keys per page", bsp->bt_minkey);
+ MAKE_STAT_LIST("Fixed record length", bsp->bt_re_len);
+ MAKE_STAT_LIST("Record pad", bsp->bt_re_pad);
+ MAKE_STAT_LIST("Page size", bsp->bt_pagesize);
+ MAKE_STAT_LIST("Levels", bsp->bt_levels);
+ MAKE_STAT_LIST("Internal pages", bsp->bt_int_pg);
+ MAKE_STAT_LIST("Leaf pages", bsp->bt_leaf_pg);
+ MAKE_STAT_LIST("Duplicate pages", bsp->bt_dup_pg);
+ MAKE_STAT_LIST("Overflow pages", bsp->bt_over_pg);
+ MAKE_STAT_LIST("Pages on freelist", bsp->bt_free);
+ MAKE_STAT_LIST("Internal pages bytes free",
+ bsp->bt_int_pgfree);
+ MAKE_STAT_LIST("Leaf pages bytes free",
+ bsp->bt_leaf_pgfree);
+ MAKE_STAT_LIST("Duplicate pages bytes free",
+ bsp->bt_dup_pgfree);
+ MAKE_STAT_LIST("Bytes free in overflow pages",
+ bsp->bt_over_pgfree);
+ }
+ }
+ Tcl_SetObjResult(interp, res);
+error:
+ __os_free(sp, 0);
+ return (result);
+}
+
+/*
+ * tcl_db_close --
+ */
+static int
+tcl_DbClose(interp, objc, objv, dbp, dbip)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB *dbp; /* Database pointer */
+ DBTCL_INFO *dbip; /* Info pointer */
+{
+ DBTCL_INFO *p, *nextp;
+ u_int32_t flag;
+ int result, ret;
+ char *arg;
+
+ result = TCL_OK;
+ flag = 0;
+ if (objc > 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-nosync?");
+ return (TCL_ERROR);
+ }
+
+ if (objc == 3) {
+ arg = Tcl_GetStringFromObj(objv[2], NULL);
+ if (strcmp(arg, "-nosync") == 0)
+ flag = DB_NOSYNC;
+ else {
+ Tcl_SetResult(interp,
+ "dbclose: unknown arg", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ }
+
+ /*
+ * First we have to close any open cursors. Then we close
+ * our db.
+ */
+ for (p = LIST_FIRST(&__db_infohead); p != NULL; p = nextp) {
+ nextp = LIST_NEXT(p, entries);
+ /*
+ * Check if this is a cursor info structure and if
+ * it is, if it belongs to this DB. If so, remove
+ * its commands and info structure.
+ */
+ if (p->i_parent == dbip && p->i_type == I_DBC) {
+ (void)Tcl_DeleteCommand(interp, p->i_name);
+ _DeleteInfo(p);
+ }
+ }
+ (void)Tcl_DeleteCommand(interp, dbip->i_name);
+ _DeleteInfo(dbip);
+ _debug_check();
+ ret = (dbp)->close(dbp, flag);
+ result = _ReturnSetup(interp, ret, "db close");
+ return (result);
+}
+
+/*
+ * tcl_db_put --
+ */
+static int
+tcl_DbPut(interp, objc, objv, dbp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB *dbp; /* Database pointer */
+{
+ static char *dbputopts[] = {
+ "-append",
+ "-nodupdata",
+ "-nooverwrite",
+ "-partial",
+ "-txn",
+ NULL
+ };
+ enum dbputopts {
+ DBPUT_APPEND,
+ DBGET_NODUPDATA,
+ DBPUT_NOOVER,
+ DBPUT_PART,
+ DBPUT_TXN
+ };
+ static char *dbputapp[] = {
+ "-append", NULL
+ };
+ enum dbputapp { DBPUT_APPEND0 };
+ DBT key, data;
+ DBTYPE type;
+ DB_TXN *txn;
+ Tcl_Obj **elemv, *res;
+ db_recno_t recno;
+ u_int32_t flag;
+ int elemc, end, i, itmp, optindex, result, ret;
+ char *arg, msg[MSG_SIZE];
+
+ txn = NULL;
+ result = TCL_OK;
+ flag = 0;
+ if (objc <= 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-args? key data");
+ return (TCL_ERROR);
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ /*
+ * If it is a QUEUE or RECNO database, the key is a record number
+ * and must be setup up to contain a db_recno_t. Otherwise the
+ * key is a "string".
+ */
+ type = dbp->get_type(dbp);
+
+ /*
+ * We need to determine where the end of required args are. If we
+ * are using a QUEUE/RECNO db and -append, then there is just one
+ * req arg (data). Otherwise there are two (key data).
+ *
+ * We preparse the list to determine this since we need to know
+ * to properly check # of args for other options below.
+ */
+ end = objc - 2;
+ if (type == DB_QUEUE || type == DB_RECNO) {
+ i = 2;
+ while (i < objc - 1) {
+ if (Tcl_GetIndexFromObj(interp, objv[i++], dbputapp,
+ "option", TCL_EXACT, &optindex) != TCL_OK)
+ continue;
+ switch ((enum dbputapp)optindex) {
+ case DBPUT_APPEND0:
+ end = objc - 1;
+ break;
+ }
+ }
+ }
+ Tcl_ResetResult(interp);
+
+ /*
+ * Get the command name index from the object based on the options
+ * defined above.
+ */
+ i = 2;
+ while (i < end) {
+ if (Tcl_GetIndexFromObj(interp, objv[i],
+ dbputopts, "option", TCL_EXACT, &optindex) != TCL_OK)
+ return (IS_HELP(objv[i]));
+ i++;
+ switch ((enum dbputopts)optindex) {
+ case DBPUT_TXN:
+ if (i > (end - 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,
+ "Put: Invalid txn: %s\n", arg);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ result = TCL_ERROR;
+ }
+ break;
+ case DBPUT_APPEND:
+ FLAG_CHECK(flag);
+ flag = DB_APPEND;
+ break;
+ case DBGET_NODUPDATA:
+ FLAG_CHECK(flag);
+ flag = DB_NODUPDATA;
+ break;
+ case DBPUT_NOOVER:
+ FLAG_CHECK(flag);
+ flag = DB_NOOVERWRITE;
+ break;
+ case DBPUT_PART:
+ if (i > (end - 1)) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-partial {offset length}?");
+ result = TCL_ERROR;
+ break;
+ }
+ /*
+ * Get sublist as {offset length}
+ */
+ result = Tcl_ListObjGetElements(interp, objv[i++],
+ &elemc, &elemv);
+ if (elemc != 2) {
+ Tcl_SetResult(interp,
+ "List must be {offset length}", TCL_STATIC);
+ result = TCL_ERROR;
+ break;
+ }
+ data.flags = DB_DBT_PARTIAL;
+ result = Tcl_GetIntFromObj(interp, elemv[0], &itmp);
+ data.doff = itmp;
+ if (result != TCL_OK)
+ break;
+ result = Tcl_GetIntFromObj(interp, elemv[1], &itmp);
+ data.dlen = itmp;
+ /*
+ * NOTE: We don't check result here because all we'd
+ * do is break anyway, and we are doing that. If you
+ * add code here, you WILL need to add the check
+ * for result. (See the check for save.doff, a few
+ * lines above and copy that.)
+ */
+ break;
+ }
+ if (result != TCL_OK)
+ break;
+ }
+
+ if (result == TCL_ERROR)
+ return (result);
+
+ /*
+ * If we are a recno db and we are NOT using append, then the 2nd
+ * last arg is the key.
+ */
+ if (type == DB_QUEUE || type == DB_RECNO) {
+ key.data = &recno;
+ key.ulen = key.size = sizeof(db_recno_t);
+ key.flags = DB_DBT_USERMEM;
+ if (flag == DB_APPEND)
+ recno = 0;
+ else {
+ result = Tcl_GetIntFromObj(interp, objv[objc-2], &itmp);
+ recno = itmp;
+ if (result != TCL_OK)
+ return (result);
+ }
+ } else {
+ key.data = Tcl_GetByteArrayFromObj(objv[objc-2], &itmp);
+ key.size = itmp;
+ }
+ /*
+ * XXX
+ * Tcl 8.1 Tcl_GetByteArrayFromObj/Tcl_GetIntFromObj bug.
+ *
+ * This line (and the line for key.data above) were moved from
+ * the beginning of the function to here.
+ *
+ * There is a bug in Tcl 8.1 and byte arrays in that if it happens
+ * to use an object as both a byte array and something else like
+ * an int, and you've done a Tcl_GetByteArrayFromObj, then you
+ * do a Tcl_GetIntFromObj, your memory is deleted.
+ *
+ * Workaround is to make sure all Tcl_GetByteArrayFromObj calls
+ * are done last.
+ */
+ data.data = Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
+ data.size = itmp;
+ _debug_check();
+ ret = dbp->put(dbp, txn, &key, &data, flag);
+ result = _ReturnSetup(interp, ret, "db put");
+ if (ret == 0 &&
+ (type == DB_RECNO || type == DB_QUEUE) && flag == DB_APPEND) {
+ res = Tcl_NewIntObj(recno);
+ Tcl_SetObjResult(interp, res);
+ }
+ return (result);
+}
+
+/*
+ * tcl_db_get --
+ */
+static int
+tcl_DbGet(interp, objc, objv, dbp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB *dbp; /* Database pointer */
+{
+ static char *dbgetopts[] = {
+ "-consume",
+ "-consume_wait",
+ "-get_both",
+ "-glob",
+ "-partial",
+ "-recno",
+ "-rmw",
+ "-txn",
+ NULL
+ };
+ enum dbgetopts {
+ DBGET_CONSUME,
+ DBGET_CONSUME_WAIT,
+ DBGET_BOTH,
+ DBGET_GLOB,
+ DBGET_PART,
+ DBGET_RECNO,
+ DBGET_RMW,
+ DBGET_TXN
+ };
+ DBC *dbc;
+ DBT key, data, save;
+ DBTYPE type;
+ DB_TXN *txn;
+ Tcl_Obj **elemv, *retlist;
+ db_recno_t recno;
+ u_int32_t flag, cflag, isdup, rmw;
+ int elemc, end, i, itmp, optindex, result, ret, useglob, userecno;
+ char *arg, *pattern, *prefix, msg[MSG_SIZE];
+
+ result = TCL_OK;
+ cflag = flag = rmw = 0;
+ useglob = userecno = 0;
+ txn = NULL;
+ pattern = prefix = NULL;
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-args? key");
+ return (TCL_ERROR);
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ memset(&save, 0, sizeof(save));
+
+ /*
+ * Get the command name index from the object based on the options
+ * defined above.
+ */
+ i = 2;
+ type = dbp->get_type(dbp);
+ end = objc;
+ while (i < end) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], dbgetopts, "option",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ if (IS_HELP(objv[i]) == TCL_OK)
+ return (TCL_OK);
+ Tcl_ResetResult(interp);
+ break;
+ }
+ i++;
+ switch ((enum dbgetopts)optindex) {
+ case DBGET_BOTH:
+ /*
+ * Change 'end' and make sure we aren't already past
+ * the new end.
+ */
+ if (i > objc - 2) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-get_both key data?");
+ result = TCL_ERROR;
+ break;
+ }
+ end = objc - 2;
+ FLAG_CHECK(flag);
+ flag = DB_GET_BOTH;
+ break;
+ case DBGET_TXN:
+ if (i == end - 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,
+ "Get: Invalid txn: %s\n", arg);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ result = TCL_ERROR;
+ }
+ break;
+ case DBGET_GLOB:
+ useglob = 1;
+ end = objc - 1;
+ break;
+ case DBGET_CONSUME:
+ FLAG_CHECK(flag);
+ flag = DB_CONSUME;
+ break;
+ case DBGET_CONSUME_WAIT:
+ FLAG_CHECK(flag);
+ flag = DB_CONSUME_WAIT;
+ break;
+ case DBGET_RECNO:
+ end = objc - 1;
+ userecno = 1;
+ if (type != DB_RECNO && type != DB_QUEUE) {
+ FLAG_CHECK(flag);
+ flag = DB_SET_RECNO;
+ }
+ break;
+ case DBGET_RMW:
+ rmw = DB_RMW;
+ break;
+ case DBGET_PART:
+ end = objc - 1;
+ if (i == end) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-partial {offset length}?");
+ result = TCL_ERROR;
+ break;
+ }
+ /*
+ * Get sublist as {offset length}
+ */
+ result = Tcl_ListObjGetElements(interp, objv[i++],
+ &elemc, &elemv);
+ if (elemc != 2) {
+ Tcl_SetResult(interp,
+ "List must be {offset length}", TCL_STATIC);
+ result = TCL_ERROR;
+ break;
+ }
+ save.flags = DB_DBT_PARTIAL;
+ result = Tcl_GetIntFromObj(interp, elemv[0], &itmp);
+ save.doff = itmp;
+ if (result != TCL_OK)
+ break;
+ result = Tcl_GetIntFromObj(interp, elemv[1], &itmp);
+ save.dlen = itmp;
+ /*
+ * NOTE: We don't check result here because all we'd
+ * do is break anyway, and we are doing that. If you
+ * add code here, you WILL need to add the check
+ * for result. (See the check for save.doff, a few
+ * lines above and copy that.)
+ */
+ break;
+ }
+ if (result != TCL_OK)
+ break;
+ }
+ if (result != TCL_OK)
+ goto out;
+
+ if (type == DB_RECNO || type == DB_QUEUE)
+ userecno = 1;
+ /*
+ * Check for illegal combos of options.
+ */
+ if (useglob && (userecno || flag == DB_SET_RECNO ||
+ type == DB_RECNO || type == DB_QUEUE)) {
+ Tcl_SetResult(interp,
+ "Cannot use -glob and record numbers.\n",
+ TCL_STATIC);
+ result = TCL_ERROR;
+ goto out;
+ }
+ if (useglob && flag == DB_GET_BOTH) {
+ Tcl_SetResult(interp,
+ "Only one of -glob or -get_both can be specified.\n",
+ TCL_STATIC);
+ result = TCL_ERROR;
+ goto out;
+ }
+
+ if (useglob)
+ pattern = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+
+ /*
+ * This is the list we return
+ */
+ retlist = Tcl_NewListObj(0, NULL);
+ save.flags |= DB_DBT_MALLOC;
+
+ /*
+ * isdup is used to know if we support duplicates. If not, we
+ * can just do a db->get call and avoid using cursors.
+ * XXX
+ * When there is a db->get_flags method, it should be used.
+ * isdup = dbp->get_flags(dbp) & DB_DUP;
+ * For now we illegally peek.
+ * XXX
+ */
+ isdup = dbp->flags & DB_AM_DUP;
+
+ /*
+ * If the database doesn't support duplicates or we're performing
+ * ops that don't require returning multiple items, use DB->get
+ * instead of a cursor operation.
+ */
+ if (pattern == NULL && (isdup == 0 ||
+ flag == DB_SET_RECNO || flag == DB_GET_BOTH ||
+ flag == DB_CONSUME || flag == DB_CONSUME_WAIT)) {
+ if (flag == DB_GET_BOTH) {
+ if (userecno) {
+ result = Tcl_GetIntFromObj(interp,
+ objv[(objc - 2)], &itmp);
+ recno = itmp;
+ if (result == TCL_OK) {
+ key.data = &recno;
+ key.size = sizeof(db_recno_t);
+ } else
+ return (result);
+ } else {
+ key.data =
+ Tcl_GetByteArrayFromObj(objv[objc-2],
+ &itmp);
+ key.size = itmp;
+ }
+ /*
+ * Already checked args above. Fill in key and save.
+ * Save is used in the dbp->get call below to fill in
+ * data.
+ */
+ save.data =
+ Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
+ save.size = itmp;
+ } else if (flag != DB_CONSUME && flag != DB_CONSUME_WAIT) {
+ if (userecno) {
+ result = Tcl_GetIntFromObj(
+ interp, objv[(objc - 1)], &itmp);
+ recno = itmp;
+ if (result == TCL_OK) {
+ key.data = &recno;
+ key.size = sizeof(db_recno_t);
+ } else
+ return (result);
+ } else {
+ key.data = Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
+ key.size = itmp;
+ }
+ }
+
+ memset(&data, 0, sizeof(data));
+ data = save;
+
+ _debug_check();
+
+ ret = dbp->get(dbp, txn, &key, &data, flag | rmw);
+ result = _ReturnSetup(interp, ret, "db get");
+ if (ret == 0) {
+ /*
+ * Success. Return a list of the form {name value}
+ * If it was a recno in key.data, we need to convert
+ * into a string/object representation of that recno.
+ */
+ if (type == DB_RECNO || type == DB_QUEUE)
+ result = _SetListRecnoElem(interp, retlist,
+ *(db_recno_t *)key.data, data.data,
+ data.size);
+ else
+ result = _SetListElem(interp, retlist,
+ key.data, key.size, data.data, data.size);
+ /*
+ * Free space from DB_DBT_MALLOC
+ */
+ __os_free(data.data, data.size);
+ }
+ if (result == TCL_OK)
+ Tcl_SetObjResult(interp, retlist);
+ goto out;
+ }
+
+ if (userecno) {
+ result = Tcl_GetIntFromObj(interp, objv[(objc - 1)], &itmp);
+ recno = itmp;
+ if (result == TCL_OK) {
+ key.data = &recno;
+ key.size = sizeof(db_recno_t);
+ } else
+ return (result);
+ } else {
+ key.data = Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
+ key.size = itmp;
+ }
+ ret = dbp->cursor(dbp, txn, &dbc, 0);
+ result = _ReturnSetup(interp, ret, "db cursor");
+ if (result == TCL_ERROR)
+ goto out;
+
+ /*
+ * At this point, we have a cursor, if we have a pattern,
+ * we go to the nearest one and step forward until we don't
+ * have any more that match the pattern prefix. If we have
+ * an exact key, we go to that key position, and step through
+ * all the duplicates. In either case we build up a list of
+ * the form {{key data} {key data}...} along the way.
+ */
+ memset(&data, 0, sizeof(data));
+ /*
+ * Restore any "partial" info we have saved.
+ */
+ data = save;
+ if (pattern) {
+ /*
+ * Note, prefix is returned in new space. Must free it.
+ */
+ ret = _GetGlobPrefix(pattern, &prefix);
+ if (ret) {
+ result = TCL_ERROR;
+ Tcl_SetResult(interp,
+ "Unable to allocate pattern space", TCL_STATIC);
+ goto out1;
+ }
+ key.data = prefix;
+ key.size = strlen(prefix);
+ /*
+ * If they give us an empty pattern string
+ * (i.e. -glob *), go through entire DB.
+ */
+ if (strlen(prefix) == 0)
+ cflag = DB_FIRST;
+ else
+ cflag = DB_SET_RANGE;
+ } else
+ cflag = DB_SET;
+ _debug_check();
+ ret = dbc->c_get(dbc, &key, &data, cflag | rmw);
+ result = _ReturnSetup(interp, ret, "db get (cursor)");
+ if (result == TCL_ERROR)
+ goto out1;
+ if (pattern)
+ cflag = DB_NEXT;
+ else
+ cflag = DB_NEXT_DUP;
+
+ while (ret == 0 && result == TCL_OK) {
+ /*
+ * Build up our {name value} sublist
+ */
+ result = _SetListElem(interp, retlist,
+ key.data, key.size,
+ data.data, data.size);
+ /*
+ * Free space from DB_DBT_MALLOC
+ */
+ __os_free(data.data, data.size);
+ if (result != TCL_OK)
+ break;
+ /*
+ * Append {name value} to return list
+ */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ /*
+ * Restore any "partial" info we have saved.
+ */
+ data = save;
+ ret = dbc->c_get(dbc, &key, &data, cflag | rmw);
+ if (ret == 0 && pattern &&
+ memcmp(key.data, prefix, strlen(prefix)) != 0) {
+ /*
+ * Free space from DB_DBT_MALLOC
+ */
+ __os_free(data.data, data.size);
+ break;
+ }
+ }
+ dbc->c_close(dbc);
+out1:
+ if (result == TCL_OK)
+ Tcl_SetObjResult(interp, retlist);
+out:
+ /*
+ * _GetGlobPrefix(), the function which allocates prefix, works
+ * by copying and condensing another string. Thus prefix may
+ * have multiple nuls at the end, so we free using __os_free().
+ */
+ if (prefix != NULL)
+ __os_free(prefix,0);
+ return (result);
+}
+
+/*
+ * tcl_db_delete --
+ */
+static int
+tcl_DbDelete(interp, objc, objv, dbp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB *dbp; /* Database pointer */
+{
+ static char *dbdelopts[] = {
+ "-glob",
+ "-txn",
+ NULL
+ };
+ enum dbdelopts {
+ DBDEL_GLOB,
+ DBDEL_TXN
+ };
+ DBC *dbc;
+ DBT key, data;
+ DBTYPE type;
+ DB_TXN *txn;
+ db_recno_t recno;
+ int i, itmp, optindex, result, ret;
+ u_int32_t flag;
+ char *arg, *pattern, *prefix, msg[MSG_SIZE];
+
+ result = TCL_OK;
+ flag = 0;
+ pattern = prefix = NULL;
+ txn = NULL;
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-args? key");
+ return (TCL_ERROR);
+ }
+
+ memset(&key, 0, sizeof(key));
+ /*
+ * The first arg must be -txn, -glob or a list of keys.
+ */
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], dbdelopts, "option",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ /*
+ * If we don't have a -glob or -txn, then the
+ * remaining args must be exact keys.
+ * Reset the result so we don't get
+ * an errant error message if there is another error.
+ */
+ if (IS_HELP(objv[i]) == TCL_OK)
+ return (TCL_OK);
+ Tcl_ResetResult(interp);
+ break;
+ }
+ i++;
+ switch ((enum dbdelopts)optindex) {
+ case DBDEL_TXN:
+ if (i == objc) {
+ /*
+ * Someone could conceivably have a key of
+ * the same name. So just break and use it.
+ */
+ i--;
+ break;
+ }
+ arg = Tcl_GetStringFromObj(objv[i++], NULL);
+ txn = NAME_TO_TXN(arg);
+ if (txn == NULL) {
+ snprintf(msg, MSG_SIZE,
+ "Delete: Invalid txn: %s\n", arg);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ result = TCL_ERROR;
+ }
+ break;
+ case DBDEL_GLOB:
+ /*
+ * Get the pattern. Get the prefix and use cursors to
+ * get all the data items.
+ */
+ if (i == objc) {
+ /*
+ * Someone could conceivably have a key of
+ * the same name. So just break and use it.
+ */
+ i--;
+ break;
+ }
+ pattern = Tcl_GetStringFromObj(objv[i++], NULL);
+ break;
+ }
+ if (result != TCL_OK)
+ break;
+ }
+
+ if (result != TCL_OK)
+ goto out;
+
+ /*
+ * If we have a pattern AND more keys to process, then there
+ * is an error. Either we have some number of exact keys,
+ * or we have a pattern.
+ */
+ if (pattern != NULL && i != objc) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?args? -glob pattern | key");
+ result = TCL_ERROR;
+ goto out;
+ }
+ /*
+ * XXX
+ * For consistency with get, we have decided for the moment, to
+ * allow -glob, or one key, not many. The code was originally
+ * written to take many keys and we'll leave it that way, because
+ * tcl_DbGet may one day accept many disjoint keys to get, rather
+ * than one, and at that time we'd make delete be consistent. In
+ * any case, the code is already here and there is no need to remove,
+ * just check that we only have one arg left.
+ */
+ if (pattern == NULL && i != (objc - 1)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?args? -glob pattern | key");
+ result = TCL_ERROR;
+ goto out;
+ }
+
+ /*
+ * If we have remaining args, they are all exact keys. Call
+ * DB->del on each of those keys.
+ *
+ * If it is a RECNO database, the key is a record number and must be
+ * setup up to contain a db_recno_t. Otherwise the key is a "string".
+ */
+ type = dbp->get_type(dbp);
+ ret = 0;
+ while (i < objc && ret == 0) {
+ memset(&key, 0, sizeof(key));
+ if (type == DB_RECNO || type == DB_QUEUE) {
+ result = Tcl_GetIntFromObj(interp, objv[i++], &itmp);
+ recno = itmp;
+ if (result == TCL_OK) {
+ key.data = &recno;
+ key.size = sizeof(db_recno_t);
+ } else
+ return (result);
+ } else {
+ key.data = Tcl_GetByteArrayFromObj(objv[i++], &itmp);
+ key.size = itmp;
+ }
+ _debug_check();
+ ret = dbp->del(dbp, txn, &key, 0);
+ /*
+ * If we have any error, set up return result and stop
+ * processing keys.
+ */
+ if (ret != 0)
+ break;
+ }
+ result = _ReturnSetup(interp, ret, "db del");
+
+ /*
+ * At this point we've either finished or, if we have a pattern,
+ * we go to the nearest one and step forward until we don't
+ * have any more that match the pattern prefix.
+ */
+ if (pattern) {
+ ret = dbp->cursor(dbp, txn, &dbc, 0);
+ if (ret != 0) {
+ result = _ReturnSetup(interp, ret, "db cursor");
+ goto out;
+ }
+ /*
+ * Note, prefix is returned in new space. Must free it.
+ */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ ret = _GetGlobPrefix(pattern, &prefix);
+ if (ret) {
+ result = TCL_ERROR;
+ Tcl_SetResult(interp,
+ "Unable to allocate pattern space", TCL_STATIC);
+ goto out;
+ }
+ key.data = prefix;
+ key.size = strlen(prefix);
+ if (strlen(prefix) == 0)
+ flag = DB_FIRST;
+ else
+ flag = DB_SET_RANGE;
+ ret = dbc->c_get(dbc, &key, &data, flag);
+ while (ret == 0 &&
+ memcmp(key.data, prefix, strlen(prefix)) == 0) {
+ /*
+ * Each time through here the cursor is pointing
+ * at the current valid item. Delete it and
+ * move ahead.
+ */
+ _debug_check();
+ ret = dbc->c_del(dbc, 0);
+ if (ret != 0) {
+ result = _ReturnSetup(interp, ret, "db c_del");
+ break;
+ }
+ /*
+ * Deleted the current, now move to the next item
+ * in the list, check if it matches the prefix pattern.
+ */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ ret = dbc->c_get(dbc, &key, &data, DB_NEXT);
+ }
+ if (ret == DB_NOTFOUND)
+ ret = 0;
+ /*
+ * _GetGlobPrefix(), the function which allocates prefix, works
+ * by copying and condensing another string. Thus prefix may
+ * have multiple nuls at the end, so we free using __os_free().
+ */
+ __os_free(prefix,0);
+ dbc->c_close(dbc);
+ result = _ReturnSetup(interp, ret, "db del");
+ }
+out:
+ return (result);
+}
+
+/*
+ * tcl_db_cursor --
+ */
+static int
+tcl_DbCursor(interp, objc, objv, dbp, dbcp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB *dbp; /* Database pointer */
+ DBC **dbcp; /* Return cursor pointer */
+{
+ static char *dbcuropts[] = {
+ "-txn", "-update",
+ NULL
+ };
+ enum dbcuropts {
+ DBCUR_TXN, DBCUR_UPDATE
+ };
+ DB_TXN *txn;
+ u_int32_t flag;
+ int i, optindex, result, ret;
+ char *arg, msg[MSG_SIZE];
+
+ result = TCL_OK;
+ flag = 0;
+ txn = NULL;
+ /*
+ * If the user asks for -glob or -recno, it MUST be the second
+ * last arg given. If it isn't given, then we must check if
+ * they gave us a correct key.
+ */
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], dbcuropts, "option",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ result = IS_HELP(objv[i]);
+ goto out;
+ }
+ i++;
+ switch ((enum dbcuropts)optindex) {
+ case DBCUR_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,
+ "Cursor: Invalid txn: %s\n", arg);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ result = TCL_ERROR;
+ }
+ break;
+ case DBCUR_UPDATE:
+ flag = DB_WRITECURSOR;
+ break;
+ }
+ if (result != TCL_OK)
+ break;
+ }
+ if (result != TCL_OK)
+ goto out;
+
+ _debug_check();
+ ret = dbp->cursor(dbp, txn, dbcp, flag);
+ if (ret != 0)
+ result = _ErrorSetup(interp, ret, "db cursor");
+out:
+ return (result);
+}
+
+/*
+ * tcl_db_join --
+ */
+static int
+tcl_DbJoin(interp, objc, objv, dbp, dbcp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB *dbp; /* Database pointer */
+ DBC **dbcp; /* Cursor pointer */
+{
+ static char *dbjopts[] = {
+ "-nosort",
+ NULL
+ };
+ enum dbjopts {
+ DBJ_NOSORT
+ };
+ DBC **listp;
+ u_int32_t flag;
+ int adj, i, j, optindex, size, result, ret;
+ char *arg, msg[MSG_SIZE];
+
+ result = TCL_OK;
+ flag = 0;
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "curs1 curs2 ...");
+ return (TCL_ERROR);
+ }
+
+ i = 2;
+ adj = i;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], dbjopts, "option",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ result = IS_HELP(objv[i]);
+ if (result == TCL_OK)
+ return (result);
+ result = TCL_OK;
+ Tcl_ResetResult(interp);
+ break;
+ }
+ i++;
+ switch ((enum dbjopts)optindex) {
+ case DBJ_NOSORT:
+ flag |= DB_JOIN_NOSORT;
+ adj++;
+ break;
+ }
+ }
+ if (result != TCL_OK)
+ return (result);
+ /*
+ * Allocate one more for NULL ptr at end of list.
+ */
+ size = sizeof(DBC *) * ((objc - adj) + 1);
+ ret = __os_malloc(dbp->dbenv, size, NULL, &listp);
+ if (ret != 0) {
+ Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ memset(listp, 0, size);
+ for (j = 0, i = adj; i < objc; i++, j++) {
+ arg = Tcl_GetStringFromObj(objv[i], NULL);
+ listp[j] = NAME_TO_DBC(arg);
+ if (listp[j] == NULL) {
+ snprintf(msg, MSG_SIZE,
+ "Join: Invalid cursor: %s\n", arg);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ result = TCL_ERROR;
+ goto out;
+ }
+ }
+ listp[j] = NULL;
+ _debug_check();
+ ret = dbp->join(dbp, listp, dbcp, flag);
+ result = _ReturnSetup(interp, ret, "db join");
+
+out:
+ __os_free(listp, size);
+ return (result);
+}
+
+/*
+ * tcl_db_getjoin --
+ */
+static int
+tcl_DbGetjoin(interp, objc, objv, dbp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB *dbp; /* Database pointer */
+{
+ static char *dbgetjopts[] = {
+ "-nosort",
+ "-txn",
+ NULL
+ };
+ enum dbgetjopts {
+ DBGETJ_NOSORT,
+ DBGETJ_TXN
+ };
+ DB_TXN *txn;
+ DB *elemdbp;
+ DBC **listp;
+ DBC *dbc;
+ DBT key, data;
+ Tcl_Obj **elemv, *retlist;
+ u_int32_t flag;
+ int adj, elemc, i, itmp, j, optindex, result, ret, size;
+ char *arg, msg[MSG_SIZE];
+
+ result = TCL_OK;
+ flag = 0;
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "{db1 key1} {db2 key2} ...");
+ return (TCL_ERROR);
+ }
+
+ txn = NULL;
+ i = 2;
+ adj = i;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], dbgetjopts, "option",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ result = IS_HELP(objv[i]);
+ if (result == TCL_OK)
+ return (result);
+ result = TCL_OK;
+ Tcl_ResetResult(interp);
+ break;
+ }
+ i++;
+ switch ((enum dbgetjopts)optindex) {
+ case DBGETJ_NOSORT:
+ flag |= DB_JOIN_NOSORT;
+ adj++;
+ break;
+ case DBGETJ_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);
+ adj += 2;
+ if (txn == NULL) {
+ snprintf(msg, MSG_SIZE,
+ "GetJoin: Invalid txn: %s\n", arg);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ result = TCL_ERROR;
+ }
+ break;
+ }
+ }
+ if (result != TCL_OK)
+ return (result);
+ size = sizeof(DBC *) * ((objc - adj) + 1);
+ ret = __os_malloc(NULL, size, NULL, &listp);
+ if (ret != 0) {
+ Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ memset(listp, 0, size);
+ for (j = 0, i = adj; i < objc; i++, j++) {
+ /*
+ * Get each sublist as {db key}
+ */
+ result = Tcl_ListObjGetElements(interp, objv[i],
+ &elemc, &elemv);
+ if (elemc != 2) {
+ Tcl_SetResult(interp, "Lists must be {db key}",
+ TCL_STATIC);
+ result = TCL_ERROR;
+ goto out;
+ }
+ /*
+ * Get a pointer to that open db. Then, open a cursor in
+ * that db, and go to the "key" place.
+ */
+ elemdbp = NAME_TO_DB(Tcl_GetStringFromObj(elemv[0], NULL));
+ if (elemdbp == NULL) {
+ snprintf(msg, MSG_SIZE, "Get_join: Invalid db: %s\n",
+ Tcl_GetStringFromObj(elemv[0], NULL));
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ result = TCL_ERROR;
+ goto out;
+ }
+ ret = elemdbp->cursor(elemdbp, txn, &listp[j], 0);
+ if ((result = _ReturnSetup(interp, ret, "db cursor")) ==
+ TCL_ERROR)
+ goto out;
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = Tcl_GetByteArrayFromObj(elemv[elemc-1], &itmp);
+ key.size = itmp;
+ ret = (listp[j])->c_get(listp[j], &key, &data, DB_SET);
+ if ((result = _ReturnSetup(interp, ret, "db cget")) ==
+ TCL_ERROR)
+ goto out;
+ }
+ listp[j] = NULL;
+ _debug_check();
+ ret = dbp->join(dbp, listp, &dbc, flag);
+ result = _ReturnSetup(interp, ret, "db join");
+ if (result == TCL_ERROR)
+ goto out;
+
+ retlist = Tcl_NewListObj(0, NULL);
+ while (ret == 0 && result == TCL_OK) {
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.flags |= DB_DBT_MALLOC;
+ data.flags |= DB_DBT_MALLOC;
+ ret = dbc->c_get(dbc, &key, &data, 0);
+ /*
+ * Build up our {name value} sublist
+ */
+ if (ret == 0) {
+ result = _SetListElem(interp, retlist,
+ key.data, key.size,
+ data.data, data.size);
+ __os_free(key.data, key.size);
+ __os_free(data.data, data.size);
+ }
+ }
+ dbc->c_close(dbc);
+ if (result == TCL_OK)
+ Tcl_SetObjResult(interp, retlist);
+out:
+ while (j) {
+ if (listp[j])
+ (listp[j])->c_close(listp[j]);
+ j--;
+ }
+ __os_free(listp, size);
+ return (result);
+}
+
+/*
+ * tcl_DbCount --
+ */
+static int
+tcl_DbCount(interp, objc, objv, dbp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB *dbp; /* Database pointer */
+{
+ Tcl_Obj *res;
+ DBC *dbc;
+ DBT key, data;
+ db_recno_t count, recno;
+ int itmp, len, result, ret;
+
+ result = TCL_OK;
+ count = 0;
+ res = NULL;
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "key");
+ return (TCL_ERROR);
+ }
+
+ memset(&key, 0, sizeof(key));
+
+ /*
+ * Get the count for our key.
+ * We do this by getting a cursor for this DB. Moving the cursor
+ * to the set location, and getting a count on that cursor.
+ */
+ ret = 0;
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ /*
+ * If it's a queue or recno database, we must make sure to
+ * treat the key as a recno rather than as a byte string.
+ */
+ if (dbp->type == DB_RECNO || dbp->type == DB_QUEUE) {
+ result = Tcl_GetIntFromObj(interp, objv[2], &itmp);
+ recno = itmp;
+ if (result == TCL_OK) {
+ key.data = &recno;
+ key.size = sizeof(db_recno_t);
+ } else
+ return (result);
+ } else {
+ key.data = Tcl_GetByteArrayFromObj(objv[2], &len);
+ key.size = len;
+ }
+ _debug_check();
+ ret = dbp->cursor(dbp, NULL, &dbc, 0);
+ if (ret != 0) {
+ result = _ReturnSetup(interp, ret, "db cursor");
+ goto out;
+ }
+ /*
+ * Move our cursor to the key.
+ */
+ ret = dbc->c_get(dbc, &key, &data, DB_SET);
+ if (ret == DB_NOTFOUND)
+ count = 0;
+ else {
+ ret = dbc->c_count(dbc, &count, 0);
+ if (ret != 0) {
+ result = _ReturnSetup(interp, ret, "db cursor");
+ goto out;
+ }
+ }
+ res = Tcl_NewIntObj(count);
+ Tcl_SetObjResult(interp, res);
+out:
+ return (result);
+}
+
+/*
+ * tcl_DbKeyRange --
+ */
+static int
+tcl_DbKeyRange(interp, objc, objv, dbp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB *dbp; /* Database pointer */
+{
+ static char *dbkeyropts[] = {
+ "-txn",
+ NULL
+ };
+ enum dbkeyropts {
+ DBKEYR_TXN
+ };
+ DB_TXN *txn;
+ DB_KEY_RANGE range;
+ DBT key;
+ DBTYPE type;
+ Tcl_Obj *myobjv[3], *retlist;
+ db_recno_t recno;
+ u_int32_t flag;
+ int i, itmp, myobjc, optindex, result, ret;
+ char *arg, msg[MSG_SIZE];
+
+ result = TCL_OK;
+ flag = 0;
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-txn id? key");
+ return (TCL_ERROR);
+ }
+
+ txn = NULL;
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], dbkeyropts, "option",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ result = IS_HELP(objv[i]);
+ if (result == TCL_OK)
+ return (result);
+ result = TCL_OK;
+ Tcl_ResetResult(interp);
+ break;
+ }
+ i++;
+ switch ((enum dbkeyropts)optindex) {
+ case DBKEYR_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,
+ "KeyRange: Invalid txn: %s\n", arg);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ result = TCL_ERROR;
+ }
+ break;
+ }
+ }
+ if (result != TCL_OK)
+ return (result);
+ type = dbp->get_type(dbp);
+ ret = 0;
+ /*
+ * Make sure we have a key.
+ */
+ if (i != (objc - 1)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?args? key");
+ result = TCL_ERROR;
+ goto out;
+ }
+ memset(&key, 0, sizeof(key));
+ if (type == DB_RECNO || type == DB_QUEUE) {
+ result = Tcl_GetIntFromObj(interp, objv[i], &itmp);
+ recno = itmp;
+ if (result == TCL_OK) {
+ key.data = &recno;
+ key.size = sizeof(db_recno_t);
+ } else
+ return (result);
+ } else {
+ key.data = Tcl_GetByteArrayFromObj(objv[i++], &itmp);
+ key.size = itmp;
+ }
+ _debug_check();
+ ret = dbp->key_range(dbp, txn, &key, &range, flag);
+ result = _ReturnSetup(interp, ret, "db join");
+ if (result == TCL_ERROR)
+ goto out;
+
+ /*
+ * If we succeeded, set up return list.
+ */
+ myobjc = 3;
+ myobjv[0] = Tcl_NewDoubleObj(range.less);
+ myobjv[1] = Tcl_NewDoubleObj(range.equal);
+ myobjv[2] = Tcl_NewDoubleObj(range.greater);
+ retlist = Tcl_NewListObj(myobjc, myobjv);
+ if (result == TCL_OK)
+ Tcl_SetObjResult(interp, retlist);
+out:
+ return (result);
+}
diff --git a/db/tcl/tcl_db_pkg.c b/db/tcl/tcl_db_pkg.c
new file mode 100644
index 000000000..f83b5a7d2
--- /dev/null
+++ b/db/tcl/tcl_db_pkg.c
@@ -0,0 +1,2246 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2000
+ * Sleepycat Software. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#ifndef lint
+static const char revid[] = "$Id: tcl_db_pkg.c,v 11.76 2001/01/19 18:02:36 bostic Exp $";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+#endif
+
+#define DB_DBM_HSEARCH 1
+
+#include "db_int.h"
+#include "tcl_db.h"
+
+/*
+ * 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_DbUpgrade __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
+static int bdb_DbVerify __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
+static int bdb_Version __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
+static int bdb_Handles __P((Tcl_Interp *, int, Tcl_Obj * CONST*));
+
+/*
+ * 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;
+
+ code = Tcl_PkgProvide(interp, "Db_tcl", "1.0");
+ if (code != TCL_OK)
+ return (code);
+
+ Tcl_CreateObjCommand(interp, "berkdb", (Tcl_ObjCmdProc *)berkdb_Cmd,
+ (ClientData)0, NULL);
+ /*
+ * Create shared global debugging variables
+ */
+ Tcl_LinkVar(interp, "__debug_on", (char *)&__debug_on, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "__debug_print", (char *)&__debug_print,
+ TCL_LINK_INT);
+ Tcl_LinkVar(interp, "__debug_stop", (char *)&__debug_stop,
+ TCL_LINK_INT);
+ 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 char *berkdbcmds[] = {
+ "dbremove",
+ "dbrename",
+ "dbverify",
+ "env",
+ "envremove",
+ "handles",
+ "open",
+ "upgrade",
+ "version",
+ /* All below are compatibility functions */
+ "hcreate", "hsearch", "hdestroy",
+ "dbminit", "fetch", "store",
+ "delete", "firstkey", "nextkey",
+ "ndbm_open", "dbmclose",
+ /* All below are convenience functions */
+ "rand", "random_int", "srand",
+ "debug_check",
+ NULL
+ };
+ /*
+ * All commands enums below ending in X are compatibility
+ */
+ enum berkdbcmds {
+ BDB_DBREMOVE,
+ BDB_DBRENAME,
+ BDB_DBVERIFY,
+ BDB_ENV,
+ BDB_ENVREMOVE,
+ BDB_HANDLES,
+ BDB_OPEN,
+ BDB_UPGRADE,
+ BDB_VERSION,
+ BDB_HCREATEX, BDB_HSEARCHX, BDB_HDESTROYX,
+ BDB_DBMINITX, BDB_FETCHX, BDB_STOREX,
+ BDB_DELETEX, BDB_FIRSTKEYX, BDB_NEXTKEYX,
+ BDB_NDBMOPENX, BDB_DBMCLOSEX,
+ BDB_RANDX, BDB_RAND_INTX, BDB_SRANDX,
+ BDB_DBGCKX
+ };
+ static int env_id = 0;
+ static int db_id = 0;
+ static int ndbm_id = 0;
+
+ DB *dbp;
+ DBM *ndbmp;
+ DBTCL_INFO *ip;
+ DB_ENV *envp;
+ Tcl_Obj *res;
+ int cmdindex, result;
+ char newname[MSG_SIZE];
+
+ COMPQUIET(notused, 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) {
+ case BDB_VERSION:
+ _debug_check();
+ result = bdb_Version(interp, objc, objv);
+ break;
+ case BDB_HANDLES:
+ result = bdb_Handles(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, &envp);
+ if (result == TCL_OK && envp != NULL) {
+ env_id++;
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)env_Cmd,
+ (ClientData)envp, NULL);
+ /* Use ip->i_name - newname is overwritten */
+ res =
+ Tcl_NewStringObj(newname, strlen(newname));
+ _SetInfoData(ip, envp);
+ } 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_UPGRADE:
+ result = bdb_DbUpgrade(interp, objc, objv);
+ break;
+ case BDB_DBVERIFY:
+ result = bdb_DbVerify(interp, objc, objv);
+ break;
+ case BDB_ENVREMOVE:
+ result = tcl_EnvRemove(interp, objc, objv, NULL, NULL);
+ 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++;
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)db_Cmd,
+ (ClientData)dbp, NULL);
+ /* Use ip->i_name - newname is overwritten */
+ res =
+ Tcl_NewStringObj(newname, strlen(newname));
+ _SetInfoData(ip, dbp);
+ } else
+ _DeleteInfo(ip);
+ } else {
+ Tcl_SetResult(interp, "Could not set up info",
+ TCL_STATIC);
+ result = TCL_ERROR;
+ }
+ break;
+ 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++;
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)ndbm_Cmd,
+ (ClientData)ndbmp, NULL);
+ /* Use ip->i_name - newname is overwritten */
+ res =
+ Tcl_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;
+ 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 DBENV->open to open the env.
+ * 5. Return env widget handle to user.
+ */
+static int
+bdb_EnvOpen(interp, objc, objv, ip, env)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DBTCL_INFO *ip; /* Our internal info */
+ DB_ENV **env; /* Environment pointer */
+{
+ static char *envopen[] = {
+ "-cachesize",
+ "-cdb",
+ "-cdb_alldb",
+ "-client_timeout",
+ "-create",
+ "-data_dir",
+ "-errfile",
+ "-errpfx",
+ "-home",
+ "-lock",
+ "-lock_conflict",
+ "-lock_detect",
+ "-lock_max",
+ "-lock_max_locks",
+ "-lock_max_lockers",
+ "-lock_max_objects",
+ "-log",
+ "-log_buffer",
+ "-log_dir",
+ "-log_max",
+ "-mmapsize",
+ "-mode",
+ "-nommap",
+ "-private",
+ "-recover",
+ "-recover_fatal",
+ "-region_init",
+ "-server",
+ "-server_timeout",
+ "-shm_key",
+ "-system_mem",
+ "-tmp_dir",
+ "-txn",
+ "-txn_max",
+ "-txn_timestamp",
+ "-use_environ",
+ "-use_environ_root",
+ "-verbose",
+ NULL
+ };
+ /*
+ * !!!
+ * These have to be in the same order as the above,
+ * which is close to but not quite alphabetical.
+ */
+ enum envopen {
+ ENV_CACHESIZE,
+ ENV_CDB,
+ ENV_CDB_ALLDB,
+ ENV_CLIENT_TO,
+ ENV_CREATE,
+ ENV_DATA_DIR,
+ ENV_ERRFILE,
+ ENV_ERRPFX,
+ ENV_HOME,
+ ENV_LOCK,
+ ENV_CONFLICT,
+ ENV_DETECT,
+ ENV_LOCK_MAX,
+ ENV_LOCK_MAX_LOCKS,
+ ENV_LOCK_MAX_LOCKERS,
+ ENV_LOCK_MAX_OBJECTS,
+ ENV_LOG,
+ ENV_LOG_BUFFER,
+ ENV_LOG_DIR,
+ ENV_LOG_MAX,
+ ENV_MMAPSIZE,
+ ENV_MODE,
+ ENV_NOMMAP,
+ ENV_PRIVATE,
+ ENV_RECOVER,
+ ENV_RECOVER_FATAL,
+ ENV_REGION_INIT,
+ ENV_SERVER,
+ ENV_SERVER_TO,
+ ENV_SHM_KEY,
+ ENV_SYSTEM_MEM,
+ ENV_TMP_DIR,
+ ENV_TXN,
+ ENV_TXN_MAX,
+ ENV_TXN_TIME,
+ ENV_USE_ENVIRON,
+ ENV_USE_ENVIRON_ROOT,
+ ENV_VERBOSE
+ };
+ Tcl_Obj **myobjv, **myobjv1;
+ time_t time;
+ u_int32_t detect, gbytes, bytes, ncaches, open_flags, set_flag, size;
+ u_int8_t *conflicts;
+ int i, intarg, itmp, j, logbufset, logmaxset;
+ int mode, myobjc, nmodes, optindex, result, ret, temp;
+ long client_to, server_to, shm;
+ char *arg, *home, *server;
+
+ result = TCL_OK;
+ mode = 0;
+ set_flag = 0;
+ home = NULL;
+ /*
+ * XXX
+ * If/when our Tcl interface becomes thread-safe, we should enable
+ * DB_THREAD here. Note that DB_THREAD currently does not work
+ * with log_get -next, -prev; if we wish to enable DB_THREAD,
+ * those must either be made thread-safe first or we must come up with
+ * a workaround. (We used to specify DB_THREAD if and only if
+ * logging was not configured.)
+ */
+ open_flags = DB_JOINENV;
+ logmaxset = logbufset = 0;
+
+ if (objc <= 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?args?");
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Server code must go before the call to db_env_create.
+ */
+ server = NULL;
+ server_to = client_to = 0;
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i++], envopen, "option",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ Tcl_ResetResult(interp);
+ continue;
+ }
+ switch ((enum envopen)optindex) {
+ case ENV_SERVER:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-server hostname");
+ result = TCL_ERROR;
+ break;
+ }
+ server = Tcl_GetStringFromObj(objv[i++], NULL);
+ break;
+ case ENV_SERVER_TO:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-server_to secs");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetLongFromObj(interp, objv[i++],
+ &server_to);
+ break;
+ case ENV_CLIENT_TO:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-client_to secs");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetLongFromObj(interp, objv[i++],
+ &client_to);
+ break;
+ default:
+ break;
+ }
+ }
+ if (server != NULL) {
+ ret = db_env_create(env, DB_CLIENT);
+ if (ret)
+ return (_ReturnSetup(interp, ret, "db_env_create"));
+ (*env)->set_errpfx((*env), ip->i_name);
+ (*env)->set_errcall((*env), _ErrorFunc);
+ if ((ret = (*env)->set_server((*env), server,
+ client_to, server_to, 0)) != 0) {
+ result = TCL_ERROR;
+ goto error;
+ }
+ } else {
+ /*
+ * Create the environment handle before parsing the args
+ * since we'll be modifying the environment as we parse.
+ */
+ ret = db_env_create(env, 0);
+ if (ret)
+ return (_ReturnSetup(interp, ret, "db_env_create"));
+ (*env)->set_errpfx((*env), ip->i_name);
+ (*env)->set_errcall((*env), _ErrorFunc);
+ }
+
+ /*
+ * Get the command name index from the object based on the bdbcmds
+ * defined above.
+ */
+ i = 2;
+ while (i < objc) {
+ 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) {
+ case ENV_SERVER:
+ case ENV_SERVER_TO:
+ case ENV_CLIENT_TO:
+ /*
+ * Already handled these, skip them and their arg.
+ */
+ i++;
+ break;
+ case ENV_CDB:
+ FLD_SET(open_flags, DB_INIT_CDB | DB_INIT_MPOOL);
+ FLD_CLR(open_flags, DB_JOINENV);
+ break;
+ case ENV_CDB_ALLDB:
+ FLD_SET(set_flag, DB_CDB_ALLDB);
+ break;
+ case ENV_LOCK:
+ FLD_SET(open_flags, DB_INIT_LOCK | DB_INIT_MPOOL);
+ FLD_CLR(open_flags, DB_JOINENV);
+ break;
+ case ENV_LOG:
+ FLD_SET(open_flags, DB_INIT_LOG | DB_INIT_MPOOL);
+ FLD_CLR(open_flags, DB_JOINENV);
+ break;
+ case ENV_TXN:
+ FLD_SET(open_flags, DB_INIT_LOCK |
+ DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN);
+ FLD_CLR(open_flags, DB_JOINENV);
+ /* Make sure we have an arg to check against! */
+ if (i < objc) {
+ arg = Tcl_GetStringFromObj(objv[i], NULL);
+ if (strcmp(arg, "nosync") == 0) {
+ FLD_SET(set_flag, DB_TXN_NOSYNC);
+ i++;
+ }
+ }
+ break;
+ case ENV_CREATE:
+ FLD_SET(open_flags, DB_CREATE | DB_INIT_MPOOL);
+ FLD_CLR(open_flags, DB_JOINENV);
+ break;
+ case 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 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 ENV_NOMMAP:
+ FLD_SET(set_flag, DB_NOMMAP);
+ break;
+ case ENV_PRIVATE:
+ FLD_SET(open_flags, DB_PRIVATE | DB_INIT_MPOOL);
+ FLD_CLR(open_flags, DB_JOINENV);
+ break;
+ case ENV_RECOVER:
+ FLD_SET(open_flags, DB_RECOVER);
+ break;
+ case ENV_RECOVER_FATAL:
+ FLD_SET(open_flags, DB_RECOVER_FATAL);
+ break;
+ case ENV_SYSTEM_MEM:
+ FLD_SET(open_flags, DB_SYSTEM_MEM);
+ break;
+ case ENV_USE_ENVIRON_ROOT:
+ FLD_SET(open_flags, DB_USE_ENVIRON_ROOT);
+ break;
+ case ENV_USE_ENVIRON:
+ FLD_SET(open_flags, DB_USE_ENVIRON);
+ break;
+ case 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, *env,
+ myobjv[0], myobjv[1]);
+ break;
+ case ENV_REGION_INIT:
+ _debug_check();
+ ret = db_env_set_region_init(1);
+ result = _ReturnSetup(interp, ret, "region_init");
+ break;
+ case ENV_CACHESIZE:
+ result = Tcl_ListObjGetElements(interp, objv[i],
+ &myobjc, &myobjv);
+ if (result == TCL_OK)
+ i++;
+ else
+ break;
+ j = 0;
+ if (myobjc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-cachesize {gbytes bytes ncaches}?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, myobjv[0], &itmp);
+ gbytes = itmp;
+ if (result != TCL_OK)
+ break;
+ result = Tcl_GetIntFromObj(interp, myobjv[1], &itmp);
+ bytes = itmp;
+ if (result != TCL_OK)
+ break;
+ result = Tcl_GetIntFromObj(interp, myobjv[2], &itmp);
+ ncaches = itmp;
+ if (result != TCL_OK)
+ break;
+ _debug_check();
+ ret = (*env)->set_cachesize(*env, gbytes, bytes,
+ ncaches);
+ result = _ReturnSetup(interp, ret, "set_cachesize");
+ break;
+ case ENV_MMAPSIZE:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-mmapsize size?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
+ if (result == TCL_OK) {
+ _debug_check();
+ ret = (*env)->set_mp_mmapsize(*env,
+ (size_t)intarg);
+ result = _ReturnSetup(interp, ret, "mmapsize");
+ }
+ break;
+ case 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 = (*env)->set_shm_key(*env, shm);
+ result = _ReturnSetup(interp, ret, "shm_key");
+ }
+ break;
+ case ENV_LOG_MAX:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-log_max max?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
+ if (result == TCL_OK && logbufset) {
+ _debug_check();
+ ret = (*env)->set_lg_max(*env,
+ (u_int32_t)intarg);
+ result = _ReturnSetup(interp, ret, "log_max");
+ logbufset = 0;
+ } else
+ logmaxset = intarg;
+ break;
+ case ENV_LOG_BUFFER:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-log_buffer size?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
+ if (result == TCL_OK) {
+ _debug_check();
+ ret = (*env)->set_lg_bsize(*env,
+ (u_int32_t)intarg);
+ result = _ReturnSetup(interp, ret, "log_bsize");
+ logbufset = 1;
+ if (logmaxset) {
+ _debug_check();
+ ret = (*env)->set_lg_max(*env,
+ (u_int32_t)logmaxset);
+ result = _ReturnSetup(interp, ret,
+ "log_max");
+ logmaxset = 0;
+ logbufset = 0;
+ }
+ }
+ break;
+ case 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;
+ }
+ size = sizeof(u_int8_t) * nmodes*nmodes;
+ ret = __os_malloc(*env, size, NULL, &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(conflicts, size);
+ break;
+ }
+ }
+ _debug_check();
+ ret = (*env)->set_lk_conflicts(*env,
+ (u_int8_t *)conflicts, nmodes);
+ __os_free(conflicts, size);
+ result = _ReturnSetup(interp, ret, "set_lk_conflicts");
+ break;
+ case 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, "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 = (*env)->set_lk_detect(*env, detect);
+ result = _ReturnSetup(interp, ret, "lock_detect");
+ break;
+ case ENV_LOCK_MAX:
+ case ENV_LOCK_MAX_LOCKS:
+ case ENV_LOCK_MAX_LOCKERS:
+ case ENV_LOCK_MAX_OBJECTS:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-lock_max max?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
+ if (result == TCL_OK) {
+ _debug_check();
+ switch ((enum envopen)optindex) {
+ case ENV_LOCK_MAX:
+ ret = (*env)->set_lk_max(*env,
+ (u_int32_t)intarg);
+ break;
+ case ENV_LOCK_MAX_LOCKS:
+ ret = (*env)->set_lk_max_locks(*env,
+ (u_int32_t)intarg);
+ break;
+ case ENV_LOCK_MAX_LOCKERS:
+ ret = (*env)->set_lk_max_lockers(*env,
+ (u_int32_t)intarg);
+ break;
+ case ENV_LOCK_MAX_OBJECTS:
+ ret = (*env)->set_lk_max_objects(*env,
+ (u_int32_t)intarg);
+ break;
+ default:
+ break;
+ }
+ result = _ReturnSetup(interp, ret, "lock_max");
+ }
+ break;
+ case ENV_TXN_MAX:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-txn_max max?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
+ if (result == TCL_OK) {
+ _debug_check();
+ ret = (*env)->set_tx_max(*env,
+ (u_int32_t)intarg);
+ result = _ReturnSetup(interp, ret, "txn_max");
+ }
+ break;
+ case ENV_TXN_TIME:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-txn_timestamp time?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetLongFromObj(interp, objv[i++],
+ (long *)&time);
+ if (result == TCL_OK) {
+ _debug_check();
+ ret = (*env)->set_tx_timestamp(*env, &time);
+ result = _ReturnSetup(interp, ret,
+ "txn_timestamp");
+ }
+ break;
+ case ENV_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 (ip->i_err != NULL)
+ fclose(ip->i_err);
+ ip->i_err = fopen(arg, "a");
+ if (ip->i_err != NULL) {
+ _debug_check();
+ (*env)->set_errfile(*env, ip->i_err);
+ }
+ break;
+ case ENV_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 (ip->i_errpfx != NULL)
+ __os_freestr(ip->i_errpfx);
+ if ((ret =
+ __os_strdup(*env, arg, &ip->i_errpfx)) != 0) {
+ result = _ReturnSetup(interp, ret,
+ "__os_strdup");
+ break;
+ }
+ if (ip->i_errpfx != NULL) {
+ _debug_check();
+ (*env)->set_errpfx(*env, ip->i_errpfx);
+ }
+ break;
+ case ENV_DATA_DIR:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "-data_dir dir");
+ result = TCL_ERROR;
+ break;
+ }
+ arg = Tcl_GetStringFromObj(objv[i++], NULL);
+ _debug_check();
+ ret = (*env)->set_data_dir(*env, arg);
+ result = _ReturnSetup(interp, ret, "set_data_dir");
+ break;
+ case 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 = (*env)->set_lg_dir(*env, arg);
+ result = _ReturnSetup(interp, ret, "set_lg_dir");
+ break;
+ case 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 = (*env)->set_tmp_dir(*env, arg);
+ result = _ReturnSetup(interp, 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 = (*env)->set_lg_max(*env, (u_int32_t)logmaxset);
+ result = _ReturnSetup(interp, ret, "log_max");
+ }
+
+ if (result != TCL_OK)
+ goto error;
+
+ if (set_flag) {
+ ret = (*env)->set_flags(*env, set_flag, 1);
+ result = _ReturnSetup(interp, 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 = (*env)->open(*env, home, open_flags, mode);
+ result = _ReturnSetup(interp, ret, "env open");
+
+error:
+ if (result == TCL_ERROR) {
+ if (ip->i_err) {
+ fclose(ip->i_err);
+ ip->i_err = NULL;
+ }
+ (void)(*env)->close(*env, 0);
+ *env = NULL;
+ }
+ 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 char *bdbenvopen[] = {
+ "-env", NULL
+ };
+ enum bdbenvopen {
+ TCL_DB_ENV0
+ };
+ static char *bdbopen[] = {
+ "-btree",
+ "-cachesize",
+ "-create",
+ "-delim",
+ "-dup",
+ "-dupsort",
+ "-env",
+ "-errfile",
+ "-errpfx",
+ "-excl",
+ "-extent",
+ "-ffactor",
+ "-hash",
+ "-len",
+ "-lorder",
+ "-minkey",
+ "-mode",
+ "-nelem",
+ "-nommap",
+ "-pad",
+ "-pagesize",
+ "-queue",
+ "-rdonly",
+ "-recno",
+ "-recnum",
+ "-renumber",
+ "-revsplitoff",
+ "-snapshot",
+ "-source",
+ "-truncate",
+ "-test",
+ "-unknown",
+ "--",
+ NULL
+ };
+ enum bdbopen {
+ TCL_DB_BTREE,
+ TCL_DB_CACHESIZE,
+ TCL_DB_CREATE,
+ TCL_DB_DELIM,
+ TCL_DB_DUP,
+ TCL_DB_DUPSORT,
+ TCL_DB_ENV,
+ TCL_DB_ERRFILE,
+ TCL_DB_ERRPFX,
+ TCL_DB_EXCL,
+ TCL_DB_EXTENT,
+ TCL_DB_FFACTOR,
+ TCL_DB_HASH,
+ TCL_DB_LEN,
+ TCL_DB_LORDER,
+ TCL_DB_MINKEY,
+ TCL_DB_MODE,
+ TCL_DB_NELEM,
+ TCL_DB_NOMMAP,
+ TCL_DB_PAD,
+ TCL_DB_PAGESIZE,
+ TCL_DB_QUEUE,
+ TCL_DB_RDONLY,
+ TCL_DB_RECNO,
+ TCL_DB_RECNUM,
+ TCL_DB_RENUMBER,
+ TCL_DB_REVSPLIT,
+ TCL_DB_SNAPSHOT,
+ TCL_DB_SOURCE,
+ TCL_DB_TRUNCATE,
+ TCL_DB_TEST,
+ TCL_DB_UNKNOWN,
+ TCL_DB_ENDARG
+ };
+
+ DBTCL_INFO *envip, *errip;
+ DBTYPE type;
+ DB_ENV *envp;
+ Tcl_Obj **myobjv;
+ u_int32_t gbytes, bytes, ncaches, open_flags;
+ int endarg, i, intarg, itmp, j, mode, myobjc;
+ int optindex, result, ret, set_err, set_flag, set_pfx, subdblen;
+ u_char *subdbtmp;
+ char *arg, *db, *subdb;
+ extern u_int32_t __ham_test __P((DB *, const void *, u_int32_t));
+
+ type = DB_UNKNOWN;
+ endarg = mode = set_err = set_flag = set_pfx = 0;
+ result = TCL_OK;
+ subdbtmp = NULL;
+ db = subdb = NULL;
+
+ /*
+ * XXX
+ * If/when our Tcl interface becomes thread-safe, we should enable
+ * DB_THREAD here. See comment in bdb_EnvOpen().
+ */
+ open_flags = 0;
+ envp = 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);
+ envp = NAME_TO_ENV(arg);
+ if (envp == 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, envp, 0);
+ if (ret)
+ return (_ReturnSetup(interp, ret, "db_create"));
+
+ /*
+ * XXX Remove restriction when err stuff is not tied to env.
+ *
+ * The DB->set_err* functions actually overwrite in the
+ * environment. So, if we are explicitly using an env,
+ * don't overwrite what we have already set up. If we are
+ * not using one, then we set up since we get a private
+ * default env.
+ */
+ /* XXX - remove this conditional if/when err is not tied to env */
+ if (envp == NULL) {
+ (*dbp)->set_errpfx((*dbp), ip->i_name);
+ (*dbp)->set_errcall((*dbp), _ErrorFunc);
+ }
+ envip = _PtrToInfo(envp); /* XXX */
+ /*
+ * If we are using an env, we keep track of err info in the env's ip.
+ * Otherwise use the DB's ip.
+ */
+ 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) {
+ 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) {
+ case TCL_DB_ENV:
+ /*
+ * Already parsed this, skip it and the env pointer.
+ */
+ i++;
+ continue;
+ 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_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_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_TEST:
+ (*dbp)->set_h_hash(*dbp, __ham_test);
+ 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_NOMMAP:
+ open_flags |= DB_NOMMAP;
+ break;
+ case TCL_DB_DUP:
+ set_flag |= DB_DUP;
+ break;
+ case TCL_DB_DUPSORT:
+ set_flag |= DB_DUPSORT;
+ break;
+ case TCL_DB_RECNUM:
+ set_flag |= DB_RECNUM;
+ break;
+ case TCL_DB_RENUMBER:
+ set_flag |= DB_RENUMBER;
+ break;
+ case TCL_DB_REVSPLIT:
+ set_flag |= DB_REVSPLITOFF;
+ break;
+ case TCL_DB_SNAPSHOT:
+ set_flag |= DB_SNAPSHOT;
+ break;
+ case TCL_DB_FFACTOR:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "-ffactor density");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
+ if (result == TCL_OK) {
+ _debug_check();
+ ret = (*dbp)->set_h_ffactor(*dbp,
+ (u_int32_t)intarg);
+ result = _ReturnSetup(interp, ret,
+ "set_h_ffactor");
+ }
+ break;
+ case TCL_DB_NELEM:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "-nelem nelem");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
+ if (result == TCL_OK) {
+ _debug_check();
+ ret = (*dbp)->set_h_nelem(*dbp,
+ (u_int32_t)intarg);
+ result = _ReturnSetup(interp, ret,
+ "set_h_nelem");
+ }
+ 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,
+ (u_int32_t)intarg);
+ result = _ReturnSetup(interp, ret,
+ "set_lorder");
+ }
+ 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,
+ "set_re_delim");
+ }
+ break;
+ case TCL_DB_LEN:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "-len length");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
+ if (result == TCL_OK) {
+ _debug_check();
+ ret = (*dbp)->set_re_len(*dbp,
+ (u_int32_t)intarg);
+ result = _ReturnSetup(interp, ret,
+ "set_re_len");
+ }
+ 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,
+ "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, "set_re_source");
+ break;
+ case TCL_DB_EXTENT:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "-extent size");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
+ if (result == TCL_OK) {
+ _debug_check();
+ ret = (*dbp)->set_q_extentsize(*dbp,
+ (u_int32_t)intarg);
+ result = _ReturnSetup(interp, ret,
+ "set_q_extentsize");
+ }
+ break;
+ case TCL_DB_MINKEY:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "-minkey minkey");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &intarg);
+ if (result == TCL_OK) {
+ _debug_check();
+ ret = (*dbp)->set_bt_minkey(*dbp, intarg);
+ result = _ReturnSetup(interp, ret,
+ "set_bt_minkey");
+ }
+ break;
+ case TCL_DB_CACHESIZE:
+ result = Tcl_ListObjGetElements(interp, objv[i++],
+ &myobjc, &myobjv);
+ if (result != TCL_OK)
+ break;
+ j = 0;
+ if (myobjc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-cachesize {gbytes bytes ncaches}?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, myobjv[0], &itmp);
+ gbytes = itmp;
+ if (result != TCL_OK)
+ break;
+ result = Tcl_GetIntFromObj(interp, myobjv[1], &itmp);
+ bytes = itmp;
+ if (result != TCL_OK)
+ break;
+ result = Tcl_GetIntFromObj(interp, myobjv[2], &itmp);
+ ncaches = itmp;
+ if (result != TCL_OK)
+ break;
+ _debug_check();
+ ret = (*dbp)->set_cachesize(*dbp, gbytes, bytes,
+ ncaches);
+ result = _ReturnSetup(interp, 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,
+ "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)
+ fclose(errip->i_err);
+ 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_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)
+ __os_freestr(errip->i_errpfx);
+ if ((ret = __os_strdup((*dbp)->dbenv,
+ arg, &errip->i_errpfx)) != 0) {
+ result = _ReturnSetup(interp, 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 (i != objc) {
+ subdbtmp =
+ Tcl_GetByteArrayFromObj(objv[i++], &subdblen);
+ if ((ret = __os_malloc(envp,
+ subdblen + 1, NULL, &subdb)) != 0) {
+ Tcl_SetResult(interp, db_strerror(ret),
+ TCL_STATIC);
+ return (0);
+ }
+ memcpy(subdb, subdbtmp, subdblen);
+ subdb[subdblen] = '\0';
+ }
+ }
+ if (set_flag) {
+ ret = (*dbp)->set_flags(*dbp, set_flag);
+ result = _ReturnSetup(interp, 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, db, subdb, type, open_flags, mode);
+ result = _ReturnSetup(interp, ret, "db open");
+
+error:
+ if (subdb)
+ __os_free(subdb, subdblen + 1);
+ if (result == TCL_ERROR) {
+ /*
+ * 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) {
+ fclose(errip->i_err);
+ errip->i_err = NULL;
+ }
+ if (set_pfx && errip && errip->i_errpfx != NULL) {
+ __os_freestr(errip->i_errpfx);
+ errip->i_errpfx = NULL;
+ }
+ (void)(*dbp)->close(*dbp, 0);
+ *dbp = NULL;
+ }
+ return (result);
+}
+
+/*
+ * bdb_DbRemove --
+ * Implements the 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 char *bdbrem[] = {
+ "-env", "--", NULL
+ };
+ enum bdbrem {
+ TCL_DBREM_ENV,
+ TCL_DBREM_ENDARG
+ };
+ DB_ENV *envp;
+ DB *dbp;
+ int endarg, i, optindex, result, ret, subdblen;
+ u_char *subdbtmp;
+ char *arg, *db, *subdb;
+
+ envp = NULL;
+ dbp = NULL;
+ result = TCL_OK;
+ subdbtmp = NULL;
+ db = subdb = NULL;
+ endarg = 0;
+
+ 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_ENV:
+ arg = Tcl_GetStringFromObj(objv[i++], NULL);
+ envp = NAME_TO_ENV(arg);
+ if (envp == NULL) {
+ Tcl_SetResult(interp,
+ "db remove: illegal environment",
+ TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ break;
+ case TCL_DBREM_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;
+ /*
+ * 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 (i != objc) {
+ subdbtmp =
+ Tcl_GetByteArrayFromObj(objv[i++], &subdblen);
+ if ((ret = __os_malloc(envp, subdblen + 1,
+ NULL, &subdb)) != 0) { Tcl_SetResult(interp,
+ db_strerror(ret), TCL_STATIC);
+ return (0);
+ }
+ memcpy(subdb, subdbtmp, subdblen);
+ subdb[subdblen] = '\0';
+ }
+ } else {
+ Tcl_WrongNumArgs(interp, 2, objv, "?args? filename ?database?");
+ result = TCL_ERROR;
+ goto error;
+ }
+ ret = db_create(&dbp, envp, 0);
+ if (ret) {
+ result = _ReturnSetup(interp, ret, "db_create");
+ goto error;
+ }
+ /*
+ * No matter what, we NULL out dbp after this call.
+ */
+ ret = dbp->remove(dbp, db, subdb, 0);
+ result = _ReturnSetup(interp, ret, "db remove");
+ dbp = NULL;
+error:
+ if (subdb)
+ __os_free(subdb, subdblen + 1);
+ if (result == TCL_ERROR && dbp)
+ (void)dbp->close(dbp, 0);
+ return (result);
+}
+
+/*
+ * bdb_DbRename --
+ * Implements the DB->rename command.
+ */
+static int
+bdb_DbRename(interp, objc, objv)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *bdbmv[] = {
+ "-env", "--", NULL
+ };
+ enum bdbmv {
+ TCL_DBMV_ENV,
+ TCL_DBMV_ENDARG
+ };
+ DB_ENV *envp;
+ DB *dbp;
+ int endarg, i, newlen, optindex, result, ret, subdblen;
+ u_char *subdbtmp;
+ char *arg, *db, *newname, *subdb;
+
+ envp = NULL;
+ dbp = NULL;
+ result = TCL_OK;
+ subdbtmp = NULL;
+ db = newname = subdb = NULL;
+ endarg = 0;
+
+ 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_ENV:
+ arg = Tcl_GetStringFromObj(objv[i++], NULL);
+ envp = NAME_TO_ENV(arg);
+ if (envp == NULL) {
+ Tcl_SetResult(interp,
+ "db rename: illegal environment",
+ TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ break;
+ case TCL_DBMV_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;
+ /*
+ * 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 (i == objc - 2) {
+ subdbtmp =
+ Tcl_GetByteArrayFromObj(objv[i++], &subdblen);
+ if ((ret = __os_malloc(envp, subdblen + 1,
+ NULL, &subdb)) != 0) {
+ Tcl_SetResult(interp,
+ db_strerror(ret), TCL_STATIC);
+ return (0);
+ }
+ memcpy(subdb, subdbtmp, subdblen);
+ subdb[subdblen] = '\0';
+ }
+ subdbtmp =
+ Tcl_GetByteArrayFromObj(objv[i++], &newlen);
+ if ((ret = __os_malloc(envp, newlen + 1,
+ NULL, &newname)) != 0) {
+ Tcl_SetResult(interp,
+ db_strerror(ret), TCL_STATIC);
+ return (0);
+ }
+ memcpy(newname, subdbtmp, newlen);
+ newname[newlen] = '\0';
+ } else {
+ Tcl_WrongNumArgs(interp, 3, objv, "?args? filename ?database? ?newname?");
+ result = TCL_ERROR;
+ goto error;
+ }
+ ret = db_create(&dbp, envp, 0);
+ if (ret) {
+ result = _ReturnSetup(interp, ret, "db_create");
+ goto error;
+ }
+ /*
+ * No matter what, we NULL out dbp after this call.
+ */
+ ret = dbp->rename(dbp, db, subdb, newname, 0);
+ result = _ReturnSetup(interp, ret, "db rename");
+ dbp = NULL;
+error:
+ if (subdb)
+ __os_free(subdb, subdblen + 1);
+ if (newname)
+ __os_free(newname, newlen + 1);
+ if (result == TCL_ERROR && dbp)
+ (void)dbp->close(dbp, 0);
+ return (result);
+}
+
+/*
+ * bdb_DbVerify --
+ * Implements the DB->verify command.
+ */
+static int
+bdb_DbVerify(interp, objc, objv)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *bdbverify[] = {
+ "-env", "-errfile", "-errpfx", "--", NULL
+ };
+ enum bdbvrfy {
+ TCL_DBVRFY_ENV,
+ TCL_DBVRFY_ERRFILE,
+ TCL_DBVRFY_ERRPFX,
+ TCL_DBVRFY_ENDARG
+ };
+ DB_ENV *envp;
+ DB *dbp;
+ FILE *errf;
+ int endarg, i, optindex, result, ret, flags;
+ char *arg, *db, *errpfx;
+
+ envp = NULL;
+ dbp = NULL;
+ result = TCL_OK;
+ db = errpfx = NULL;
+ errf = NULL;
+ flags = endarg = 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_ENV:
+ arg = Tcl_GetStringFromObj(objv[i++], NULL);
+ envp = NAME_TO_ENV(arg);
+ if (envp == 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)
+ fclose(errf);
+ 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_freestr(errpfx);
+ if ((ret = __os_strdup(NULL, arg, &errpfx)) != 0) {
+ result = _ReturnSetup(interp, ret,
+ "__os_strdup");
+ break;
+ }
+ 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.
+ */
+ 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, envp, 0);
+ if (ret) {
+ result = _ReturnSetup(interp, ret, "db_create");
+ goto error;
+ }
+
+ if (errf != NULL)
+ dbp->set_errfile(dbp, errf);
+ if (errpfx != NULL)
+ dbp->set_errpfx(dbp, errpfx);
+
+ ret = dbp->verify(dbp, db, NULL, NULL, flags);
+ result = _ReturnSetup(interp, ret, "db verify");
+error:
+ if (errf != NULL)
+ fclose(errf);
+ if (errpfx != NULL)
+ __os_freestr(errpfx);
+ if (dbp)
+ (void)dbp->close(dbp, 0);
+ return (result);
+}
+
+/*
+ * 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 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 = Tcl_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);
+}
+
+/*
+ * 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);
+
+ for (p = LIST_FIRST(&__db_infohead); p != NULL;
+ p = LIST_NEXT(p, entries)) {
+ handle = Tcl_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_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 char *bdbupg[] = {
+ "-dupsort", "-env", "--", NULL
+ };
+ enum bdbupg {
+ TCL_DBUPG_DUPSORT,
+ TCL_DBUPG_ENV,
+ TCL_DBUPG_ENDARG
+ };
+ DB_ENV *envp;
+ DB *dbp;
+ int endarg, i, optindex, result, ret, flags;
+ char *arg, *db;
+
+ envp = 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);
+ envp = NAME_TO_ENV(arg);
+ if (envp == 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, envp, 0);
+ if (ret) {
+ result = _ReturnSetup(interp, ret, "db_create");
+ goto error;
+ }
+
+ ret = dbp->upgrade(dbp, db, flags);
+ result = _ReturnSetup(interp, ret, "db upgrade");
+error:
+ if (dbp)
+ (void)dbp->close(dbp, 0);
+ return (result);
+}
diff --git a/db/tcl/tcl_dbcursor.c b/db/tcl/tcl_dbcursor.c
new file mode 100644
index 000000000..26e7b58c6
--- /dev/null
+++ b/db/tcl/tcl_dbcursor.c
@@ -0,0 +1,744 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2000
+ * Sleepycat Software. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#ifndef lint
+static const char revid[] = "$Id: tcl_dbcursor.c,v 11.26 2001/01/11 18:19:55 bostic Exp $";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+#endif
+
+#include "db_int.h"
+#include "tcl_db.h"
+
+/*
+ * Prototypes for procedures defined later in this file:
+ */
+static int tcl_DbcDup __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DBC *));
+static int tcl_DbcGet __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DBC *));
+static int tcl_DbcPut __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DBC *));
+
+/*
+ * PUBLIC: int dbc_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
+ *
+ * dbc_cmd --
+ * Implements the cursor command.
+ */
+int
+dbc_Cmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Cursor handle */
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *dbccmds[] = {
+ "close",
+ "del",
+ "dup",
+ "get",
+ "put",
+ NULL
+ };
+ enum dbccmds {
+ DBCCLOSE,
+ DBCDELETE,
+ DBCDUP,
+ DBCGET,
+ DBCPUT
+ };
+ DBC *dbc;
+ DBTCL_INFO *dbip;
+ int cmdindex, result, ret;
+
+ Tcl_ResetResult(interp);
+ dbc = (DBC *)clientData;
+ dbip = _PtrToInfo((void *)dbc);
+ result = TCL_OK;
+
+ if (objc <= 1) {
+ Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs");
+ return (TCL_ERROR);
+ }
+ if (dbc == NULL) {
+ Tcl_SetResult(interp, "NULL dbc pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (dbip == NULL) {
+ Tcl_SetResult(interp, "NULL dbc info pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the command name index from the object based on the berkdbcmds
+ * defined above.
+ */
+ if (Tcl_GetIndexFromObj(interp, objv[1], dbccmds, "command",
+ TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+ switch ((enum dbccmds)cmdindex) {
+ case DBCCLOSE:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = dbc->c_close(dbc);
+ result = _ReturnSetup(interp, ret, "dbc close");
+ if (result == TCL_OK) {
+ (void)Tcl_DeleteCommand(interp, dbip->i_name);
+ _DeleteInfo(dbip);
+ }
+ break;
+ case DBCDELETE:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = dbc->c_del(dbc, 0);
+ result = _ReturnSetup(interp, ret, "dbc delete");
+ break;
+ case DBCDUP:
+ result = tcl_DbcDup(interp, objc, objv, dbc);
+ break;
+ case DBCGET:
+ result = tcl_DbcGet(interp, objc, objv, dbc);
+ break;
+ case DBCPUT:
+ result = tcl_DbcPut(interp, objc, objv, dbc);
+ break;
+ }
+ return (result);
+}
+
+/*
+ * tcl_DbcPut --
+ */
+static int
+tcl_DbcPut(interp, objc, objv, dbc)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DBC *dbc; /* Cursor pointer */
+{
+ static char *dbcutopts[] = {
+ "-after", "-before", "-current",
+ "-keyfirst", "-keylast", "-nodupdata",
+ "-partial",
+ NULL
+ };
+ enum dbcutopts {
+ DBCPUT_AFTER, DBCPUT_BEFORE, DBCPUT_CURRENT,
+ DBCPUT_KEYFIRST,DBCPUT_KEYLAST, DBCPUT_NODUPDATA,
+ DBCPUT_PART
+ };
+ DB *thisdbp;
+ DBT key, data;
+ DBTCL_INFO *dbcip, *dbip;
+ DBTYPE type;
+ Tcl_Obj **elemv, *res;
+ db_recno_t recno;
+ u_int32_t flag;
+ int elemc, i, itmp, optindex, result, ret;
+
+ result = TCL_OK;
+ flag = 0;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-args? ?key?");
+ return (TCL_ERROR);
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ /*
+ * Get the command name index from the object based on the options
+ * defined above.
+ */
+ i = 2;
+ while (i < (objc - 1)) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], dbcutopts, "option",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ /*
+ * Reset the result so we don't get
+ * an errant error message if there is another error.
+ */
+ if (IS_HELP(objv[i]) == TCL_OK) {
+ result = TCL_OK;
+ goto out;
+ }
+ Tcl_ResetResult(interp);
+ break;
+ }
+ i++;
+ switch ((enum dbcutopts)optindex) {
+ case DBCPUT_AFTER:
+ FLAG_CHECK(flag);
+ flag = DB_AFTER;
+ break;
+ case DBCPUT_BEFORE:
+ FLAG_CHECK(flag);
+ flag = DB_BEFORE;
+ break;
+ case DBCPUT_CURRENT:
+ FLAG_CHECK(flag);
+ flag = DB_CURRENT;
+ break;
+ case DBCPUT_KEYFIRST:
+ FLAG_CHECK(flag);
+ flag = DB_KEYFIRST;
+ break;
+ case DBCPUT_KEYLAST:
+ FLAG_CHECK(flag);
+ flag = DB_KEYLAST;
+ break;
+ case DBCPUT_NODUPDATA:
+ FLAG_CHECK(flag);
+ flag = DB_NODUPDATA;
+ break;
+ case DBCPUT_PART:
+ if (i > (objc - 2)) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-partial {offset length}?");
+ result = TCL_ERROR;
+ break;
+ }
+ /*
+ * Get sublist as {offset length}
+ */
+ result = Tcl_ListObjGetElements(interp, objv[i++],
+ &elemc, &elemv);
+ if (elemc != 2) {
+ Tcl_SetResult(interp,
+ "List must be {offset length}", TCL_STATIC);
+ result = TCL_ERROR;
+ break;
+ }
+ data.flags |= DB_DBT_PARTIAL;
+ result = Tcl_GetIntFromObj(interp, elemv[0], &itmp);
+ data.doff = itmp;
+ if (result != TCL_OK)
+ break;
+ result = Tcl_GetIntFromObj(interp, elemv[1], &itmp);
+ data.dlen = itmp;
+ /*
+ * NOTE: We don't check result here because all we'd
+ * do is break anyway, and we are doing that. If you
+ * add code here, you WILL need to add the check
+ * for result. (See the check for save.doff, a few
+ * lines above and copy that.)
+ */
+ }
+ if (result != TCL_OK)
+ break;
+ }
+ if (result != TCL_OK)
+ goto out;
+
+ /*
+ * We need to determine if we are a recno database or not. If we are,
+ * then key.data is a recno, not a string.
+ */
+ dbcip = _PtrToInfo(dbc);
+ if (dbcip == NULL)
+ type = DB_UNKNOWN;
+ else {
+ dbip = dbcip->i_parent;
+ if (dbip == NULL) {
+ Tcl_SetResult(interp, "Cursor without parent database",
+ TCL_STATIC);
+ result = TCL_ERROR;
+ return (result);
+ }
+ thisdbp = dbip->i_dbp;
+ type = thisdbp->get_type(thisdbp);
+ }
+ /*
+ * When we get here, we better have:
+ * 1 arg if -after, -before or -current
+ * 2 args in all other cases
+ */
+ if (flag == DB_AFTER || flag == DB_BEFORE || flag == DB_CURRENT) {
+ if (i != (objc - 1)) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-args? data");
+ result = TCL_ERROR;
+ goto out;
+ }
+ /*
+ * We want to get the key back, so we need to set
+ * up the location to get it back in.
+ */
+ if (type == DB_RECNO || type == DB_QUEUE) {
+ recno = 0;
+ key.data = &recno;
+ key.size = sizeof(db_recno_t);
+ }
+ } else {
+ if (i != (objc - 2)) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-args? key data");
+ result = TCL_ERROR;
+ goto out;
+ }
+ if (type == DB_RECNO || type == DB_QUEUE) {
+ result = Tcl_GetIntFromObj(interp, objv[objc-2], &itmp);
+ recno = itmp;
+ if (result == TCL_OK) {
+ key.data = &recno;
+ key.size = sizeof(db_recno_t);
+ } else
+ return (result);
+ } else {
+ key.data = Tcl_GetByteArrayFromObj(objv[objc-2], &itmp);
+ key.size = itmp;
+ }
+ }
+ data.data = Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
+ data.size = itmp;
+ _debug_check();
+ ret = dbc->c_put(dbc, &key, &data, flag);
+ result = _ReturnSetup(interp, ret, "dbc put");
+ if (ret == 0 && (flag == DB_AFTER || flag == DB_BEFORE)
+ && type == DB_RECNO) {
+ res = Tcl_NewIntObj(*(db_recno_t *)key.data);
+ Tcl_SetObjResult(interp, res);
+ }
+out:
+ return (result);
+}
+
+/*
+ * tcl_dbc_get --
+ */
+static int
+tcl_DbcGet(interp, objc, objv, dbc)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DBC *dbc; /* Cursor pointer */
+{
+ static char *dbcgetopts[] = {
+ "-current",
+ "-first",
+ "-get_both",
+ "-get_recno",
+ "-join_item",
+ "-last",
+ "-next",
+ "-nextdup",
+ "-nextnodup",
+ "-partial",
+ "-prev",
+ "-prevnodup",
+ "-rmw",
+ "-set",
+ "-set_range",
+ "-set_recno",
+ NULL
+ };
+ enum dbcgetopts {
+ DBCGET_CURRENT,
+ DBCGET_FIRST,
+ DBCGET_BOTH,
+ DBCGET_RECNO,
+ DBCGET_JOIN,
+ DBCGET_LAST,
+ DBCGET_NEXT,
+ DBCGET_NEXTDUP,
+ DBCGET_NEXTNODUP,
+ DBCGET_PART,
+ DBCGET_PREV,
+ DBCGET_PREVNODUP,
+ DBCGET_RMW,
+ DBCGET_SET,
+ DBCGET_SETRANGE,
+ DBCGET_SETRECNO
+ };
+ DB *thisdbp;
+ DBT key, data;
+ DBTCL_INFO *dbcip, *dbip;
+ DBTYPE type;
+ Tcl_Obj **elemv, *myobj, *retlist;
+ db_recno_t recno;
+ u_int32_t flag;
+ int elemc, i, itmp, optindex, result, ret;
+
+ result = TCL_OK;
+ flag = 0;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-args? ?key?");
+ return (TCL_ERROR);
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ /*
+ * Get the command name index from the object based on the options
+ * defined above.
+ */
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], dbcgetopts,
+ "option", TCL_EXACT, &optindex) != TCL_OK) {
+ /*
+ * Reset the result so we don't get
+ * an errant error message if there is another error.
+ */
+ if (IS_HELP(objv[i]) == TCL_OK) {
+ result = TCL_OK;
+ goto out;
+ }
+ Tcl_ResetResult(interp);
+ break;
+ }
+ i++;
+ switch ((enum dbcgetopts)optindex) {
+ case DBCGET_RMW:
+ flag |= DB_RMW;
+ break;
+ case DBCGET_CURRENT:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_CURRENT;
+ break;
+ case DBCGET_FIRST:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_FIRST;
+ break;
+ case DBCGET_LAST:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_LAST;
+ break;
+ case DBCGET_NEXT:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_NEXT;
+ break;
+ case DBCGET_PREV:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_PREV;
+ break;
+ case DBCGET_PREVNODUP:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_PREV_NODUP;
+ break;
+ case DBCGET_NEXTNODUP:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_NEXT_NODUP;
+ break;
+ case DBCGET_NEXTDUP:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_NEXT_DUP;
+ break;
+ case DBCGET_BOTH:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_GET_BOTH;
+ break;
+ case DBCGET_RECNO:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_GET_RECNO;
+ break;
+ case DBCGET_JOIN:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_JOIN_ITEM;
+ break;
+ case DBCGET_SET:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_SET;
+ break;
+ case DBCGET_SETRANGE:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_SET_RANGE;
+ break;
+ case DBCGET_SETRECNO:
+ FLAG_CHECK2(flag, DB_RMW);
+ flag |= DB_SET_RECNO;
+ break;
+ case DBCGET_PART:
+ if (i == objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-partial {offset length}?");
+ result = TCL_ERROR;
+ break;
+ }
+ /*
+ * Get sublist as {offset length}
+ */
+ result = Tcl_ListObjGetElements(interp, objv[i++],
+ &elemc, &elemv);
+ if (elemc != 2) {
+ Tcl_SetResult(interp,
+ "List must be {offset length}", TCL_STATIC);
+ result = TCL_ERROR;
+ break;
+ }
+ data.flags |= DB_DBT_PARTIAL;
+ result = Tcl_GetIntFromObj(interp, elemv[0], &itmp);
+ data.doff = itmp;
+ if (result != TCL_OK)
+ break;
+ result = Tcl_GetIntFromObj(interp, elemv[1], &itmp);
+ data.dlen = itmp;
+ /*
+ * NOTE: We don't check result here because all we'd
+ * do is break anyway, and we are doing that. If you
+ * add code here, you WILL need to add the check
+ * for result. (See the check for save.doff, a few
+ * lines above and copy that.)
+ */
+ break;
+ }
+ if (result != TCL_OK)
+ break;
+ }
+ if (result != TCL_OK)
+ goto out;
+
+ /*
+ * We need to determine if we are a recno database
+ * or not. If we are, then key.data is a recno, not
+ * a string.
+ */
+ dbcip = _PtrToInfo(dbc);
+ if (dbcip == NULL)
+ type = DB_UNKNOWN;
+ else {
+ dbip = dbcip->i_parent;
+ if (dbip == NULL) {
+ Tcl_SetResult(interp, "Cursor without parent database",
+ TCL_STATIC);
+ result = TCL_ERROR;
+ goto out;
+ }
+ thisdbp = dbip->i_dbp;
+ type = thisdbp->get_type(thisdbp);
+ }
+ /*
+ * When we get here, we better have:
+ * 2 args, key and data if GET_BOTH was specified.
+ * 1 arg if -set, -set_range or -set_recno
+ * 0 in all other cases.
+ */
+ if ((flag & DB_OPFLAGS_MASK) == DB_GET_BOTH) {
+ if (i != (objc - 2)) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-args? -get_both key data");
+ result = TCL_ERROR;
+ goto out;
+ } else {
+ if (type == DB_RECNO || type == DB_QUEUE) {
+ result = Tcl_GetIntFromObj(
+ interp, objv[objc-2], &itmp);
+ recno = itmp;
+ if (result == TCL_OK) {
+ key.data = &recno;
+ key.size = sizeof(db_recno_t);
+ } else
+ goto out;
+ } else {
+ key.data = Tcl_GetByteArrayFromObj(
+ objv[objc - 2], &itmp);
+ key.size = itmp;
+ }
+ data.data =
+ Tcl_GetByteArrayFromObj(objv[objc - 1], &itmp);
+ data.size = itmp;
+ }
+ } else if ((flag & DB_OPFLAGS_MASK) == DB_SET ||
+ (flag & DB_OPFLAGS_MASK) == DB_SET_RANGE ||
+ (flag & DB_OPFLAGS_MASK) == DB_SET_RECNO) {
+ if (i != (objc - 1)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-args? key");
+ result = TCL_ERROR;
+ goto out;
+ }
+ data.flags |= DB_DBT_MALLOC;
+ if ((flag & DB_OPFLAGS_MASK) == DB_SET_RECNO ||
+ type == DB_RECNO || type == DB_QUEUE) {
+ result = Tcl_GetIntFromObj(interp,
+ objv[objc - 1], (int *)&recno);
+ key.data = &recno;
+ key.size = sizeof(db_recno_t);
+ } else {
+ key.data =
+ Tcl_GetByteArrayFromObj(objv[objc - 1], &itmp);
+ key.size = itmp;
+ }
+ } else {
+ if (i != objc) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-args?");
+ result = TCL_ERROR;
+ goto out;
+ }
+ key.flags |= DB_DBT_MALLOC;
+ data.flags |= DB_DBT_MALLOC;
+ }
+
+ _debug_check();
+ ret = dbc->c_get(dbc, &key, &data, flag);
+ result = _ReturnSetup(interp, ret, "dbc get");
+ if (result == TCL_ERROR)
+ goto out;
+
+ retlist = Tcl_NewListObj(0, NULL);
+ if (ret == DB_NOTFOUND)
+ goto out1;
+ if ((flag & DB_OPFLAGS_MASK) == DB_GET_RECNO) {
+ recno = *((db_recno_t *)data.data);
+ myobj = Tcl_NewIntObj((int)recno);
+ result = Tcl_ListObjAppendElement(interp, retlist, myobj);
+ } else {
+ if ((type == DB_RECNO || type == DB_QUEUE) && key.data != NULL)
+ result = _SetListRecnoElem(interp, retlist,
+ *(db_recno_t *)key.data, data.data, data.size);
+ else
+ result = _SetListElem(interp, retlist,
+ key.data, key.size, data.data, data.size);
+ }
+ if (key.flags & DB_DBT_MALLOC)
+ __os_free(key.data, key.size);
+ if (data.flags & DB_DBT_MALLOC)
+ __os_free(data.data, data.size);
+out1:
+ if (result == TCL_OK)
+ Tcl_SetObjResult(interp, retlist);
+out:
+ return (result);
+
+}
+
+/*
+ * tcl_DbcDup --
+ */
+static int
+tcl_DbcDup(interp, objc, objv, dbc)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DBC *dbc; /* Cursor pointer */
+{
+ static char *dbcdupopts[] = {
+ "-position",
+ NULL
+ };
+ enum dbcdupopts {
+ DBCDUP_POS
+ };
+ DB *thisdbp;
+ DBC *newdbc;
+ DBTCL_INFO *dbcip, *newdbcip, *dbip;
+ Tcl_Obj *res;
+ u_int32_t flag;
+ int i, optindex, result, ret;
+ char newname[MSG_SIZE];
+
+ result = TCL_OK;
+ flag = 0;
+ res = NULL;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-args?");
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the command name index from the object based on the options
+ * defined above.
+ */
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], dbcdupopts,
+ "option", TCL_EXACT, &optindex) != TCL_OK) {
+ /*
+ * Reset the result so we don't get
+ * an errant error message if there is another error.
+ */
+ if (IS_HELP(objv[i]) == TCL_OK) {
+ result = TCL_OK;
+ goto out;
+ }
+ Tcl_ResetResult(interp);
+ break;
+ }
+ i++;
+ switch ((enum dbcdupopts)optindex) {
+ case DBCDUP_POS:
+ flag = DB_POSITION;
+ break;
+ }
+ if (result != TCL_OK)
+ break;
+ }
+ if (result != TCL_OK)
+ goto out;
+
+ /*
+ * We need to determine if we are a recno database
+ * or not. If we are, then key.data is a recno, not
+ * a string.
+ */
+ dbcip = _PtrToInfo(dbc);
+ if (dbcip == NULL) {
+ Tcl_SetResult(interp, "Cursor without info structure",
+ TCL_STATIC);
+ result = TCL_ERROR;
+ goto out;
+ } else {
+ dbip = dbcip->i_parent;
+ if (dbip == NULL) {
+ Tcl_SetResult(interp, "Cursor without parent database",
+ TCL_STATIC);
+ result = TCL_ERROR;
+ goto out;
+ }
+ thisdbp = dbip->i_dbp;
+ }
+ /*
+ * Now duplicate the cursor. If successful, we need to create
+ * a new cursor command.
+ */
+
+ snprintf(newname, sizeof(newname),
+ "%s.c%d", dbip->i_name, dbip->i_dbdbcid);
+ newdbcip = _NewInfo(interp, NULL, newname, I_DBC);
+ if (newdbcip != NULL) {
+ ret = dbc->c_dup(dbc, &newdbc, flag);
+ if (ret == 0) {
+ dbip->i_dbdbcid++;
+ newdbcip->i_parent = dbip;
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)dbc_Cmd,
+ (ClientData)newdbc, NULL);
+ res = Tcl_NewStringObj(newname, strlen(newname));
+ _SetInfoData(newdbcip, newdbc);
+ Tcl_SetObjResult(interp, res);
+ } else {
+ result = _ReturnSetup(interp, ret, "db dup");
+ _DeleteInfo(newdbcip);
+ }
+ } else {
+ Tcl_SetResult(interp, "Could not set up info", TCL_STATIC);
+ result = TCL_ERROR;
+ }
+out:
+ return (result);
+
+}
diff --git a/db/tcl/tcl_env.c b/db/tcl/tcl_env.c
new file mode 100644
index 000000000..cb7b0d974
--- /dev/null
+++ b/db/tcl/tcl_env.c
@@ -0,0 +1,678 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2000
+ * Sleepycat Software. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#ifndef lint
+static const char revid[] = "$Id: tcl_env.c,v 11.33 2001/01/11 18:19:55 bostic Exp $";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <tcl.h>
+#endif
+
+#include "db_int.h"
+#include "tcl_db.h"
+
+/*
+ * Prototypes for procedures defined later in this file:
+ */
+static void _EnvInfoDelete __P((Tcl_Interp *, DBTCL_INFO *));
+
+/*
+ * PUBLIC: int env_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
+ *
+ * env_Cmd --
+ * Implements the "env" command.
+ */
+int
+env_Cmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Env handle */
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *envcmds[] = {
+ "close",
+ "lock_detect",
+ "lock_id",
+ "lock_get",
+ "lock_stat",
+ "lock_vec",
+ "log_archive",
+ "log_compare",
+ "log_file",
+ "log_flush",
+ "log_get",
+ "log_put",
+ "log_register",
+ "log_stat",
+ "log_unregister",
+ "mpool",
+ "mpool_stat",
+ "mpool_sync",
+ "mpool_trickle",
+ "mutex",
+#if CONFIG_TEST
+ "test",
+#endif
+ "txn",
+ "txn_checkpoint",
+ "txn_stat",
+ "verbose",
+ NULL
+ };
+ enum envcmds {
+ ENVCLOSE,
+ ENVLKDETECT,
+ ENVLKID,
+ ENVLKGET,
+ ENVLKSTAT,
+ ENVLKVEC,
+ ENVLOGARCH,
+ ENVLOGCMP,
+ ENVLOGFILE,
+ ENVLOGFLUSH,
+ ENVLOGGET,
+ ENVLOGPUT,
+ ENVLOGREG,
+ ENVLOGSTAT,
+ ENVLOGUNREG,
+ ENVMP,
+ ENVMPSTAT,
+ ENVMPSYNC,
+ ENVTRICKLE,
+ ENVMUTEX,
+#if CONFIG_TEST
+ ENVTEST,
+#endif
+ ENVTXN,
+ ENVTXNCKP,
+ ENVTXNSTAT,
+ ENVVERB
+ };
+ DBTCL_INFO *envip;
+ DB_ENV *envp;
+ Tcl_Obj *res;
+ u_int32_t newval;
+ int cmdindex, result, ret;
+
+ Tcl_ResetResult(interp);
+ envp = (DB_ENV *)clientData;
+ envip = _PtrToInfo((void *)envp);
+ result = TCL_OK;
+
+ if (objc <= 1) {
+ Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs");
+ return (TCL_ERROR);
+ }
+ if (envp == NULL) {
+ Tcl_SetResult(interp, "NULL env pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (envip == NULL) {
+ Tcl_SetResult(interp, "NULL env info pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the command name index from the object based on the berkdbcmds
+ * defined above.
+ */
+ if (Tcl_GetIndexFromObj(interp, objv[1], envcmds, "command",
+ TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+ res = NULL;
+ switch ((enum envcmds)cmdindex) {
+ case ENVCLOSE:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ /*
+ * Any transactions will be aborted, and an mpools
+ * closed automatically. We must delete any txn
+ * and mp widgets we have here too for this env.
+ * NOTE: envip is freed when we come back from
+ * this function. Set it to NULL to make sure no
+ * one tries to use it later.
+ */
+ _EnvInfoDelete(interp, envip);
+ envip = NULL;
+ _debug_check();
+ ret = envp->close(envp, 0);
+ result = _ReturnSetup(interp, ret, "env close");
+ break;
+ case ENVLKDETECT:
+ result = tcl_LockDetect(interp, objc, objv, envp);
+ break;
+ case ENVLKSTAT:
+ result = tcl_LockStat(interp, objc, objv, envp);
+ break;
+ case ENVLKID:
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = lock_id(envp, &newval);
+ result = _ReturnSetup(interp, ret, "lock_id");
+ if (result == TCL_OK)
+ res = Tcl_NewIntObj((int)newval);
+ break;
+ case ENVLKGET:
+ result = tcl_LockGet(interp, objc, objv, envp);
+ break;
+ case ENVLKVEC:
+ result = tcl_LockVec(interp, objc, objv, envp);
+ break;
+ case ENVLOGARCH:
+ result = tcl_LogArchive(interp, objc, objv, envp);
+ break;
+ case ENVLOGCMP:
+ result = tcl_LogCompare(interp, objc, objv);
+ break;
+ case ENVLOGFILE:
+ result = tcl_LogFile(interp, objc, objv, envp);
+ break;
+ case ENVLOGFLUSH:
+ result = tcl_LogFlush(interp, objc, objv, envp);
+ break;
+ case ENVLOGGET:
+ result = tcl_LogGet(interp, objc, objv, envp);
+ break;
+ case ENVLOGPUT:
+ result = tcl_LogPut(interp, objc, objv, envp);
+ break;
+ case ENVLOGREG:
+ result = tcl_LogRegister(interp, objc, objv, envp);
+ break;
+ case ENVLOGUNREG:
+ result = tcl_LogUnregister(interp, objc, objv, envp);
+ break;
+ case ENVLOGSTAT:
+ result = tcl_LogStat(interp, objc, objv, envp);
+ break;
+ case ENVMPSTAT:
+ result = tcl_MpStat(interp, objc, objv, envp);
+ break;
+ case ENVMPSYNC:
+ result = tcl_MpSync(interp, objc, objv, envp);
+ break;
+ case ENVTRICKLE:
+ result = tcl_MpTrickle(interp, objc, objv, envp);
+ break;
+ case ENVMP:
+ result = tcl_Mp(interp, objc, objv, envp, envip);
+ break;
+ case ENVTXNCKP:
+ result = tcl_TxnCheckpoint(interp, objc, objv, envp);
+ break;
+ case ENVTXNSTAT:
+ result = tcl_TxnStat(interp, objc, objv, envp);
+ break;
+ case ENVTXN:
+ result = tcl_Txn(interp, objc, objv, envp, envip);
+ break;
+ case ENVMUTEX:
+ result = tcl_Mutex(interp, objc, objv, envp, envip);
+ break;
+#if CONFIG_TEST
+ case ENVTEST:
+ result = tcl_EnvTest(interp, objc, objv, envp);
+ break;
+#endif
+ case ENVVERB:
+ /*
+ * Two args for this. Error if different.
+ */
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ result = tcl_EnvVerbose(interp, envp, objv[2], objv[3]);
+ break;
+ }
+ /*
+ * Only set result if we have a res. Otherwise, lower
+ * functions have already done so.
+ */
+ if (result == TCL_OK && res)
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+/*
+ * PUBLIC: int tcl_EnvRemove __P((Tcl_Interp *, int, Tcl_Obj * CONST*,
+ * PUBLIC: DB_ENV *, DBTCL_INFO *));
+ *
+ * tcl_EnvRemove --
+ */
+int
+tcl_EnvRemove(interp, objc, objv, envp, envip)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Env pointer */
+ DBTCL_INFO *envip; /* Info pointer */
+{
+ static char *envremopts[] = {
+ "-data_dir",
+ "-force",
+ "-home",
+ "-log_dir",
+ "-server",
+ "-tmp_dir",
+ "-use_environ",
+ "-use_environ_root",
+ NULL
+ };
+ enum envremopts {
+ ENVREM_DATADIR,
+ ENVREM_FORCE,
+ ENVREM_HOME,
+ ENVREM_LOGDIR,
+ ENVREM_SERVER,
+ ENVREM_TMPDIR,
+ ENVREM_USE_ENVIRON,
+ ENVREM_USE_ENVIRON_ROOT
+ };
+ DB_ENV *e;
+ u_int32_t cflag, flag, forceflag;
+ int i, optindex, result, ret;
+ char *datadir, *home, *logdir, *server, *tmpdir;
+
+ result = TCL_OK;
+ cflag = flag = forceflag = 0;
+ home = NULL;
+ datadir = logdir = tmpdir = NULL;
+ server = NULL;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?args?");
+ return (TCL_ERROR);
+ }
+
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], envremopts, "option",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ result = IS_HELP(objv[i]);
+ goto error;
+ }
+ i++;
+ switch ((enum envremopts)optindex) {
+ case ENVREM_FORCE:
+ forceflag |= DB_FORCE;
+ break;
+ case ENVREM_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 ENVREM_SERVER:
+ /* Make sure we have an arg to check against! */
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-server name?");
+ result = TCL_ERROR;
+ break;
+ }
+ server = Tcl_GetStringFromObj(objv[i++], NULL);
+ cflag = DB_CLIENT;
+ break;
+ case ENVREM_USE_ENVIRON:
+ flag |= DB_USE_ENVIRON;
+ break;
+ case ENVREM_USE_ENVIRON_ROOT:
+ flag |= DB_USE_ENVIRON_ROOT;
+ break;
+ case ENVREM_DATADIR:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "-data_dir dir");
+ result = TCL_ERROR;
+ break;
+ }
+ datadir = Tcl_GetStringFromObj(objv[i++], NULL);
+ break;
+ case ENVREM_LOGDIR:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "-log_dir dir");
+ result = TCL_ERROR;
+ break;
+ }
+ logdir = Tcl_GetStringFromObj(objv[i++], NULL);
+ break;
+ case ENVREM_TMPDIR:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "-tmp_dir dir");
+ result = TCL_ERROR;
+ break;
+ }
+ tmpdir = Tcl_GetStringFromObj(objv[i++], NULL);
+ break;
+ }
+ /*
+ * If, at any time, parsing the args we get an error,
+ * bail out and return.
+ */
+ if (result != TCL_OK)
+ goto error;
+ }
+
+ /*
+ * If envp is NULL, we don't have an open env and we need to open
+ * one of the user. Don't bother with the info stuff.
+ */
+ if (envp == NULL) {
+ if ((ret = db_env_create(&e, cflag)) != 0) {
+ result = _ReturnSetup(interp, ret, "db_env_create");
+ goto error;
+ }
+ if (server != NULL) {
+ ret = e->set_server(e, server, 0, 0, 0);
+ result = _ReturnSetup(interp, ret, "set_server");
+ if (result != TCL_OK)
+ goto error;
+ }
+ if (datadir != NULL) {
+ _debug_check();
+ ret = e->set_data_dir(e, datadir);
+ result = _ReturnSetup(interp, ret, "set_data_dir");
+ if (result != TCL_OK)
+ goto error;
+ }
+ if (logdir != NULL) {
+ _debug_check();
+ ret = e->set_lg_dir(e, logdir);
+ result = _ReturnSetup(interp, ret, "set_log_dir");
+ if (result != TCL_OK)
+ goto error;
+ }
+ if (tmpdir != NULL) {
+ _debug_check();
+ ret = e->set_tmp_dir(e, tmpdir);
+ result = _ReturnSetup(interp, ret, "set_tmp_dir");
+ if (result != TCL_OK)
+ goto error;
+ }
+ } else {
+ /*
+ * We have to clean up any info associated with this env,
+ * regardless of the result of the remove so do it first.
+ * NOTE: envip is freed when we come back from this function.
+ */
+ _EnvInfoDelete(interp, envip);
+ envip = NULL;
+ e = envp;
+ }
+
+ flag |= forceflag;
+ /*
+ * When we get here we have parsed all the args. Now remove
+ * the environment.
+ */
+ _debug_check();
+ ret = e->remove(e, home, flag);
+ result = _ReturnSetup(interp, ret, "env remove");
+error:
+ return (result);
+}
+
+static void
+_EnvInfoDelete(interp, envip)
+ Tcl_Interp *interp; /* Tcl Interpreter */
+ DBTCL_INFO *envip; /* Info for env */
+{
+ DBTCL_INFO *nextp, *p;
+
+ /*
+ * Before we can delete the environment info, we must close
+ * any open subsystems in this env. We will:
+ * 1. Abort any transactions (which aborts any nested txns).
+ * 2. Close any mpools (which will put any pages itself).
+ * 3. Put any locks.
+ * 4. Close the error file.
+ */
+ for (p = LIST_FIRST(&__db_infohead); p != NULL; p = nextp) {
+ /*
+ * Check if this info structure "belongs" to this
+ * env. If so, remove its commands and info structure.
+ * We do not close/abort/whatever here, because we
+ * don't want to replicate DB behavior.
+ */
+ if (p->i_parent == envip) {
+ switch (p->i_type) {
+ case I_TXN:
+ _TxnInfoDelete(interp, p);
+ break;
+ case I_MP:
+ _MpInfoDelete(interp, p);
+ break;
+ default:
+ Tcl_SetResult(interp,
+ "_EnvInfoDelete: bad info type",
+ TCL_STATIC);
+ break;
+ }
+ nextp = LIST_NEXT(p, entries);
+ (void)Tcl_DeleteCommand(interp, p->i_name);
+ _DeleteInfo(p);
+ } else
+ nextp = LIST_NEXT(p, entries);
+ }
+ (void)Tcl_DeleteCommand(interp, envip->i_name);
+ _DeleteInfo(envip);
+}
+
+/*
+ * PUBLIC: int tcl_EnvVerbose __P((Tcl_Interp *, DB_ENV *, Tcl_Obj *,
+ * PUBLIC: Tcl_Obj *));
+ *
+ * tcl_EnvVerbose --
+ */
+int
+tcl_EnvVerbose(interp, envp, which, onoff)
+ Tcl_Interp *interp; /* Interpreter */
+ DB_ENV *envp; /* Env pointer */
+ Tcl_Obj *which; /* Which subsystem */
+ Tcl_Obj *onoff; /* On or off */
+{
+ static char *verbwhich[] = {
+ "chkpt",
+ "deadlock",
+ "recovery",
+ "wait",
+ NULL
+ };
+ enum verbwhich {
+ ENVVERB_CHK,
+ ENVVERB_DEAD,
+ ENVVERB_REC,
+ ENVVERB_WAIT
+ };
+ static char *verbonoff[] = {
+ "off",
+ "on",
+ NULL
+ };
+ enum verbonoff {
+ ENVVERB_OFF,
+ ENVVERB_ON
+ };
+ int on, optindex, ret;
+ u_int32_t wh;
+
+ if (Tcl_GetIndexFromObj(interp, which, verbwhich, "option",
+ TCL_EXACT, &optindex) != TCL_OK)
+ return (IS_HELP(which));
+
+ switch ((enum verbwhich)optindex) {
+ case ENVVERB_CHK:
+ wh = DB_VERB_CHKPOINT;
+ break;
+ case ENVVERB_DEAD:
+ wh = DB_VERB_DEADLOCK;
+ break;
+ case ENVVERB_REC:
+ wh = DB_VERB_RECOVERY;
+ break;
+ case ENVVERB_WAIT:
+ wh = DB_VERB_WAITSFOR;
+ break;
+ default:
+ return (TCL_ERROR);
+ }
+ if (Tcl_GetIndexFromObj(interp, onoff, verbonoff, "option",
+ TCL_EXACT, &optindex) != TCL_OK)
+ return (IS_HELP(onoff));
+ switch ((enum verbonoff)optindex) {
+ case ENVVERB_OFF:
+ on = 0;
+ break;
+ case ENVVERB_ON:
+ on = 1;
+ break;
+ default:
+ return (TCL_ERROR);
+ }
+ ret = envp->set_verbose(envp, wh, on);
+ return (_ReturnSetup(interp, ret, "env set verbose"));
+}
+
+#if CONFIG_TEST
+/*
+ * PUBLIC: int tcl_EnvTest __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB_ENV *));
+ *
+ * tcl_EnvTest --
+ */
+int
+tcl_EnvTest(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Env pointer */
+{
+ static char *envtestcmd[] = {
+ "abort",
+ "copy",
+ NULL
+ };
+ enum envtestcmd {
+ ENVTEST_ABORT,
+ ENVTEST_COPY
+ };
+ static char *envtestat[] = {
+ "none",
+ "preopen",
+ "prerename",
+ "postlog",
+ "postlogmeta",
+ "postopen",
+ "postrename",
+ "postsync",
+ NULL
+ };
+ enum envtestat {
+ ENVTEST_NONE,
+ ENVTEST_PREOPEN,
+ ENVTEST_PRERENAME,
+ ENVTEST_POSTLOG,
+ ENVTEST_POSTLOGMETA,
+ ENVTEST_POSTOPEN,
+ ENVTEST_POSTRENAME,
+ ENVTEST_POSTSYNC
+ };
+ int *loc, optindex, result, testval;
+
+ result = TCL_OK;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "abort|copy location");
+ return (TCL_ERROR);
+ }
+
+ /*
+ * This must be the "copy" or "abort" portion of the command.
+ */
+ if (Tcl_GetIndexFromObj(interp, objv[2], envtestcmd, "command",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ result = IS_HELP(objv[2]);
+ return (result);
+ }
+ switch ((enum envtestcmd)optindex) {
+ case ENVTEST_ABORT:
+ loc = &envp->test_abort;
+ break;
+ case ENVTEST_COPY:
+ loc = &envp->test_copy;
+ break;
+ default:
+ Tcl_SetResult(interp, "Illegal store location", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ /*
+ * This must be the location portion of the command.
+ */
+ if (Tcl_GetIndexFromObj(interp, objv[3], envtestat, "location",
+ TCL_EXACT, &optindex) != TCL_OK) {
+ result = IS_HELP(objv[3]);
+ return (result);
+ }
+ switch ((enum envtestat)optindex) {
+ case ENVTEST_NONE:
+ testval = 0;
+ break;
+ case ENVTEST_PREOPEN:
+ testval = DB_TEST_PREOPEN;
+ break;
+ case ENVTEST_PRERENAME:
+ testval = DB_TEST_PRERENAME;
+ break;
+ case ENVTEST_POSTLOG:
+ testval = DB_TEST_POSTLOG;
+ break;
+ case ENVTEST_POSTLOGMETA:
+ testval = DB_TEST_POSTLOGMETA;
+ break;
+ case ENVTEST_POSTOPEN:
+ testval = DB_TEST_POSTOPEN;
+ break;
+ case ENVTEST_POSTRENAME:
+ testval = DB_TEST_POSTRENAME;
+ break;
+ case ENVTEST_POSTSYNC:
+ testval = DB_TEST_POSTSYNC;
+ break;
+ default:
+ Tcl_SetResult(interp, "Illegal test location", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ *loc = testval;
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+ return (result);
+}
+#endif
diff --git a/db/tcl/tcl_internal.c b/db/tcl/tcl_internal.c
new file mode 100644
index 000000000..bdab60f4a
--- /dev/null
+++ b/db/tcl/tcl_internal.c
@@ -0,0 +1,440 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2000
+ * Sleepycat Software. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#ifndef lint
+static const char revid[] = "$Id: tcl_internal.c,v 11.27 2000/05/22 18:36:51 sue Exp $";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+#endif
+
+#include "db_int.h"
+#include "tcl_db.h"
+#include "db_page.h"
+#include "db_am.h"
+#include "db_ext.h"
+
+/*
+ *
+ * internal.c --
+ *
+ * This file contains internal functions we need to maintain
+ * state for our Tcl interface.
+ *
+ * NOTE: This all uses a linear linked list. If we end up with
+ * too many info structs such that this is a performance hit, it
+ * should be redone using hashes or a list per type. The assumption
+ * is that the user won't have more than a few dozen info structs
+ * in operation at any given point in time. Even a complicated
+ * application with a few environments, nested transactions, locking,
+ * and several databases open, using cursors should not have a
+ * negative performance impact, in terms of searching the list to
+ * get/manipulate the info structure.
+ */
+
+/*
+ * Prototypes for procedures defined later in this file:
+ */
+
+#define GLOB_CHAR(c) ((c) == '*' || (c) == '?')
+
+/*
+ * PUBLIC: DBTCL_INFO *_NewInfo __P((Tcl_Interp *,
+ * PUBLIC: void *, char *, enum INFOTYPE));
+ *
+ * _NewInfo --
+ *
+ * This function will create a new info structure and fill it in
+ * with the name and pointer, id and type.
+ */
+DBTCL_INFO *
+_NewInfo(interp, anyp, name, type)
+ Tcl_Interp *interp;
+ void *anyp;
+ char *name;
+ enum INFOTYPE type;
+{
+ DBTCL_INFO *p;
+ int i, ret;
+
+ if ((ret = __os_malloc(NULL, sizeof(DBTCL_INFO), NULL, &p)) != 0) {
+ Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
+ return (NULL);
+ }
+
+ if ((ret = __os_strdup(NULL, name, &p->i_name)) != 0) {
+ Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
+ __os_free(p, sizeof(DBTCL_INFO));
+ return (NULL);
+ }
+ p->i_interp = interp;
+ p->i_anyp = anyp;
+ p->i_data = 0;
+ p->i_data2 = 0;
+ p->i_type = type;
+ p->i_parent = NULL;
+ p->i_err = NULL;
+ p->i_errpfx = NULL;
+ p->i_lockobj.data = NULL;
+ for (i = 0; i < MAX_ID; i++)
+ p->i_otherid[i] = 0;
+
+ LIST_INSERT_HEAD(&__db_infohead, p, entries);
+ return (p);
+}
+
+/*
+ * PUBLIC: void *_NameToPtr __P((CONST char *));
+ */
+void *
+_NameToPtr(name)
+ CONST char *name;
+{
+ DBTCL_INFO *p;
+
+ for (p = LIST_FIRST(&__db_infohead); p != NULL;
+ p = LIST_NEXT(p, entries))
+ if (strcmp(name, p->i_name) == 0)
+ return (p->i_anyp);
+ return (NULL);
+}
+
+/*
+ * PUBLIC: char *_PtrToName __P((CONST void *));
+ */
+char *
+_PtrToName(ptr)
+ CONST void *ptr;
+{
+ DBTCL_INFO *p;
+
+ for (p = LIST_FIRST(&__db_infohead); p != NULL;
+ p = LIST_NEXT(p, entries))
+ if (p->i_anyp == ptr)
+ return (p->i_name);
+ return (NULL);
+}
+
+/*
+ * PUBLIC: DBTCL_INFO *_PtrToInfo __P((CONST void *));
+ */
+DBTCL_INFO *
+_PtrToInfo(ptr)
+ CONST void *ptr;
+{
+ DBTCL_INFO *p;
+
+ for (p = LIST_FIRST(&__db_infohead); p != NULL;
+ p = LIST_NEXT(p, entries))
+ if (p->i_anyp == ptr)
+ return (p);
+ return (NULL);
+}
+
+/*
+ * PUBLIC: DBTCL_INFO *_NameToInfo __P((CONST char *));
+ */
+DBTCL_INFO *
+_NameToInfo(name)
+ CONST char *name;
+{
+ DBTCL_INFO *p;
+
+ for (p = LIST_FIRST(&__db_infohead); p != NULL;
+ p = LIST_NEXT(p, entries))
+ if (strcmp(name, p->i_name) == 0)
+ return (p);
+ return (NULL);
+}
+
+/*
+ * PUBLIC: void _SetInfoData __P((DBTCL_INFO *, void *));
+ */
+void
+_SetInfoData(p, data)
+ DBTCL_INFO *p;
+ void *data;
+{
+ if (p == NULL)
+ return;
+ p->i_anyp = data;
+ return;
+}
+
+/*
+ * PUBLIC: void _DeleteInfo __P((DBTCL_INFO *));
+ */
+void
+_DeleteInfo(p)
+ DBTCL_INFO *p;
+{
+ if (p == NULL)
+ return;
+ LIST_REMOVE(p, entries);
+ if (p->i_lockobj.data != NULL)
+ __os_free(p->i_lockobj.data, p->i_lockobj.size);
+ if (p->i_err != NULL) {
+ fclose(p->i_err);
+ p->i_err = NULL;
+ }
+ if (p->i_errpfx != NULL)
+ __os_freestr(p->i_errpfx);
+ __os_freestr(p->i_name);
+ __os_free(p, sizeof(DBTCL_INFO));
+
+ return;
+}
+
+/*
+ * PUBLIC: int _SetListElem __P((Tcl_Interp *,
+ * PUBLIC: Tcl_Obj *, void *, int, void *, int));
+ */
+int
+_SetListElem(interp, list, elem1, e1cnt, elem2, e2cnt)
+ Tcl_Interp *interp;
+ Tcl_Obj *list;
+ void *elem1, *elem2;
+ int e1cnt, e2cnt;
+{
+ Tcl_Obj *myobjv[2], *thislist;
+ int myobjc;
+
+ myobjc = 2;
+ myobjv[0] = Tcl_NewByteArrayObj((u_char *)elem1, e1cnt);
+ myobjv[1] = Tcl_NewByteArrayObj((u_char *)elem2, e2cnt);
+ thislist = Tcl_NewListObj(myobjc, myobjv);
+ if (thislist == NULL)
+ return (TCL_ERROR);
+ return (Tcl_ListObjAppendElement(interp, list, thislist));
+
+}
+
+/*
+ * PUBLIC: int _SetListElemInt __P((Tcl_Interp *, Tcl_Obj *, void *, int));
+ */
+int
+_SetListElemInt(interp, list, elem1, elem2)
+ Tcl_Interp *interp;
+ Tcl_Obj *list;
+ void *elem1;
+ int elem2;
+{
+ Tcl_Obj *myobjv[2], *thislist;
+ int myobjc;
+
+ myobjc = 2;
+ myobjv[0] = Tcl_NewByteArrayObj((u_char *)elem1, strlen((char *)elem1));
+ myobjv[1] = Tcl_NewIntObj(elem2);
+ thislist = Tcl_NewListObj(myobjc, myobjv);
+ if (thislist == NULL)
+ return (TCL_ERROR);
+ return (Tcl_ListObjAppendElement(interp, list, thislist));
+}
+
+/*
+ * PUBLIC: int _SetListRecnoElem __P((Tcl_Interp *, Tcl_Obj *,
+ * PUBLIC: db_recno_t, u_char *, int));
+ */
+int
+_SetListRecnoElem(interp, list, elem1, elem2, e2size)
+ Tcl_Interp *interp;
+ Tcl_Obj *list;
+ db_recno_t elem1;
+ u_char *elem2;
+ int e2size;
+{
+ Tcl_Obj *myobjv[2], *thislist;
+ int myobjc;
+
+ myobjc = 2;
+ myobjv[0] = Tcl_NewIntObj(elem1);
+ myobjv[1] = Tcl_NewByteArrayObj(elem2, e2size);
+ thislist = Tcl_NewListObj(myobjc, myobjv);
+ if (thislist == NULL)
+ return (TCL_ERROR);
+ return (Tcl_ListObjAppendElement(interp, list, thislist));
+
+}
+
+/*
+ * PUBLIC: int _GetGlobPrefix __P((char *, char **));
+ */
+int
+_GetGlobPrefix(pattern, prefix)
+ char *pattern;
+ char **prefix;
+{
+ int i, j;
+ char *p;
+
+ /*
+ * Duplicate it, we get enough space and most of the work is done.
+ */
+ if (__os_strdup(NULL, pattern, prefix) != 0)
+ return (1);
+
+ p = *prefix;
+ for (i = 0, j = 0; p[i] && !GLOB_CHAR(p[i]); i++, j++)
+ /*
+ * Check for an escaped character and adjust
+ */
+ if (p[i] == '\\' && p[i+1]) {
+ p[j] = p[i+1];
+ i++;
+ } else
+ p[j] = p[i];
+ p[j] = 0;
+ return (0);
+}
+
+/*
+ * PUBLIC: int _ReturnSetup __P((Tcl_Interp *, int, char *));
+ */
+int
+_ReturnSetup(interp, ret, errmsg)
+ Tcl_Interp *interp;
+ int ret;
+ char *errmsg;
+{
+ char *msg;
+
+ if (ret > 0)
+ return (_ErrorSetup(interp, ret, errmsg));
+
+ /*
+ * We either have success or a DB error. If a DB error, set up the
+ * string. We return an error if not one of the errors we catch.
+ * If anyone wants to reset the result to return anything different,
+ * then the calling function is responsible for doing so via
+ * Tcl_ResetResult or another Tcl_SetObjResult.
+ */
+ if (ret == 0) {
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+ return (TCL_OK);
+ }
+
+ msg = db_strerror(ret);
+ Tcl_AppendResult(interp, msg, NULL);
+
+ switch (ret) {
+ case DB_NOTFOUND:
+ case DB_KEYEXIST:
+ case DB_KEYEMPTY:
+ return (TCL_OK);
+ default:
+ Tcl_SetErrorCode(interp, "BerkeleyDB", msg, NULL);
+ return (TCL_ERROR);
+ }
+}
+
+/*
+ * PUBLIC: int _ErrorSetup __P((Tcl_Interp *, int, char *));
+ */
+int
+_ErrorSetup(interp, ret, errmsg)
+ Tcl_Interp *interp;
+ int ret;
+ char *errmsg;
+{
+ Tcl_SetErrno(ret);
+ Tcl_AppendResult(interp, errmsg, ":", Tcl_PosixError(interp), NULL);
+ return (TCL_ERROR);
+}
+
+/*
+ * PUBLIC: void _ErrorFunc __P((CONST char *, char *));
+ */
+void
+_ErrorFunc(pfx, msg)
+ CONST char *pfx;
+ char *msg;
+{
+ DBTCL_INFO *p;
+ Tcl_Interp *interp;
+ int size;
+ char *err;
+
+ p = _NameToInfo(pfx);
+ if (p == NULL)
+ return;
+ interp = p->i_interp;
+
+ size = strlen(pfx) + strlen(msg) + 4;
+ /*
+ * If we cannot allocate enough to put together the prefix
+ * and message then give them just the message.
+ */
+ if (__os_malloc(NULL, size, NULL, &err) != 0) {
+ Tcl_AddErrorInfo(interp, msg);
+ Tcl_AppendResult(interp, msg, "\n", NULL);
+ return;
+ }
+ snprintf(err, size, "%s: %s", pfx, msg);
+ Tcl_AddErrorInfo(interp, err);
+ Tcl_AppendResult(interp, err, "\n", NULL);
+ __os_free(err, size);
+ return;
+}
+
+#define INVALID_LSNMSG "Invalid LSN with %d parts. Should have 2.\n"
+
+/*
+ * PUBLIC: int _GetLsn __P((Tcl_Interp *, Tcl_Obj *, DB_LSN *));
+ */
+int
+_GetLsn(interp, obj, lsn)
+ Tcl_Interp *interp;
+ Tcl_Obj *obj;
+ DB_LSN *lsn;
+{
+ Tcl_Obj **myobjv;
+ int itmp, myobjc, result;
+ char msg[MSG_SIZE];
+
+ result = Tcl_ListObjGetElements(interp, obj, &myobjc, &myobjv);
+ if (result == TCL_ERROR)
+ return (result);
+ if (myobjc != 2) {
+ result = TCL_ERROR;
+ snprintf(msg, MSG_SIZE, INVALID_LSNMSG, myobjc);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ return (result);
+ }
+ result = Tcl_GetIntFromObj(interp, myobjv[0], &itmp);
+ if (result == TCL_ERROR)
+ return (result);
+ lsn->file = itmp;
+ result = Tcl_GetIntFromObj(interp, myobjv[1], &itmp);
+ lsn->offset = itmp;
+ return (result);
+}
+
+int __debug_stop, __debug_on, __debug_print, __debug_test;
+
+/*
+ * PUBLIC: void _debug_check __P((void));
+ */
+void
+_debug_check()
+{
+ if (__debug_on == 0)
+ return;
+
+ if (__debug_print != 0) {
+ printf("\r%6d:", __debug_on);
+ fflush(stdout);
+ }
+ if (__debug_on++ == __debug_test || __debug_stop)
+ __db_loadme();
+}
diff --git a/db/tcl/tcl_lock.c b/db/tcl/tcl_lock.c
new file mode 100644
index 000000000..89f6eeb2b
--- /dev/null
+++ b/db/tcl/tcl_lock.c
@@ -0,0 +1,655 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2000
+ * Sleepycat Software. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#ifndef lint
+static const char revid[] = "$Id: tcl_lock.c,v 11.21 2001/01/11 18:19:55 bostic Exp $";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+#endif
+
+#include "db_int.h"
+#include "tcl_db.h"
+
+/*
+ * Prototypes for procedures defined later in this file:
+ */
+static int lock_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
+static int _LockMode __P((Tcl_Interp *, Tcl_Obj *, db_lockmode_t *));
+static int _GetThisLock __P((Tcl_Interp *, DB_ENV *, u_int32_t,
+ u_int32_t, DBT *, db_lockmode_t, char *));
+static void _LockPutInfo __P((Tcl_Interp *, db_lockop_t, DB_LOCK *,
+ u_int32_t, DBT *));
+
+static char *lkmode[] = {
+ "ng", "read", "write",
+ "iwrite", "iread", "iwr",
+ NULL
+};
+enum lkmode {
+ LK_NG, LK_READ, LK_WRITE,
+ LK_IWRITE, LK_IREAD, LK_IWR
+};
+
+/*
+ * tcl_LockDetect --
+ *
+ * PUBLIC: int tcl_LockDetect __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LockDetect(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ static char *ldopts[] = {
+ "-lock_conflict",
+ "default",
+ "oldest",
+ "random",
+ "youngest",
+ NULL
+ };
+ enum ldopts {
+ LD_CONFLICT,
+ LD_DEFAULT,
+ LD_OLDEST,
+ LD_RANDOM,
+ LD_YOUNGEST
+ };
+ u_int32_t flag, policy;
+ int i, optindex, result, ret;
+
+ result = TCL_OK;
+ flag = policy = 0;
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i],
+ ldopts, "option", TCL_EXACT, &optindex) != TCL_OK)
+ return (IS_HELP(objv[i]));
+ i++;
+ switch ((enum ldopts)optindex) {
+ case LD_DEFAULT:
+ FLAG_CHECK(policy);
+ policy = DB_LOCK_DEFAULT;
+ break;
+ case LD_OLDEST:
+ FLAG_CHECK(policy);
+ policy = DB_LOCK_OLDEST;
+ break;
+ case LD_YOUNGEST:
+ FLAG_CHECK(policy);
+ policy = DB_LOCK_YOUNGEST;
+ break;
+ case LD_RANDOM:
+ FLAG_CHECK(policy);
+ policy = DB_LOCK_RANDOM;
+ break;
+ case LD_CONFLICT:
+ flag |= DB_LOCK_CONFLICT;
+ break;
+ }
+ }
+
+ _debug_check();
+ ret = lock_detect(envp, flag, policy, NULL);
+ result = _ReturnSetup(interp, ret, "lock detect");
+ return (result);
+}
+
+/*
+ * tcl_LockGet --
+ *
+ * PUBLIC: int tcl_LockGet __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LockGet(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ static char *lgopts[] = {
+ "-nowait",
+ NULL
+ };
+ enum lgopts {
+ LGNOWAIT
+ };
+ DBT obj;
+ Tcl_Obj *res;
+ db_lockmode_t mode;
+ u_int32_t flag, lockid;
+ int itmp, optindex, result;
+ char newname[MSG_SIZE];
+
+ result = TCL_OK;
+ memset(newname, 0, MSG_SIZE);
+ if (objc != 5 && objc != 6) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-nowait? mode id obj");
+ return (TCL_ERROR);
+ }
+ /*
+ * Work back from required args.
+ * Last arg is obj.
+ * Second last is lock id.
+ * Third last is lock mode.
+ */
+ memset(&obj, 0, sizeof(obj));
+
+ if ((result =
+ Tcl_GetIntFromObj(interp, objv[objc-2], &itmp)) != TCL_OK)
+ return (result);
+ lockid = itmp;
+
+ /*
+ * XXX
+ * Tcl 8.1 Tcl_GetByteArrayFromObj/Tcl_GetIntFromObj bug.
+ *
+ * The line below was originally before the Tcl_GetIntFromObj.
+ *
+ * There is a bug in Tcl 8.1 and byte arrays in that if it happens
+ * to use an object as both a byte array and something else like
+ * an int, and you've done a Tcl_GetByteArrayFromObj, then you
+ * do a Tcl_GetIntFromObj, your memory is deleted.
+ *
+ * Workaround is to make sure all Tcl_GetByteArrayFromObj calls
+ * are done last.
+ */
+ obj.data = Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
+ obj.size = itmp;
+ if ((result = _LockMode(interp, objv[(objc - 3)], &mode)) != TCL_OK)
+ return (result);
+
+ /*
+ * Any left over arg is the flag.
+ */
+ flag = 0;
+ if (objc == 6) {
+ if (Tcl_GetIndexFromObj(interp, objv[(objc - 4)],
+ lgopts, "option", TCL_EXACT, &optindex) != TCL_OK)
+ return (IS_HELP(objv[(objc - 4)]));
+ switch ((enum lgopts)optindex) {
+ case LGNOWAIT:
+ flag |= DB_LOCK_NOWAIT;
+ break;
+ }
+ }
+
+ result = _GetThisLock(interp, envp, lockid, flag, &obj, mode, newname);
+ if (result == TCL_OK) {
+ res = Tcl_NewStringObj(newname, strlen(newname));
+ Tcl_SetObjResult(interp, res);
+ }
+ return (result);
+}
+
+/*
+ * tcl_LockStat --
+ *
+ * PUBLIC: int tcl_LockStat __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LockStat(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ DB_LOCK_STAT *sp;
+ Tcl_Obj *res;
+ int result, ret;
+
+ result = TCL_OK;
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = lock_stat(envp, &sp, NULL);
+ result = _ReturnSetup(interp, ret, "lock stat");
+ if (result == TCL_ERROR)
+ return (result);
+ /*
+ * Have our stats, now construct the name value
+ * list pairs and free up the memory.
+ */
+ res = Tcl_NewObj();
+ /*
+ * MAKE_STAT_LIST assumes 'res' and 'error' label.
+ */
+ MAKE_STAT_LIST("Region size", sp->st_regsize);
+ MAKE_STAT_LIST("Max locks", sp->st_maxlocks);
+ MAKE_STAT_LIST("Max lockers", sp->st_maxlockers);
+ MAKE_STAT_LIST("Max objects", sp->st_maxobjects);
+ MAKE_STAT_LIST("Lock modes", sp->st_nmodes);
+ MAKE_STAT_LIST("Current number of locks", sp->st_nlocks);
+ MAKE_STAT_LIST("Maximum number of locks so far", sp->st_maxnlocks);
+ MAKE_STAT_LIST("Current number of lockers", sp->st_nlockers);
+ MAKE_STAT_LIST("Maximum number of lockers so far", sp->st_maxnlockers);
+ MAKE_STAT_LIST("Current number of objects", sp->st_nobjects);
+ MAKE_STAT_LIST("Maximum number of objects so far", sp->st_maxnobjects);
+ MAKE_STAT_LIST("Number of conflicts", sp->st_nconflicts);
+ MAKE_STAT_LIST("Lock requests", sp->st_nrequests);
+ MAKE_STAT_LIST("Lock releases", sp->st_nreleases);
+ MAKE_STAT_LIST("Deadlocks detected", sp->st_ndeadlocks);
+ MAKE_STAT_LIST("Number of region lock waits", sp->st_region_wait);
+ MAKE_STAT_LIST("Number of region lock nowaits", sp->st_region_nowait);
+ Tcl_SetObjResult(interp, res);
+error:
+ __os_free(sp, sizeof(*sp));
+ return (result);
+}
+
+/*
+ * lock_Cmd --
+ * Implements the "lock" widget.
+ */
+static int
+lock_Cmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Lock handle */
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *lkcmds[] = {
+ "put",
+ NULL
+ };
+ enum lkcmds {
+ LKPUT
+ };
+ DB_ENV *env;
+ DB_LOCK *lock;
+ DBTCL_INFO *lkip;
+ int cmdindex, result, ret;
+
+ Tcl_ResetResult(interp);
+ lock = (DB_LOCK *)clientData;
+ lkip = _PtrToInfo((void *)lock);
+ result = TCL_OK;
+
+ if (lock == NULL) {
+ Tcl_SetResult(interp, "NULL lock", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (lkip == NULL) {
+ Tcl_SetResult(interp, "NULL lock info pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ env = NAME_TO_ENV(lkip->i_parent->i_name);
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ /*
+ * Get the command name index from the object based on the dbcmds
+ * defined above.
+ */
+ if (Tcl_GetIndexFromObj(interp,
+ objv[1], lkcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+
+ switch ((enum lkcmds)cmdindex) {
+ case LKPUT:
+ _debug_check();
+ ret = lock_put(env, lock);
+ result = _ReturnSetup(interp, ret, "lock put");
+ (void)Tcl_DeleteCommand(interp, lkip->i_name);
+ _DeleteInfo(lkip);
+ __os_free(lock, sizeof(DB_LOCK));
+ break;
+ }
+ return (result);
+}
+
+/*
+ * tcl_LockVec --
+ *
+ * PUBLIC: int tcl_LockVec __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LockVec(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* environment pointer */
+{
+ static char *lvopts[] = {
+ "-nowait",
+ NULL
+ };
+ enum lvopts {
+ LVNOWAIT
+ };
+ static char *lkops[] = {
+ "get", "put", "put_all", "put_obj",
+ NULL
+ };
+ enum lkops {
+ LKGET, LKPUT, LKPUTALL, LKPUTOBJ
+ };
+ DB_LOCK *lock;
+ DB_LOCKREQ list;
+ DBT obj;
+ Tcl_Obj **myobjv, *res, *thisop;
+ db_lockmode_t mode;
+ u_int32_t flag, lockid;
+ int i, itmp, myobjc, optindex, result, ret;
+ char *lockname, msg[MSG_SIZE], newname[MSG_SIZE];
+
+ result = TCL_OK;
+ memset(newname, 0, MSG_SIZE);
+ flag = 0;
+ mode = 0;
+ /*
+ * If -nowait is given, it MUST be first arg.
+ */
+ if (Tcl_GetIndexFromObj(interp, objv[2],
+ lvopts, "option", TCL_EXACT, &optindex) == TCL_OK) {
+ switch ((enum lvopts)optindex) {
+ case LVNOWAIT:
+ flag |= DB_LOCK_NOWAIT;
+ break;
+ }
+ i = 3;
+ } else {
+ if (IS_HELP(objv[2]) == TCL_OK)
+ return (TCL_OK);
+ Tcl_ResetResult(interp);
+ i = 2;
+ }
+
+ /*
+ * Our next arg MUST be the locker ID.
+ */
+ result = Tcl_GetIntFromObj(interp, objv[i++], &itmp);
+ if (result != TCL_OK)
+ return (result);
+ lockid = itmp;
+
+ /*
+ * All other remaining args are operation tuples.
+ * Go through sequentially to decode, execute and build
+ * up list of return values.
+ */
+ res = Tcl_NewListObj(0, NULL);
+ while (i < objc) {
+ /*
+ * Get the list of the tuple.
+ */
+ lock = NULL;
+ result = Tcl_ListObjGetElements(interp, objv[i],
+ &myobjc, &myobjv);
+ if (result == TCL_OK)
+ i++;
+ else
+ break;
+ /*
+ * First we will set up the list of requests.
+ * We will make a "second pass" after we get back
+ * the results from the lock_vec call to create
+ * the return list.
+ */
+ if (Tcl_GetIndexFromObj(interp, myobjv[0],
+ lkops, "option", TCL_EXACT, &optindex) != TCL_OK) {
+ result = IS_HELP(myobjv[0]);
+ goto error;
+ }
+ switch ((enum lkops)optindex) {
+ case LKGET:
+ if (myobjc != 3) {
+ Tcl_WrongNumArgs(interp, 1, myobjv,
+ "{get obj mode}");
+ result = TCL_ERROR;
+ goto error;
+ }
+ result = _LockMode(interp, myobjv[2], &list.mode);
+ if (result != TCL_OK)
+ goto error;
+ /*
+ * XXX
+ * Tcl 8.1 Tcl_GetByteArrayFromObj/Tcl_GetIntFromObj
+ * bug.
+ *
+ * There is a bug in Tcl 8.1 and byte arrays in that if
+ * it happens to use an object as both a byte array and
+ * something else like an int, and you've done a
+ * Tcl_GetByteArrayFromObj, then you do a
+ * Tcl_GetIntFromObj, your memory is deleted.
+ *
+ * Workaround is to make sure all
+ * Tcl_GetByteArrayFromObj calls are done last.
+ */
+ obj.data = Tcl_GetByteArrayFromObj(myobjv[1], &itmp);
+ obj.size = itmp;
+ ret = _GetThisLock(interp, envp, lockid, flag,
+ &obj, list.mode, newname);
+ if (ret != 0) {
+ result = _ReturnSetup(interp, ret, "lock vec");
+ thisop = Tcl_NewIntObj(ret);
+ (void)Tcl_ListObjAppendElement(interp, res,
+ thisop);
+ goto error;
+ }
+ thisop = Tcl_NewStringObj(newname, strlen(newname));
+ (void)Tcl_ListObjAppendElement(interp, res, thisop);
+ continue;
+ case LKPUT:
+ if (myobjc != 2) {
+ Tcl_WrongNumArgs(interp, 1, myobjv,
+ "{put lock}");
+ result = TCL_ERROR;
+ goto error;
+ }
+ list.op = DB_LOCK_PUT;
+ lockname = Tcl_GetStringFromObj(myobjv[1], NULL);
+ lock = NAME_TO_LOCK(lockname);
+ if (lock == NULL) {
+ snprintf(msg, MSG_SIZE, "Invalid lock: %s\n",
+ lockname);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ result = TCL_ERROR;
+ goto error;
+ }
+ list.lock = *lock;
+ break;
+ case LKPUTALL:
+ if (myobjc != 1) {
+ Tcl_WrongNumArgs(interp, 1, myobjv,
+ "{put_all}");
+ result = TCL_ERROR;
+ goto error;
+ }
+ list.op = DB_LOCK_PUT_ALL;
+ break;
+ case LKPUTOBJ:
+ if (myobjc != 2) {
+ Tcl_WrongNumArgs(interp, 1, myobjv,
+ "{put_obj obj}");
+ result = TCL_ERROR;
+ goto error;
+ }
+ list.op = DB_LOCK_PUT_OBJ;
+ obj.data = Tcl_GetByteArrayFromObj(myobjv[1], &itmp);
+ obj.size = itmp;
+ list.obj = &obj;
+ break;
+ }
+ /*
+ * We get here, we have set up our request, now call
+ * lock_vec.
+ */
+ _debug_check();
+ ret = lock_vec(envp, lockid, flag, &list, 1, NULL);
+ /*
+ * Now deal with whether or not the operation succeeded.
+ * Get's were done above, all these are only puts.
+ */
+ thisop = Tcl_NewIntObj(ret);
+ result = Tcl_ListObjAppendElement(interp, res, thisop);
+ if (ret != 0 && result == TCL_OK)
+ result = _ReturnSetup(interp, ret, "lock put");
+ /*
+ * We did a put of some kind. Since we did that,
+ * we have to delete the commands associated with
+ * any of the locks we just put.
+ */
+ _LockPutInfo(interp, list.op, lock, lockid, &obj);
+ }
+
+ if (result == TCL_OK && res)
+ Tcl_SetObjResult(interp, res);
+error:
+ return (result);
+}
+
+static int
+_LockMode(interp, obj, mode)
+ Tcl_Interp *interp;
+ Tcl_Obj *obj;
+ db_lockmode_t *mode;
+{
+ int optindex;
+
+ if (Tcl_GetIndexFromObj(interp, obj, lkmode, "option",
+ TCL_EXACT, &optindex) != TCL_OK)
+ return (IS_HELP(obj));
+ switch ((enum lkmode)optindex) {
+ case LK_NG:
+ *mode = DB_LOCK_NG;
+ break;
+ case LK_READ:
+ *mode = DB_LOCK_READ;
+ break;
+ case LK_WRITE:
+ *mode = DB_LOCK_WRITE;
+ break;
+ case LK_IREAD:
+ *mode = DB_LOCK_IREAD;
+ break;
+ case LK_IWRITE:
+ *mode = DB_LOCK_IWRITE;
+ break;
+ case LK_IWR:
+ *mode = DB_LOCK_IWR;
+ break;
+ }
+ return (TCL_OK);
+}
+
+static void
+_LockPutInfo(interp, op, lock, lockid, objp)
+ Tcl_Interp *interp;
+ db_lockop_t op;
+ DB_LOCK *lock;
+ u_int32_t lockid;
+ DBT *objp;
+{
+ DBTCL_INFO *p, *nextp;
+ int found;
+
+ for (p = LIST_FIRST(&__db_infohead); p != NULL; p = nextp) {
+ found = 0;
+ nextp = LIST_NEXT(p, entries);
+ if ((op == DB_LOCK_PUT && (p->i_lock == lock)) ||
+ (op == DB_LOCK_PUT_ALL && p->i_locker == lockid) ||
+ (op == DB_LOCK_PUT_OBJ && p->i_lockobj.data &&
+ memcmp(p->i_lockobj.data, objp->data, objp->size) == 0))
+ found = 1;
+ if (found) {
+ (void)Tcl_DeleteCommand(interp, p->i_name);
+ __os_free(p->i_lock, sizeof(DB_LOCK));
+ _DeleteInfo(p);
+ }
+ }
+}
+
+static int
+_GetThisLock(interp, envp, lockid, flag, objp, mode, newname)
+ Tcl_Interp *interp; /* Interpreter */
+ DB_ENV *envp; /* Env handle */
+ u_int32_t lockid; /* Locker ID */
+ u_int32_t flag; /* Lock flag */
+ DBT *objp; /* Object to lock */
+ db_lockmode_t mode; /* Lock mode */
+ char *newname; /* New command name */
+{
+ DB_LOCK *lock;
+ DBTCL_INFO *envip, *ip;
+ int result, ret;
+
+ result = TCL_OK;
+ envip = _PtrToInfo((void *)envp);
+ if (envip == NULL) {
+ Tcl_SetResult(interp, "Could not find env info\n", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ snprintf(newname, MSG_SIZE, "%s.lock%d",
+ envip->i_name, envip->i_envlockid);
+ ip = _NewInfo(interp, NULL, newname, I_LOCK);
+ if (ip == NULL) {
+ Tcl_SetResult(interp, "Could not set up info",
+ TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ ret = __os_malloc(envp, sizeof(DB_LOCK), NULL, &lock);
+ if (ret != 0) {
+ Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = lock_get(envp, lockid, flag, objp, mode, lock);
+ result = _ReturnSetup(interp, ret, "lock get");
+ if (result == TCL_ERROR) {
+ __os_free(lock, sizeof(DB_LOCK));
+ _DeleteInfo(ip);
+ return (result);
+ }
+ /*
+ * Success. Set up return. Set up new info
+ * and command widget for this lock.
+ */
+ ret = __os_malloc(envp, objp->size, NULL, &ip->i_lockobj.data);
+ if (ret != 0) {
+ Tcl_SetResult(interp, "Could not duplicate obj",
+ TCL_STATIC);
+ (void)lock_put(envp, lock);
+ __os_free(lock, sizeof(DB_LOCK));
+ _DeleteInfo(ip);
+ result = TCL_ERROR;
+ goto error;
+ }
+ memcpy(ip->i_lockobj.data, objp->data, objp->size);
+ ip->i_lockobj.size = objp->size;
+ envip->i_envlockid++;
+ ip->i_parent = envip;
+ ip->i_locker = lockid;
+ _SetInfoData(ip, lock);
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)lock_Cmd, (ClientData)lock, NULL);
+error:
+ return (result);
+}
diff --git a/db/tcl/tcl_log.c b/db/tcl/tcl_log.c
new file mode 100644
index 000000000..20f8e8c02
--- /dev/null
+++ b/db/tcl/tcl_log.c
@@ -0,0 +1,581 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2000
+ * Sleepycat Software. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#ifndef lint
+static const char revid[] = "$Id: tcl_log.c,v 11.21 2000/11/30 00:58:45 ubell Exp $";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+#endif
+
+#include "db_int.h"
+#include "tcl_db.h"
+
+/*
+ * tcl_LogArchive --
+ *
+ * PUBLIC: int tcl_LogArchive __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LogArchive(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ static char *archopts[] = {
+ "-arch_abs", "-arch_data", "-arch_log",
+ NULL
+ };
+ enum archopts {
+ ARCH_ABS, ARCH_DATA, ARCH_LOG
+ };
+ Tcl_Obj *fileobj, *res;
+ u_int32_t flag;
+ int i, optindex, result, ret;
+ char **file, **list;
+
+ result = TCL_OK;
+ flag = 0;
+ /*
+ * Get the flag index from the object based on the options
+ * defined above.
+ */
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i],
+ archopts, "option", TCL_EXACT, &optindex) != TCL_OK)
+ return (IS_HELP(objv[i]));
+ i++;
+ switch ((enum archopts)optindex) {
+ case ARCH_ABS:
+ flag |= DB_ARCH_ABS;
+ break;
+ case ARCH_DATA:
+ flag |= DB_ARCH_DATA;
+ break;
+ case ARCH_LOG:
+ flag |= DB_ARCH_LOG;
+ break;
+ }
+ }
+ _debug_check();
+ list = NULL;
+ ret = log_archive(envp, &list, flag, NULL);
+ result = _ReturnSetup(interp, ret, "log archive");
+ if (result == TCL_OK) {
+ res = Tcl_NewListObj(0, NULL);
+ for (file = list; file != NULL && *file != NULL; file++) {
+ fileobj = Tcl_NewStringObj(*file, strlen(*file));
+ result = Tcl_ListObjAppendElement(interp, res, fileobj);
+ if (result != TCL_OK)
+ break;
+ }
+ Tcl_SetObjResult(interp, res);
+ }
+ if (list != NULL)
+ __os_free(list, 0);
+ return (result);
+}
+
+/*
+ * tcl_LogCompare --
+ *
+ * PUBLIC: int tcl_LogCompare __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*));
+ */
+int
+tcl_LogCompare(interp, objc, objv)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ DB_LSN lsn0, lsn1;
+ Tcl_Obj *res;
+ int result, ret;
+
+ result = TCL_OK;
+ /*
+ * No flags, must be 4 args.
+ */
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "lsn1 lsn2");
+ return (TCL_ERROR);
+ }
+
+ result = _GetLsn(interp, objv[2], &lsn0);
+ if (result == TCL_ERROR)
+ return (result);
+ result = _GetLsn(interp, objv[3], &lsn1);
+ if (result == TCL_ERROR)
+ return (result);
+
+ _debug_check();
+ ret = log_compare(&lsn0, &lsn1);
+ res = Tcl_NewIntObj(ret);
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+/*
+ * tcl_LogFile --
+ *
+ * PUBLIC: int tcl_LogFile __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LogFile(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ DB_LSN lsn;
+ Tcl_Obj *res;
+ size_t len;
+ int result, ret;
+ char *name;
+
+ result = TCL_OK;
+ /*
+ * No flags, must be 3 args.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "lsn");
+ return (TCL_ERROR);
+ }
+
+ result = _GetLsn(interp, objv[2], &lsn);
+ if (result == TCL_ERROR)
+ return (result);
+
+ len = MSG_SIZE;
+ ret = ENOMEM;
+ name = NULL;
+ while (ret == ENOMEM) {
+ if (name != NULL)
+ __os_free(name, len/2);
+ ret = __os_malloc(envp, len, NULL, &name);
+ if (ret != 0) {
+ Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
+ break;
+ }
+ _debug_check();
+ ret = log_file(envp, &lsn, name, len);
+ len *= 2;
+ }
+ result = _ReturnSetup(interp, ret, "log_file");
+ if (ret == 0) {
+ res = Tcl_NewStringObj(name, strlen(name));
+ Tcl_SetObjResult(interp, res);
+ }
+
+ if (name != NULL)
+ __os_free(name, len/2);
+
+ return (result);
+}
+
+/*
+ * tcl_LogFlush --
+ *
+ * PUBLIC: int tcl_LogFlush __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LogFlush(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ DB_LSN lsn, *lsnp;
+ int result, ret;
+
+ result = TCL_OK;
+ /*
+ * No flags, must be 2 or 3 args.
+ */
+ if (objc > 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?lsn?");
+ return (TCL_ERROR);
+ }
+
+ if (objc == 3) {
+ lsnp = &lsn;
+ result = _GetLsn(interp, objv[2], &lsn);
+ if (result == TCL_ERROR)
+ return (result);
+ } else
+ lsnp = NULL;
+
+ _debug_check();
+ ret = log_flush(envp, lsnp);
+ result = _ReturnSetup(interp, ret, "log_flush");
+ return (result);
+}
+
+/*
+ * tcl_LogGet --
+ *
+ * PUBLIC: int tcl_LogGet __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LogGet(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ static char *loggetopts[] = {
+ "-checkpoint", "-current", "-first",
+ "-last", "-next", "-prev",
+ "-set",
+ NULL
+ };
+ enum loggetopts {
+ LOGGET_CKP, LOGGET_CUR, LOGGET_FIRST,
+ LOGGET_LAST, LOGGET_NEXT, LOGGET_PREV,
+ LOGGET_SET
+ };
+ DB_LSN lsn;
+ DBT data;
+ Tcl_Obj *dataobj, *lsnlist, *myobjv[2], *res;
+ u_int32_t flag;
+ int i, myobjc, optindex, result, ret;
+
+ result = TCL_OK;
+ flag = 0;
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-args? lsn");
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the command name index from the object based on the options
+ * defined above.
+ */
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i],
+ loggetopts, "option", TCL_EXACT, &optindex) != TCL_OK)
+ return (IS_HELP(objv[i]));
+ i++;
+ switch ((enum loggetopts)optindex) {
+ case LOGGET_CKP:
+ FLAG_CHECK(flag);
+ flag |= DB_CHECKPOINT;
+ break;
+ case LOGGET_CUR:
+ FLAG_CHECK(flag);
+ flag |= DB_CURRENT;
+ break;
+ case LOGGET_FIRST:
+ FLAG_CHECK(flag);
+ flag |= DB_FIRST;
+ break;
+ case LOGGET_LAST:
+ FLAG_CHECK(flag);
+ flag |= DB_LAST;
+ break;
+ case LOGGET_NEXT:
+ FLAG_CHECK(flag);
+ flag |= DB_NEXT;
+ break;
+ case LOGGET_PREV:
+ FLAG_CHECK(flag);
+ flag |= DB_PREV;
+ break;
+ case LOGGET_SET:
+ FLAG_CHECK(flag);
+ flag |= DB_SET;
+ if (i == objc) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-set lsn?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = _GetLsn(interp, objv[i++], &lsn);
+ break;
+ }
+ }
+
+ if (result == TCL_ERROR)
+ return (result);
+
+ memset(&data, 0, sizeof(data));
+ data.flags |= DB_DBT_MALLOC;
+ _debug_check();
+ ret = log_get(envp, &lsn, &data, flag);
+ res = Tcl_NewListObj(0, NULL);
+ result = _ReturnSetup(interp, ret, "log_get");
+ if (ret == 0) {
+ /*
+ * Success. Set up return list as {LSN data} where LSN
+ * is a sublist {file offset}.
+ */
+ myobjc = 2;
+ myobjv[0] = Tcl_NewIntObj(lsn.file);
+ myobjv[1] = Tcl_NewIntObj(lsn.offset);
+ lsnlist = Tcl_NewListObj(myobjc, myobjv);
+ if (lsnlist == NULL) {
+ if (data.data != NULL)
+ __os_free(data.data, data.size);
+ return (TCL_ERROR);
+ }
+ result = Tcl_ListObjAppendElement(interp, res, lsnlist);
+ dataobj = Tcl_NewStringObj(data.data, data.size);
+ result = Tcl_ListObjAppendElement(interp, res, dataobj);
+ }
+ if (data.data != NULL)
+ __os_free(data.data, data.size);
+
+ if (result == TCL_OK)
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+/*
+ * tcl_LogPut --
+ *
+ * PUBLIC: int tcl_LogPut __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LogPut(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ static char *logputopts[] = {
+ "-checkpoint", "-curlsn", "-flush",
+ NULL
+ };
+ enum logputopts {
+ LOGPUT_CKP, LOGPUT_CUR, LOGPUT_FLUSH
+ };
+ DB_LSN lsn;
+ DBT data;
+ Tcl_Obj *intobj, *res;
+ u_int32_t flag;
+ int itmp, optindex, result, ret;
+
+ result = TCL_OK;
+ flag = 0;
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-args? record");
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Data/record must be the last arg.
+ */
+ memset(&data, 0, sizeof(data));
+ data.data = Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
+ data.size = itmp;
+
+ /*
+ * Get the command name index from the object based on the options
+ * defined above.
+ */
+ if (objc == 4) {
+ if (Tcl_GetIndexFromObj(interp, objv[2],
+ logputopts, "option", TCL_EXACT, &optindex) != TCL_OK) {
+ return (IS_HELP(objv[2]));
+ }
+ switch ((enum logputopts)optindex) {
+ case LOGPUT_CKP:
+ flag = DB_CHECKPOINT;
+ break;
+ case LOGPUT_CUR:
+ flag = DB_CURLSN;
+ break;
+ case LOGPUT_FLUSH:
+ flag = DB_FLUSH;
+ break;
+ }
+ }
+
+ if (result == TCL_ERROR)
+ return (result);
+
+ _debug_check();
+ ret = log_put(envp, &lsn, &data, flag);
+ result = _ReturnSetup(interp, ret, "log_put");
+ if (result == TCL_ERROR)
+ return (result);
+ res = Tcl_NewListObj(0, NULL);
+ intobj = Tcl_NewIntObj(lsn.file);
+ result = Tcl_ListObjAppendElement(interp, res, intobj);
+ intobj = Tcl_NewIntObj(lsn.offset);
+ result = Tcl_ListObjAppendElement(interp, res, intobj);
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+/*
+ * tcl_LogRegister --
+ *
+ * PUBLIC: int tcl_LogRegister __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LogRegister(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ DB *dbp;
+ Tcl_Obj *res;
+ int result, ret;
+ char *arg, msg[MSG_SIZE];
+
+ result = TCL_OK;
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "db filename");
+ return (TCL_ERROR);
+ }
+ /*
+ * First comes the DB.
+ */
+ arg = Tcl_GetStringFromObj(objv[2], NULL);
+ dbp = NAME_TO_DB(arg);
+ if (dbp == NULL) {
+ snprintf(msg, MSG_SIZE,
+ "LogRegister: Invalid db: %s\n", arg);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Next is the filename.
+ */
+ arg = Tcl_GetStringFromObj(objv[3], NULL);
+
+ _debug_check();
+ ret = log_register(envp, dbp, arg);
+ result = _ReturnSetup(interp, ret, "log_register");
+ if (result == TCL_OK) {
+ res = Tcl_NewIntObj((int)dbp->log_fileid);
+ Tcl_SetObjResult(interp, res);
+ }
+ return (result);
+}
+
+/*
+ * tcl_LogStat --
+ *
+ * PUBLIC: int tcl_LogStat __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LogStat(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ DB_LOG_STAT *sp;
+ Tcl_Obj *res;
+ int result, ret;
+
+ result = TCL_OK;
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = log_stat(envp, &sp, NULL);
+ result = _ReturnSetup(interp, ret, "log stat");
+ if (result == TCL_ERROR)
+ return (result);
+
+ /*
+ * Have our stats, now construct the name value
+ * list pairs and free up the memory.
+ */
+ res = Tcl_NewObj();
+ /*
+ * MAKE_STAT_LIST assumes 'res' and 'error' label.
+ */
+ MAKE_STAT_LIST("Magic", sp->st_magic);
+ MAKE_STAT_LIST("Log file Version", sp->st_version);
+ MAKE_STAT_LIST("Region size", sp->st_regsize);
+ MAKE_STAT_LIST("Log file mode", sp->st_mode);
+ MAKE_STAT_LIST("Log record cache size", sp->st_lg_bsize);
+ MAKE_STAT_LIST("Maximum log file size", sp->st_lg_max);
+ MAKE_STAT_LIST("Mbytes written", sp->st_w_mbytes);
+ MAKE_STAT_LIST("Bytes written (over Mb)", sp->st_w_bytes);
+ MAKE_STAT_LIST("Mbytes written since checkpoint", sp->st_wc_mbytes);
+ MAKE_STAT_LIST("Bytes written (over Mb) since checkpoint",
+ sp->st_wc_bytes);
+ MAKE_STAT_LIST("Times log written", sp->st_wcount);
+ MAKE_STAT_LIST("Times log written because cache filled up",
+ sp->st_wcount_fill);
+ MAKE_STAT_LIST("Times log flushed", sp->st_scount);
+ MAKE_STAT_LIST("Current log file number", sp->st_cur_file);
+ MAKE_STAT_LIST("Current log file offset", sp->st_cur_offset);
+ MAKE_STAT_LIST("Number of region lock waits", sp->st_region_wait);
+ MAKE_STAT_LIST("Number of region lock nowaits", sp->st_region_nowait);
+ Tcl_SetObjResult(interp, res);
+error:
+ __os_free(sp, sizeof(*sp));
+ return (result);
+}
+
+/*
+ * tcl_LogUnregister --
+ *
+ * PUBLIC: int tcl_LogUnregister __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_LogUnregister(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ DB *dbp;
+ char *arg, msg[MSG_SIZE];
+ int result, ret;
+
+ result = TCL_OK;
+ /*
+ * 1 arg for this. Error if more or less.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ arg = Tcl_GetStringFromObj(objv[2], NULL);
+ dbp = NAME_TO_DB(arg);
+ if (dbp == NULL) {
+ snprintf(msg, MSG_SIZE,
+ "log_unregister: Invalid db identifier: %s\n", arg);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = log_unregister(envp, dbp);
+ result = _ReturnSetup(interp, ret, "log_unregister");
+
+ return (result);
+}
diff --git a/db/tcl/tcl_mp.c b/db/tcl/tcl_mp.c
new file mode 100644
index 000000000..b424deea2
--- /dev/null
+++ b/db/tcl/tcl_mp.c
@@ -0,0 +1,822 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2000
+ * Sleepycat Software. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#ifndef lint
+static const char revid[] = "$Id: tcl_mp.c,v 11.24 2001/01/09 16:13:59 sue Exp $";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+#endif
+
+#include "db_int.h"
+#include "tcl_db.h"
+
+/*
+ * Prototypes for procedures defined later in this file:
+ */
+static int mp_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
+static int pg_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
+static int tcl_MpGet __P((Tcl_Interp *, int, Tcl_Obj * CONST*,
+ DB_MPOOLFILE *, DBTCL_INFO *));
+static int tcl_Pg __P((Tcl_Interp *, int, Tcl_Obj * CONST*,
+ void *, DB_MPOOLFILE *, DBTCL_INFO *, int));
+static int tcl_PgInit __P((Tcl_Interp *, int, Tcl_Obj * CONST*,
+ void *, DBTCL_INFO *));
+static int tcl_PgIsset __P((Tcl_Interp *, int, Tcl_Obj * CONST*,
+ void *, DBTCL_INFO *));
+
+/*
+ * _MpInfoDelete --
+ * Removes "sub" mp page info structures that are children
+ * of this mp.
+ *
+ * PUBLIC: void _MpInfoDelete __P((Tcl_Interp *, DBTCL_INFO *));
+ */
+void
+_MpInfoDelete(interp, mpip)
+ Tcl_Interp *interp; /* Interpreter */
+ DBTCL_INFO *mpip; /* Info for mp */
+{
+ DBTCL_INFO *nextp, *p;
+
+ for (p = LIST_FIRST(&__db_infohead); p != NULL; p = nextp) {
+ /*
+ * Check if this info structure "belongs" to this
+ * mp. Remove its commands and info structure.
+ */
+ nextp = LIST_NEXT(p, entries);
+ if (p->i_parent == mpip && p->i_type == I_PG) {
+ (void)Tcl_DeleteCommand(interp, p->i_name);
+ _DeleteInfo(p);
+ }
+ }
+}
+
+/*
+ * tcl_MpSync --
+ *
+ * PUBLIC: int tcl_MpSync __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_MpSync(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+
+ DB_LSN lsn;
+ int result, ret;
+
+ result = TCL_OK;
+ /*
+ * No flags, must be 3 args.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "lsn");
+ return (TCL_ERROR);
+ }
+
+ result = _GetLsn(interp, objv[2], &lsn);
+ if (result == TCL_ERROR)
+ return (result);
+
+ _debug_check();
+ ret = memp_sync(envp, &lsn);
+ result = _ReturnSetup(interp, ret, "memp sync");
+ return (result);
+}
+
+/*
+ * tcl_MpTrickle --
+ *
+ * PUBLIC: int tcl_MpTrickle __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_MpTrickle(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+
+ int pages;
+ int percent;
+ int result;
+ int ret;
+ Tcl_Obj *res;
+
+ result = TCL_OK;
+ /*
+ * No flags, must be 3 args.
+ */
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "percent");
+ return (TCL_ERROR);
+ }
+
+ result = Tcl_GetIntFromObj(interp, objv[2], &percent);
+ if (result == TCL_ERROR)
+ return (result);
+
+ _debug_check();
+ ret = memp_trickle(envp, percent, &pages);
+ result = _ReturnSetup(interp, ret, "memp trickle");
+ if (result == TCL_ERROR)
+ return (result);
+
+ res = Tcl_NewIntObj(pages);
+ Tcl_SetObjResult(interp, res);
+ return (result);
+
+}
+
+/*
+ * tcl_Mp --
+ *
+ * PUBLIC: int tcl_Mp __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *, DBTCL_INFO *));
+ */
+int
+tcl_Mp(interp, objc, objv, envp, envip)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+ DBTCL_INFO *envip; /* Info pointer */
+{
+ static char *mpopts[] = {
+ "-create",
+ "-mode",
+ "-nommap",
+ "-pagesize",
+ "-rdonly",
+ NULL
+ };
+ enum mpopts {
+ MPCREATE,
+ MPMODE,
+ MPNOMMAP,
+ MPPAGE,
+ MPRDONLY
+ };
+ DBTCL_INFO *ip;
+ DB_MPOOLFILE *mpf;
+ Tcl_Obj *res;
+ u_int32_t flag;
+ int i, pgsize, mode, optindex, result, ret;
+ char *file, newname[MSG_SIZE];
+
+ result = TCL_OK;
+ i = 2;
+ flag = 0;
+ mode = 0;
+ pgsize = 0;
+ memset(newname, 0, MSG_SIZE);
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i],
+ mpopts, "option", TCL_EXACT, &optindex) != TCL_OK) {
+ /*
+ * Reset the result so we don't get an errant
+ * error message if there is another error.
+ * This arg is the file name.
+ */
+ if (IS_HELP(objv[i]) == TCL_OK)
+ return (TCL_OK);
+ Tcl_ResetResult(interp);
+ break;
+ }
+ i++;
+ switch ((enum mpopts)optindex) {
+ case MPCREATE:
+ flag |= DB_CREATE;
+ break;
+ case MPNOMMAP:
+ flag |= DB_NOMMAP;
+ break;
+ case MPPAGE:
+ if (i >= objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-pagesize size?");
+ 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++], &pgsize);
+ break;
+ case MPRDONLY:
+ flag |= DB_RDONLY;
+ break;
+ case MPMODE:
+ 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;
+ }
+ if (result != TCL_OK)
+ goto error;
+ }
+ /*
+ * Any left over arg is a file name. It better be the last arg.
+ */
+ file = NULL;
+ if (i != objc) {
+ if (i != objc - 1) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?args? ?file?");
+ result = TCL_ERROR;
+ goto error;
+ }
+ file = Tcl_GetStringFromObj(objv[i++], NULL);
+ }
+
+ snprintf(newname, sizeof(newname), "%s.mp%d",
+ envip->i_name, envip->i_envmpid);
+ ip = _NewInfo(interp, NULL, newname, I_MP);
+ if (ip == NULL) {
+ Tcl_SetResult(interp, "Could not set up info",
+ TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ /*
+ * XXX finfop is NULL here. Interface currently doesn't
+ * have all the stuff. Should expand interface.
+ */
+ _debug_check();
+ ret = memp_fopen(envp, file, flag, mode, (size_t)pgsize, NULL, &mpf);
+ if (ret != 0) {
+ result = _ReturnSetup(interp, ret, "mpool");
+ _DeleteInfo(ip);
+ } else {
+ /*
+ * Success. Set up return. Set up new info
+ * and command widget for this mpool.
+ */
+ envip->i_envmpid++;
+ ip->i_parent = envip;
+ ip->i_pgsz = pgsize;
+ _SetInfoData(ip, mpf);
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)mp_Cmd, (ClientData)mpf, NULL);
+ res = Tcl_NewStringObj(newname, strlen(newname));
+ Tcl_SetObjResult(interp, res);
+ }
+error:
+ return (result);
+}
+
+/*
+ * tcl_MpStat --
+ *
+ * PUBLIC: int tcl_MpStat __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_MpStat(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ DB_MPOOL_STAT *sp;
+ DB_MPOOL_FSTAT **fsp, **savefsp;
+ int result;
+ int ret;
+ Tcl_Obj *res;
+ Tcl_Obj *res1;
+
+ result = TCL_OK;
+ savefsp = NULL;
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = memp_stat(envp, &sp, &fsp, NULL);
+ result = _ReturnSetup(interp, ret, "memp stat");
+ if (result == TCL_ERROR)
+ return (result);
+
+ /*
+ * Have our stats, now construct the name value
+ * list pairs and free up the memory.
+ */
+ res = Tcl_NewObj();
+ /*
+ * MAKE_STAT_LIST assumes 'res' and 'error' label.
+ */
+ MAKE_STAT_LIST("Region size", sp->st_regsize);
+ MAKE_STAT_LIST("Cache size (gbytes)", sp->st_gbytes);
+ MAKE_STAT_LIST("Cache size (bytes)", sp->st_bytes);
+ MAKE_STAT_LIST("Cache hits", sp->st_cache_hit);
+ MAKE_STAT_LIST("Cache misses", sp->st_cache_miss);
+ MAKE_STAT_LIST("Number of caches", sp->st_ncache);
+ MAKE_STAT_LIST("Pages mapped into address space", sp->st_map);
+ MAKE_STAT_LIST("Pages created", sp->st_page_create);
+ MAKE_STAT_LIST("Pages read in", sp->st_page_in);
+ MAKE_STAT_LIST("Pages written", sp->st_page_out);
+ MAKE_STAT_LIST("Clean page evictions", sp->st_ro_evict);
+ MAKE_STAT_LIST("Dirty page evictions", sp->st_rw_evict);
+ MAKE_STAT_LIST("Hash buckets", sp->st_hash_buckets);
+ MAKE_STAT_LIST("Hash lookups", sp->st_hash_searches);
+ MAKE_STAT_LIST("Longest hash chain found", sp->st_hash_longest);
+ MAKE_STAT_LIST("Hash elements examined", sp->st_hash_examined);
+ MAKE_STAT_LIST("Cached clean pages", sp->st_page_clean);
+ MAKE_STAT_LIST("Cached dirty pages", sp->st_page_dirty);
+ MAKE_STAT_LIST("Dirty pages trickled", sp->st_page_trickle);
+ MAKE_STAT_LIST("Number of region lock waits", sp->st_region_wait);
+ MAKE_STAT_LIST("Number of region lock nowaits", sp->st_region_nowait);
+ /*
+ * Save global stat list as res1. The MAKE_STAT_LIST
+ * macro assumes 'res' so we'll use that to build up
+ * our per-file sublist.
+ */
+ res1 = res;
+ savefsp = fsp;
+ for (; fsp != NULL && *fsp != NULL; fsp++) {
+ res = Tcl_NewObj();
+ result = _SetListElem(interp, res, "File Name",
+ strlen("File Name"), (*fsp)->file_name,
+ strlen((*fsp)->file_name));
+ if (result != TCL_OK)
+ goto error;
+ MAKE_STAT_LIST("Page size", (*fsp)->st_pagesize);
+ MAKE_STAT_LIST("Cache Hits", (*fsp)->st_cache_hit);
+ MAKE_STAT_LIST("Cache Misses", (*fsp)->st_cache_miss);
+ MAKE_STAT_LIST("Pages mapped into address space",
+ (*fsp)->st_map);
+ MAKE_STAT_LIST("Pages created", (*fsp)->st_page_create);
+ MAKE_STAT_LIST("Pages read in", (*fsp)->st_page_in);
+ MAKE_STAT_LIST("Pages written", (*fsp)->st_page_out);
+ /*
+ * Now that we have a complete "per-file" stat
+ * list, append that to the other list.
+ */
+ result = Tcl_ListObjAppendElement(interp, res1, res);
+ if (result != TCL_OK)
+ goto error;
+ }
+ Tcl_SetObjResult(interp, res1);
+error:
+ __os_free(sp, sizeof(*sp));
+ if (savefsp != NULL)
+ __os_free(savefsp, 0);
+ return (result);
+}
+
+/*
+ * mp_Cmd --
+ * Implements the "mp" widget.
+ */
+static int
+mp_Cmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Mp handle */
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *mpcmds[] = {
+ "close", "fsync", "get",
+ NULL
+ };
+ enum mpcmds {
+ MPCLOSE, MPFSYNC, MPGET
+ };
+ DB_MPOOLFILE *mp;
+ int cmdindex, length, result, ret;
+ DBTCL_INFO *mpip;
+ Tcl_Obj *res;
+ char *obj_name;
+
+ Tcl_ResetResult(interp);
+ mp = (DB_MPOOLFILE *)clientData;
+ obj_name = Tcl_GetStringFromObj(objv[0], &length);
+ mpip = _NameToInfo(obj_name);
+ result = TCL_OK;
+
+ if (mp == NULL) {
+ Tcl_SetResult(interp, "NULL mp pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (mpip == NULL) {
+ Tcl_SetResult(interp, "NULL mp info pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the command name index from the object based on the dbcmds
+ * defined above.
+ */
+ if (Tcl_GetIndexFromObj(interp,
+ objv[1], mpcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+
+ res = NULL;
+ switch ((enum mpcmds)cmdindex) {
+ case MPCLOSE:
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = memp_fclose(mp);
+ result = _ReturnSetup(interp, ret, "mp close");
+ _MpInfoDelete(interp, mpip);
+ (void)Tcl_DeleteCommand(interp, mpip->i_name);
+ _DeleteInfo(mpip);
+ break;
+ case MPFSYNC:
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = memp_fsync(mp);
+ res = Tcl_NewIntObj(ret);
+ break;
+ case MPGET:
+ result = tcl_MpGet(interp, objc, objv, mp, mpip);
+ break;
+ }
+ /*
+ * Only set result if we have a res. Otherwise, lower
+ * functions have already done so.
+ */
+ if (result == TCL_OK && res)
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+/*
+ * tcl_MpGet --
+ */
+static int
+tcl_MpGet(interp, objc, objv, mp, mpip)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_MPOOLFILE *mp; /* mp pointer */
+ DBTCL_INFO *mpip; /* mp info pointer */
+{
+ static char *mpget[] = {
+ "-create", "-last", "-new",
+ NULL
+ };
+ enum mpget {
+ MPGET_CREATE, MPGET_LAST, MPGET_NEW
+ };
+
+ DBTCL_INFO *ip;
+ Tcl_Obj *res;
+ db_pgno_t pgno;
+ u_int32_t flag;
+ int i, ipgno, optindex, result, ret;
+ char newname[MSG_SIZE];
+ void *page;
+
+ result = TCL_OK;
+ memset(newname, 0, MSG_SIZE);
+ i = 2;
+ flag = 0;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i],
+ mpget, "option", TCL_EXACT, &optindex) != TCL_OK) {
+ /*
+ * Reset the result so we don't get an errant
+ * error message if there is another error.
+ * This arg is the page number.
+ */
+ if (IS_HELP(objv[i]) == TCL_OK)
+ return (TCL_OK);
+ Tcl_ResetResult(interp);
+ break;
+ }
+ i++;
+ switch ((enum mpget)optindex) {
+ case MPGET_CREATE:
+ flag |= DB_MPOOL_CREATE;
+ break;
+ case MPGET_LAST:
+ flag |= DB_MPOOL_LAST;
+ break;
+ case MPGET_NEW:
+ flag |= DB_MPOOL_NEW;
+ break;
+ }
+ if (result != TCL_OK)
+ goto error;
+ }
+ /*
+ * Any left over arg is a page number. It better be the last arg.
+ */
+ ipgno = 0;
+ if (i != objc) {
+ if (i != objc - 1) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?args? ?pgno?");
+ result = TCL_ERROR;
+ goto error;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &ipgno);
+ if (result != TCL_OK)
+ goto error;
+ }
+
+ snprintf(newname, sizeof(newname), "%s.pg%d",
+ mpip->i_name, mpip->i_mppgid);
+ ip = _NewInfo(interp, NULL, newname, I_PG);
+ if (ip == NULL) {
+ Tcl_SetResult(interp, "Could not set up info",
+ TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ pgno = ipgno;
+ ret = memp_fget(mp, &pgno, flag, &page);
+ result = _ReturnSetup(interp, ret, "mpool get");
+ if (result == TCL_ERROR)
+ _DeleteInfo(ip);
+ else {
+ /*
+ * Success. Set up return. Set up new info
+ * and command widget for this mpool.
+ */
+ mpip->i_mppgid++;
+ ip->i_parent = mpip;
+ ip->i_pgno = pgno;
+ ip->i_pgsz = mpip->i_pgsz;
+ _SetInfoData(ip, page);
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)pg_Cmd, (ClientData)page, NULL);
+ res = Tcl_NewStringObj(newname, strlen(newname));
+ Tcl_SetObjResult(interp, res);
+ }
+error:
+ return (result);
+}
+
+/*
+ * pg_Cmd --
+ * Implements the "pg" widget.
+ */
+static int
+pg_Cmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Page handle */
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *pgcmds[] = {
+ "init",
+ "is_setto",
+ "pgnum",
+ "pgsize",
+ "put",
+ "set",
+ NULL
+ };
+ enum pgcmds {
+ PGINIT,
+ PGISSET,
+ PGNUM,
+ PGSIZE,
+ PGPUT,
+ PGSET
+ };
+ DB_MPOOLFILE *mp;
+ int cmdindex, length, result;
+ char *obj_name;
+ void *page;
+ DBTCL_INFO *pgip;
+ Tcl_Obj *res;
+
+ Tcl_ResetResult(interp);
+ page = (void *)clientData;
+ obj_name = Tcl_GetStringFromObj(objv[0], &length);
+ pgip = _NameToInfo(obj_name);
+ mp = NAME_TO_MP(pgip->i_parent->i_name);
+ result = TCL_OK;
+
+ if (page == NULL) {
+ Tcl_SetResult(interp, "NULL page pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (mp == NULL) {
+ Tcl_SetResult(interp, "NULL mp pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (pgip == NULL) {
+ Tcl_SetResult(interp, "NULL page info pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the command name index from the object based on the dbcmds
+ * defined above.
+ */
+ if (Tcl_GetIndexFromObj(interp,
+ objv[1], pgcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+
+ res = NULL;
+ switch ((enum pgcmds)cmdindex) {
+ case PGNUM:
+ res = Tcl_NewIntObj(pgip->i_pgno);
+ break;
+ case PGSIZE:
+ res = Tcl_NewLongObj(pgip->i_pgsz);
+ break;
+ case PGSET:
+ case PGPUT:
+ result = tcl_Pg(interp, objc, objv, page, mp, pgip,
+ cmdindex == PGSET ? 0 : 1);
+ break;
+ case PGINIT:
+ result = tcl_PgInit(interp, objc, objv, page, pgip);
+ break;
+ case PGISSET:
+ result = tcl_PgIsset(interp, objc, objv, page, pgip);
+ break;
+ }
+ /*
+ * Only set result if we have a res. Otherwise, lower
+ * functions have already done so.
+ */
+ if (result == TCL_OK && res)
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+static int
+tcl_Pg(interp, objc, objv, page, mp, pgip, putop)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ void *page; /* Page pointer */
+ DB_MPOOLFILE *mp; /* Mpool pointer */
+ DBTCL_INFO *pgip; /* Info pointer */
+ int putop; /* Operation */
+{
+ static char *pgopt[] = {
+ "-clean", "-dirty", "-discard",
+ NULL
+ };
+ enum pgopt {
+ PGCLEAN, PGDIRTY, PGDISCARD
+ };
+ u_int32_t flag;
+ int i, optindex, result, ret;
+
+ result = TCL_OK;
+ i = 2;
+ flag = 0;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i],
+ pgopt, "option", TCL_EXACT, &optindex) != TCL_OK)
+ return (IS_HELP(objv[i]));
+ i++;
+ switch ((enum pgopt)optindex) {
+ case PGCLEAN:
+ flag |= DB_MPOOL_CLEAN;
+ break;
+ case PGDIRTY:
+ flag |= DB_MPOOL_DIRTY;
+ break;
+ case PGDISCARD:
+ flag |= DB_MPOOL_DISCARD;
+ break;
+ }
+ }
+
+ _debug_check();
+ if (putop)
+ ret = memp_fput(mp, page, flag);
+ else
+ ret = memp_fset(mp, page, flag);
+
+ result = _ReturnSetup(interp, ret, "page");
+
+ if (putop) {
+ (void)Tcl_DeleteCommand(interp, pgip->i_name);
+ _DeleteInfo(pgip);
+ }
+ return (result);
+}
+
+static int
+tcl_PgInit(interp, objc, objv, page, pgip)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ void *page; /* Page pointer */
+ DBTCL_INFO *pgip; /* Info pointer */
+{
+ Tcl_Obj *res;
+ size_t pgsz;
+ long *p, *endp, newval;
+ int length, result;
+ u_char *s;
+
+ result = TCL_OK;
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "val");
+ return (TCL_ERROR);
+ }
+
+ pgsz = pgip->i_pgsz;
+ result = Tcl_GetLongFromObj(interp, objv[2], &newval);
+ if (result != TCL_OK) {
+ s = Tcl_GetByteArrayFromObj(objv[2], &length);
+ if (s == NULL)
+ return (TCL_ERROR);
+ memcpy(page, s, ((size_t)length < pgsz) ? length : pgsz);
+ result = TCL_OK;
+ } else {
+ p = (long *)page;
+ for (endp = p + (pgsz / sizeof(long)); p < endp; p++)
+ *p = newval;
+ }
+ res = Tcl_NewIntObj(0);
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+static int
+tcl_PgIsset(interp, objc, objv, page, pgip)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ void *page; /* Page pointer */
+ DBTCL_INFO *pgip; /* Info pointer */
+{
+ Tcl_Obj *res;
+ size_t pgsz;
+ long *p, *endp, newval;
+ int length, result;
+ u_char *s;
+
+ result = TCL_OK;
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "val");
+ return (TCL_ERROR);
+ }
+
+ pgsz = pgip->i_pgsz;
+ result = Tcl_GetLongFromObj(interp, objv[2], &newval);
+ if (result != TCL_OK) {
+ if ((s = Tcl_GetByteArrayFromObj(objv[2], &length)) == NULL)
+ return (TCL_ERROR);
+ result = TCL_OK;
+
+ if (memcmp(page,
+ s, ((size_t)length < pgsz) ? length : pgsz ) != 0) {
+ res = Tcl_NewIntObj(0);
+ Tcl_SetObjResult(interp, res);
+ return (result);
+ }
+ } else {
+ p = (long *)page;
+ /*
+ * If any value is not the same, return 0 (is not set to
+ * this value). Otherwise, if we finish the loop, we return 1
+ * (is set to this value).
+ */
+ for (endp = p + (pgsz/sizeof(long)); p < endp; p++)
+ if (*p != newval) {
+ res = Tcl_NewIntObj(0);
+ Tcl_SetObjResult(interp, res);
+ return (result);
+ }
+ }
+
+ res = Tcl_NewIntObj(1);
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
diff --git a/db/tcl/tcl_txn.c b/db/tcl/tcl_txn.c
new file mode 100644
index 000000000..dfe6b6cf6
--- /dev/null
+++ b/db/tcl/tcl_txn.c
@@ -0,0 +1,473 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1999, 2000
+ * Sleepycat Software. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#ifndef lint
+static const char revid[] = "$Id: tcl_txn.c,v 11.24 2000/12/31 19:26:23 bostic Exp $";
+#endif /* not lint */
+
+#ifndef NO_SYSTEM_INCLUDES
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+#endif
+
+#include "db_int.h"
+#include "tcl_db.h"
+
+/*
+ * Prototypes for procedures defined later in this file:
+ */
+static int tcl_TxnCommit __P((Tcl_Interp *, int, Tcl_Obj * CONST*,
+ DB_TXN *, DBTCL_INFO *));
+
+/*
+ * _TxnInfoDelete --
+ * Removes nested txn info structures that are children
+ * of this txn.
+ * RECURSIVE: Transactions can be arbitrarily nested, so we
+ * must recurse down until we get them all.
+ *
+ * PUBLIC: void _TxnInfoDelete __P((Tcl_Interp *, DBTCL_INFO *));
+ */
+void
+_TxnInfoDelete(interp, txnip)
+ Tcl_Interp *interp; /* Interpreter */
+ DBTCL_INFO *txnip; /* Info for txn */
+{
+ DBTCL_INFO *nextp, *p;
+
+ for (p = LIST_FIRST(&__db_infohead); p != NULL; p = nextp) {
+ /*
+ * Check if this info structure "belongs" to this
+ * txn. Remove its commands and info structure.
+ */
+ nextp = LIST_NEXT(p, entries);
+ if (p->i_parent == txnip && p->i_type == I_TXN) {
+ _TxnInfoDelete(interp, p);
+ (void)Tcl_DeleteCommand(interp, p->i_name);
+ _DeleteInfo(p);
+ }
+ }
+}
+
+/*
+ * tcl_TxnCheckpoint --
+ *
+ * PUBLIC: int tcl_TxnCheckpoint __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_TxnCheckpoint(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+ static char *txnckpopts[] = {
+ "-kbyte", "-min",
+ NULL
+ };
+ enum txnckpopts {
+ TXNCKP_KB, TXNCKP_MIN
+ };
+ int i, kb, min, optindex, result, ret;
+
+ result = TCL_OK;
+ kb = min = 0;
+
+ /*
+ * Get the flag index from the object based on the options
+ * defined above.
+ */
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i],
+ txnckpopts, "option", TCL_EXACT, &optindex) != TCL_OK) {
+ return (IS_HELP(objv[i]));
+ }
+ i++;
+ switch ((enum txnckpopts)optindex) {
+ case TXNCKP_KB:
+ if (i == objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-kbyte kb?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &kb);
+ break;
+ case TXNCKP_MIN:
+ if (i == objc) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-min min?");
+ result = TCL_ERROR;
+ break;
+ }
+ result = Tcl_GetIntFromObj(interp, objv[i++], &min);
+ break;
+ }
+ }
+ _debug_check();
+ ret = txn_checkpoint(envp, (u_int32_t)kb, (u_int32_t)min, 0);
+ result = _ReturnSetup(interp, ret, "txn checkpoint");
+ return (result);
+}
+
+/*
+ * tcl_Txn --
+ *
+ * PUBLIC: int tcl_Txn __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *, DBTCL_INFO *));
+ */
+int
+tcl_Txn(interp, objc, objv, envp, envip)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+ DBTCL_INFO *envip; /* Info pointer */
+{
+ static char *txnopts[] = {
+ "-nosync",
+ "-nowait",
+ "-parent",
+ "-sync",
+ NULL
+ };
+ enum txnopts {
+ TXN_NOSYNC,
+ TXN_NOWAIT,
+ TXN_PARENT,
+ TXN_SYNC
+ };
+ DBTCL_INFO *ip;
+ DB_TXN *parent;
+ DB_TXN *txn;
+ Tcl_Obj *res;
+ u_int32_t flag;
+ int i, optindex, result, ret;
+ char *arg, msg[MSG_SIZE], newname[MSG_SIZE];
+
+ result = TCL_OK;
+ memset(newname, 0, MSG_SIZE);
+
+ parent = NULL;
+ flag = 0;
+ i = 2;
+ while (i < objc) {
+ if (Tcl_GetIndexFromObj(interp, objv[i],
+ txnopts, "option", TCL_EXACT, &optindex) != TCL_OK) {
+ return (IS_HELP(objv[i]));
+ }
+ i++;
+ switch ((enum txnopts)optindex) {
+ case TXN_PARENT:
+ if (i == objc) {
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "?-parent txn?");
+ result = TCL_ERROR;
+ break;
+ }
+ arg = Tcl_GetStringFromObj(objv[i++], NULL);
+ parent = NAME_TO_TXN(arg);
+ if (parent == NULL) {
+ snprintf(msg, MSG_SIZE,
+ "Invalid parent txn: %s\n",
+ arg);
+ Tcl_SetResult(interp, msg, TCL_VOLATILE);
+ return (TCL_ERROR);
+ }
+ break;
+ case TXN_NOWAIT:
+ FLAG_CHECK(flag);
+ flag |= DB_TXN_NOWAIT;
+ break;
+ case TXN_SYNC:
+ FLAG_CHECK(flag);
+ flag |= DB_TXN_SYNC;
+ break;
+ case TXN_NOSYNC:
+ FLAG_CHECK(flag);
+ flag |= DB_TXN_NOSYNC;
+ break;
+ }
+ }
+ snprintf(newname, sizeof(newname), "%s.txn%d",
+ envip->i_name, envip->i_envtxnid);
+ ip = _NewInfo(interp, NULL, newname, I_TXN);
+ if (ip == NULL) {
+ Tcl_SetResult(interp, "Could not set up info",
+ TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = txn_begin(envp, parent, &txn, flag);
+ result = _ReturnSetup(interp, ret, "txn");
+ if (result == TCL_ERROR)
+ _DeleteInfo(ip);
+ else {
+ /*
+ * Success. Set up return. Set up new info
+ * and command widget for this txn.
+ */
+ envip->i_envtxnid++;
+ if (parent)
+ ip->i_parent = _PtrToInfo(parent);
+ else
+ ip->i_parent = envip;
+ _SetInfoData(ip, txn);
+ Tcl_CreateObjCommand(interp, newname,
+ (Tcl_ObjCmdProc *)txn_Cmd, (ClientData)txn, NULL);
+ res = Tcl_NewStringObj(newname, strlen(newname));
+ Tcl_SetObjResult(interp, res);
+ }
+ return (result);
+}
+
+/*
+ * tcl_TxnStat --
+ *
+ * PUBLIC: int tcl_TxnStat __P((Tcl_Interp *, int,
+ * PUBLIC: Tcl_Obj * CONST*, DB_ENV *));
+ */
+int
+tcl_TxnStat(interp, objc, objv, envp)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_ENV *envp; /* Environment pointer */
+{
+#define MAKE_STAT_LSN(s, lsn) \
+do { \
+ myobjc = 2; \
+ myobjv[0] = Tcl_NewIntObj((lsn)->file); \
+ myobjv[1] = Tcl_NewIntObj((lsn)->offset); \
+ lsnlist = Tcl_NewListObj(myobjc, myobjv); \
+ myobjc = 2; \
+ myobjv[0] = Tcl_NewStringObj((s), strlen(s)); \
+ myobjv[1] = lsnlist; \
+ thislist = Tcl_NewListObj(myobjc, myobjv); \
+ result = Tcl_ListObjAppendElement(interp, res, thislist); \
+ if (result != TCL_OK) \
+ goto error; \
+} while (0);
+
+ DBTCL_INFO *ip;
+ DB_TXN_ACTIVE *p;
+ DB_TXN_STAT *sp;
+ Tcl_Obj *myobjv[2], *res, *thislist, *lsnlist;
+ u_int32_t i;
+ int myobjc, result, ret;
+
+ result = TCL_OK;
+ /*
+ * No args for this. Error if there are some.
+ */
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = txn_stat(envp, &sp, NULL);
+ result = _ReturnSetup(interp, ret, "txn stat");
+ if (result == TCL_ERROR)
+ return (result);
+
+ /*
+ * Have our stats, now construct the name value
+ * list pairs and free up the memory.
+ */
+ res = Tcl_NewObj();
+ /*
+ * MAKE_STAT_LIST assumes 'res' and 'error' label.
+ */
+ MAKE_STAT_LIST("Region size", sp->st_regsize);
+ MAKE_STAT_LSN("LSN of last checkpoint", &sp->st_last_ckp);
+ MAKE_STAT_LSN("LSN of pending checkpoint", &sp->st_pending_ckp);
+ MAKE_STAT_LIST("Time of last checkpoint", sp->st_time_ckp);
+ MAKE_STAT_LIST("Last txn ID allocated", sp->st_last_txnid);
+ MAKE_STAT_LIST("Max Txns", sp->st_maxtxns);
+ MAKE_STAT_LIST("Number aborted txns", sp->st_naborts);
+ MAKE_STAT_LIST("Number active txns", sp->st_nactive);
+ MAKE_STAT_LIST("Number txns begun", sp->st_nbegins);
+ MAKE_STAT_LIST("Number committed txns", sp->st_ncommits);
+ MAKE_STAT_LIST("Number of region lock waits", sp->st_region_wait);
+ MAKE_STAT_LIST("Number of region lock nowaits", sp->st_region_nowait);
+ for (i = 0, p = sp->st_txnarray; i < sp->st_nactive; i++, p++)
+ for (ip = LIST_FIRST(&__db_infohead); ip != NULL;
+ ip = LIST_NEXT(ip, entries)) {
+ if (ip->i_type != I_TXN)
+ continue;
+ if (ip->i_type == I_TXN &&
+ (txn_id(ip->i_txnp) == p->txnid)) {
+ MAKE_STAT_LSN(ip->i_name, &p->lsn);
+ if (p->parentid != 0)
+ MAKE_STAT_STRLIST("Parent",
+ ip->i_parent->i_name);
+ else
+ MAKE_STAT_LIST("Parent", 0);
+ break;
+ }
+ }
+ Tcl_SetObjResult(interp, res);
+error:
+ __os_free(sp, sizeof(*sp));
+ return (result);
+}
+
+/*
+ * txn_Cmd --
+ * Implements the "txn" widget.
+ *
+ * PUBLIC: int txn_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
+ */
+int
+txn_Cmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Txn handle */
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+{
+ static char *txncmds[] = {
+ "abort",
+ "commit",
+ "id",
+ "prepare",
+ NULL
+ };
+ enum txncmds {
+ TXNABORT,
+ TXNCOMMIT,
+ TXNID,
+ TXNPREPARE
+ };
+ DBTCL_INFO *txnip;
+ DB_TXN *txnp;
+ Tcl_Obj *res;
+ int cmdindex, result, ret;
+
+ Tcl_ResetResult(interp);
+ txnp = (DB_TXN *)clientData;
+ txnip = _PtrToInfo((void *)txnp);
+ result = TCL_OK;
+ if (txnp == NULL) {
+ Tcl_SetResult(interp, "NULL txn pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (txnip == NULL) {
+ Tcl_SetResult(interp, "NULL txn info pointer", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ /*
+ * Get the command name index from the object based on the dbcmds
+ * defined above.
+ */
+ if (Tcl_GetIndexFromObj(interp,
+ objv[1], txncmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
+ return (IS_HELP(objv[1]));
+
+ res = NULL;
+ switch ((enum txncmds)cmdindex) {
+ case TXNID:
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = txn_id(txnp);
+ res = Tcl_NewIntObj(ret);
+ break;
+ case TXNPREPARE:
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = txn_prepare(txnp);
+ result = _ReturnSetup(interp, ret, "txn prepare");
+ break;
+ case TXNCOMMIT:
+ result = tcl_TxnCommit(interp, objc, objv, txnp, txnip);
+ _TxnInfoDelete(interp, txnip);
+ (void)Tcl_DeleteCommand(interp, txnip->i_name);
+ _DeleteInfo(txnip);
+ break;
+ case TXNABORT:
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, NULL);
+ return (TCL_ERROR);
+ }
+ _debug_check();
+ ret = txn_abort(txnp);
+ result = _ReturnSetup(interp, ret, "txn abort");
+ _TxnInfoDelete(interp, txnip);
+ (void)Tcl_DeleteCommand(interp, txnip->i_name);
+ _DeleteInfo(txnip);
+ break;
+ }
+ /*
+ * Only set result if we have a res. Otherwise, lower
+ * functions have already done so.
+ */
+ if (result == TCL_OK && res)
+ Tcl_SetObjResult(interp, res);
+ return (result);
+}
+
+static int
+tcl_TxnCommit(interp, objc, objv, txnp, txnip)
+ Tcl_Interp *interp; /* Interpreter */
+ int objc; /* How many arguments? */
+ Tcl_Obj *CONST objv[]; /* The argument objects */
+ DB_TXN *txnp; /* Transaction pointer */
+ DBTCL_INFO *txnip; /* Info pointer */
+{
+ static char *commitopt[] = {
+ "-nosync",
+ "-sync",
+ NULL
+ };
+ enum commitopt {
+ COMSYNC,
+ COMNOSYNC
+ };
+ u_int32_t flag;
+ int optindex, result, ret;
+
+ COMPQUIET(txnip, NULL);
+
+ result = TCL_OK;
+ flag = 0;
+ if (objc != 2 && objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, NULL);
+ return (TCL_ERROR);
+ }
+ if (objc == 3) {
+ if (Tcl_GetIndexFromObj(interp, objv[2], commitopt,
+ "option", TCL_EXACT, &optindex) != TCL_OK)
+ return (IS_HELP(objv[2]));
+ switch ((enum commitopt)optindex) {
+ case COMSYNC:
+ FLAG_CHECK(flag);
+ flag = DB_TXN_SYNC;
+ break;
+ case COMNOSYNC:
+ FLAG_CHECK(flag);
+ flag = DB_TXN_NOSYNC;
+ break;
+ }
+ }
+
+ _debug_check();
+ ret = txn_commit(txnp, flag);
+ result = _ReturnSetup(interp, ret, "txn commit");
+ return (result);
+}