]> git.sur5r.net Git - bacula/bacula/commitdiff
ebl Add tokyocabinet source to bacula
authorEric Bollengier <eric@eb.homelinux.org>
Mon, 19 May 2008 14:31:52 +0000 (14:31 +0000)
committerEric Bollengier <eric@eb.homelinux.org>
Mon, 19 May 2008 14:31:52 +0000 (14:31 +0000)
git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@6986 91ce42f0-d328-0410-95d8-f526ca767f89

31 files changed:
bacula/src/lib/tokyocabinet/COPYING [new file with mode: 0644]
bacula/src/lib/tokyocabinet/ChangeLog [new file with mode: 0644]
bacula/src/lib/tokyocabinet/Makefile.in [new file with mode: 0644]
bacula/src/lib/tokyocabinet/README [new file with mode: 0644]
bacula/src/lib/tokyocabinet/THANKS [new file with mode: 0644]
bacula/src/lib/tokyocabinet/TODO [new file with mode: 0644]
bacula/src/lib/tokyocabinet/configure [new file with mode: 0755]
bacula/src/lib/tokyocabinet/configure.in [new file with mode: 0644]
bacula/src/lib/tokyocabinet/myconf.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/myconf.h [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcadb.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcadb.h [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcamgr.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcatest.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcbdb.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcbdb.h [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcbmgr.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcbmttest.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcbtest.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tchdb.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tchdb.h [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tchmgr.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tchmttest.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tchtest.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcucodec.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcumttest.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcutest.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcutil.c [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tcutil.h [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tokyocabinet.idl [new file with mode: 0644]
bacula/src/lib/tokyocabinet/tokyocabinet.pc.in [new file with mode: 0644]

diff --git a/bacula/src/lib/tokyocabinet/COPYING b/bacula/src/lib/tokyocabinet/COPYING
new file mode 100644 (file)
index 0000000..b1e3f5a
--- /dev/null
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/bacula/src/lib/tokyocabinet/ChangeLog b/bacula/src/lib/tokyocabinet/ChangeLog
new file mode 100644 (file)
index 0000000..4e8298e
--- /dev/null
@@ -0,0 +1,418 @@
+2008-04-22  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcbdb.c (tcbdbcmpdecimal, tcbdbcmpint32, tcbdbcmpint64): bugs of overflow were fixed.
+
+       - Release: 1.2.5
+
+2008-04-13  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcbdb.c (tcbdbopenimpl): comparison function checking was added.
+
+       * tcadb.c (tcadbopen): "capnum" option is now supported for B+ tree.
+
+       - Release: 1.2.4
+
+2008-04-07  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcregexmatch, tcregexreplace, tcglobpat): new functions.
+
+       * tcbdb.c (tcbdbcmpfunc, tcbdbcmpop): new functions.
+
+       * tcbdb.c (tcbdboptimizeimpl): leaf size limitation is now implemented.
+
+       * tcbdb.c (tcbdbsetcapnum): new function.
+
+       - Release: 1.2.3
+
+2008-03-19  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcmapnew2): "calloc" is now used.
+
+       * tcbdb.c (tcbdbputimpl): algorithm to divide large leaves was modified.
+
+       - Release: 1.2.2
+
+2008-03-13  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tclistelemcmp, tclistelemcmpci): bugs about multibyte ordering were fixed.
+
+       * tcutil.c (tclistsortex): new function.
+
+       * tcbdb.c (tcbdbsetecode): new function.
+
+       * tcadb.c (tcadbopen): "c", "t", "e", and "w" options were added.
+
+       - Release: 1.2.1
+
+2008-02-18  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcmdb.c (tcmdbfwmkeys, tcmdbfwmkeys2): new functions.
+
+       * tchdb.c (tchdbfwmkeys, tchdbfwmkeys2): new functions.
+
+       * tcbdb.c (tcbdbfwmkeys, tcbdbfwmkeys2): new functions instead of "tcbdbrange3".
+
+       * tcadb.c (tcadbfwmkeys, tcadbfwmkeys2): new functions.
+
+       * tcbdb.c (tcbdbrangeimpl): a bug related to mutex operation was fixed.
+
+       - The library version was bumped up for some binary packages.
+
+       - Release: 1.2.0
+
+2008-02-15  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcbwtsortchrcount): time efficiency was improved.
+
+       - The library version was bumped up for some binary packages.
+
+       - Release: 1.1.15
+
+2008-02-05  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcatest.c (runwicked, procwicked): new functions.
+
+       - Release: 1.1.14
+
+2008-01-30  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tctime): measure unit was changed to in seconds.
+
+       * tchdb.c (tchdbcopy): shared lock is now used.
+
+       * tcadb.c (tcadbsync, tcadbcopy): new functions.
+
+       - Release: 1.1.13
+
+2008-01-23  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcbdb.c (tcbdbleafkill, tcbdbnodesubidx): new functions.
+
+       * tcbtest.c (runqueue, procqueue): new functions.
+
+       - Release: 1.1.12
+
+2008-01-20  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcmapmsiz, tcmdbmsiz, tcadbsize): new functions.
+
+       * tcutil.c (tcglobalmutexshared): new function.
+
+       * tcutil.c (tcglobalmutexinit, tcglobalmutexdestroy): new functions.
+
+       * tcutil.c (tcreadfile): a bug related to the size assignment was fixed.
+
+       * tcadb.c (tcadbopen): "capsiz" parameter was added.
+
+       - Release: 1.1.11
+
+2008-01-17  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tclistshift, tclistshift2): memory alignment was improved.
+
+       * tcutil.c (tcvxstrprintf): a bug related to format of long long integer was fixed.
+
+       - Release: 1.1.10
+
+2008-01-10  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcmdbvsiz, tcmdbvsiz2): new functions.
+
+       * tcutil.c (tcmdbiternext): a bug related to handling sparse map was fixed.
+
+       * tcadb.h, tcadb.c: new files.
+
+       * tcatest.c, tcadb.c: new files.
+
+       - Release: 1.1.9
+
+2008-01-03  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcstrutftoucs, tcstrucstoutf, tcstrjoin): new function.
+
+       * tcutil.c (tcstrdist, tcstrdistutf): new function.
+
+       - Release: 1.1.8
+
+2007-12-28  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * myconf.h: some header includings were removed for environments not conforming to C99.
+
+       * tcutil.c (tctccalendar, tcdatestrwww, tcdatestrhttp): new functions.
+
+       * tcutil.c (tcmapaddint): new function.
+
+       * tcucodec.c (rundate, procdate): new functions.
+
+       - Release: 1.1.7
+
+2007-12-24  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tcseekread, tcseekwrite): pread and pwrite were to be used.
+
+       - Release: 1.1.6
+
+2007-12-21  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcmdbnew, tcmdbput, tcmdbget): concurrency was improved.
+
+       * tcutil.c (tcmapcutfront, tcmdbcutfront): new functions.
+
+       * tchdb.c (tchdbasyncimpl): large deferred buffer was to be flushed.
+
+       * tcumttest.c: new file.
+
+       - Release: 1.1.5
+
+2007-12-19  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tclrand, tcdrand, tcdrandnd): new functions.
+
+       * tchdb.c (tchdbcacheadjust): count checking was removed.
+
+       * tchmttest.c (myrandnd): new function.
+
+       * tcbmttest.c (myrandnd): new function.
+
+       - Release: 1.1.4
+
+2007-12-10  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcglobalmutexlock): the type of the global mutex was changed to rwlock.
+
+       * tchdb.c (tchdbsetmutex, tchdbopenimpl): multiple reading file descriptors were added.
+
+       * tchmttest.c (runtypical, proctypical): reading ratio assignment was added.
+
+       * tcbmttest.c (runtypical, proctypical): reading ratio assignment was added.
+
+       - Release: 1.1.3
+
+2007-12-08  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcmpoolput): mutex for operations was added.
+
+       - Release: 1.1.2
+
+2007-12-08  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcberencode, tcberdecode): new functions.
+
+       * tcbdb.c (tcbdbleafload): speed was improved.
+
+       - Release: 1.1.1
+
+2007-12-07  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tchdbsetcache): new function.
+
+       * tchmttest.c (procwrite, procread): random mode was added.
+
+       * tcbmttest.c (procwrite, procread): random mode was added.
+
+       - Release: 1.1.0
+
+2007-12-01  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchmgr.c (procget): a bug related to the open omode was fixed.
+
+       * tchdb.c (tchdbreadrec): a bug related to overflow on 32-bit environment was fixed.
+
+       * tchdb.c (tchdbgetbucket): the type of the return value was changed.
+
+       * tcbdb.c (tcbdbcurfirst, tcbdbcurnext): cache adjustment was to be performed.
+
+       * tcbdb.c (tcbdbrange, tcbdbrange2, tcbdbrange3): new functions.
+
+       * tchmgr.c (proclist): "-m" option was added.
+
+       * tchmgr.c (procimporttsv): new function.
+
+       * tcbmgr.c (proclist): "-m", "-rb", and "-rp" options were added.
+
+       * tcbmgr.c (procimporttsv): new function.
+
+       - Release: 1.0.9
+
+2007-11-28  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchmttest.c (threadtypical): a test case was added.
+
+       * tchdb.c (tchdbfbpmerge): a bug related to iterator was fixed.
+
+       * tcbdb.c (tcbdbgetimpl): a bug related to concurrent cache cleaning was fixed.
+
+       - Release: 1.0.8
+
+2007-11-20  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tchdblockmethod): a bug related to R/W lock was fixed.
+
+       * tcbdb.c (tcbdblockmethod): a bug related to R/W lock was fixed.
+
+       * tchdb.c, tcbdb.c: locking functions were re-imlemented as macros.
+
+       * tchmttest.c, tcbmttest.c: test cases of typical operations were added.
+
+       * tokyocabinet.idl: methods handling list parameters were added.
+
+       - Release: 1.0.7
+
+2007-11-15  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcbdb.c (tcbdboptimize): the default behaviour of bnum was changed.
+
+       - Release: 1.0.6
+
+2007-11-10  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcutil.c (tcrealpath, tccopyfile): new functions.
+
+       * tchdb.c (tchdbcopy): new function.
+
+       * tcbdb.c (tcbdbcopy): new function.
+
+       - Release: 1.0.5
+
+2007-11-10  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcbdb.c (tcbdbtune): comparing functions were optimized with macros.
+
+       * tcbdb.c (tcbdbsetlsmax): new function.
+
+       * myconf.c, tcutil.c, tchdb.c, tcbdb.c: code cleanup and optimization.
+
+       - Release: 1.0.4
+
+2007-11-08  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcbdb.c (tcbdbleafsave, tcbdbleafload): warnings on 64-bit system were cleared.
+
+       * configure.in: 64-bit offset mode for 32-bit system was added.
+
+       * Makefile.in: Mac OS X is now supported.
+
+       - Release: 1.0.3
+
+2007-11-01  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcbdb.c (tcbdbdel): a bug that opened database was not closed was fixed.
+
+       * tcbdb.c (tcbdbcurout): a bug of deleting always the first value was fixed.
+
+       * tcbdb.c (tcbdbopenimpl): a potential bug of not initializing meta data was fixed.
+
+       * tcutil.h, tchdb.h, tcbdb.h: wrapped in C linkage block for C++.
+
+       * tokyocabinet.idl: definition of constants were added.
+
+       - commands for performance test were added.
+
+       - Release: 1.0.2
+
+2007-10-28  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcbdb.c (tchdboptimize): a bug related to custom comparison function was fixed.
+
+       * tcbdb.c (tcbdbleafsave): empty pages was to be removed for time space efficiency.
+
+       * tcbdb.c (tcbdbputdup3): new function.
+
+       * tchdb.c (tchdbvanish): new function.
+
+       * tcbdb.c (tcbdbvanish): new function.
+
+       * tokyocabinet.idl: new file.
+
+       - Release: 1.0.1
+
+2007-10-24  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tokyocabinet.pc.in: new file.
+
+       - document files were fulfilled.
+
+       - Release: 1.0.0
+
+2007-10-15  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tcbdb.c (tchdbtranbegin): locking mode was aolished.
+
+       * tcbdb.c (tcbdbsetcmpfunc): new function.
+
+       - Release: 0.4.1
+
+2007-10-11  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tchdbopenimpl): database corruption was to be handled automatically.
+
+       * tcbdb.c, tcbtest.c, tcbmttest.c, tcbmgr.c: new files.
+
+       - Release: 0.4.0
+
+2007-09-09  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tchdbsetmutex, tchdblockobj, tchdbunlockobj): new functions.
+
+       * tchmttest.c: new file.
+
+       - Release: 0.3.4
+
+2007-09-05  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tchdbopen): TCBS compression mode is now supported.
+
+       - Release: 0.3.3
+
+2007-09-01  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tcbsencode, tcbsdecode): new functions.
+
+       - Release: 0.3.2
+
+2007-08-25  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tcpackencode, tcpackdecode): new functions.
+
+       * tchdb.c (tcbwtencode, tcbwtdecode): new functions.
+
+       - Release: 0.3.1
+
+2007-08-22  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tchdbputasync, tchdbputasync2): new functions.
+
+       - Release: 0.3.0
+
+2007-08-18  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tchdboptimize): a bug causing data corruption was fixed.
+
+       - Release: 0.2.8
+
+2007-08-15  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tchdboptimize): new function.
+
+       - Release: 0.2.7
+
+2007-08-14  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tchdbsavefbp, tchdbloadfbp): a bug related to 64-bit support was fixed.
+
+       * tchdb.c (tchdbreadrec, tchdbwriterec): a bug related to 64-bit support was fixed.
+
+       - Release: 0.2.6
+
+2007-08-13  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       * tchdb.c (tchdbsavefbp, tchdbloadfbp): new functions.
+
+       - Release: 0.2.5
+
+2007-08-12  Mikio Hirabayashi  <mikio@users.sourceforge.net>
+
+       - The initial version.
+
+       - Release: 0.2.4
+
diff --git a/bacula/src/lib/tokyocabinet/Makefile.in b/bacula/src/lib/tokyocabinet/Makefile.in
new file mode 100644 (file)
index 0000000..8e757fa
--- /dev/null
@@ -0,0 +1,512 @@
+# Makefile for Tokyo Cabinet
+
+
+
+#================================================================
+# Setting Variables
+#================================================================
+
+
+# Generic settings
+SHELL = @SHELL@
+
+# Package information
+PACKAGE = @PACKAGE_NAME@
+VERSION = @PACKAGE_VERSION@
+PACKAGEDIR = $(PACKAGE)-$(VERSION)
+PACKAGETGZ = $(PACKAGE)-$(VERSION).tar.gz
+LIBVER = @MYLIBVER@
+LIBREV = @MYLIBREV@
+FORMATVER = @MYFORMATVER@
+
+# Targets
+HEADERFILES = @MYHEADERFILES@
+LIBRARYFILES = @MYLIBRARYFILES@
+LIBOBJFILES = @MYLIBOBJFILES@
+COMMANDFILES = @MYCOMMANDFILES@
+MAN1FILES = @MYMAN1FILES@
+MAN3FILES = @MYMAN3FILES@
+DOCUMENTFILES = @MYDOCUMENTFILES@
+PCFILES = @MYPCFILES@
+
+# Install destinations
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
+INCLUDEDIR = @includedir@
+LIBDIR = @libdir@
+BINDIR = @bindir@
+LIBEXECDIR = @libexecdir@
+DATADIR = @datadir@/$(PACKAGE)
+MAN1DIR = @mandir@/man1
+MAN3DIR = @mandir@/man3
+PCDIR = @libdir@/pkgconfig
+DESTDIR =
+
+# Building configuration
+CC = @CC@
+CPPFLAGS = @MYCPPFLAGS@ \
+  -D_TC_PREFIX="\"$(prefix)\"" -D_TC_INCLUDEDIR="\"$(INCLUDEDIR)\"" \
+  -D_TC_LIBDIR="\"$(LIBDIR)\"" -D_TC_BINDIR="\"$(BINDIR)\"" -D_TC_LIBEXECDIR="\"$(LIBEXECDIR)\"" \
+  -D_TC_APPINC="\"-I$(INCLUDEDIR)\"" -D_TC_APPLIBS="\"-L$(LIBDIR) -ltokyocabinet @LIBS@\""
+CFLAGS = @MYCFLAGS@
+LDFLAGS = @MYLDFLAGS@
+LIBS = @LIBS@
+LDENV = LD_RUN_PATH=/lib:/usr/lib:$(LIBDIR):$(HOME)/lib:/usr/local/lib:@MYRUNPATH@:.
+RUNENV = @MYLDLIBPATHENV@=.:/lib:/usr/lib:$(LIBDIR):$(HOME)/lib:/usr/local/lib:@MYRUNPATH@
+POSTCMD = @MYPOSTCMD@
+NO_ECHO = @
+
+
+#================================================================
+# Suffix rules
+#================================================================
+
+
+.SUFFIXES :
+.SUFFIXES : .c .o
+
+.c.o :
+       @echo "Compiling $<"
+       $(NO_ECHO)$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
+
+
+
+#================================================================
+# Actions
+#================================================================
+
+all:  $(LIBRARYFILES)
+
+not-all : $(LIBRARYFILES) $(COMMANDFILES)
+       @$(POSTCMD)
+       @printf '\n'
+       @printf '#================================================================\n'
+       @printf '# Ready to install.\n'
+       @printf '#================================================================\n'
+
+
+clean :
+       rm -rf $(LIBRARYFILES) $(LIBOBJFILES) $(COMMANDFILES) \
+         *.o a.out check.in check.out gmon.out leak.log casket casket-* casket.* *~ hoge moge
+
+
+version :
+       vernum=`expr $(LIBVER)00 + $(LIBREV)` ; \
+         sed -e 's/_TC_VERSION.*/_TC_VERSION    "$(VERSION)"/' \
+           -e "s/_TC_LIBVER.*/_TC_LIBVER     $$vernum/" \
+           -e 's/_TC_FORMATVER.*/_TC_FORMATVER  "$(FORMATVER)"/' tcutil.h > tcutil.h~
+       [ -f tcutil.h~ ] && mv -f tcutil.h~ tcutil.h
+
+untabify :
+       ls *.c *.h *.idl | while read name ; \
+         do \
+           sed -e 's/\t/        /g' -e 's/ *$$//' $$name > $$name~; \
+           [ -f $$name~ ] && mv -f $$name~ $$name ; \
+         done
+
+install:
+
+install-strip:
+
+uninstall:
+
+do-notinstall :
+       mkdir -p $(DESTDIR)$(INCLUDEDIR)
+       cp -Rf $(HEADERFILES) $(DESTDIR)$(INCLUDEDIR)
+       mkdir -p $(DESTDIR)$(LIBDIR)
+       cp -Rf $(LIBRARYFILES) $(DESTDIR)$(LIBDIR)
+       mkdir -p $(DESTDIR)$(BINDIR)
+       cp -Rf $(COMMANDFILES) $(DESTDIR)$(BINDIR)
+       mkdir -p $(DESTDIR)$(DATADIR)
+       cp -Rf $(DOCUMENTFILES) $(DESTDIR)$(DATADIR)
+       mkdir -p $(DESTDIR)$(MAN1DIR)
+       cd man && cp -Rf $(MAN1FILES) $(DESTDIR)$(MAN1DIR)
+       mkdir -p $(DESTDIR)$(MAN3DIR)
+       cd man && cp -Rf $(MAN3FILES) $(DESTDIR)$(MAN3DIR)
+       mkdir -p $(DESTDIR)$(PCDIR)
+       cp -Rf $(PCFILES) $(DESTDIR)$(PCDIR)
+       @printf '\n'
+       @printf '#================================================================\n'
+       @printf '# Thanks for using Tokyo Cabinet.\n'
+       @printf '#================================================================\n'
+
+
+do-notinstall-strip :
+       make DESTDIR=$(DESTDIR) install
+       cd $(DESTDIR)$(BINDIR) && strip $(MYCOMMANDS)
+
+
+do-notuninstall :
+       cd $(DESTDIR)$(INCLUDEDIR) && rm -f $(HEADERFILES)
+       cd $(DESTDIR)$(LIBDIR) && rm -f $(LIBRARYFILES)
+       cd $(DESTDIR)$(BINDIR) && rm -f $(COMMANDFILES)
+       cd $(DESTDIR)$(MAN1DIR) && rm -f $(MAN1FILES)
+       cd $(DESTDIR)$(MAN3DIR) && rm -f $(MAN3FILES)
+       rm -rf $(DESTDIR)$(DATADIR)
+       cd $(DESTDIR)$(PCDIR) && rm -f $(PCFILES)
+
+
+dist :
+       make version
+       make untabify
+       make distclean
+       cd .. && tar cvf - $(PACKAGEDIR) | gzip -c > $(PACKAGETGZ)
+       sync ; sync
+
+
+distclean : clean
+       rm -rf Makefile tokyocabinet.pc config.cache config.log config.status autom4te.cache
+
+
+check :
+       rm -rf casket*
+       $(RUNENV) $(RUNCMD) ./tcamgr version
+       $(RUNENV) $(RUNCMD) ./tcutest xstr 50000
+       $(RUNENV) $(RUNCMD) ./tcutest list 50000
+       $(RUNENV) $(RUNCMD) ./tcutest map 50000
+       $(RUNENV) $(RUNCMD) ./tcutest mdb 50000
+       $(RUNENV) $(RUNCMD) ./tcutest misc 500
+       $(RUNENV) $(RUNCMD) ./tcutest wicked 50000
+       $(RUNENV) $(RUNCMD) ./tcumttest combo 5 50000 500
+       $(RUNENV) $(RUNCMD) ./tcumttest combo -rnd 5 50000 500
+       $(RUNENV) $(RUNCMD) ./tchmttest typical casket 5 50000 5000
+       $(RUNENV) $(RUNCMD) ./tchmttest typical -rr 1000 casket 5 50000 5000
+       $(RUNENV) $(RUNCMD) ./tchmttest typical -rc 50000 -nc casket 5 50000 5000
+       $(RUNENV) $(RUNCMD) ./tcucodec url Makefile > check.in
+       $(RUNENV) $(RUNCMD) ./tcucodec url -d check.in > check.out
+       $(RUNENV) $(RUNCMD) ./tcucodec base Makefile > check.in
+       $(RUNENV) $(RUNCMD) ./tcucodec base -d check.in > check.out
+       $(RUNENV) $(RUNCMD) ./tcucodec quote Makefile > check.in
+       $(RUNENV) $(RUNCMD) ./tcucodec quote -d check.in > check.out
+       $(RUNENV) $(RUNCMD) ./tcucodec mime Makefile > check.in
+       $(RUNENV) $(RUNCMD) ./tcucodec mime -d check.in > check.out
+       $(RUNENV) $(RUNCMD) ./tcucodec pack -bwt Makefile > check.in
+       $(RUNENV) $(RUNCMD) ./tcucodec pack -d -bwt check.in > check.out
+       $(RUNENV) $(RUNCMD) ./tcucodec tcbs Makefile > check.in
+       $(RUNENV) $(RUNCMD) ./tcucodec tcbs -d check.in > check.out
+       $(RUNENV) $(RUNCMD) ./tcucodec zlib Makefile > check.in
+       $(RUNENV) $(RUNCMD) ./tcucodec zlib -d check.in > check.out
+       $(RUNENV) $(RUNCMD) ./tcucodec xml Makefile > check.in
+       $(RUNENV) $(RUNCMD) ./tcucodec xml -d check.in > check.out
+       $(RUNENV) $(RUNCMD) ./tcucodec ucs Makefile > check.in
+       $(RUNENV) $(RUNCMD) ./tcucodec ucs -d check.in > check.out
+       $(RUNENV) $(RUNCMD) ./tcucodec date -ds '1978-02-11T18:05:30+09:00' -rf > check.out
+       $(RUNENV) $(RUNCMD) ./tcucodec conf
+       $(RUNENV) $(RUNCMD) ./tchtest write casket 50000 5000 5 5
+       $(RUNENV) $(RUNCMD) ./tchtest read casket
+       $(RUNENV) $(RUNCMD) ./tchtest remove casket
+       $(RUNENV) $(RUNCMD) ./tchtest write -mt -tl -td -rc 50 casket 50000 5000 5 5
+       $(RUNENV) $(RUNCMD) ./tchtest read -mt -nb -rc 50 casket
+       $(RUNENV) $(RUNCMD) ./tchtest remove -mt -rc 50 casket
+       $(RUNENV) $(RUNCMD) ./tchtest write -as -tb -rc 50 casket 50000 50000 5 5
+       $(RUNENV) $(RUNCMD) ./tchtest read -nl -rc 50 casket
+       $(RUNENV) $(RUNCMD) ./tchtest remove -rc 50 casket
+       $(RUNENV) $(RUNCMD) ./tchtest rcat -pn 500 casket 50000 5000 5 5
+       $(RUNENV) $(RUNCMD) ./tchtest rcat -tl -td -pn 5000 casket 50000 500 5 15
+       $(RUNENV) $(RUNCMD) ./tchtest rcat -nl -pn 500 -rl casket 5000 500 5 5
+       $(RUNENV) $(RUNCMD) ./tchtest rcat -tb -pn 500 casket 5000 500 5 5
+       $(RUNENV) $(RUNCMD) ./tchmgr list -pv casket > check.out
+       $(RUNENV) $(RUNCMD) ./tchmgr list -pv -fm 1 -px casket > check.out
+       $(RUNENV) $(RUNCMD) ./tchtest misc casket 5000
+       $(RUNENV) $(RUNCMD) ./tchtest misc -tl -td casket 5000
+       $(RUNENV) $(RUNCMD) ./tchtest misc -mt -tb casket 500
+       $(RUNENV) $(RUNCMD) ./tchtest wicked casket 50000
+       $(RUNENV) $(RUNCMD) ./tchtest wicked -tl -td casket 50000
+       $(RUNENV) $(RUNCMD) ./tchtest wicked -mt -tb casket 5000
+       $(RUNENV) $(RUNCMD) ./tchmttest write -tl casket 5 5000 500 5
+       $(RUNENV) $(RUNCMD) ./tchmttest read casket 5
+       $(RUNENV) $(RUNCMD) ./tchmttest read -rnd casket 5
+       $(RUNENV) $(RUNCMD) ./tchmttest remove casket 5
+       $(RUNENV) $(RUNCMD) ./tchmttest wicked -nc casket 5 5000
+       $(RUNENV) $(RUNCMD) ./tchmttest wicked -tl -td casket 5 5000
+       $(RUNENV) $(RUNCMD) ./tchmttest wicked -tb casket 5 5000
+       $(RUNENV) $(RUNCMD) ./tchmttest typical casket 5 50000 5000
+       $(RUNENV) $(RUNCMD) ./tchmttest typical -rr 1000 casket 5 50000 5000
+       $(RUNENV) $(RUNCMD) ./tchmttest typical -tl -rc 50000 -nc casket 5 50000 5000
+       $(RUNENV) $(RUNCMD) ./tchmgr create casket 3 1 1
+       $(RUNENV) $(RUNCMD) ./tchmgr inform casket
+       $(RUNENV) $(RUNCMD) ./tchmgr put casket one first
+       $(RUNENV) $(RUNCMD) ./tchmgr put casket two second
+       $(RUNENV) $(RUNCMD) ./tchmgr put -dk casket three third
+       $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third
+       $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third
+       $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third
+       $(RUNENV) $(RUNCMD) ./tchmgr put casket four fourth
+       $(RUNENV) $(RUNCMD) ./tchmgr put -dk casket five fifth
+       $(RUNENV) $(RUNCMD) ./tchmgr out casket one
+       $(RUNENV) $(RUNCMD) ./tchmgr out casket two
+       $(RUNENV) $(RUNCMD) ./tchmgr get casket three > check.out
+       $(RUNENV) $(RUNCMD) ./tchmgr get casket four > check.out
+       $(RUNENV) $(RUNCMD) ./tchmgr get casket five > check.out
+       $(RUNENV) $(RUNCMD) ./tchmgr list -pv casket > check.out
+       $(RUNENV) $(RUNCMD) ./tchmgr optimize casket
+       $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third
+       $(RUNENV) $(RUNCMD) ./tchmgr get casket three > check.out
+       $(RUNENV) $(RUNCMD) ./tchmgr get casket four > check.out
+       $(RUNENV) $(RUNCMD) ./tchmgr get casket five > check.out
+       $(RUNENV) $(RUNCMD) ./tchmgr list -pv casket > check.out
+       $(RUNENV) $(RUNCMD) ./tcbtest write casket 50000 5 5 5000 5 5
+       $(RUNENV) $(RUNCMD) ./tcbtest read casket
+       $(RUNENV) $(RUNCMD) ./tcbtest remove casket
+       $(RUNENV) $(RUNCMD) ./tcbmgr list -rb 00001000 00002000 casket > check.out
+       $(RUNENV) $(RUNCMD) ./tcbmgr list -fm 000001 casket > check.out
+       $(RUNENV) $(RUNCMD) ./tcbtest write -mt -tl -td -ls 1024 casket 50000 5000 5000 5000 5 5
+       $(RUNENV) $(RUNCMD) ./tcbtest read -mt -nb casket
+       $(RUNENV) $(RUNCMD) ./tcbtest remove -mt casket
+       $(RUNENV) $(RUNCMD) ./tcbtest write -tb casket 50000 5 5 50000 5 5
+       $(RUNENV) $(RUNCMD) ./tcbtest read -nl casket
+       $(RUNENV) $(RUNCMD) ./tcbtest remove casket
+       $(RUNENV) $(RUNCMD) ./tcbtest rcat -lc 5 -nc 5 -pn 500 casket 50000 5 5 5000 5 5
+       $(RUNENV) $(RUNCMD) ./tcbtest rcat -tl -td -pn 5000 casket 50000 5 5 500 5 15
+       $(RUNENV) $(RUNCMD) ./tcbtest rcat -nl -pn 5000 -rl casket 15000 5 5 500 5 5
+       $(RUNENV) $(RUNCMD) ./tcbtest rcat -ca 1000 -tb -pn 5000 casket 15000 5 5 500 5 5
+       $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+       $(RUNENV) $(RUNCMD) ./tcbtest queue casket 15000 5 5
+       $(RUNENV) $(RUNCMD) ./tcbtest misc casket 5000
+       $(RUNENV) $(RUNCMD) ./tcbtest misc -tl -td casket 5000
+       $(RUNENV) $(RUNCMD) ./tcbtest misc -mt -tb casket 500
+       $(RUNENV) $(RUNCMD) ./tcbtest wicked casket 50000
+       $(RUNENV) $(RUNCMD) ./tcbtest wicked -tl -td casket 50000
+       $(RUNENV) $(RUNCMD) ./tcbtest wicked -mt -tb casket 5000
+       $(RUNENV) $(RUNCMD) ./tcbtest write -cd -lc 5 -nc 5 casket 5000 5 5 5 5 5
+       $(RUNENV) $(RUNCMD) ./tcbtest read -cd -lc 5 -nc 5 casket
+       $(RUNENV) $(RUNCMD) ./tcbtest remove -cd -lc 5 -nc 5 casket
+       $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+       $(RUNENV) $(RUNCMD) ./tcbtest write -ci -td -lc 5 -nc 5 casket 5000 5 5 5 5 5
+       $(RUNENV) $(RUNCMD) ./tcbtest read -ci -lc 5 -nc 5 casket
+       $(RUNENV) $(RUNCMD) ./tcbtest remove -ci -lc 5 -nc 5 casket
+       $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+       $(RUNENV) $(RUNCMD) ./tcbtest write -cj -tb -lc 5 -nc 5 casket 5000 5 5 5 5 5
+       $(RUNENV) $(RUNCMD) ./tcbtest read -cj -lc 5 -nc 5 casket
+       $(RUNENV) $(RUNCMD) ./tcbtest remove -cj -lc 5 -nc 5 casket
+       $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+       $(RUNENV) $(RUNCMD) ./tcbmttest write -tl casket 5 5000 5 5 500 5
+       $(RUNENV) $(RUNCMD) ./tcbmttest read casket 5
+       $(RUNENV) $(RUNCMD) ./tcbmttest read -rnd casket 5
+       $(RUNENV) $(RUNCMD) ./tcbmttest remove casket 5
+       $(RUNENV) $(RUNCMD) ./tcbmttest wicked -nc casket 5 5000
+       $(RUNENV) $(RUNCMD) ./tcbmttest wicked -tl -td casket 5 5000
+       $(RUNENV) $(RUNCMD) ./tchmttest wicked -tb casket 5 5000
+       $(RUNENV) $(RUNCMD) ./tcbmttest typical casket 5 50000 5 5
+       $(RUNENV) $(RUNCMD) ./tcbmttest typical -rr 1000 casket 5 50000 5 5
+       $(RUNENV) $(RUNCMD) ./tcbmttest typical -tl -nc casket 5 50000 5 5
+       $(RUNENV) $(RUNCMD) ./tcbmgr create casket 4 4 3 1 1
+       $(RUNENV) $(RUNCMD) ./tcbmgr inform casket
+       $(RUNENV) $(RUNCMD) ./tcbmgr put casket one first
+       $(RUNENV) $(RUNCMD) ./tcbmgr put casket two second
+       $(RUNENV) $(RUNCMD) ./tcbmgr put -dk casket three third
+       $(RUNENV) $(RUNCMD) ./tcbmgr put -dc casket three third
+       $(RUNENV) $(RUNCMD) ./tcbmgr put -dc casket three third
+       $(RUNENV) $(RUNCMD) ./tcbmgr put -dd casket three third
+       $(RUNENV) $(RUNCMD) ./tcbmgr put -dd casket three third
+       $(RUNENV) $(RUNCMD) ./tcbmgr put casket four fourth
+       $(RUNENV) $(RUNCMD) ./tcbmgr put -dk casket five fifth
+       $(RUNENV) $(RUNCMD) ./tcbmgr out casket one
+       $(RUNENV) $(RUNCMD) ./tcbmgr out casket two
+       $(RUNENV) $(RUNCMD) ./tcbmgr get casket three > check.out
+       $(RUNENV) $(RUNCMD) ./tcbmgr get casket four > check.out
+       $(RUNENV) $(RUNCMD) ./tcbmgr get casket five > check.out
+       $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+       $(RUNENV) $(RUNCMD) ./tcbmgr list -j three -pv casket > check.out
+       $(RUNENV) $(RUNCMD) ./tcbmgr optimize casket
+       $(RUNENV) $(RUNCMD) ./tcbmgr put -dc casket three third
+       $(RUNENV) $(RUNCMD) ./tcbmgr get casket three > check.out
+       $(RUNENV) $(RUNCMD) ./tcbmgr get casket four > check.out
+       $(RUNENV) $(RUNCMD) ./tcbmgr get casket five > check.out
+       $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+       $(RUNENV) $(RUNCMD) ./tcatest write 'casket.tch#mode=wct#bnum=5000' 50000
+       $(RUNENV) $(RUNCMD) ./tcatest read 'casket.tch#mode=r'
+       $(RUNENV) $(RUNCMD) ./tcatest remove 'casket.tch#mode=w'
+       $(RUNENV) $(RUNCMD) ./tcatest misc 'casket.tch#mode=wct#bnum=500#opts=ld' 5000
+       $(RUNENV) $(RUNCMD) ./tcatest wicked 'casket.tch#mode=wct' 5000
+       $(RUNENV) $(RUNCMD) ./tcatest write 'casket.tcb#mode=wct#lmemb=5#nmemb=5' 50000
+       $(RUNENV) $(RUNCMD) ./tcatest read 'casket.tcb#mode=r'
+       $(RUNENV) $(RUNCMD) ./tcatest remove 'casket.tcb#mode=w'
+       $(RUNENV) $(RUNCMD) ./tcatest misc 'casket.tcb#mode=wct#lmemb=5#nmemb=5#opts=ld' 5000
+       $(RUNENV) $(RUNCMD) ./tcatest wicked 'casket.tcb#mode=wct' 5000
+       $(RUNENV) $(RUNCMD) ./tcatest write '*#bnum=5000#cap=100' 50000
+       $(RUNENV) $(RUNCMD) ./tcatest misc '*' 5000
+       $(RUNENV) $(RUNCMD) ./tcatest wicked '*' 5000
+       $(RUNENV) $(RUNCMD) ./tcamgr create 'casket.tch#mode=wct#bnum=3'
+       $(RUNENV) $(RUNCMD) ./tcamgr inform 'casket.tch'
+       $(RUNENV) $(RUNCMD) ./tcamgr put casket.tch one first
+       $(RUNENV) $(RUNCMD) ./tcamgr put casket.tch two second
+       $(RUNENV) $(RUNCMD) ./tcamgr put -dk casket.tch three third
+       $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third
+       $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third
+       $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third
+       $(RUNENV) $(RUNCMD) ./tcamgr put casket.tch four fourth
+       $(RUNENV) $(RUNCMD) ./tcamgr put -dk casket.tch five fifth
+       $(RUNENV) $(RUNCMD) ./tcamgr out casket.tch one
+       $(RUNENV) $(RUNCMD) ./tcamgr out casket.tch two
+       $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch three > check.out
+       $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch four > check.out
+       $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch five > check.out
+       $(RUNENV) $(RUNCMD) ./tcamgr list -pv -fm f casket.tch > check.out
+       $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third
+       $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch three > check.out
+       $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch four > check.out
+       $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch five > check.out
+       $(RUNENV) $(RUNCMD) ./tcamgr list -pv casket.tch > check.out
+       rm -rf casket*
+       @printf '\n'
+       @printf '#================================================================\n'
+       @printf '# Checking completed.\n'
+       @printf '#================================================================\n'
+
+
+check-valgrind :
+       make RUNCMD="valgrind --tool=memcheck --log-fd=1" check | tee leak.log
+       grep ERROR leak.log
+       grep 'at exit' leak.log
+
+
+check-large :
+       rm -rf casket*
+       $(RUNENV) $(RUNCMD) ./tchmttest typical casket 3 1000000 5000000 13 8
+       $(RUNENV) $(RUNCMD) ./tchmttest typical -nc casket 3 1000000 5000000 13 8
+       $(RUNENV) $(RUNCMD) ./tcbmttest typical casket 3 500000 8 8 500000 16 8
+       $(RUNENV) $(RUNCMD) ./tcbmttest typical -nc casket 3 500000 8 8 500000 16 8
+       rm -rf casket*
+
+
+check-thread :
+       rm -rf casket*
+       $(RUNENV) $(RUNCMD) ./tcumttest typical 5 500000 500000
+       $(RUNENV) $(RUNCMD) ./tcumttest typical -nc -rr 1000 5 500000 500000
+       $(RUNENV) $(RUNCMD) ./tchmttest typical casket 5 500000 500000
+       $(RUNENV) $(RUNCMD) ./tchmttest typical -rc 500000 -nc -rr 1000 casket 5 500000 500000
+       $(RUNENV) $(RUNCMD) ./tcbmttest typical casket 5 100000 5 5
+       $(RUNENV) $(RUNCMD) ./tcbmttest typical -nc -rr 1000 casket 5 100000 5 5
+       rm -rf casket*
+
+
+check-forever :
+       while true ; \
+         do \
+           make check || break ; \
+           make check || break ; \
+           make check || break ; \
+           make check || break ; \
+           make check || break ; \
+           make check-thread || break ; \
+         done
+
+
+dicthdb :
+       rm -f casket
+       sed -e 's/.*$$/&\t&/' /usr/share/dict/words | \
+         ./tchmgr importtsv -sc casket
+
+
+dictbdb :
+       rm -f casket
+       sed -e 's/.*$$/&\t&/' /usr/share/dict/words | \
+         ./tcbmgr importtsv -sc casket
+
+
+.PHONY : all clean install check
+
+
+
+#================================================================
+# Building binaries
+#================================================================
+
+
+libtokyocabinet.a : $(LIBOBJFILES)
+       $(AR) $(ARFLAGS) $@ $(LIBOBJFILES)
+
+
+libtokyocabinet.so.$(LIBVER).$(LIBREV).0 : $(LIBOBJFILES)
+       $(CC) -shared -Wl,-soname,libtokyocabinet.so.$(LIBVER) -o $@ $(LIBOBJFILES) \
+         $(LDFLAGS) $(LIBS)
+
+
+libtokyocabinet.so.$(LIBVER) : libtokyocabinet.so.$(LIBVER).$(LIBREV).0
+       ln -f -s libtokyocabinet.so.$(LIBVER).$(LIBREV).0 $@
+
+
+libtokyocabinet.so : libtokyocabinet.so.$(LIBVER).$(LIBREV).0
+       ln -f -s libtokyocabinet.so.$(LIBVER).$(LIBREV).0 $@
+
+
+libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib : $(LIBOBJFILES)
+       $(CC) -dynamiclib -o $@ \
+         -install_name $(LIBDIR)/libtokyocabinet.$(LIBVER).dylib \
+         -current_version $(LIBVER).$(LIBREV).0 -compatibility_version $(LIBVER) \
+         $(LIBOBJFILES) $(LDFLAGS) $(LIBS)
+
+
+libtokyocabinet.$(LIBVER).dylib : libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib
+       ln -f -s libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib $@
+
+
+libtokyocabinet.dylib : libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib
+       ln -f -s libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib $@
+
+
+tcutest : tcutest.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcumttest : tcumttest.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcucodec : tcucodec.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tchtest : tchtest.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tchmttest : tchmttest.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tchmgr : tchmgr.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcbtest : tcbtest.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcbmttest : tcbmttest.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcbmgr : tcbmgr.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcatest : tcatest.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcamgr : tcamgr.o $(LIBRARYFILES)
+       $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+myconf.o : myconf.h
+
+tcutil.o : tcutil.h myconf.h
+
+tchdb.o : tchdb.h tcutil.h myconf.h
+
+tcbdb.o : tcbdb.h tchdb.h tcutil.h myconf.h
+
+tcutest.o tcucodec.o : tcutil.h myconf.h
+
+tchtest.o tchmttest.o tchmgr.o : tcutil.h tchdb.h myconf.h
+
+tcbtest.o tcbmttest.o tcbmgr.o : tcutil.h tchdb.h tcbdb.h myconf.h
+
+tcatest.o : tcutil.h tchdb.h tcbdb.h tcadb.h myconf.h
+
+
+
+# END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/README b/bacula/src/lib/tokyocabinet/README
new file mode 100644 (file)
index 0000000..ed9ad32
--- /dev/null
@@ -0,0 +1,38 @@
+================================================================
+ Tokyo Cabinet: a modern implementation of DBM
+ Copyright (C) 2006-2008 Mikio Hirabayashi
+================================================================
+
+
+Please read the following documents with a WWW browser.
+How to install Tokyo Cabinet is explained in the specification.
+
+  README         - this file
+  COPYING        - license
+  ChangeLog      - history of enhancement
+  THANKS         - list of contributors
+  doc/index.html - index of documents
+
+
+Contents of the directory tree is below.
+
+  ./             - sources of Tokyo Cabinet
+  ./doc/         - manuals and specifications
+  ./man/         - manuals for nroff
+  ./example/     - sample code of tutorial
+  ./lab/         - for test and experiment
+  ./bros/        - for comparison with other database managers
+
+
+Tokyo Cabinet is released under the terms of the GNU Lesser General
+Public License.  See the file `COPYING' for details.
+
+Tokyo Cabinet was written by Mikio Hirabayashi.  You can contact the
+author by e-mail to `mikio@users.sourceforge.net'.
+
+
+Thanks.
+
+
+
+== END OF FILE ==
diff --git a/bacula/src/lib/tokyocabinet/THANKS b/bacula/src/lib/tokyocabinet/THANKS
new file mode 100644 (file)
index 0000000..1a80e0b
--- /dev/null
@@ -0,0 +1,12 @@
+================================================================
+ Thanks to all of the following for their valuable suggestions
+ or contributions.
+================================================================
+
+
+mixi Inc. and and its developers
+  - allowing me to develop and publish Tokyo Cabinet as business work
+
+
+
+== END OF FILE ==
diff --git a/bacula/src/lib/tokyocabinet/TODO b/bacula/src/lib/tokyocabinet/TODO
new file mode 100644 (file)
index 0000000..1dc71db
--- /dev/null
@@ -0,0 +1,4 @@
+
+tcmdbfwmkeysを貝偏
+
+adbも実装
\ No newline at end of file
diff --git a/bacula/src/lib/tokyocabinet/configure b/bacula/src/lib/tokyocabinet/configure
new file mode 100755 (executable)
index 0000000..269fee8
--- /dev/null
@@ -0,0 +1,3868 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59 for tokyocabinet 1.2.5.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)$' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+         /^X\/\(\/\/\)$/{ s//\1/; q; }
+         /^X\/\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+        case $as_dir in
+        /*)
+          if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+            $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+            $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+            CONFIG_SHELL=$as_dir/$as_base
+            export CONFIG_SHELL
+            exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+          fi;;
+        esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='     ' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS="  $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME='tokyocabinet'
+PACKAGE_TARNAME='tokyocabinet'
+PACKAGE_VERSION='1.2.5'
+PACKAGE_STRING='tokyocabinet 1.2.5'
+PACKAGE_BUGREPORT=''
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT MYLIBVER MYLIBREV MYFORMATVER MYHEADERFILES MYLIBRARYFILES MYLIBOBJFILES MYCOMMANDFILES MYMAN1FILES MYMAN3FILES MYDOCUMENTFILES MYPCFILES MYCFLAGS MYCPPFLAGS MYLDFLAGS MYRUNPATH MYLDLIBPATHENV MYPOSTCMD LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+             localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$0" : 'X\(//\)[^/]' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures tokyocabinet 1.2.5 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                         [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                         [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of tokyocabinet 1.2.5:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-debug          build for debugging
+  --enable-devel          build for development
+  --enable-profile        build for profiling
+  --enable-off64          build with 64-bit file offset on 32-bit system
+  --enable-fastest        build for fastest run
+  --enable-swab           build for swapping byte-orders
+  --enable-uyield         build for detecting race conditions
+  --disable-zlib          build without ZLIB compression
+  --disable-pthread       build without POSIX thread support
+  --disable-shared        avoid to build shared libraries
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+          test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+tokyocabinet configure 1.2.5
+generated by GNU Autoconf 2.59
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by tokyocabinet $as_me 1.2.5, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"   "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+       "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+       "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=$`echo $ac_var`
+       echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+              sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+       { echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+       { echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+       ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"   "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Package information
+MYLIBVER=3
+MYLIBREV=6
+MYFORMATVER="1.0"
+
+# Targets
+MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcadb.h"
+MYLIBRARYFILES="libtokyocabinet.a"
+MYLIBOBJFILES="tcutil.o tchdb.o tcbdb.o tcadb.o myconf.o"
+MYCOMMANDFILES="tcutest tcumttest tcucodec tchtest tchmttest tchmgr"
+MYCOMMANDFILES="$MYCOMMANDFILES tcbtest tcbmttest tcbmgr tcatest tcamgr"
+MYMAN1FILES="tcutest.1 tcumttest.1 tcucodec.1 tchtest.1 tchmttest.1 tchmgr.1"
+MYMAN1FILES="$MYMAN1FILES tcbtest.1 tcbmttest.1 tcbmgr.1 tcatest.1 tcamgr.1"
+MYMAN3FILES="tokyocabinet.3 tcutil.3 tcxstr.3 tclist.3 tcmap.3 tcmdb.3 tcmpool.3"
+MYMAN3FILES="$MYMAN3FILES tchdb.3 tcbdb.3 tcadb.3"
+MYDOCUMENTFILES="COPYING ChangeLog THANKS doc"
+MYPCFILES="tokyocabinet.pc"
+
+# Building flags
+MYCFLAGS="-std=c99 -Wall -fPIC -fsigned-char -O2"
+MYCPPFLAGS="-I. -I\$(INCLUDEDIR) -L$HOME/include -L/usr/local/include -DNDEBUG -D_GNU_SOURCE=1"
+MYLDFLAGS="-L. -L\$(LIBDIR) -L$HOME/lib -L/usr/local/lib"
+MYRUNPATH="\$(LIBDIR)"
+MYLDLIBPATHENV="LD_LIBRARY_PATH"
+MYPOSTCMD="true"
+
+# Building paths
+pathtmp="$PATH"
+PATH="$HOME/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"
+PATH="$PATH:/usr/ccs/bin:/usr/ucb:/usr/xpg4/bin:/usr/xpg6/bin:$pathtmp"
+LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LIBRARY_PATH"
+LD_LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LD_LIBRARY_PATH"
+CPATH="$HOME/include:/usr/local/include:$CPATH"
+PKG_CONFIG_PATH="$HOME/lib/pkgconfig:/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH"
+export PATH LIBRARY_PATH LD_LIBRARY_PATH CPATH PKG_CONFIG_PATH
+
+
+
+#================================================================
+# Options
+#================================================================
+
+
+# Internal variables
+enables=""
+
+# Debug mode
+# Check whether --enable-debug or --disable-debug was given.
+if test "${enable_debug+set}" = set; then
+  enableval="$enable_debug"
+
+fi;
+if test "$enable_debug" = "yes"
+then
+  MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g"
+  MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG"
+  MYLDFLAGS="$MYLDFLAGS -static"
+  enables="$enables (debug)"
+fi
+
+# Developping mode
+# Check whether --enable-devel or --disable-devel was given.
+if test "${enable_devel+set}" = set; then
+  enableval="$enable_devel"
+
+fi;
+if test "$enable_devel" = "yes"
+then
+  MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O2 -pipe"
+  MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG"
+  enables="$enables (devel)"
+fi
+
+# Profiling mode
+# Check whether --enable-profile or --disable-profile was given.
+if test "${enable_profile+set}" = set; then
+  enableval="$enable_profile"
+
+fi;
+if test "$enable_profile" = "yes"
+then
+  MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -pg -O2 -pipe"
+  enables="$enables (profile)"
+fi
+
+# 64-bit offset mode
+# Check whether --enable-off64 or --disable-off64 was given.
+if test "${enable_off64+set}" = set; then
+  enableval="$enable_off64"
+
+fi;
+if test "$enable_off64" = "yes"
+then
+  MYCPPFLAGS="$MYCPPFLAGS -D_FILE_OFFSET_BITS=64"
+  enables="$enables (off64)"
+fi
+
+# Fastest mode
+# Check whether --enable-fastest or --disable-fastest was given.
+if test "${enable_fastest+set}" = set; then
+  enableval="$enable_fastest"
+
+fi;
+if test "$enable_fastest" = "yes"
+then
+  MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -O3"
+  MYCFLAGS="$MYCFLAGS -fomit-frame-pointer -fforce-addr -minline-all-stringops"
+  MYCPPFLAGS="$MYCPPFLAGS -D_MYFASTEST"
+  enables="$enables (fastest)"
+fi
+
+# Swapping byte-orders mode
+# Check whether --enable-swab or --disable-swab was given.
+if test "${enable_swab+set}" = set; then
+  enableval="$enable_swab"
+
+fi;
+if test "$enable_swab" = "yes"
+then
+  MYCPPFLAGS="$MYCPPFLAGS -D_MYSWAB"
+  enables="$enables (swab)"
+fi
+
+# Micro yield mode
+# Check whether --enable-uyield or --disable-uyield was given.
+if test "${enable_uyield+set}" = set; then
+  enableval="$enable_uyield"
+
+fi;
+if test "$enable_uyield" = "yes"
+then
+  MYCPPFLAGS="$MYCPPFLAGS -D_MYMICROYIELD"
+  enables="$enables (uyield)"
+fi
+
+# Disable ZLIB compression
+# Check whether --enable-zlib or --disable-zlib was given.
+if test "${enable_zlib+set}" = set; then
+  enableval="$enable_zlib"
+
+fi;
+if test "$enable_zlib" = "no"
+then
+  MYCPPFLAGS="$MYCPPFLAGS -D_MYNOZLIB"
+  enables="$enables (no-zlib)"
+fi
+
+# Disable POSIX thread
+# Check whether --enable-pthread or --disable-pthread was given.
+if test "${enable_pthread+set}" = set; then
+  enableval="$enable_pthread"
+
+fi;
+if test "$enable_pthread" = "no"
+then
+  MYCPPFLAGS="$MYCPPFLAGS -D_MYNOPTHREAD"
+  enables="$enables (no-pthread)"
+fi
+
+# Disable shared object
+# Check whether --enable-shared or --disable-shared was given.
+if test "${enable_shared+set}" = set; then
+  enableval="$enable_shared"
+
+fi;
+if test "$enable_shared" = "no"
+then
+  enables="$enables (no-shared)"
+fi
+
+# Messages
+printf '#================================================================\n'
+printf '# Configuring Tokyo Cabinet version %s%s.\n' "$PACKAGE_VERSION" "$enables"
+printf '#================================================================\n'
+
+
+
+#================================================================
+# Checking Commands and Libraries
+#================================================================
+
+# C compiler
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CC" && break
+done
+
+  CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+       ;;
+    conftest.$ac_ext )
+       # This is the source file.
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+       ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       # FIXME: I believe we export ac_cv_exeext for Libtool,
+       # but it would be cool to find out if it's true.  Does anybody
+       # maintain Libtool? --akim.
+       export ac_cv_exeext
+       break;;
+    * )
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         export ac_cv_exeext
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std1 is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std1.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX                  -qlanglvl=ansi
+# Ultrix and OSF/1     -std1
+# HP-UX 10.20 and later        -Ae
+# HP-UX older versions -Aa -D_HPUX_SOURCE
+# SVR4                 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno)
+    echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+  *)
+    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+    CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C.  Since we use `exit',
+# in C++ we need to declare it.  In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+  choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Byte order
+
+echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
+echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6
+if test "${ac_cv_c_bigendian+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # See if sys/param.h defines the BYTE_ORDER macro.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_c_bigendian=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+# It does not; compile a test program.
+if test "$cross_compiling" = yes; then
+  # try to guess the endianness by grepping values into an object file
+  ac_cv_c_bigendian=unknown
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; }
+short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; }
+int
+main ()
+{
+ _ascii (); _ebcdic ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then
+  ac_cv_c_bigendian=yes
+fi
+if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+  if test "$ac_cv_c_bigendian" = unknown; then
+    ac_cv_c_bigendian=no
+  else
+    # finding both strings is unlikely to happen, but who knows?
+    ac_cv_c_bigendian=unknown
+  fi
+fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+int
+main ()
+{
+  /* Are we little or big endian?  From Harbison&Steele.  */
+  union
+  {
+    long l;
+    char c[sizeof (long)];
+  } u;
+  u.l = 1;
+  exit (u.c[sizeof (long) - 1] == 1);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=no
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_bigendian=yes
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5
+echo "${ECHO_T}$ac_cv_c_bigendian" >&6
+case $ac_cv_c_bigendian in
+  yes)
+    MYCPPFLAGS="$MYCPPFLAGS -D_MYBIGEND" ;;
+  no)
+     ;;
+  *)
+    { { echo "$as_me:$LINENO: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&5
+echo "$as_me: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
+   { (exit 1); exit 1; }; } ;;
+esac
+
+
+# Underlying libraries
+
+echo "$as_me:$LINENO: checking for main in -lc" >&5
+echo $ECHO_N "checking for main in -lc... $ECHO_C" >&6
+if test "${ac_cv_lib_c_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_c_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_c_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_c_main" >&5
+echo "${ECHO_T}$ac_cv_lib_c_main" >&6
+if test $ac_cv_lib_c_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBC 1
+_ACEOF
+
+  LIBS="-lc $LIBS"
+
+fi
+
+
+echo "$as_me:$LINENO: checking for main in -lm" >&5
+echo $ECHO_N "checking for main in -lm... $ECHO_C" >&6
+if test "${ac_cv_lib_m_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_m_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_m_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_m_main" >&5
+echo "${ECHO_T}$ac_cv_lib_m_main" >&6
+if test $ac_cv_lib_m_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBM 1
+_ACEOF
+
+  LIBS="-lm $LIBS"
+
+fi
+
+if test "$enable_pthread" != "no"
+then
+
+echo "$as_me:$LINENO: checking for main in -lpthread" >&5
+echo $ECHO_N "checking for main in -lpthread... $ECHO_C" >&6
+if test "${ac_cv_lib_pthread_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_pthread_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_pthread_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_main" >&5
+echo "${ECHO_T}$ac_cv_lib_pthread_main" >&6
+if test $ac_cv_lib_pthread_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBPTHREAD 1
+_ACEOF
+
+  LIBS="-lpthread $LIBS"
+
+fi
+
+fi
+if test "$enable_zlib" != "no"
+then
+
+echo "$as_me:$LINENO: checking for main in -lz" >&5
+echo $ECHO_N "checking for main in -lz... $ECHO_C" >&6
+if test "${ac_cv_lib_z_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lz  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_z_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_z_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_z_main" >&5
+echo "${ECHO_T}$ac_cv_lib_z_main" >&6
+if test $ac_cv_lib_z_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBZ 1
+_ACEOF
+
+  LIBS="-lz $LIBS"
+
+fi
+
+fi
+
+# Shared libraries
+if test "$enable_shared" != "no" && test "$enable_profile" != "yes"
+then
+  if uname | grep Darwin >/dev/null
+  then
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.$MYLIBVER.$MYLIBREV.0.dylib"
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.$MYLIBVER.dylib"
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.dylib"
+    MYLDLIBPATHENV="DYLD_LIBRARY_PATH"
+  else
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER.$MYLIBREV.0"
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER"
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so"
+  fi
+fi
+
+
+
+#================================================================
+# Generic Settings
+#================================================================
+
+# Export variables
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Targets
+                    ac_config_files="$ac_config_files Makefile tokyocabinet.pc"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+       "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[    ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[      ]*$//;
+}'
+fi
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then we branch to the quote section.  Otherwise,
+# look for a macro that doesn't take arguments.
+cat >confdef2opt.sed <<\_ACEOF
+t clear
+: clear
+s,^[    ]*#[    ]*define[       ][      ]*\([^  (][^    (]*([^)]*)\)[   ]*\(.*\),-D\1=\2,g
+t quote
+s,^[    ]*#[    ]*define[       ][      ]*\([^  ][^     ]*\)[   ]*\(.*\),-D\1=\2,g
+t quote
+d
+: quote
+s,[     `~#$^&*(){}\\|;'"<>?],\\&,g
+s,\[,\\&,g
+s,\],\\&,g
+s,\$,$$,g
+p
+_ACEOF
+# We use echo to avoid assuming a particular line-breaking character.
+# The extra dot is to prevent the shell from consuming trailing
+# line-breaks from the sub-command output.  A line-break within
+# single-quotes doesn't work because, if this script is created in a
+# platform that uses two characters for line-breaks (e.g., DOS), tr
+# would break.
+ac_LF_and_DOT=`echo; echo .`
+DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'`
+rm -f confdef2opt.sed
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+        sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)$' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+         /^X\/\(\/\/\)$/{ s//\1/; q; }
+         /^X\/\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+        case $as_dir in
+        /*)
+          if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+            $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+            $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+            CONFIG_SHELL=$as_dir/$as_base
+            export CONFIG_SHELL
+            exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+          fi;;
+        esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='     ' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS="  $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by tokyocabinet $as_me 1.2.5, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+                  instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+tokyocabinet config.status 1.2.5
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  "tokyocabinet.pc" ) CONFIG_FILES="$CONFIG_FILES tokyocabinet.pc" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@MYLIBVER@,$MYLIBVER,;t t
+s,@MYLIBREV@,$MYLIBREV,;t t
+s,@MYFORMATVER@,$MYFORMATVER,;t t
+s,@MYHEADERFILES@,$MYHEADERFILES,;t t
+s,@MYLIBRARYFILES@,$MYLIBRARYFILES,;t t
+s,@MYLIBOBJFILES@,$MYLIBOBJFILES,;t t
+s,@MYCOMMANDFILES@,$MYCOMMANDFILES,;t t
+s,@MYMAN1FILES@,$MYMAN1FILES,;t t
+s,@MYMAN3FILES@,$MYMAN3FILES,;t t
+s,@MYDOCUMENTFILES@,$MYDOCUMENTFILES,;t t
+s,@MYPCFILES@,$MYPCFILES,;t t
+s,@MYCFLAGS@,$MYCFLAGS,;t t
+s,@MYCPPFLAGS@,$MYCPPFLAGS,;t t
+s,@MYLDFLAGS@,$MYLDFLAGS,;t t
+s,@MYRUNPATH@,$MYRUNPATH,;t t
+s,@MYLDLIBPATHENV@,$MYLDLIBPATHENV,;t t
+s,@MYPOSTCMD@,$MYPOSTCMD,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+       ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+       ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+       cat >$tmp/stdin
+       ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+       ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+       ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+                                    sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+        # Absolute (can't be DOS-style, as IFS=:)
+        test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+        echo "$f";;
+      *) # Relative
+        if test -f "$f"; then
+          # Build tree
+          echo "$f"
+        elif test -f "$srcdir/$f"; then
+          # Source tree
+          echo "$srcdir/$f"
+        else
+          # /dev/null tree
+          { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+        fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
+# Messages
+printf '#================================================================\n'
+printf '# Ready to make.\n'
+printf '#================================================================\n'
+
+
+
+# END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/configure.in b/bacula/src/lib/tokyocabinet/configure.in
new file mode 100644 (file)
index 0000000..c84d1ac
--- /dev/null
@@ -0,0 +1,232 @@
+# Source of configuration for Tokyo Cabinet
+
+
+
+#================================================================
+# Generic Settings
+#================================================================
+
+# Package name
+AC_INIT(tokyocabinet, 1.2.5)
+
+# Package information
+MYLIBVER=3
+MYLIBREV=6
+MYFORMATVER="1.0"
+
+# Targets
+MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcadb.h"
+MYLIBRARYFILES="libtokyocabinet.a"
+MYLIBOBJFILES="tcutil.o tchdb.o tcbdb.o tcadb.o myconf.o"
+MYCOMMANDFILES="tcutest tcumttest tcucodec tchtest tchmttest tchmgr"
+MYCOMMANDFILES="$MYCOMMANDFILES tcbtest tcbmttest tcbmgr tcatest tcamgr"
+MYMAN1FILES="tcutest.1 tcumttest.1 tcucodec.1 tchtest.1 tchmttest.1 tchmgr.1"
+MYMAN1FILES="$MYMAN1FILES tcbtest.1 tcbmttest.1 tcbmgr.1 tcatest.1 tcamgr.1"
+MYMAN3FILES="tokyocabinet.3 tcutil.3 tcxstr.3 tclist.3 tcmap.3 tcmdb.3 tcmpool.3"
+MYMAN3FILES="$MYMAN3FILES tchdb.3 tcbdb.3 tcadb.3"
+MYDOCUMENTFILES="COPYING ChangeLog THANKS doc"
+MYPCFILES="tokyocabinet.pc"
+
+# Building flags
+MYCFLAGS="-std=c99 -Wall -fPIC -fsigned-char -O2"
+MYCPPFLAGS="-I. -I\$(INCLUDEDIR) -L$HOME/include -L/usr/local/include -DNDEBUG -D_GNU_SOURCE=1"
+MYLDFLAGS="-L. -L\$(LIBDIR) -L$HOME/lib -L/usr/local/lib"
+MYRUNPATH="\$(LIBDIR)"
+MYLDLIBPATHENV="LD_LIBRARY_PATH"
+MYPOSTCMD="true"
+
+# Building paths
+pathtmp="$PATH"
+PATH="$HOME/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"
+PATH="$PATH:/usr/ccs/bin:/usr/ucb:/usr/xpg4/bin:/usr/xpg6/bin:$pathtmp"
+LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LIBRARY_PATH"
+LD_LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LD_LIBRARY_PATH"
+CPATH="$HOME/include:/usr/local/include:$CPATH"
+PKG_CONFIG_PATH="$HOME/lib/pkgconfig:/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH"
+export PATH LIBRARY_PATH LD_LIBRARY_PATH CPATH PKG_CONFIG_PATH
+
+
+
+#================================================================
+# Options
+#================================================================
+
+
+# Internal variables
+enables=""
+
+# Debug mode
+AC_ARG_ENABLE(debug,
+  AC_HELP_STRING([--enable-debug], [build for debugging]))
+if test "$enable_debug" = "yes"
+then
+  MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g"
+  MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG"
+  MYLDFLAGS="$MYLDFLAGS -static"
+  enables="$enables (debug)"
+fi
+
+# Developping mode
+AC_ARG_ENABLE(devel,
+  AC_HELP_STRING([--enable-devel], [build for development]))
+if test "$enable_devel" = "yes"
+then
+  MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O2 -pipe"
+  MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG"
+  enables="$enables (devel)"
+fi
+
+# Profiling mode
+AC_ARG_ENABLE(profile,
+  AC_HELP_STRING([--enable-profile], [build for profiling]))
+if test "$enable_profile" = "yes"
+then
+  MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -pg -O2 -pipe"
+  enables="$enables (profile)"
+fi
+
+# 64-bit offset mode
+AC_ARG_ENABLE(off64,
+  AC_HELP_STRING([--enable-off64], [build with 64-bit file offset on 32-bit system]))
+if test "$enable_off64" = "yes"
+then
+  MYCPPFLAGS="$MYCPPFLAGS -D_FILE_OFFSET_BITS=64"
+  enables="$enables (off64)"
+fi
+
+# Fastest mode
+AC_ARG_ENABLE(fastest,
+  AC_HELP_STRING([--enable-fastest], [build for fastest run]))
+if test "$enable_fastest" = "yes"
+then
+  MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -O3"
+  MYCFLAGS="$MYCFLAGS -fomit-frame-pointer -fforce-addr -minline-all-stringops"
+  MYCPPFLAGS="$MYCPPFLAGS -D_MYFASTEST"
+  enables="$enables (fastest)"
+fi
+
+# Swapping byte-orders mode
+AC_ARG_ENABLE(swab,
+  AC_HELP_STRING([--enable-swab], [build for swapping byte-orders]))
+if test "$enable_swab" = "yes"
+then
+  MYCPPFLAGS="$MYCPPFLAGS -D_MYSWAB"
+  enables="$enables (swab)"
+fi
+
+# Micro yield mode
+AC_ARG_ENABLE(uyield,
+  AC_HELP_STRING([--enable-uyield], [build for detecting race conditions]))
+if test "$enable_uyield" = "yes"
+then
+  MYCPPFLAGS="$MYCPPFLAGS -D_MYMICROYIELD"
+  enables="$enables (uyield)"
+fi
+
+# Disable ZLIB compression
+AC_ARG_ENABLE(zlib,
+  AC_HELP_STRING([--disable-zlib], [build without ZLIB compression]))
+if test "$enable_zlib" = "no"
+then
+  MYCPPFLAGS="$MYCPPFLAGS -D_MYNOZLIB"
+  enables="$enables (no-zlib)"
+fi
+
+# Disable POSIX thread
+AC_ARG_ENABLE(pthread,
+  AC_HELP_STRING([--disable-pthread], [build without POSIX thread support]))
+if test "$enable_pthread" = "no"
+then
+  MYCPPFLAGS="$MYCPPFLAGS -D_MYNOPTHREAD"
+  enables="$enables (no-pthread)"
+fi
+
+# Disable shared object
+AC_ARG_ENABLE(shared,
+  AC_HELP_STRING([--disable-shared], [avoid to build shared libraries]))
+if test "$enable_shared" = "no"
+then
+  enables="$enables (no-shared)"
+fi
+
+# Messages
+printf '#================================================================\n'
+printf '# Configuring Tokyo Cabinet version %s%s.\n' "$PACKAGE_VERSION" "$enables"
+printf '#================================================================\n'
+
+
+
+#================================================================
+# Checking Commands and Libraries
+#================================================================
+
+# C compiler
+AC_PROG_CC
+
+# Byte order
+AC_C_BIGENDIAN(MYCPPFLAGS="$MYCPPFLAGS -D_MYBIGEND")
+
+# Underlying libraries
+AC_CHECK_LIB(c, main)
+AC_CHECK_LIB(m, main)
+if test "$enable_pthread" != "no"
+then
+AC_CHECK_LIB(pthread, main)
+fi
+if test "$enable_zlib" != "no"
+then
+  AC_CHECK_LIB(z, main)
+fi
+
+# Shared libraries
+if test "$enable_shared" != "no" && test "$enable_profile" != "yes"
+then
+  if uname | grep Darwin >/dev/null
+  then
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.$MYLIBVER.$MYLIBREV.0.dylib"
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.$MYLIBVER.dylib"
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.dylib"
+    MYLDLIBPATHENV="DYLD_LIBRARY_PATH"
+  else
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER.$MYLIBREV.0"
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER"
+    MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so"
+  fi
+fi
+
+
+
+#================================================================
+# Generic Settings
+#================================================================
+
+# Export variables
+AC_SUBST(MYLIBVER)
+AC_SUBST(MYLIBREV)
+AC_SUBST(MYFORMATVER)
+AC_SUBST(MYHEADERFILES)
+AC_SUBST(MYLIBRARYFILES)
+AC_SUBST(MYLIBOBJFILES)
+AC_SUBST(MYCOMMANDFILES)
+AC_SUBST(MYMAN1FILES)
+AC_SUBST(MYMAN3FILES)
+AC_SUBST(MYDOCUMENTFILES)
+AC_SUBST(MYPCFILES)
+AC_SUBST(MYCFLAGS)
+AC_SUBST(MYCPPFLAGS)
+AC_SUBST(MYLDFLAGS)
+AC_SUBST(MYRUNPATH)
+AC_SUBST(MYLDLIBPATHENV)
+AC_SUBST(MYPOSTCMD)
+
+# Targets
+AC_OUTPUT(Makefile tokyocabinet.pc)
+
+# Messages
+printf '#================================================================\n'
+printf '# Ready to make.\n'
+printf '#================================================================\n'
+
+
+
+# END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/myconf.c b/bacula/src/lib/tokyocabinet/myconf.c
new file mode 100644 (file)
index 0000000..afcc086
--- /dev/null
@@ -0,0 +1,225 @@
+/*************************************************************************************************
+ * System-dependent configurations of Tokyo Cabinet
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "myconf.h"
+
+
+
+/*************************************************************************************************
+ * common settings
+ *************************************************************************************************/
+
+
+int _tc_dummyfunc(void){
+  return 0;
+}
+
+
+int _tc_dummyfuncv(int a, ...){
+  return 0;
+}
+
+
+
+/*************************************************************************************************
+ * for ZLIB
+ *************************************************************************************************/
+
+
+#if TCUSEZLIB
+
+
+#include <zlib.h>
+
+#define ZLIBBUFSIZ     8192
+
+
+static char *_tc_deflate_impl(const char *ptr, int size, int *sp, int mode);
+static char *_tc_inflate_impl(const char *ptr, int size, int *sp, int mode);
+static unsigned int _tc_getcrc_impl(const char *ptr, int size);
+
+
+char *(*_tc_deflate)(const char *, int, int *, int) = _tc_deflate_impl;
+char *(*_tc_inflate)(const char *, int, int *, int) = _tc_inflate_impl;
+unsigned int (*_tc_getcrc)(const char *, int) = _tc_getcrc_impl;
+
+
+static char *_tc_deflate_impl(const char *ptr, int size, int *sp, int mode){
+  assert(ptr && size >= 0 && sp);
+  z_stream zs;
+  char *buf, *swap;
+  unsigned char obuf[ZLIBBUFSIZ];
+  int rv, asiz, bsiz, osiz;
+  zs.zalloc = Z_NULL;
+  zs.zfree = Z_NULL;
+  zs.opaque = Z_NULL;
+  switch(mode){
+  case _TCZMRAW:
+    if(deflateInit2(&zs, 5, Z_DEFLATED, -15, 7, Z_DEFAULT_STRATEGY) != Z_OK)
+      return NULL;
+    break;
+  case _TCZMGZIP:
+    if(deflateInit2(&zs, 6, Z_DEFLATED, 15 + 16, 9, Z_DEFAULT_STRATEGY) != Z_OK)
+      return NULL;
+    break;
+  default:
+    if(deflateInit2(&zs, 6, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY) != Z_OK)
+      return NULL;
+    break;
+  }
+  asiz = size + 16;
+  if(asiz < ZLIBBUFSIZ) asiz = ZLIBBUFSIZ;
+  if(!(buf = malloc(asiz))){
+    deflateEnd(&zs);
+    return NULL;
+  }
+  bsiz = 0;
+  zs.next_in = (unsigned char *)ptr;
+  zs.avail_in = size;
+  zs.next_out = obuf;
+  zs.avail_out = ZLIBBUFSIZ;
+  while((rv = deflate(&zs, Z_FINISH)) == Z_OK){
+    osiz = ZLIBBUFSIZ - zs.avail_out;
+    if(bsiz + osiz > asiz){
+      asiz = asiz * 2 + osiz;
+      if(!(swap = realloc(buf, asiz))){
+        free(buf);
+        deflateEnd(&zs);
+        return NULL;
+      }
+      buf = swap;
+    }
+    memcpy(buf + bsiz, obuf, osiz);
+    bsiz += osiz;
+    zs.next_out = obuf;
+    zs.avail_out = ZLIBBUFSIZ;
+  }
+  if(rv != Z_STREAM_END){
+    free(buf);
+    deflateEnd(&zs);
+    return NULL;
+  }
+  osiz = ZLIBBUFSIZ - zs.avail_out;
+  if(bsiz + osiz + 1 > asiz){
+    asiz = asiz * 2 + osiz;
+    if(!(swap = realloc(buf, asiz))){
+      free(buf);
+      deflateEnd(&zs);
+      return NULL;
+    }
+    buf = swap;
+  }
+  memcpy(buf + bsiz, obuf, osiz);
+  bsiz += osiz;
+  buf[bsiz] = '\0';
+  if(mode == _TCZMRAW) bsiz++;
+  *sp = bsiz;
+  deflateEnd(&zs);
+  return buf;
+}
+
+
+static char *_tc_inflate_impl(const char *ptr, int size, int *sp, int mode){
+  assert(ptr && size >= 0 && sp);
+  z_stream zs;
+  char *buf, *swap;
+  unsigned char obuf[ZLIBBUFSIZ];
+  int rv, asiz, bsiz, osiz;
+  zs.zalloc = Z_NULL;
+  zs.zfree = Z_NULL;
+  zs.opaque = Z_NULL;
+  switch(mode){
+  case _TCZMRAW:
+    if(inflateInit2(&zs, -15) != Z_OK) return NULL;
+    break;
+  case _TCZMGZIP:
+    if(inflateInit2(&zs, 15 + 16) != Z_OK) return NULL;
+    break;
+  default:
+    if(inflateInit2(&zs, 15) != Z_OK) return NULL;
+    break;
+  }
+  asiz = size * 2 + 16;
+  if(asiz < ZLIBBUFSIZ) asiz = ZLIBBUFSIZ;
+  if(!(buf = malloc(asiz))){
+    inflateEnd(&zs);
+    return NULL;
+  }
+  bsiz = 0;
+  zs.next_in = (unsigned char *)ptr;
+  zs.avail_in = size;
+  zs.next_out = obuf;
+  zs.avail_out = ZLIBBUFSIZ;
+  while((rv = inflate(&zs, Z_NO_FLUSH)) == Z_OK){
+    osiz = ZLIBBUFSIZ - zs.avail_out;
+    if(bsiz + osiz >= asiz){
+      asiz = asiz * 2 + osiz;
+      if(!(swap = realloc(buf, asiz))){
+        free(buf);
+        inflateEnd(&zs);
+        return NULL;
+      }
+      buf = swap;
+    }
+    memcpy(buf + bsiz, obuf, osiz);
+    bsiz += osiz;
+    zs.next_out = obuf;
+    zs.avail_out = ZLIBBUFSIZ;
+  }
+  if(rv != Z_STREAM_END){
+    free(buf);
+    inflateEnd(&zs);
+    return NULL;
+  }
+  osiz = ZLIBBUFSIZ - zs.avail_out;
+  if(bsiz + osiz >= asiz){
+    asiz = asiz * 2 + osiz;
+    if(!(swap = realloc(buf, asiz))){
+      free(buf);
+      inflateEnd(&zs);
+      return NULL;
+    }
+    buf = swap;
+  }
+  memcpy(buf + bsiz, obuf, osiz);
+  bsiz += osiz;
+  buf[bsiz] = '\0';
+  *sp = bsiz;
+  inflateEnd(&zs);
+  return buf;
+}
+
+
+static unsigned int _tc_getcrc_impl(const char *ptr, int size){
+  assert(ptr && size >= 0);
+  int crc = crc32(0, Z_NULL, 0);
+  return crc32(crc, (unsigned char *)ptr, size);
+}
+
+
+#else
+
+
+char *(*_tc_deflate)(const char *, int, int *, int) = NULL;
+char *(*_tc_inflate)(const char *, int, int *, int) = NULL;
+unsigned int (*_tc_getcrc)(const char *, int) = NULL;
+
+
+#endif
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/myconf.h b/bacula/src/lib/tokyocabinet/myconf.h
new file mode 100644 (file)
index 0000000..3e44152
--- /dev/null
@@ -0,0 +1,438 @@
+/*************************************************************************************************
+ * System-dependent configurations of Tokyo Cabinet
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _MYCONF_H                        // duplication check
+#define _MYCONF_H
+
+
+
+/*************************************************************************************************
+ * system discrimination
+ *************************************************************************************************/
+
+
+#if defined(__linux__)
+
+#define _SYS_LINUX_
+#define ESTSYSNAME  "Linux"
+
+#elif defined(__FreeBSD__)
+
+#define _SYS_FREEBSD_
+#define ESTSYSNAME  "FreeBSD"
+
+#elif defined(__NetBSD__)
+
+#define _SYS_NETBSD_
+#define ESTSYSNAME  "NetBSD"
+
+#elif defined(__OpenBSD__)
+
+#define _SYS_OPENBSD_
+#define ESTSYSNAME  "OpenBSD"
+
+#elif defined(__sun__)
+
+#define _SYS_SUNOS_
+#define ESTSYSNAME  "SunOS"
+
+#elif defined(__hpux)
+
+#define _SYS_HPUX_
+#define ESTSYSNAME  "HP-UX"
+
+#elif defined(__osf)
+
+#define _SYS_TRU64_
+#define ESTSYSNAME  "Tru64"
+
+#elif defined(_AIX)
+
+#define _SYS_AIX_
+#define ESTSYSNAME  "AIX"
+
+#elif defined(__APPLE__) && defined(__MACH__)
+
+#define _SYS_MACOSX_
+#define ESTSYSNAME  "Mac OS X"
+
+#elif defined(_MSC_VER)
+
+#define _SYS_MSVC_
+#define ESTSYSNAME  "Windows (VC++)"
+
+#elif defined(_WIN32)
+
+#define _SYS_MINGW_
+#define ESTSYSNAME  "Windows (MinGW)"
+
+#elif defined(__CYGWIN__)
+
+#define _SYS_CYGWIN_
+#define ESTSYSNAME  "Windows (Cygwin)"
+
+#else
+
+#define _SYS_GENERIC_
+#define ESTSYSNAME  "Generic"
+
+#endif
+
+
+
+/*************************************************************************************************
+ * common settings
+ *************************************************************************************************/
+
+
+#if defined(NDEBUG)
+#define TCDODEBUG(TC_expr) \
+  do { \
+  } while(false)
+#else
+#define TCDODEBUG(TC_expr) \
+  do { \
+    TC_expr; \
+  } while(false)
+#endif
+
+#define TCSWAB16(TC_num) \
+  ( \
+   ((TC_num & 0x00ffU) << 8) | \
+   ((TC_num & 0xff00U) >> 8) \
+  )
+
+#define TCSWAB32(TC_num) \
+  ( \
+   ((TC_num & 0x000000ffUL) << 24) | \
+   ((TC_num & 0x0000ff00UL) << 8) | \
+   ((TC_num & 0x00ff0000UL) >> 8) | \
+   ((TC_num & 0xff000000UL) >> 24) \
+  )
+
+#define TCSWAB64(TC_num) \
+  ( \
+   ((TC_num & 0x00000000000000ffULL) << 56) | \
+   ((TC_num & 0x000000000000ff00ULL) << 40) | \
+   ((TC_num & 0x0000000000ff0000ULL) << 24) | \
+   ((TC_num & 0x00000000ff000000ULL) << 8) | \
+   ((TC_num & 0x000000ff00000000ULL) >> 8) | \
+   ((TC_num & 0x0000ff0000000000ULL) >> 24) | \
+   ((TC_num & 0x00ff000000000000ULL) >> 40) | \
+   ((TC_num & 0xff00000000000000ULL) >> 56) \
+  )
+
+#if defined(_MYBIGEND) || defined(_MYSWAB)
+#define TCBIGEND       1
+#define TCHTOIS(TC_num)   TCSWAB16(TC_num)
+#define TCHTOIL(TC_num)   TCSWAB32(TC_num)
+#define TCHTOILL(TC_num)  TCSWAB64(TC_num)
+#define TCITOHS(TC_num)   TCSWAB16(TC_num)
+#define TCITOHL(TC_num)   TCSWAB32(TC_num)
+#define TCITOHLL(TC_num)  TCSWAB64(TC_num)
+#else
+#define TCBIGEND       0
+#define TCHTOIS(TC_num)   (TC_num)
+#define TCHTOIL(TC_num)   (TC_num)
+#define TCHTOILL(TC_num)  (TC_num)
+#define TCITOHS(TC_num)   (TC_num)
+#define TCITOHL(TC_num)   (TC_num)
+#define TCITOHLL(TC_num)  (TC_num)
+#endif
+
+#if defined(_MYNOZLIB)
+#define TCUSEZLIB      0
+#else
+#define TCUSEZLIB      1
+#endif
+
+#if defined(_MYNOPTHREAD)
+#define TCUSEPTHREAD   0
+#else
+#define TCUSEPTHREAD   1
+#endif
+
+#if defined(_MYMICROYIELD)
+#define TCMICROYIELD   1
+#else
+#define TCMICROYIELD   0
+#endif
+
+#define sizeof(a)      ((int)sizeof(a))
+
+int _tc_dummyfunc(void);
+int _tc_dummyfuncv(int a, ...);
+
+
+
+/*************************************************************************************************
+ * general headers
+ *************************************************************************************************/
+
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <regex.h>
+#include <glob.h>
+
+#if TCUSEPTHREAD
+#include <pthread.h>
+#endif
+
+
+
+/*************************************************************************************************
+ * notation of filesystems
+ *************************************************************************************************/
+
+
+#define MYPATHCHR       '/'
+#define MYPATHSTR       "/"
+#define MYEXTCHR        '.'
+#define MYEXTSTR        "."
+#define MYCDIRSTR       "."
+#define MYPDIRSTR       ".."
+
+
+
+/*************************************************************************************************
+ * for ZLIB
+ *************************************************************************************************/
+
+
+enum {
+  _TCZMZLIB,
+  _TCZMRAW,
+  _TCZMGZIP
+};
+
+
+extern char *(*_tc_deflate)(const char *, int, int *, int);
+
+extern char *(*_tc_inflate)(const char *, int, int *, int);
+
+extern unsigned int (*_tc_getcrc)(const char *, int);
+
+
+
+/*************************************************************************************************
+ * for POSIX thread disability
+ *************************************************************************************************/
+
+
+#if ! TCUSEPTHREAD
+
+#define pthread_once_t                   int
+#undef PTHREAD_ONCE_INIT
+#define PTHREAD_ONCE_INIT                0
+#define pthread_once(TC_a, TC_b)         _tc_dummyfuncv((int)(TC_a), (TC_b))
+
+#define pthread_mutexattr_t              int
+#undef PTHREAD_MUTEX_RECURSIVE
+#define PTHREAD_MUTEX_RECURSIVE          0
+#define pthread_mutexattr_init(TC_a)     _tc_dummyfuncv((int)(TC_a))
+#define pthread_mutexattr_destroy(TC_a)  _tc_dummyfuncv((int)(TC_a))
+#define pthread_mutexattr_settype(TC_a, TC_b)  _tc_dummyfuncv((int)(TC_a), (TC_b))
+
+#define pthread_mutex_t                  int
+#undef PTHREAD_MUTEX_INITIALIZER
+#define PTHREAD_MUTEX_INITIALIZER        0
+#define pthread_mutex_init(TC_a, TC_b)   _tc_dummyfuncv((int)(TC_a), (TC_b))
+#define pthread_mutex_destroy(TC_a)      _tc_dummyfuncv((int)(TC_a))
+#define pthread_mutex_lock(TC_a)         _tc_dummyfuncv((int)(TC_a))
+#define pthread_mutex_unlock(TC_a)       _tc_dummyfuncv((int)(TC_a))
+
+#define pthread_rwlock_t                 int
+#undef PTHREAD_RWLOCK_INITIALIZER
+#define PTHREAD_RWLOCK_INITIALIZER       0
+#define pthread_rwlock_init(TC_a, TC_b)  _tc_dummyfuncv((int)(TC_a), (TC_b))
+#define pthread_rwlock_destroy(TC_a)     _tc_dummyfuncv((int)(TC_a))
+#define pthread_rwlock_rdlock(TC_a)      _tc_dummyfuncv((int)(TC_a))
+#define pthread_rwlock_wrlock(TC_a)      _tc_dummyfuncv((int)(TC_a))
+#define pthread_rwlock_unlock(TC_a)      _tc_dummyfuncv((int)(TC_a))
+
+#define pthread_key_create(TC_a, TC_b)   _tc_dummyfuncv((int)(TC_a), (TC_b))
+#define pthread_key_delete(TC_a)         _tc_dummyfuncv((int)(TC_a))
+#define pthread_setspecific(TC_a, TC_b)  _tc_dummyfuncv((int)(TC_a))
+#define pthread_getspecific(TC_a)        _tc_dummyfuncv((int)(TC_a))
+
+#define pthread_create(TC_th, TC_attr, TC_func, TC_arg) \
+  (*(TC_th) = 0, (TC_func)(TC_arg), 0)
+#define pthread_join(TC_th, TC_rv) \
+  (*(TC_rv) = NULL, 0)
+#define pthread_detach(TC_th)         0
+
+#endif
+
+#if TCUSEPTHREAD && TCMICROYIELD
+#define TCTESTYIELD() \
+  do { \
+    static uint32_t cnt = 0; \
+    if(((++cnt) & (0x20 - 1)) == 0){ \
+      pthread_yield(); \
+      if(cnt > 0x1000) cnt = (uint32_t)time(NULL) % 0x1000; \
+    } \
+  } while(false)
+#undef assert
+#define assert(TC_expr) \
+  do { \
+    if(!(TC_expr)){ \
+      fprintf(stderr, "assertion failed: %s\n", #TC_expr); \
+      abort(); \
+    } \
+    TCTESTYIELD(); \
+  } while(false)
+#else
+#define TCTESTYIELD() \
+  do { \
+  } while(false);
+#endif
+
+
+
+/*************************************************************************************************
+ * utilities for implementation
+ *************************************************************************************************/
+
+
+#define TCNUMBUFSIZ    32                // size of a buffer for a number
+
+/* set a buffer for a variable length number */
+#define TCSETVNUMBUF(TC_len, TC_buf, TC_num) \
+  do { \
+    int _TC_num = (TC_num); \
+    if(_TC_num == 0){ \
+      ((signed char *)(TC_buf))[0] = 0; \
+      (TC_len) = 1; \
+    } else { \
+      (TC_len) = 0; \
+      while(_TC_num > 0){ \
+        int _TC_rem = _TC_num & 0x7f; \
+        _TC_num >>= 7; \
+        if(_TC_num > 0){ \
+          ((signed char *)(TC_buf))[(TC_len)] = -_TC_rem - 1; \
+        } else { \
+          ((signed char *)(TC_buf))[(TC_len)] = _TC_rem; \
+        } \
+        (TC_len)++; \
+      } \
+    } \
+  } while(false)
+
+/* set a buffer for a variable length number of 64-bit */
+#define TCSETVNUMBUF64(TC_len, TC_buf, TC_num) \
+  do { \
+    long long int _TC_num = (TC_num); \
+    if(_TC_num == 0){ \
+      ((signed char *)(TC_buf))[0] = 0; \
+      (TC_len) = 1; \
+    } else { \
+      (TC_len) = 0; \
+      while(_TC_num > 0){ \
+        int _TC_rem = _TC_num & 0x7f; \
+        _TC_num >>= 7; \
+        if(_TC_num > 0){ \
+          ((signed char *)(TC_buf))[(TC_len)] = -_TC_rem - 1; \
+        } else { \
+          ((signed char *)(TC_buf))[(TC_len)] = _TC_rem; \
+        } \
+        (TC_len)++; \
+      } \
+    } \
+  } while(false)
+
+/* read a variable length buffer */
+#define TCREADVNUMBUF(TC_buf, TC_num, TC_step) \
+  do { \
+    TC_num = 0; \
+    int _TC_base = 1; \
+    int _TC_i = 0; \
+    while(true){ \
+      if(((signed char *)(TC_buf))[_TC_i] >= 0){ \
+        TC_num += ((signed char *)(TC_buf))[_TC_i] * _TC_base; \
+        break; \
+      } \
+      TC_num += _TC_base * (((signed char *)(TC_buf))[_TC_i] + 1) * -1; \
+      _TC_base <<= 7; \
+      _TC_i++; \
+    } \
+    (TC_step) = _TC_i + 1; \
+  } while(false)
+
+/* read a variable length buffer */
+#define TCREADVNUMBUF64(TC_buf, TC_num, TC_step) \
+  do { \
+    TC_num = 0; \
+    long long int _TC_base = 1; \
+    int _TC_i = 0; \
+    while(true){ \
+      if(((signed char *)(TC_buf))[_TC_i] >= 0){ \
+        TC_num += ((signed char *)(TC_buf))[_TC_i] * _TC_base; \
+        break; \
+      } \
+      TC_num += _TC_base * (((signed char *)(TC_buf))[_TC_i] + 1) * -1; \
+      _TC_base <<= 7; \
+      _TC_i++; \
+    } \
+    (TC_step) = _TC_i + 1; \
+  } while(false)
+
+/* Compare keys of two records by lexical order. */
+#define TCCMPLEXICAL(TC_rv, TC_aptr, TC_asiz, TC_bptr, TC_bsiz) \
+  do { \
+    (TC_rv) = 0; \
+    int _TC_min = (TC_asiz) < (TC_bsiz) ? (TC_asiz) : (TC_bsiz); \
+    for(int _TC_i = 0; _TC_i < _TC_min; _TC_i++){ \
+      if(((unsigned char *)(TC_aptr))[_TC_i] != ((unsigned char *)(TC_bptr))[_TC_i]){ \
+        (TC_rv) = ((unsigned char *)(TC_aptr))[_TC_i] - ((unsigned char *)(TC_bptr))[_TC_i]; \
+        break; \
+      } \
+    } \
+    if((TC_rv) == 0) (TC_rv) = (TC_asiz) - (TC_bsiz); \
+  } while(false)
+
+
+
+#endif                                   // duplication check
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcadb.c b/bacula/src/lib/tokyocabinet/tcadb.c
new file mode 100644 (file)
index 0000000..73b952e
--- /dev/null
@@ -0,0 +1,523 @@
+/*************************************************************************************************
+ * The abstract database API of Tokyo Cabinet
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "tcutil.h"
+#include "tchdb.h"
+#include "tcbdb.h"
+#include "tcadb.h"
+#include "myconf.h"
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+/* Create an abstract database object. */
+TCADB *tcadbnew(void){
+  TCADB *adb;
+  TCMALLOC(adb, sizeof(*adb));
+  adb->name = NULL;
+  adb->mdb = NULL;
+  adb->hdb = NULL;
+  adb->bdb = NULL;
+  adb->capnum = -1;
+  adb->capsiz = -1;
+  adb->capcnt = 0;
+  adb->cur = NULL;
+  return adb;
+}
+
+
+/* Delete an abstract database object. */
+void tcadbdel(TCADB *adb){
+  assert(adb);
+  if(adb->name) tcadbclose(adb);
+  free(adb);
+}
+
+
+/* Open an abstract database. */
+bool tcadbopen(TCADB *adb, const char *name){
+  assert(adb && name);
+  TCLIST *elems = tcstrsplit(name, "#");
+  char *path = tclistshift2(elems);
+  if(!path){
+    tclistdel(elems);
+    return false;
+  }
+  int64_t bnum = -1;
+  int64_t capnum = -1;
+  int64_t capsiz = -1;
+  bool owmode = true;
+  bool ocmode = true;
+  bool otmode = false;
+  bool onlmode = false;
+  bool onbmode = false;
+  int8_t apow = -1;
+  int8_t fpow = -1;
+  bool tlmode = false;
+  bool tdmode = false;
+  int32_t rcnum = -1;
+  int32_t lmemb = -1;
+  int32_t nmemb = -1;
+  int32_t lcnum = -1;
+  int32_t ncnum = -1;
+  int ln = TCLISTNUM(elems);
+  for(int i = 0; i < ln; i++){
+    const char *elem = TCLISTVALPTR(elems, i);
+    char *pv = strchr(elem, '=');
+    if(!pv) continue;
+    *(pv++) = '\0';
+    if(!tcstricmp(elem, "bnum")){
+      bnum = strtoll(pv, NULL, 10);
+    } else if(!tcstricmp(elem, "capnum")){
+      capnum = strtoll(pv, NULL, 10);
+    } else if(!tcstricmp(elem, "capsiz")){
+      capsiz = strtoll(pv, NULL, 10);
+    } else if(!tcstricmp(elem, "mode")){
+      owmode = strchr(pv, 'w') || strchr(pv, 'W');
+      ocmode = strchr(pv, 'c') || strchr(pv, 'C');
+      otmode = strchr(pv, 't') || strchr(pv, 'T');
+      onlmode = strchr(pv, 'e') || strchr(pv, 'E');
+      onbmode = strchr(pv, 'f') || strchr(pv, 'F');
+    } else if(!tcstricmp(elem, "apow")){
+      apow = strtol(pv, NULL, 10);
+    } else if(!tcstricmp(elem, "fpow")){
+      fpow = strtol(pv, NULL, 10);
+    } else if(!tcstricmp(elem, "opts")){
+      if(strchr(pv, 'l') || strchr(pv, 'L')) tlmode = true;
+      if(strchr(pv, 'd') || strchr(pv, 'D')) tdmode = true;
+    } else if(!tcstricmp(elem, "rcnum")){
+      rcnum = strtol(pv, NULL, 10);
+    } else if(!tcstricmp(elem, "lmemb")){
+      lmemb = strtol(pv, NULL, 10);
+    } else if(!tcstricmp(elem, "nmemb")){
+      nmemb = strtol(pv, NULL, 10);
+    } else if(!tcstricmp(elem, "lcnum")){
+      lcnum = strtol(pv, NULL, 10);
+    } else if(!tcstricmp(elem, "ncnum")){
+      ncnum = strtol(pv, NULL, 10);
+    }
+  }
+  tclistdel(elems);
+  if(!tcstricmp(path, "*")){
+    adb->mdb = bnum > 0 ? tcmdbnew2(bnum) : tcmdbnew();
+    adb->capnum = capnum;
+    adb->capsiz = capsiz;
+    adb->capcnt = 0;
+  } else if(tcstribwm(path, ".tch")){
+    TCHDB *hdb = tchdbnew();
+    tchdbsetmutex(hdb);
+    int opts = 0;
+    if(tlmode) opts |= HDBTLARGE;
+    if(tdmode) opts |= HDBTDEFLATE;
+    tchdbtune(hdb, bnum, apow, fpow, opts);
+    tchdbsetcache(hdb, rcnum);
+    int omode = owmode ? HDBOWRITER : HDBOREADER;
+    if(ocmode) omode |= HDBOCREAT;
+    if(otmode) omode |= HDBOTRUNC;
+    if(onlmode) omode |= HDBONOLCK;
+    if(onbmode) omode |= HDBOLCKNB;
+    if(!tchdbopen(hdb, path, omode)){
+      tchdbdel(hdb);
+      free(path);
+      return false;
+    }
+    adb->hdb = hdb;
+  } else if(tcstribwm(path, ".tcb")){
+    TCBDB *bdb = tcbdbnew();
+    tcbdbsetmutex(bdb);
+    int opts = 0;
+    if(tlmode) opts |= BDBTLARGE;
+    if(tdmode) opts |= BDBTDEFLATE;
+    tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts);
+    tcbdbsetcache(bdb, lcnum, ncnum);
+    if(capnum > 0) tcbdbsetcapnum(bdb, capnum);
+    int omode = owmode ? BDBOWRITER : BDBOREADER;
+    if(ocmode) omode |= BDBOCREAT;
+    if(otmode) omode |= BDBOTRUNC;
+    if(onlmode) omode |= BDBONOLCK;
+    if(onbmode) omode |= BDBOLCKNB;
+    if(!tcbdbopen(bdb, path, omode)){
+      tcbdbdel(bdb);
+      free(path);
+      return false;
+    }
+    adb->bdb = bdb;
+    adb->cur = tcbdbcurnew(bdb);
+  } else {
+    free(path);
+    return false;
+  }
+  free(path);
+  adb->name = tcstrdup(name);
+  return true;
+}
+
+
+/* Close an abstract database object. */
+bool tcadbclose(TCADB *adb){
+  assert(adb);
+  int err = false;
+  if(!adb->name) return false;
+  if(adb->mdb){
+    tcmdbdel(adb->mdb);
+    adb->mdb = NULL;
+  } else if(adb->hdb){
+    if(!tchdbclose(adb->hdb)) err = true;
+    tchdbdel(adb->hdb);
+    adb->hdb = NULL;
+  } else if(adb->bdb){
+    tcbdbcurdel(adb->cur);
+    if(!tcbdbclose(adb->bdb)) err = true;
+    tcbdbdel(adb->bdb);
+    adb->bdb = NULL;
+  }
+  free(adb->name);
+  adb->name = NULL;
+  return !err;
+}
+
+
+/* Store a record into an abstract database object. */
+bool tcadbput(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  bool err = false;
+  if(adb->mdb){
+    tcmdbput(adb->mdb, kbuf, ksiz, vbuf, vsiz);
+    if(adb->capnum > 0 || adb->capsiz > 0){
+      adb->capcnt++;
+      if((adb->capcnt & 0xff) == 0){
+        if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum) tcmdbcutfront(adb->mdb, 0x100);
+        if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200);
+      }
+    }
+  } else if(adb->hdb){
+    if(!tchdbput(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+  } else if(adb->bdb){
+    if(!tcbdbput(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+  } else {
+    err = true;
+  }
+  return !err;
+}
+
+
+/* Store a string record into an abstract object. */
+bool tcadbput2(TCADB *adb, const char *kstr, const char *vstr){
+  assert(adb && kstr && vstr);
+  return tcadbput(adb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a new record into an abstract database object. */
+bool tcadbputkeep(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  bool err = false;
+  if(adb->mdb){
+    if(tcmdbputkeep(adb->mdb, kbuf, ksiz, vbuf, vsiz)){
+      if(adb->capnum > 0 || adb->capsiz > 0){
+        adb->capcnt++;
+        if((adb->capcnt & 0xff) == 0){
+          if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum) tcmdbcutfront(adb->mdb, 0x100);
+          if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200);
+        }
+      }
+    } else {
+      err = true;
+    }
+  } else if(adb->hdb){
+    if(!tchdbputkeep(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+  } else if(adb->bdb){
+    if(!tcbdbputkeep(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+  } else {
+    err = true;
+  }
+  return !err;
+}
+
+
+/* Store a new string record into an abstract database object. */
+bool tcadbputkeep2(TCADB *adb, const char *kstr, const char *vstr){
+  assert(adb && kstr && vstr);
+  return tcadbputkeep(adb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Concatenate a value at the end of the existing record in an abstract database object. */
+bool tcadbputcat(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  bool err = false;
+  if(adb->mdb){
+    tcmdbputcat(adb->mdb, kbuf, ksiz, vbuf, vsiz);
+    if(adb->capnum > 0 || adb->capsiz > 0){
+      adb->capcnt++;
+      if((adb->capcnt & 0xff) == 0){
+        if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum) tcmdbcutfront(adb->mdb, 0x100);
+        if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200);
+      }
+    }
+  } else if(adb->hdb){
+    if(!tchdbputcat(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+  } else if(adb->bdb){
+    if(!tcbdbputcat(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+  } else {
+    err = true;
+  }
+  return !err;
+}
+
+
+/* Concatenate a string value at the end of the existing record in an abstract database object. */
+bool tcadbputcat2(TCADB *adb, const char *kstr, const char *vstr){
+  assert(adb && kstr && vstr);
+  return tcadbputcat(adb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Remove a record of an abstract database object. */
+bool tcadbout(TCADB *adb, const void *kbuf, int ksiz){
+  assert(adb && kbuf && ksiz >= 0);
+  bool err = false;
+  if(adb->mdb){
+    if(!tcmdbout(adb->mdb, kbuf, ksiz)) err = true;
+  } else if(adb->hdb){
+    if(!tchdbout(adb->hdb, kbuf, ksiz)) err = true;
+  } else if(adb->bdb){
+    if(!tcbdbout(adb->bdb, kbuf, ksiz)) err = true;
+  } else {
+    err = true;
+  }
+  return !err;
+}
+
+
+/* Remove a string record of an abstract database object. */
+bool tcadbout2(TCADB *adb, const char *kstr){
+  assert(adb && kstr);
+  return tcadbout(adb, kstr, strlen(kstr));
+}
+
+
+/* Retrieve a record in an abstract database object. */
+void *tcadbget(TCADB *adb, const void *kbuf, int ksiz, int *sp){
+  assert(adb && kbuf && ksiz >= 0 && sp);
+  char *rv;
+  if(adb->mdb){
+    rv = tcmdbget(adb->mdb, kbuf, ksiz, sp);
+  } else if(adb->hdb){
+    rv = tchdbget(adb->hdb, kbuf, ksiz, sp);
+  } else if(adb->bdb){
+    rv = tcbdbget(adb->bdb, kbuf, ksiz, sp);
+  } else {
+    rv = NULL;
+  }
+  return rv;
+}
+
+
+/* Retrieve a string record in an abstract database object. */
+char *tcadbget2(TCADB *adb, const char *kstr){
+  assert(adb && kstr);
+  int vsiz;
+  return tcadbget(adb, kstr, strlen(kstr), &vsiz);
+}
+
+
+/* Get the size of the value of a record in an abstract database object. */
+int tcadbvsiz(TCADB *adb, const void *kbuf, int ksiz){
+  assert(adb && kbuf && ksiz >= 0);
+  int rv;
+  if(adb->mdb){
+    rv = tcmdbvsiz(adb->mdb, kbuf, ksiz);
+  } else if(adb->hdb){
+    rv = tchdbvsiz(adb->hdb, kbuf, ksiz);
+  } else if(adb->bdb){
+    rv = tcbdbvsiz(adb->bdb, kbuf, ksiz);
+  } else {
+    rv = -1;
+  }
+  return rv;
+}
+
+
+/* Get the size of the value of a string record in an abstract database object. */
+int tcadbvsiz2(TCADB *adb, const char *kstr){
+  assert(adb && kstr);
+  return tcadbvsiz(adb, kstr, strlen(kstr));
+}
+
+
+/* Initialize the iterator of an abstract database object. */
+bool tcadbiterinit(TCADB *adb){
+  assert(adb);
+  bool err = false;
+  if(adb->mdb){
+    tcmdbiterinit(adb->mdb);
+  } else if(adb->hdb){
+    if(!tchdbiterinit(adb->hdb)) err = true;
+  } else if(adb->bdb){
+    if(!tcbdbcurfirst(adb->cur)){
+      int ecode = tcbdbecode(adb->bdb);
+      if(ecode != TCESUCCESS && ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC)
+        err = true;
+    }
+  } else {
+    err = true;
+  }
+  return !err;
+}
+
+
+/* Get the next key of the iterator of an abstract database object. */
+void *tcadbiternext(TCADB *adb, int *sp){
+  assert(adb && sp);
+  char *rv;
+  if(adb->mdb){
+    rv = tcmdbiternext(adb->mdb, sp);
+  } else if(adb->hdb){
+    rv = tchdbiternext(adb->hdb, sp);
+  } else if(adb->bdb){
+    rv = tcbdbcurkey(adb->cur, sp);
+    tcbdbcurnext(adb->cur);
+  } else {
+    rv = NULL;
+  }
+  return rv;
+}
+
+
+/* Get the next key string of the iterator of an abstract database object. */
+char *tcadbiternext2(TCADB *adb){
+  assert(adb);
+  int vsiz;
+  return tcadbiternext(adb, &vsiz);
+}
+
+
+/* Get forward matching keys in an abstract database object. */
+TCLIST *tcadbfwmkeys(TCADB *adb, const void *pbuf, int psiz, int max){
+  assert(adb && pbuf && psiz >= 0);
+  TCLIST *rv;
+  if(adb->mdb){
+    rv = tcmdbfwmkeys(adb->mdb, pbuf, psiz, max);
+  } else if(adb->hdb){
+    rv = tchdbfwmkeys(adb->hdb, pbuf, psiz, max);
+  } else if(adb->bdb){
+    rv = tcbdbfwmkeys(adb->bdb, pbuf, psiz, max);
+  } else {
+    rv = tclistnew();
+  }
+  return rv;
+}
+
+
+/* Get forward matching string keys in an abstract database object. */
+TCLIST *tcadbfwmkeys2(TCADB *adb, const char *pstr, int max){
+  assert(adb && pstr);
+  return tcadbfwmkeys(adb, pstr, strlen(pstr), max);
+}
+
+
+/* Synchronize updated contents of an abstract database object with the file and the device. */
+bool tcadbsync(TCADB *adb){
+  assert(adb);
+  bool err = false;
+  if(adb->mdb){
+    err = true;
+  } else if(adb->hdb){
+    if(!tchdbsync(adb->hdb)) err = true;
+  } else if(adb->bdb){
+    if(!tcbdbsync(adb->bdb)) err = true;
+  } else {
+    err = true;
+  }
+  return !err;
+}
+
+
+/* Remove all records of an abstract database object. */
+bool tcadbvanish(TCADB *adb){
+  assert(adb);
+  bool err = false;
+  if(adb->mdb){
+    tcmdbvanish(adb->mdb);
+  } else if(adb->hdb){
+    if(!tchdbvanish(adb->hdb)) err = true;
+  } else if(adb->bdb){
+    if(!tcbdbvanish(adb->bdb)) err = true;
+  } else {
+    err = true;
+  }
+  return !err;
+}
+
+
+/* Copy the database file of an abstract database object. */
+bool tcadbcopy(TCADB *adb, const char *path){
+  assert(adb && path);
+  bool err = false;
+  if(adb->mdb){
+    err = true;
+  } else if(adb->hdb){
+    if(!tchdbcopy(adb->hdb, path)) err = true;
+  } else if(adb->bdb){
+    if(!tcbdbcopy(adb->bdb, path)) err = true;
+  } else {
+    err = true;
+  }
+  return !err;
+}
+
+
+/* Get the number of records of an abstract database object. */
+uint64_t tcadbrnum(TCADB *adb){
+  assert(adb);
+  uint64_t rv;
+  if(adb->mdb){
+    rv = tcmdbrnum(adb->mdb);
+  } else if(adb->hdb){
+    rv = tchdbrnum(adb->hdb);
+  } else if(adb->bdb){
+    rv = tcbdbrnum(adb->bdb);
+  } else {
+    rv = 0;
+  }
+  return rv;
+}
+
+
+/* Get the size of the database of an abstract database object. */
+uint64_t tcadbsize(TCADB *adb){
+  assert(adb);
+  uint64_t rv;
+  if(adb->mdb){
+    rv = tcmdbmsiz(adb->mdb);
+  } else if(adb->hdb){
+    rv = tchdbfsiz(adb->hdb);
+  } else if(adb->bdb){
+    rv = tcbdbfsiz(adb->bdb);
+  } else {
+    rv = 0;
+  }
+  return rv;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcadb.h b/bacula/src/lib/tokyocabinet/tcadb.h
new file mode 100644 (file)
index 0000000..b33773b
--- /dev/null
@@ -0,0 +1,320 @@
+/*************************************************************************************************
+ * The abstract database API of Tokyo Cabinet
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _TCADB_H                         /* duplication check */
+#define _TCADB_H
+
+#if defined(__cplusplus)
+#define __TCADB_CLINKAGEBEGIN extern "C" {
+#define __TCADB_CLINKAGEEND }
+#else
+#define __TCADB_CLINKAGEBEGIN
+#define __TCADB_CLINKAGEEND
+#endif
+__TCADB_CLINKAGEBEGIN
+
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <tcutil.h>
+#include <tchdb.h>
+#include <tcbdb.h>
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of structure for an abstract database */
+  char *name;                            /* name of the database */
+  TCMDB *mdb;                            /* on-memory database object */
+  TCHDB *hdb;                            /* hash database object */
+  TCBDB *bdb;                            /* B+ tree database object */
+  int64_t capnum;                        /* capacity number of records */
+  int64_t capsiz;                        /* capacity size of using memory */
+  uint32_t capcnt;                       /* count for capacity check */
+  BDBCUR *cur;                           /* cursor of B+ tree */
+} TCADB;
+
+
+/* Create an abstract database object.
+   The return value is the new abstract database object. */
+TCADB *tcadbnew(void);
+
+
+/* Delete an abstract database object.
+   `adb' specifies the abstract database object. */
+void tcadbdel(TCADB *adb);
+
+
+/* Open an abstract database.
+   `adb' specifies the abstract database object.
+   `name' specifies the name of the database.  If it is "*", the database will be an on-memory
+   database.  If its suffix is ".tch", the database will be a hash database.  If its suffix is
+   ".tcb", the database will be a B+ tree database.  Otherwise, this function fails.  Tuning
+   parameters can trail the name, separated by "#".  Each parameter is composed of the name and
+   the number, separated by "=".  On-memory database supports "bnum", "capnum", and "capsiz".
+   Hash database supports "mode", "bnum", "apow", "fpow", "opts", and "rcnum".  B+ tree database
+   supports "mode", "lmemb", "nmemb", "bnum", "apow", "fpow", "opts", "lcnum", and "ncnum".
+   "capnum" specifies the capacity number of records.  "capsiz" specifies the capacity size of
+   using memory.  Records spilled the capacity are removed by the storing order.  "mode" can
+   contain "w" of writer, "r" of reader, "c" of creating, "t" of truncating, "e" of no locking,
+   and "f" of non-blocking lock.  The default mode is relevant to "wc".  "opts" can contains "l"
+   of large option, "d" of Deflate option, and "b" of TCBS option.  For example,
+   "casket.tch#bnum=1000000#opts=ld" means that the name of the database file is "casket.tch",
+   and the bucket number is 1000000, and the options are large and Deflate.
+   If successful, the return value is true, else, it is false. */
+bool tcadbopen(TCADB *adb, const char *name);
+
+
+/* Close an abstract database object.
+   `adb' specifies the abstract database object.
+   If successful, the return value is true, else, it is false.
+   Update of a database is assured to be written when the database is closed.  If a writer opens
+   a database but does not close it appropriately, the database will be broken. */
+bool tcadbclose(TCADB *adb);
+
+
+/* Store a record into an abstract database object.
+   `adb' specifies the abstract database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, it is overwritten. */
+bool tcadbput(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into an abstract object.
+   `adb' specifies the abstract database object.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, it is overwritten. */
+bool tcadbput2(TCADB *adb, const char *kstr, const char *vstr);
+
+
+/* Store a new record into an abstract database object.
+   `adb' specifies the abstract database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, this function has no effect. */
+bool tcadbputkeep(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into an abstract database object.
+   `adb' specifies the abstract database object.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, this function has no effect. */
+bool tcadbputkeep2(TCADB *adb, const char *kstr, const char *vstr);
+
+
+/* Concatenate a value at the end of the existing record in an abstract database object.
+   `adb' specifies the abstract database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If there is no corresponding record, a new record is created. */
+bool tcadbputcat(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Concatenate a string value at the end of the existing record in an abstract database object.
+   `adb' specifies the abstract database object.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If there is no corresponding record, a new record is created. */
+bool tcadbputcat2(TCADB *adb, const char *kstr, const char *vstr);
+
+
+/* Remove a record of an abstract database object.
+   `adb' specifies the abstract database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true, else, it is false. */
+bool tcadbout(TCADB *adb, const void *kbuf, int ksiz);
+
+
+/* Remove a string record of an abstract database object.
+   `adb' specifies the abstract database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is true, else, it is false. */
+bool tcadbout2(TCADB *adb, const char *kstr);
+
+
+/* Retrieve a record in an abstract database object.
+   `adb' specifies the abstract database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the corresponding
+   record.  `NULL' is returned if no record corresponds.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when
+   it is no longer in use. */
+void *tcadbget(TCADB *adb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve a string record in an abstract database object.
+   `adb' specifies the abstract database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is the string of the value of the corresponding record.
+   `NULL' is returned if no record corresponds.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcadbget2(TCADB *adb, const char *kstr);
+
+
+/* Get the size of the value of a record in an abstract database object.
+   `adb' specifies the abstract database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1. */
+int tcadbvsiz(TCADB *adb, const void *kbuf, int ksiz);
+
+
+/* Get the size of the value of a string record in an abstract database object.
+   `adb' specifies the abstract database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1. */
+int tcadbvsiz2(TCADB *adb, const char *kstr);
+
+
+/* Initialize the iterator of an abstract database object.
+   `adb' specifies the abstract database object.
+   If successful, the return value is true, else, it is false.
+   The iterator is used in order to access the key of every record stored in a database. */
+bool tcadbiterinit(TCADB *adb);
+
+
+/* Get the next key of the iterator of an abstract database object.
+   `adb' specifies the abstract database object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the next key, else, it is
+   `NULL'.  `NULL' is returned when no record is to be get out of the iterator.
+   Because an additional zero code is appended at the end of the region of the return value, the
+   return value can be treated as a character string.  Because the region of the return value is
+   allocated with the `malloc' call, it should be released with the `free' call when it is no
+   longer in use.  It is possible to access every record by iteration of calling this function.
+   It is allowed to update or remove records whose keys are fetched while the iteration.
+   However, it is not assured if updating the database is occurred while the iteration.  Besides,
+   the order of this traversal access method is arbitrary, so it is not assured that the order of
+   storing matches the one of the traversal access. */
+void *tcadbiternext(TCADB *adb, int *sp);
+
+
+/* Get the next key string of the iterator of an abstract database object.
+   `adb' specifies the abstract database object.
+   If successful, the return value is the string of the next key, else, it is `NULL'.  `NULL' is
+   returned when no record is to be get out of the iterator.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use.  It is possible to access every
+   record by iteration of calling this function.  However, it is not assured if updating the
+   database is occurred while the iteration.  Besides, the order of this traversal access method
+   is arbitrary, so it is not assured that the order of storing matches the one of the traversal
+   access. */
+char *tcadbiternext2(TCADB *adb);
+
+
+/* Get forward matching keys in an abstract database object.
+   `adb' specifies the abstract database object.
+   `pbuf' specifies the pointer to the region of the prefix.
+   `psiz' specifies the size of the region of the prefix.
+   `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
+   specified.
+   The return value is a list object of the corresponding keys.  This function does never fail
+   and return an empty list even if no key corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should be
+   deleted with the function `tclistdel' when it is no longer in use.  Note that this function
+   may be very slow because every key in the database is scanned. */
+TCLIST *tcadbfwmkeys(TCADB *adb, const void *pbuf, int psiz, int max);
+
+
+/* Get forward matching string keys in an abstract database object.
+   `adb' specifies the abstract database object.
+   `pstr' specifies the string of the prefix.
+   `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
+   specified.
+   The return value is a list object of the corresponding keys.  This function does never fail
+   and return an empty list even if no key corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should be
+   deleted with the function `tclistdel' when it is no longer in use.  Note that this function
+   may be very slow because every key in the database is scanned. */
+TCLIST *tcadbfwmkeys2(TCADB *adb, const char *pstr, int max);
+
+
+/* Synchronize updated contents of an abstract database object with the file and the device.
+   `adb' specifies the abstract database object.
+   If successful, the return value is true, else, it is false.
+   This function fails and has no effect for on-memory database. */
+bool tcadbsync(TCADB *adb);
+
+
+/* Remove all records of an abstract database object.
+   `adb' specifies the abstract database object.
+   If successful, the return value is true, else, it is false. */
+bool tcadbvanish(TCADB *adb);
+
+
+/* Copy the database file of an abstract database object.
+   `adb' specifies the abstract database object.
+   `path' specifies the path of the destination file.  If it begins with `@', the trailing
+   substring is executed as a command line.
+   If successful, the return value is true, else, it is false.  False is returned if the executed
+   command returns non-zero code.
+   The database file is assured to be kept synchronized and not modified while the copying or
+   executing operation is in progress.  So, this function is useful to create a backup file of
+   the database file.  This function fails and has no effect for on-memory database. */
+bool tcadbcopy(TCADB *adb, const char *path);
+
+
+/* Get the number of records of an abstract database object.
+   `adb' specifies the abstract database object.
+   The return value is the number of records or 0 if the object does not connect to any database
+   instance. */
+uint64_t tcadbrnum(TCADB *adb);
+
+
+/* Get the size of the database of an abstract database object.
+   `adb' specifies the abstract database object.
+   The return value is the size of the database or 0 if the object does not connect to any
+   database instance. */
+uint64_t tcadbsize(TCADB *adb);
+
+
+
+__TCADB_CLINKAGEEND
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */
diff --git a/bacula/src/lib/tokyocabinet/tcamgr.c b/bacula/src/lib/tokyocabinet/tcamgr.c
new file mode 100644 (file)
index 0000000..691099b
--- /dev/null
@@ -0,0 +1,538 @@
+/*************************************************************************************************
+ * The command line utility of the abstract database API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tcadb.h>
+#include "myconf.h"
+
+
+/* global variables */
+const char *g_progname;                  // program name
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void printerr(TCADB *adb);
+static int printdata(const char *ptr, int size, bool px);
+static char *hextoobj(const char *str, int *sp);
+static int runcreate(int argc, char **argv);
+static int runinform(int argc, char **argv);
+static int runput(int argc, char **argv);
+static int runout(int argc, char **argv);
+static int runget(int argc, char **argv);
+static int runlist(int argc, char **argv);
+static int runversion(int argc, char **argv);
+static int proccreate(const char *name);
+static int procinform(const char *name);
+static int procput(const char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                   int dmode);
+static int procout(const char *name, const char *kbuf, int ksiz);
+static int procget(const char *name, const char *kbuf, int ksiz, bool px, bool pz);
+static int proclist(const char *name, int max, bool pv, bool px, const char *fmstr);
+static int procversion(void);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "create")){
+    rv = runcreate(argc, argv);
+  } else if(!strcmp(argv[1], "inform")){
+    rv = runinform(argc, argv);
+  } else if(!strcmp(argv[1], "put")){
+    rv = runput(argc, argv);
+  } else if(!strcmp(argv[1], "out")){
+    rv = runout(argc, argv);
+  } else if(!strcmp(argv[1], "get")){
+    rv = runget(argc, argv);
+  } else if(!strcmp(argv[1], "list")){
+    rv = runlist(argc, argv);
+  } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){
+    rv = runversion(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: the command line utility of the abstract database API\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s create name\n", g_progname);
+  fprintf(stderr, "  %s inform name\n", g_progname);
+  fprintf(stderr, "  %s put [-sx] [-dk|-dc] name key value\n", g_progname);
+  fprintf(stderr, "  %s out [-sx] name key\n", g_progname);
+  fprintf(stderr, "  %s get [-sx] [-px] [-pz] name key\n", g_progname);
+  fprintf(stderr, "  %s list [-m num] [-pv] [-px] [-fm str] name\n", g_progname);
+  fprintf(stderr, "  %s version\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print error information */
+static void printerr(TCADB *adb){
+  fprintf(stderr, "%s: error\n", g_progname);
+}
+
+
+/* print record data */
+static int printdata(const char *ptr, int size, bool px){
+  int len = 0;
+  while(size-- > 0){
+    if(px){
+      if(len > 0) putchar(' ');
+      len += printf("%02X", *(unsigned char *)ptr);
+    } else {
+      putchar(*ptr);
+      len++;
+    }
+    ptr++;
+  }
+  return len;
+}
+
+
+/* create a binary object from a hexadecimal string */
+static char *hextoobj(const char *str, int *sp){
+  int len = strlen(str);
+  char *buf = tcmalloc(len + 1);
+  int j = 0;
+  for(int i = 0; i < len; i += 2){
+    while(strchr(" \n\r\t\f\v", str[i])){
+      i++;
+    }
+    char mbuf[3];
+    if((mbuf[0] = str[i]) == '\0') break;
+    if((mbuf[1] = str[i+1]) == '\0') break;
+    mbuf[2] = '\0';
+    buf[j++] = (char)strtol(mbuf, NULL, 16);
+  }
+  buf[j] = '\0';
+  *sp = j;
+  return buf;
+}
+
+
+/* parse arguments of create command */
+static int runcreate(int argc, char **argv){
+  char *name = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  int rv = proccreate(name);
+  return rv;
+}
+
+
+/* parse arguments of inform command */
+static int runinform(int argc, char **argv){
+  char *name = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  name = tcsprintf("%s#mode=r", name);
+  int rv = procinform(name);
+  free(name);
+  return rv;
+}
+
+
+/* parse arguments of put command */
+static int runput(int argc, char **argv){
+  char *name = NULL;
+  char *key = NULL;
+  char *value = NULL;
+  int dmode = 0;
+  bool sx = false;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-dk")){
+        dmode = -1;
+      } else if(!strcmp(argv[i], "-dc")){
+        dmode = 1;
+      } else if(!strcmp(argv[i], "-sx")){
+        sx = true;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else if(!value){
+      value = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key || !value) usage();
+  int ksiz, vsiz;
+  char *kbuf, *vbuf;
+  if(sx){
+    kbuf = hextoobj(key, &ksiz);
+    vbuf = hextoobj(value, &vsiz);
+  } else {
+    ksiz = strlen(key);
+    kbuf = tcmemdup(key, ksiz);
+    vsiz = strlen(value);
+    vbuf = tcmemdup(value, vsiz);
+  }
+  int rv = procput(name, kbuf, ksiz, vbuf, vsiz, dmode);
+  free(vbuf);
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of out command */
+static int runout(int argc, char **argv){
+  char *name = NULL;
+  char *key = NULL;
+  bool sx = false;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-sx")){
+        sx = true;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key) usage();
+  int ksiz;
+  char *kbuf;
+  if(sx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    ksiz = strlen(key);
+    kbuf = tcmemdup(key, ksiz);
+  }
+  int rv = procout(name, kbuf, ksiz);
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of get command */
+static int runget(int argc, char **argv){
+  char *name = NULL;
+  char *key = NULL;
+  bool sx = false;
+  bool px = false;
+  bool pz = false;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-sx")){
+        sx = true;
+      } else if(!strcmp(argv[i], "-px")){
+        px = true;
+      } else if(!strcmp(argv[i], "-pz")){
+        pz = true;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key) usage();
+  int ksiz;
+  char *kbuf;
+  if(sx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    ksiz = strlen(key);
+    kbuf = tcmemdup(key, ksiz);
+  }
+  name = tcsprintf("%s#mode=r", name);
+  int rv = procget(name, kbuf, ksiz, px, pz);
+  free(name);
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+static int runlist(int argc, char **argv){
+  char *name = NULL;
+  int max = -1;
+  bool pv = false;
+  bool px = false;
+  char *fmstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-m")){
+        if(++i >= argc) usage();
+        max = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-pv")){
+        pv = true;
+      } else if(!strcmp(argv[i], "-px")){
+        px = true;
+      } else if(!strcmp(argv[i], "-fm")){
+        if(++i >= argc) usage();
+        fmstr = argv[i];
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  name = tcsprintf("%s#mode=r", name);
+  int rv = proclist(name, max, pv, px, fmstr);
+  free(name);
+  return rv;
+}
+
+
+/* parse arguments of version command */
+static int runversion(int argc, char **argv){
+  int rv = procversion();
+  return rv;
+}
+
+
+/* perform create command */
+static int proccreate(const char *name){
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    printerr(adb);
+    tcadbdel(adb);
+    return 1;
+  }
+  bool err = false;
+  if(!tcadbclose(adb)){
+    printerr(adb);
+    err = true;
+  }
+  tcadbdel(adb);
+  return err ? 1 : 0;
+}
+
+
+/* perform inform command */
+static int procinform(const char *name){
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    printerr(adb);
+    tcadbdel(adb);
+    return 1;
+  }
+  bool err = false;
+  printf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+  printf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+  if(!tcadbclose(adb)){
+    printerr(adb);
+    err = true;
+  }
+  tcadbdel(adb);
+  return err ? 1 : 0;
+}
+
+
+/* perform put command */
+static int procput(const char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                   int dmode){
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    printerr(adb);
+    tcadbdel(adb);
+    return 1;
+  }
+  bool err = false;
+  switch(dmode){
+  case -1:
+    if(!tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(adb);
+      err = true;
+    }
+    break;
+  case 1:
+    if(!tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(adb);
+      err = true;
+    }
+    break;
+  default:
+    if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(adb);
+      err = true;
+    }
+    break;
+  }
+  if(!tcadbclose(adb)){
+    if(!err) printerr(adb);
+    err = true;
+  }
+  tcadbdel(adb);
+  return err ? 1 : 0;
+}
+
+
+/* perform out command */
+static int procout(const char *name, const char *kbuf, int ksiz){
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    printerr(adb);
+    tcadbdel(adb);
+    return 1;
+  }
+  bool err = false;
+  if(!tcadbout(adb, kbuf, ksiz)){
+    printerr(adb);
+    err = true;
+  }
+  if(!tcadbclose(adb)){
+    if(!err) printerr(adb);
+    err = true;
+  }
+  tcadbdel(adb);
+  return err ? 1 : 0;
+}
+
+
+/* perform get command */
+static int procget(const char *name, const char *kbuf, int ksiz, bool px, bool pz){
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    printerr(adb);
+    tcadbdel(adb);
+    return 1;
+  }
+  bool err = false;
+  int vsiz;
+  char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+  if(vbuf){
+    printdata(vbuf, vsiz, px);
+    if(!pz) putchar('\n');
+    free(vbuf);
+  } else {
+    printerr(adb);
+    err = true;
+  }
+  if(!tcadbclose(adb)){
+    if(!err) printerr(adb);
+    err = true;
+  }
+  tcadbdel(adb);
+  return err ? 1 : 0;
+}
+
+
+/* perform list command */
+static int proclist(const char *name, int max, bool pv, bool px, const char *fmstr){
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    printerr(adb);
+    tcadbdel(adb);
+    return 1;
+  }
+  bool err = false;
+  if(fmstr){
+    TCLIST *keys = tcadbfwmkeys2(adb, fmstr, max);
+    for(int i = 0; i < tclistnum(keys); i++){
+      int ksiz;
+      const char *kbuf = tclistval(keys, i, &ksiz);
+      printdata(kbuf, ksiz, px);
+      if(pv){
+        int vsiz;
+        char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+        if(vbuf){
+          putchar('\t');
+          printdata(vbuf, vsiz, px);
+          free(vbuf);
+        }
+      }
+      putchar('\n');
+    }
+    tclistdel(keys);
+  } else {
+    if(!tcadbiterinit(adb)){
+      printerr(adb);
+      err = true;
+    }
+    int ksiz;
+    char *kbuf;
+    int cnt = 0;
+    while((kbuf = tcadbiternext(adb, &ksiz)) != NULL){
+      printdata(kbuf, ksiz, px);
+      if(pv){
+        int vsiz;
+        char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+        if(vbuf){
+          putchar('\t');
+          printdata(vbuf, vsiz, px);
+          free(vbuf);
+        }
+      }
+      putchar('\n');
+      free(kbuf);
+      if(max >= 0 && ++cnt >= max) break;
+    }
+  }
+  if(!tcadbclose(adb)){
+    if(!err) printerr(adb);
+    err = true;
+  }
+  tcadbdel(adb);
+  return err ? 1 : 0;
+}
+
+
+/* perform version command */
+static int procversion(void){
+  printf("Tokyo Cabinet version %s (%d:%s)\n", tcversion, _TC_LIBVER, _TC_FORMATVER);
+  printf("Copyright (C) 2006-2008 Mikio Hirabayashi\n");
+  return 0;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcatest.c b/bacula/src/lib/tokyocabinet/tcatest.c
new file mode 100644 (file)
index 0000000..44a2567
--- /dev/null
@@ -0,0 +1,797 @@
+/*************************************************************************************************
+ * The test cases of the abstract database API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tcadb.h>
+#include "myconf.h"
+
+#define RECBUFSIZ      32                // buffer for records
+
+
+/* global variables */
+const char *g_progname;                  // program name
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(TCADB *adb, const char *func);
+static int myrand(int range);
+static int runwrite(int argc, char **argv);
+static int runread(int argc, char **argv);
+static int runremove(int argc, char **argv);
+static int runrcat(int argc, char **argv);
+static int runmisc(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int procwrite(const char *name, int rnum);
+static int procread(const char *name);
+static int procremove(const char *name);
+static int procrcat(const char *name, int rnum);
+static int procmisc(const char *name, int rnum);
+static int procwicked(const char *name, int rnum);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "write")){
+    rv = runwrite(argc, argv);
+  } else if(!strcmp(argv[1], "read")){
+    rv = runread(argc, argv);
+  } else if(!strcmp(argv[1], "remove")){
+    rv = runremove(argc, argv);
+  } else if(!strcmp(argv[1], "rcat")){
+    rv = runrcat(argc, argv);
+  } else if(!strcmp(argv[1], "misc")){
+    rv = runmisc(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: test cases of the abstract database API of Tokyo Cabinet\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write name rnum\n", g_progname);
+  fprintf(stderr, "  %s read name\n", g_progname);
+  fprintf(stderr, "  %s remove name\n", g_progname);
+  fprintf(stderr, "  %s rcat name rnum\n", g_progname);
+  fprintf(stderr, "  %s misc name rnum\n", g_progname);
+  fprintf(stderr, "  %s wicked name rnum\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+  va_list ap;
+  va_start(ap, format);
+  vprintf(format, ap);
+  fflush(stdout);
+  va_end(ap);
+}
+
+
+/* print error message of abstract database */
+static void eprint(TCADB *adb, const char *func){
+  fprintf(stderr, "%s: %s: error\n", g_progname, func);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+  return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* parse arguments of write command */
+static int runwrite(int argc, char **argv){
+  char *name = NULL;
+  char *rstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procwrite(name, rnum);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+static int runread(int argc, char **argv){
+  char *name = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  int rv = procread(name);
+  return rv;
+}
+
+
+/* parse arguments of remove command */
+static int runremove(int argc, char **argv){
+  char *name = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  int rv = procremove(name);
+  return rv;
+}
+
+
+/* parse arguments of rcat command */
+static int runrcat(int argc, char **argv){
+  char *name = NULL;
+  char *rstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procrcat(name, rnum);
+  return rv;
+}
+
+
+/* parse arguments of misc command */
+static int runmisc(int argc, char **argv){
+  char *name = NULL;
+  char *rstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procmisc(name, rnum);
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+  char *name = NULL;
+  char *rstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procwicked(name, rnum);
+  return rv;
+}
+
+
+/* perform write command */
+static int procwrite(const char *name, int rnum){
+  iprintf("<Writing Test>\n  name=%s  rnum=%d\n\n", name, rnum);
+  bool err = false;
+  double stime = tctime();
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    eprint(adb, "tcadbopen");
+    err = true;
+  }
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    if(!tcadbput(adb, buf, len, buf, len)){
+      eprint(adb, "tcadbput");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+  iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+  if(!tcadbclose(adb)){
+    eprint(adb, "tcadbclose");
+    err = true;
+  }
+  tcadbdel(adb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform read command */
+static int procread(const char *name){
+  iprintf("<Reading Test>\n  name=%s\n\n", name);
+  bool err = false;
+  double stime = tctime();
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    eprint(adb, "tcadbopen");
+    err = true;
+  }
+  int rnum = tcadbrnum(adb);
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", i);
+    int vsiz;
+    char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+    if(!vbuf){
+      eprint(adb, "tcadbget");
+      err = true;
+      break;
+    }
+    free(vbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+  iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+  if(!tcadbclose(adb)){
+    eprint(adb, "tcadbclose");
+    err = true;
+  }
+  tcadbdel(adb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform remove command */
+static int procremove(const char *name){
+  iprintf("<Removing Test>\n  name=%s\n\n", name);
+  bool err = false;
+  double stime = tctime();
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    eprint(adb, "tcadbopen");
+    err = true;
+  }
+  int rnum = tcadbrnum(adb);
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", i);
+    if(!tcadbout(adb, kbuf, ksiz)){
+      eprint(adb, "tcadbout");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+  iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+  if(!tcadbclose(adb)){
+    eprint(adb, "tcadbclose");
+    err = true;
+  }
+  tcadbdel(adb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform rcat command */
+static int procrcat(const char *name, int rnum){
+  iprintf("<Random Concatenating Test>\n  name=%s  rnum=%d\n\n", name, rnum);
+  int pnum = rnum / 5 + 1;
+  bool err = false;
+  double stime = tctime();
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    eprint(adb, "tcadbopen");
+    err = true;
+  }
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(pnum));
+    if(!tcadbputcat(adb, kbuf, ksiz, kbuf, ksiz)){
+      eprint(adb, "tcadbputcat");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+  iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+  if(!tcadbclose(adb)){
+    eprint(adb, "tcadbclose");
+    err = true;
+  }
+  tcadbdel(adb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform misc command */
+static int procmisc(const char *name, int rnum){
+  iprintf("<Random Concatenating Test>\n  name=%s  rnum=%d\n\n", name, rnum);
+  bool err = false;
+  double stime = tctime();
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    eprint(adb, "tcadbopen");
+    err = true;
+  }
+  iprintf("writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    if(!tcadbputkeep(adb, buf, len, buf, len)){
+      eprint(adb, "tcadbputkeep");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("reading:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", i);
+    int vsiz;
+    char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+    if(!vbuf){
+      eprint(adb, "tcadbget");
+      err = true;
+      break;
+    } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){
+      eprint(adb, "(validation)");
+      err = true;
+      free(vbuf);
+      break;
+    }
+    free(vbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(tcadbrnum(adb) != rnum){
+    eprint(adb, "(validation)");
+    err = true;
+  }
+  iprintf("random writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    char vbuf[RECBUFSIZ];
+    int vsiz = myrand(RECBUFSIZ);
+    memset(vbuf, '*', vsiz);
+    if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){
+      eprint(adb, "tcadbput");
+      err = true;
+      break;
+    }
+    int rsiz;
+    char *rbuf = tcadbget(adb, kbuf, ksiz, &rsiz);
+    if(!rbuf){
+      eprint(adb, "tcadbget");
+      err = true;
+      break;
+    }
+    if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+      eprint(adb, "(validation)");
+      err = true;
+      free(rbuf);
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+    free(rbuf);
+  }
+  iprintf("word writing:\n");
+  const char *words[] = {
+    "a", "A", "bb", "BB", "ccc", "CCC", "dddd", "DDDD", "eeeee", "EEEEEE",
+    "mikio", "hirabayashi", "tokyo", "cabinet", "hyper", "estraier", "19780211", "birth day",
+    "one", "first", "two", "second", "three", "third", "four", "fourth", "five", "fifth",
+    "_[1]_", "uno", "_[2]_", "dos", "_[3]_", "tres", "_[4]_", "cuatro", "_[5]_", "cinco",
+    "[\xe5\xb9\xb3\xe6\x9e\x97\xe5\xb9\xb9\xe9\x9b\x84]", "[\xe9\xa6\xac\xe9\xb9\xbf]", NULL
+  };
+  for(int i = 0; words[i] != NULL; i += 2){
+    const char *kbuf = words[i];
+    int ksiz = strlen(kbuf);
+    const char *vbuf = words[i+1];
+    int vsiz = strlen(vbuf);
+    if(!tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz)){
+      eprint(adb, "tcadbputkeep");
+      err = true;
+      break;
+    }
+    if(rnum > 250) putchar('.');
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", sizeof(words) / sizeof(*words));
+  iprintf("random erasing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    tcadbout(adb, kbuf, ksiz);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "[%d]", i);
+    char vbuf[RECBUFSIZ];
+    int vsiz = i % RECBUFSIZ;
+    memset(vbuf, '*', vsiz);
+    if(!tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz)){
+      eprint(adb, "tcadbputkeep");
+      err = true;
+      break;
+    }
+    if(vsiz < 1){
+      char tbuf[PATH_MAX];
+      for(int j = 0; j < PATH_MAX; j++){
+        tbuf[j] = myrand(0x100);
+      }
+      if(!tcadbput(adb, kbuf, ksiz, tbuf, PATH_MAX)){
+        eprint(adb, "tcadbput");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("erasing:\n");
+  for(int i = 1; i <= rnum; i++){
+    if(i % 2 == 1){
+      char kbuf[RECBUFSIZ];
+      int ksiz = sprintf(kbuf, "[%d]", i);
+      if(!tcadbout(adb, kbuf, ksiz)){
+        eprint(adb, "tcadbout");
+        err = true;
+        break;
+      }
+      tcadbout(adb, kbuf, ksiz);
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("iterator checking:\n");
+  if(!tcadbiterinit(adb)){
+    eprint(adb, "tcadbiterinit");
+    err = true;
+  }
+  char *kbuf;
+  int ksiz;
+  int inum = 0;
+  for(int i = 1; (kbuf = tcadbiternext(adb, &ksiz)) != NULL; i++, inum++){
+    int vsiz;
+    char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+    if(!vbuf){
+      eprint(adb, "tcadbget");
+      err = true;
+      free(kbuf);
+      break;
+    }
+    free(vbuf);
+    free(kbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", inum);
+  if(inum != tcadbrnum(adb)){
+    eprint(adb, "(validation)");
+    err = true;
+  }
+  if(*name != '*' && !tcadbsync(adb)){
+    eprint(adb, "tcadbsync");
+    err = true;
+  }
+  if(!tcadbvanish(adb)){
+    eprint(adb, "tcadbsync");
+    err = true;
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+  iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+  if(!tcadbclose(adb)){
+    eprint(adb, "tcadbclose");
+    err = true;
+  }
+  tcadbdel(adb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(const char *name, int rnum){
+  iprintf("<Wicked Writing Test>\n  name=%s  rnum=%d\n\n", name, rnum);
+  bool err = false;
+  double stime = tctime();
+  TCADB *adb = tcadbnew();
+  if(!tcadbopen(adb, name)){
+    eprint(adb, "tcadbopen");
+    err = true;
+  }
+  TCMAP *map = tcmapnew2(rnum / 5);
+  for(int i = 1; i <= rnum && !err; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    char vbuf[RECBUFSIZ];
+    int vsiz = myrand(RECBUFSIZ);
+    memset(vbuf, '*', vsiz);
+    vbuf[vsiz] = '\0';
+    char *rbuf;
+    switch(myrand(16)){
+    case 0:
+      putchar('0');
+      if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(adb, "tcadbput");
+        err = true;
+      }
+      tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 1:
+      putchar('1');
+      if(!tcadbput2(adb, kbuf, vbuf)){
+        eprint(adb, "tcadbput2");
+        err = true;
+      }
+      tcmapput2(map, kbuf, vbuf);
+      break;
+    case 2:
+      putchar('2');
+      tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz);
+      tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 3:
+      putchar('3');
+      tcadbputkeep2(adb, kbuf, vbuf);
+      tcmapputkeep2(map, kbuf, vbuf);
+      break;
+    case 4:
+      putchar('4');
+      if(!tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(adb, "tcadbputcat");
+        err = true;
+      }
+      tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 5:
+      putchar('5');
+      if(!tcadbputcat2(adb, kbuf, vbuf)){
+        eprint(adb, "tcadbputcat2");
+        err = true;
+      }
+      tcmapputcat2(map, kbuf, vbuf);
+      break;
+    case 6:
+      putchar('6');
+      if(myrand(10) == 0){
+        tcadbout(adb, kbuf, ksiz);
+        tcmapout(map, kbuf, ksiz);
+      }
+      break;
+    case 7:
+      putchar('7');
+      if(myrand(10) == 0){
+        tcadbout2(adb, kbuf);
+        tcmapout2(map, kbuf);
+      }
+      break;
+    case 8:
+      putchar('8');
+      if((rbuf = tcadbget(adb, kbuf, ksiz, &vsiz)) != NULL) free(rbuf);
+      break;
+    case 9:
+      putchar('9');
+      if((rbuf = tcadbget2(adb, kbuf)) != NULL) free(rbuf);
+      break;
+    case 10:
+      putchar('A');
+      tcadbvsiz(adb, kbuf, ksiz);
+      break;
+    case 11:
+      putchar('B');
+      tcadbvsiz2(adb, kbuf);
+      break;
+    case 12:
+      putchar('E');
+      if(myrand(rnum / 50) == 0){
+        if(!tcadbiterinit(adb)){
+          eprint(adb, "tcadbiterinit");
+          err = true;
+        }
+      }
+      for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
+        int iksiz;
+        char *ikbuf = tcadbiternext(adb, &iksiz);
+        if(ikbuf) free(ikbuf);
+      }
+      break;
+    default:
+      putchar('@');
+      if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+      break;
+    }
+    if(i % 50 == 0) iprintf(" (%08d)\n", i);
+  }
+  if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+  tcadbsync(adb);
+  if(tcadbrnum(adb) != tcmaprnum(map)){
+    eprint(adb, "(validation)");
+    err = true;
+  }
+  for(int i = 1; i <= rnum && !err; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", i - 1);
+    int vsiz;
+    const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
+    int rsiz;
+    char *rbuf = tcadbget(adb, kbuf, ksiz, &rsiz);
+    if(vbuf){
+      putchar('.');
+      if(!rbuf){
+        eprint(adb, "tcadbget");
+        err = true;
+      } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+        eprint(adb, "(validation)");
+        err = true;
+      }
+    } else {
+      putchar('*');
+      if(rbuf){
+        eprint(adb, "(validation)");
+        err = true;
+      }
+    }
+    free(rbuf);
+    if(i % 50 == 0) iprintf(" (%08d)\n", i);
+  }
+  if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+  tcmapiterinit(map);
+  int ksiz;
+  const char *kbuf;
+  for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){
+    putchar('+');
+    int vsiz;
+    const char *vbuf = tcmapiterval(kbuf, &vsiz);
+    int rsiz;
+    char *rbuf = tcadbget(adb, kbuf, ksiz, &rsiz);
+    if(!rbuf){
+      eprint(adb, "tcadbget");
+      err = true;
+    } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+      eprint(adb, "(validation)");
+      err = true;
+    }
+    free(rbuf);
+    if(!tcadbout(adb, kbuf, ksiz)){
+      eprint(adb, "tcadbout");
+      err = true;
+    }
+    if(i % 50 == 0) iprintf(" (%08d)\n", i);
+  }
+  int mrnum = tcmaprnum(map);
+  if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum);
+  if(tcadbrnum(adb) != 0){
+    eprint(adb, "(validation)");
+    err = true;
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+  iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+  tcmapdel(map);
+  if(!tcadbclose(adb)){
+    eprint(adb, "tcadbclose");
+    err = true;
+  }
+  tcadbdel(adb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcbdb.c b/bacula/src/lib/tokyocabinet/tcbdb.c
new file mode 100644 (file)
index 0000000..c97f837
--- /dev/null
@@ -0,0 +1,3690 @@
+/*************************************************************************************************
+ * The B+ tree database API of Tokyo Cabinet
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "tcutil.h"
+#include "tchdb.h"
+#include "tcbdb.h"
+#include "myconf.h"
+
+#define BDBFILEMODE    00644             // permission of created files
+#define BDBOPAQUESIZ   64                // size of using opaque field
+#define BDBPAGEBUFSIZ  32768             // size of a buffer to read each page
+#define BDBNODEIDBASE   ((1LL<<48)+1)    // base number of node ID
+#define BDBLEVELMAX    64                // max level of B+ tree
+#define BDBCACHEOUT    8                 // number of pages in a process of cacheout
+
+#define BDBDEFLMEMB    128               // default number of members in each leaf
+#define BDBMINLMEMB    4                 // minimum number of members in each leaf
+#define BDBDEFNMEMB    256               // default number of members in each node
+#define BDBMINNMEMB    4                 // minimum number of members in each node
+#define BDBDEFBNUM     16381             // default bucket number
+#define BDBDEFAPOW     8                 // default alignment power
+#define BDBDEFFPOW     10                // default free block pool power
+#define BDBDEFLCNUM    1024              // default number of leaf cache
+#define BDBDEFNCNUM    512               // default number of node cache
+
+typedef struct {                         // type of structure for a record
+  char *kbuf;                            // pointer to the key region
+  int ksiz;                              // size of the key region
+  char *vbuf;                            // pointer to the value region
+  int vsiz;                              // size of the value region
+  TCLIST *rest;                          // list of value objects
+} BDBREC;
+
+typedef struct {                         // type of structure for a leaf page
+  uint64_t id;                           // ID number of the leaf
+  TCLIST *recs;                          // list of records
+  uint64_t prev;                         // ID number of the previous leaf
+  uint64_t next;                         // ID number of the next leaf
+  bool dirty;                            // whether to be written back
+  bool dead;                             // whether to be removed
+} BDBLEAF;
+
+typedef struct {                         // type of structure for a page index
+  uint64_t pid;                          // ID number of the referring page
+  char *kbuf;                            // pointer to the key region
+  int ksiz;                              // size of the key region
+} BDBIDX;
+
+typedef struct {                         // type of structure for a node page
+  uint64_t id;                           // ID number of the node
+  uint64_t heir;                         // ID of the child before the first index
+  TCLIST *idxs;                          // list of indexes
+  bool dirty;                            // whether to be written back
+} BDBNODE;
+
+enum {                                   // enumeration for duplication behavior
+  BDBPDOVER,                             // overwrite an existing value
+  BDBPDKEEP,                             // keep the existing value
+  BDBPDCAT,                              // concatenate values
+  BDBPDDUP,                              // allow duplication of keys
+  BDBPDDUPB,                             // allow backward duplication
+};
+
+
+/* private macros */
+#define BDBLOCKMETHOD(TC_bdb, TC_wr) \
+  ((TC_bdb)->mmtx ? tcbdblockmethod((TC_bdb), (TC_wr)) : true)
+#define BDBUNLOCKMETHOD(TC_bdb) \
+  ((TC_bdb)->mmtx ? tcbdbunlockmethod(TC_bdb) : true)
+#define BDBLOCKCACHE(TC_bdb) \
+  ((TC_bdb)->mmtx ? tcbdblockcache(TC_bdb) : true)
+#define BDBUNLOCKCACHE(TC_bdb) \
+  ((TC_bdb)->mmtx ? tcbdbunlockcache(TC_bdb) : true)
+#define BDBLOCKTRAN(TC_bdb) \
+  ((TC_bdb)->mmtx ? tcbdblocktran(TC_bdb) : true)
+#define BDBUNLOCKTRAN(TC_bdb) \
+  ((TC_bdb)->mmtx ? tcbdbunlocktran(TC_bdb) : true)
+
+
+/* private function prototypes */
+static void tcbdbclear(TCBDB *bdb);
+static void tcdumpmeta(TCBDB *bdb);
+static void tcloadmeta(TCBDB *bdb);
+static BDBLEAF *tcbdbleafnew(TCBDB *bdb, uint64_t prev, uint64_t next);
+static bool tcbdbleafcacheout(TCBDB *bdb, BDBLEAF *leaf);
+static bool tcbdbleafsave(TCBDB *bdb, BDBLEAF *leaf);
+static BDBLEAF *tcbdbleafload(TCBDB *bdb, uint64_t id);
+static BDBLEAF *tcbdbgethistleaf(TCBDB *bdb, const char *kbuf, int ksiz);
+static bool tcbdbleafaddrec(TCBDB *bdb, BDBLEAF *leaf, int dmode,
+                            const char *kbuf, int ksiz, const char *vbuf, int vsiz);
+static int tcbdbleafdatasize(BDBLEAF *leaf);
+static BDBLEAF *tcbdbleafdivide(TCBDB *bdb, BDBLEAF *leaf);
+static bool tcbdbleafkill(TCBDB *bdb, BDBLEAF *leaf);
+static BDBNODE *tcbdbnodenew(TCBDB *bdb, uint64_t heir);
+static bool tcbdbnodecacheout(TCBDB *bdb, BDBNODE *node);
+static bool tcbdbnodesave(TCBDB *bdb, BDBNODE *node);
+static BDBNODE *tcbdbnodeload(TCBDB *bdb, uint64_t id);
+static void tcbdbnodeaddidx(TCBDB *bdb, BDBNODE *node, bool order, uint64_t pid,
+                            const char *kbuf, int ksiz);
+static bool tcbdbnodesubidx(TCBDB *bdb, BDBNODE *node, uint64_t pid);
+static uint64_t tcbdbsearchleaf(TCBDB *bdb, const char *kbuf, int ksiz);
+static BDBREC *tcbdbsearchrec(TCBDB *bdb, BDBLEAF *leaf, const char *kbuf, int ksiz, int *ip);
+static bool tcbdbcacheadjust(TCBDB *bdb);
+static void tcbdbcachepurge(TCBDB *bdb);
+static bool tcbdbopenimpl(TCBDB *bdb, const char *path, int omode);
+static bool tcbdbcloseimpl(TCBDB *bdb);
+static bool tcbdbputimpl(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
+                         int dmode);
+static bool tcbdboutimpl(TCBDB *bdb, const char *kbuf, int ksiz);
+static bool tcbdboutlist(TCBDB *bdb, const char *kbuf, int ksiz);
+static const char *tcbdbgetimpl(TCBDB *bdb, const char *kbuf, int ksiz, int *sp);
+static int tcbdbgetnum(TCBDB *bdb, const char *kbuf, int ksiz);
+static TCLIST *tcbdbgetlist(TCBDB *bdb, const char *kbuf, int ksiz);
+static bool tcbdbrangeimpl(TCBDB *bdb, const char *bkbuf, int bksiz, bool binc,
+                           const char *ekbuf, int eksiz, bool einc, int max, TCLIST *keys);
+static bool tcbdbrangefwm(TCBDB *bdb, const char *pbuf, int psiz, int max, TCLIST *keys);
+static bool tcbdboptimizeimpl(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+                              int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+static bool tcbdbvanishimpl(TCBDB *bdb);
+static bool tcbdblockmethod(TCBDB *bdb, bool wr);
+static bool tcbdbunlockmethod(TCBDB *bdb);
+static bool tcbdblockcache(TCBDB *bdb);
+static bool tcbdbunlockcache(TCBDB *bdb);
+static bool tcbdblocktran(TCBDB *bdb);
+static bool tcbdbunlocktran(TCBDB *bdb);
+static bool tcbdbcurfirstimpl(BDBCUR *cur);
+static bool tcbdbcurlastimpl(BDBCUR *cur);
+static bool tcbdbcurjumpimpl(BDBCUR *cur, const char *kbuf, int ksiz, bool forward);
+static bool tcbdbcuradjust(BDBCUR *cur, bool forward);
+static bool tcbdbcurprevimpl(BDBCUR *cur);
+static bool tcbdbcurnextimpl(BDBCUR *cur);
+static bool tcbdbcurputimpl(BDBCUR *cur, const char *vbuf, int vsiz, int mode);
+static bool tcbdbcuroutimpl(BDBCUR *cur);
+static bool tcbdbcurrecimpl(BDBCUR *cur, const char **kbp, int *ksp, const char **vbp, int *vsp);
+
+
+/* debugging function prototypes */
+void tcbdbprintmeta(TCBDB *bdb);
+void tcbdbprintleaf(TCBDB *bdb, BDBLEAF *leaf);
+void tcbdbprintnode(TCBDB *bdb, BDBNODE *node);
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+/* Get the message string corresponding to an error code. */
+const char *tcbdberrmsg(int ecode){
+  return tchdberrmsg(ecode);
+}
+
+
+/* Create a B+ tree database object. */
+TCBDB *tcbdbnew(void){
+  TCBDB *bdb;
+  TCMALLOC(bdb, sizeof(*bdb));
+  tcbdbclear(bdb);
+  bdb->hdb = tchdbnew();
+  TCMALLOC(bdb->hist, sizeof(*bdb->hist) * BDBLEVELMAX);
+  return bdb;
+}
+
+
+/* Delete a B+ tree database object. */
+void tcbdbdel(TCBDB *bdb){
+  assert(bdb);
+  if(bdb->open) tcbdbclose(bdb);
+  free(bdb->hist);
+  tchdbdel(bdb->hdb);
+  if(bdb->mmtx){
+    pthread_mutex_destroy(bdb->tmtx);
+    pthread_mutex_destroy(bdb->cmtx);
+    pthread_rwlock_destroy(bdb->mmtx);
+    free(bdb->tmtx);
+    free(bdb->cmtx);
+    free(bdb->mmtx);
+  }
+  free(bdb);
+}
+
+
+/* Get the last happened error code of a B+ tree database object. */
+int tcbdbecode(TCBDB *bdb){
+  assert(bdb);
+  return tchdbecode(bdb->hdb);
+}
+
+
+/* Set mutual exclusion control of a B+ tree database object for threading. */
+bool tcbdbsetmutex(TCBDB *bdb){
+  assert(bdb);
+  if(!TCUSEPTHREAD) return true;
+  if(!tcglobalmutexlock()) return false;
+  if(bdb->mmtx || bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    tcglobalmutexunlock();
+    return false;
+  }
+  TCMALLOC(bdb->mmtx, sizeof(pthread_rwlock_t));
+  TCMALLOC(bdb->cmtx, sizeof(pthread_mutex_t));
+  TCMALLOC(bdb->tmtx, sizeof(pthread_mutex_t));
+  if(pthread_rwlock_init(bdb->mmtx, NULL) != 0 || pthread_mutex_init(bdb->cmtx, NULL) != 0 ||
+     pthread_mutex_init(bdb->tmtx, NULL) != 0){
+    free(bdb->tmtx);
+    free(bdb->cmtx);
+    free(bdb->mmtx);
+    bdb->tmtx = NULL;
+    bdb->cmtx = NULL;
+    bdb->mmtx = NULL;
+    tcglobalmutexunlock();
+    return false;
+  }
+  tcglobalmutexunlock();
+  return tchdbsetmutex(bdb->hdb);
+}
+
+
+/* Set the custom comparison function of a B+ tree database object. */
+bool tcbdbsetcmpfunc(TCBDB *bdb, BDBCMP cmp, void *cmpop){
+  assert(bdb && cmp);
+  if(bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  bdb->cmp = cmp;
+  bdb->cmpop = cmpop;
+  return true;
+}
+
+
+/* Set the tuning parameters of a B+ tree database object. */
+bool tcbdbtune(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+               int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+  assert(bdb);
+  if(bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  bdb->lmemb = (lmemb > 0) ? tclmax(lmemb, BDBMINLMEMB) : BDBDEFLMEMB;
+  bdb->nmemb = (nmemb > 0) ? tclmax(nmemb, BDBMINNMEMB) : BDBDEFNMEMB;
+  bdb->opts = opts;
+  uint8_t hopts = 0;
+  if(opts & BDBTLARGE) hopts |= HDBTLARGE;
+  if(opts & BDBTDEFLATE) hopts |= HDBTDEFLATE;
+  if(opts & BDBTTCBS) hopts |= HDBTTCBS;
+  bnum = (bnum > 0) ? bnum : BDBDEFBNUM;
+  apow = (apow >= 0) ? apow : BDBDEFAPOW;
+  fpow = (fpow >= 0) ? fpow : BDBDEFFPOW;
+  return tchdbtune(bdb->hdb, bnum, apow, fpow, hopts);
+}
+
+
+/* Set the caching parameters of a B+ tree database object. */
+bool tcbdbsetcache(TCBDB *bdb, int32_t lcnum, int32_t ncnum){
+  assert(bdb);
+  if(bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  if(lcnum > 0) bdb->lcnum = tclmax(lcnum, BDBLEVELMAX);
+  if(ncnum > 0) bdb->ncnum = tclmax(ncnum, BDBLEVELMAX);
+  return true;
+}
+
+
+/* Open a database file and connect a B+ tree database object. */
+bool tcbdbopen(TCBDB *bdb, const char *path, int omode){
+  assert(bdb && path);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbopenimpl(bdb, path, omode);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Close a B+ tree database object. */
+bool tcbdbclose(TCBDB *bdb){
+  assert(bdb);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbcloseimpl(bdb);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Store a record into a B+ tree database object. */
+bool tcbdbput(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDOVER);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Store a string record into a B+ tree database object. */
+bool tcbdbput2(TCBDB *bdb, const char *kstr, const char *vstr){
+  assert(bdb && kstr && vstr);
+  return tcbdbput(bdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a new record into a B+ tree database object. */
+bool tcbdbputkeep(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDKEEP);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Store a new string record into a B+ tree database object. */
+bool tcbdbputkeep2(TCBDB *bdb, const char *kstr, const char *vstr){
+  assert(bdb && kstr && vstr);
+  return tcbdbputkeep(bdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Concatenate a value at the end of the existing record in a B+ tree database object. */
+bool tcbdbputcat(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDCAT);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Concatenate a string value at the end of the existing record in a B+ tree database object. */
+bool tcbdbputcat2(TCBDB *bdb, const char *kstr, const char *vstr){
+  assert(bdb && kstr && vstr);
+  return tcbdbputcat(bdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a record into a B+ tree database object with allowing duplication of keys. */
+bool tcbdbputdup(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDDUP);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Store a string record into a B+ tree database object with allowing duplication of keys. */
+bool tcbdbputdup2(TCBDB *bdb, const char *kstr, const char *vstr){
+  assert(bdb && kstr && vstr);
+  return tcbdbputdup(bdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store records into a B+ tree database object with allowing duplication of keys. */
+bool tcbdbputdup3(TCBDB *bdb, const void *kbuf, int ksiz, const TCLIST *vals){
+  assert(bdb && kbuf && ksiz >= 0 && vals);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool err = false;
+  int ln = TCLISTNUM(vals);
+  for(int i = 0; i < ln; i++){
+    int vsiz;
+    const char *vbuf = tclistval(vals, i, &vsiz);
+    if(!tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDDUP)) err = true;
+  }
+  BDBUNLOCKMETHOD(bdb);
+  return !err;
+}
+
+
+/* Remove a record of a B+ tree database object. */
+bool tcbdbout(TCBDB *bdb, const void *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdboutimpl(bdb, kbuf, ksiz);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Remove a string record of a B+ tree database object. */
+bool tcbdbout2(TCBDB *bdb, const char *kstr){
+  assert(bdb && kstr);
+  return tcbdbout(bdb, kstr, strlen(kstr));
+}
+
+
+/* Remove records of a B+ tree database object. */
+bool tcbdbout3(TCBDB *bdb, const void *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdboutlist(bdb, kbuf, ksiz);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Retrieve a record in a B+ tree database object. */
+void *tcbdbget(TCBDB *bdb, const void *kbuf, int ksiz, int *sp){
+  assert(bdb && kbuf && ksiz >= 0 && sp);
+  if(!BDBLOCKMETHOD(bdb, false)) return NULL;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return NULL;
+  }
+  const char *vbuf = tcbdbgetimpl(bdb, kbuf, ksiz, sp);
+  char *rv;
+  if(vbuf){
+    TCMEMDUP(rv, vbuf, *sp);
+  } else {
+    rv = NULL;
+  }
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    if(!bdb->tran && !tcbdbcacheadjust(bdb)){
+      free(rv);
+      rv = NULL;
+    }
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return rv;
+}
+
+
+/* Retrieve a string record in a B+ tree database object. */
+char *tcbdbget2(TCBDB *bdb, const char *kstr){
+  assert(bdb && kstr);
+  int vsiz;
+  return tcbdbget(bdb, kstr, strlen(kstr), &vsiz);
+}
+
+
+/* Retrieve a record in a B+ tree database object and write the value into a buffer. */
+const void *tcbdbget3(TCBDB *bdb, const void *kbuf, int ksiz, int *sp){
+  assert(bdb && kbuf && ksiz >= 0 && sp);
+  if(!BDBLOCKMETHOD(bdb, false)) return NULL;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return NULL;
+  }
+  const char *rv = tcbdbgetimpl(bdb, kbuf, ksiz, sp);
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = NULL;
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return rv;
+}
+
+
+/* Retrieve records in a B+ tree database object. */
+TCLIST *tcbdbget4(TCBDB *bdb, const void *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  if(!BDBLOCKMETHOD(bdb, false)) return NULL;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return NULL;
+  }
+  TCLIST *rv = tcbdbgetlist(bdb, kbuf, ksiz);
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    if(!bdb->tran && !tcbdbcacheadjust(bdb)){
+      if(rv) tclistdel(rv);
+      rv = NULL;
+    }
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return rv;
+}
+
+
+/* Get the number of records corresponding a key in a B+ tree database object. */
+int tcbdbvnum(TCBDB *bdb, const void *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  if(!BDBLOCKMETHOD(bdb, false)) return 0;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return 0;
+  }
+  int rv = tcbdbgetnum(bdb, kbuf, ksiz);
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = 0;
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return rv;
+}
+
+
+/* Get the number of records corresponding a string key in a B+ tree database object. */
+int tcbdbvnum2(TCBDB *bdb, const char *kstr){
+  assert(bdb && kstr);
+  return tcbdbvnum(bdb, kstr, strlen(kstr));
+}
+
+
+/* Get the size of the value of a record in a B+ tree database object. */
+int tcbdbvsiz(TCBDB *bdb, const void *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  int vsiz;
+  if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz)) return -1;
+  return vsiz;
+}
+
+
+/* Get the size of the value of a string record in a B+ tree database object. */
+int tcbdbvsiz2(TCBDB *bdb, const char *kstr){
+  assert(bdb && kstr);
+  return tcbdbvsiz(bdb, kstr, strlen(kstr));
+}
+
+
+/* Get keys of ranged records in a B+ tree database object. */
+TCLIST *tcbdbrange(TCBDB *bdb, const void *bkbuf, int bksiz, bool binc,
+                   const void *ekbuf, int eksiz, bool einc, int max){
+  assert(bdb);
+  TCLIST *keys = tclistnew();
+  if(!BDBLOCKMETHOD(bdb, false)) return keys;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return keys;
+  }
+  tcbdbrangeimpl(bdb, bkbuf, bksiz, binc, ekbuf, eksiz, einc, max, keys);
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    tcbdbcacheadjust(bdb);
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return keys;
+}
+
+
+/* Get string keys of ranged records in a B+ tree database object. */
+TCLIST *tcbdbrange2(TCBDB *bdb, const char *bkstr, bool binc,
+                    const char *ekstr, bool einc, int max){
+  return tcbdbrange(bdb, bkstr, bkstr ? strlen(bkstr) : 0, binc,
+                    ekstr, ekstr ? strlen(ekstr) : 0, einc, max);
+}
+
+
+/* Get forward matching keys in a B+ tree database object. */
+TCLIST *tcbdbfwmkeys(TCBDB *bdb, const void *pbuf, int psiz, int max){
+  assert(bdb && pbuf && psiz >= 0);
+  TCLIST *keys = tclistnew();
+  if(!BDBLOCKMETHOD(bdb, false)) return keys;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return keys;
+  }
+  tcbdbrangefwm(bdb, pbuf, psiz, max, keys);
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    tcbdbcacheadjust(bdb);
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return keys;
+}
+
+
+/* Get forward matching string keys in a B+ tree database object. */
+TCLIST *tcbdbfwmkeys2(TCBDB *bdb, const char *pstr, int max){
+  assert(bdb && pstr);
+  return tcbdbfwmkeys(bdb, pstr, strlen(pstr), max);
+}
+
+
+
+/* Synchronize updated contents of a B+ tree database object with the file and the device. */
+bool tcbdbsync(TCBDB *bdb){
+  assert(bdb);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode || bdb->tran){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbmemsync(bdb, true);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Optimize the file of a B+ tree database object. */
+bool tcbdboptimize(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+                   int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+  assert(bdb);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode || bdb->tran){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdboptimizeimpl(bdb, lmemb, nmemb, bnum, apow, fpow, opts);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Remove all records of a B+ tree database object. */
+bool tcbdbvanish(TCBDB *bdb){
+  assert(bdb);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode || bdb->tran){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbvanishimpl(bdb);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Copy the database file of a B+ tree database object. */
+bool tcbdbcopy(TCBDB *bdb, const char *path){
+  assert(bdb && path);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  TCLIST *lids = tclistnew();
+  TCLIST *nids = tclistnew();
+  const char *vbuf;
+  int vsiz;
+  TCMAP *leafc = bdb->leafc;
+  tcmapiterinit(leafc);
+  while((vbuf = tcmapiternext(leafc, &vsiz)) != NULL){
+    tclistpush(lids, vbuf, vsiz);
+  }
+  TCMAP *nodec = bdb->nodec;
+  tcmapiterinit(nodec);
+  while((vbuf = tcmapiternext(nodec, &vsiz)) != NULL){
+    tclistpush(nids, vbuf, vsiz);
+  }
+  BDBUNLOCKMETHOD(bdb);
+  bool err = false;
+  int ln = TCLISTNUM(lids);
+  for(int i = 0; i < ln; i++){
+    vbuf = TCLISTVALPTR(lids, i);
+    if(BDBLOCKMETHOD(bdb, true)){
+      if(bdb->open){
+        int rsiz;
+        BDBLEAF *leaf = (BDBLEAF *)tcmapget(bdb->leafc, vbuf, sizeof(leaf->id), &rsiz);
+        if(leaf && leaf->dirty && !tcbdbleafsave(bdb, leaf)) err = true;
+      } else {
+        err = true;
+      }
+      BDBUNLOCKMETHOD(bdb);
+    } else {
+      err = true;
+    }
+  }
+  ln = TCLISTNUM(nids);
+  for(int i = 0; i < ln; i++){
+    vbuf = TCLISTVALPTR(nids, i);
+    if(BDBLOCKMETHOD(bdb, true)){
+      if(bdb->open){
+        int rsiz;
+        BDBNODE *node = (BDBNODE *)tcmapget(bdb->nodec, vbuf, sizeof(node->id), &rsiz);
+        if(node && node->dirty && !tcbdbnodesave(bdb, node)) err = true;
+      } else {
+        err = true;
+      }
+      BDBUNLOCKMETHOD(bdb);
+    } else {
+      err = true;
+    }
+  }
+  tclistdel(nids);
+  tclistdel(lids);
+  if(!tcbdbtranbegin(bdb)) err = true;
+  if(BDBLOCKMETHOD(bdb, false)){
+    if(!tchdbcopy(bdb->hdb, path)) err = true;
+    BDBUNLOCKMETHOD(bdb);
+  } else {
+    err = true;
+  }
+  if(!tcbdbtrancommit(bdb)) err = true;
+  return !err;
+}
+
+
+/* Begin the transaction of a B+ tree database object. */
+bool tcbdbtranbegin(TCBDB *bdb){
+  assert(bdb);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode || bdb->tran){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(!tcbdbmemsync(bdb, false)){
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(!BDBLOCKTRAN(bdb)){
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bdb->tran = true;
+  TCMEMDUP(bdb->rbopaque, bdb->opaque, BDBOPAQUESIZ);
+  BDBUNLOCKMETHOD(bdb);
+  return true;
+}
+
+
+/* Commit the transaction of a B+ tree database object. */
+bool tcbdbtrancommit(TCBDB *bdb){
+  assert(bdb);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode || !bdb->tran){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  free(bdb->rbopaque);
+  bdb->tran = false;
+  bdb->rbopaque = NULL;
+  bool err = false;
+  if(!tcbdbmemsync(bdb, false)) err = true;
+  BDBUNLOCKTRAN(bdb);
+  BDBUNLOCKMETHOD(bdb);
+  return !err;
+}
+
+
+/* Abort the transaction of a B+ tree database object. */
+bool tcbdbtranabort(TCBDB *bdb){
+  assert(bdb);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode || !bdb->tran){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  tcbdbcachepurge(bdb);
+  memcpy(bdb->opaque, bdb->rbopaque, BDBOPAQUESIZ);
+  tcloadmeta(bdb);
+  free(bdb->rbopaque);
+  bdb->tran = false;
+  bdb->rbopaque = NULL;
+  BDBUNLOCKTRAN(bdb);
+  BDBUNLOCKMETHOD(bdb);
+  return true;
+}
+
+
+/* Get the file path of a B+ tree database object. */
+const char *tcbdbpath(TCBDB *bdb){
+  assert(bdb);
+  if(!BDBLOCKMETHOD(bdb, false)) return NULL;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return NULL;
+  }
+  const char *rv = tchdbpath(bdb->hdb);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Get the number of records of a B+ tree database object. */
+uint64_t tcbdbrnum(TCBDB *bdb){
+  assert(bdb);
+  if(!BDBLOCKMETHOD(bdb, false)) return 0;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return 0;
+  }
+  uint64_t rv = bdb->rnum;
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Get the size of the database file of a B+ tree database object. */
+uint64_t tcbdbfsiz(TCBDB *bdb){
+  assert(bdb);
+  if(!BDBLOCKMETHOD(bdb, false)) return 0;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return 0;
+  }
+  uint64_t rv = tchdbfsiz(bdb->hdb);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Create a cursor object. */
+BDBCUR *tcbdbcurnew(TCBDB *bdb){
+  assert(bdb);
+  BDBCUR *cur;
+  TCMALLOC(cur, sizeof(*cur));
+  cur->bdb = bdb;
+  cur->id = 0;
+  cur->kidx = 0;
+  cur->vidx = 0;
+  return cur;
+}
+
+
+/* Delete a cursor object. */
+void tcbdbcurdel(BDBCUR *cur){
+  assert(cur);
+  free(cur);
+}
+
+
+/* Move a cursor object to the first record. */
+bool tcbdbcurfirst(BDBCUR *cur){
+  assert(cur);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbcurfirstimpl(cur);
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false;
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return rv;
+}
+
+
+/* Move a cursor object to the last record. */
+bool tcbdbcurlast(BDBCUR *cur){
+  assert(cur);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbcurlastimpl(cur);
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false;
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return rv;
+}
+
+
+/* Move a cursor object to the front of records corresponding a key. */
+bool tcbdbcurjump(BDBCUR *cur, const void *kbuf, int ksiz){
+  assert(cur && kbuf && ksiz >= 0);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbcurjumpimpl(cur, kbuf, ksiz, true);
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false;
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return rv;
+}
+
+
+/* Move a cursor object to the front of records corresponding a key string. */
+bool tcbdbcurjump2(BDBCUR *cur, const char *kstr){
+  assert(cur && kstr);
+  return tcbdbcurjump(cur, kstr, strlen(kstr));
+}
+
+
+/* Move a cursor object to the previous record. */
+bool tcbdbcurprev(BDBCUR *cur){
+  assert(cur);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(cur->id < 1){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbcurprevimpl(cur);
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false;
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return rv;
+}
+
+
+/* Move a cursor object to the next record. */
+bool tcbdbcurnext(BDBCUR *cur){
+  assert(cur);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(cur->id < 1){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbcurnextimpl(cur);
+  bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+  BDBUNLOCKMETHOD(bdb);
+  if(adj && BDBLOCKMETHOD(bdb, true)){
+    if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false;
+    BDBUNLOCKMETHOD(bdb);
+  }
+  return rv;
+}
+
+
+/* Insert a record around a cursor object. */
+bool tcbdbcurput(BDBCUR *cur, const void *vbuf, int vsiz, int cpmode){
+  assert(cur && vbuf && vsiz >= 0);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(cur->id < 1){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbcurputimpl(cur, vbuf, vsiz, cpmode);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Insert a string record around a cursor object. */
+bool tcbdbcurput2(BDBCUR *cur, const char *vstr, int cpmode){
+  assert(cur && vstr);
+  return tcbdbcurput(cur, vstr, strlen(vstr), cpmode);
+}
+
+
+/* Delete the record where a cursor object is. */
+bool tcbdbcurout(BDBCUR *cur){
+  assert(cur);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(cur->id < 1){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbcuroutimpl(cur);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Get the key of the record where the cursor object is. */
+char *tcbdbcurkey(BDBCUR *cur, int *sp){
+  assert(cur && sp);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(cur->id < 1){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  const char *kbuf, *vbuf;
+  int ksiz, vsiz;
+  char *rv;
+  if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+    TCMEMDUP(rv, kbuf, ksiz);
+    *sp = ksiz;
+  } else {
+    rv = NULL;
+  }
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Get the key string of the record where the cursor object is. */
+char *tcbdbcurkey2(BDBCUR *cur){
+  assert(cur);
+  int ksiz;
+  return tcbdbcurkey(cur, &ksiz);
+}
+
+
+/* Get the key of the record where the cursor object is, as a volatile buffer. */
+const char *tcbdbcurkey3(BDBCUR *cur, int *sp){
+  assert(cur && sp);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(cur->id < 1){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  const char *kbuf, *vbuf;
+  int ksiz, vsiz;
+  const char *rv;
+  if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+    rv = kbuf;
+    *sp = ksiz;
+  } else {
+    rv = NULL;
+  }
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Get the value of the record where the cursor object is. */
+char *tcbdbcurval(BDBCUR *cur, int *sp){
+  assert(cur && sp);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(cur->id < 1){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  const char *kbuf, *vbuf;
+  int ksiz, vsiz;
+  char *rv;
+  if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+    TCMEMDUP(rv, vbuf, vsiz);
+    *sp = vsiz;
+  } else {
+    rv = NULL;
+  }
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Get the value string of the record where the cursor object is. */
+char *tcbdbcurval2(BDBCUR *cur){
+  assert(cur);
+  int vsiz;
+  return tcbdbcurval(cur, &vsiz);
+}
+
+
+/* Get the value of the record where the cursor object is, as a volatile buffer. */
+const char *tcbdbcurval3(BDBCUR *cur, int *sp){
+  assert(cur && sp);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(cur->id < 1){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  const char *kbuf, *vbuf;
+  int ksiz, vsiz;
+  const char *rv;
+  if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+    rv = vbuf;
+    *sp = vsiz;
+  } else {
+    rv = NULL;
+  }
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Get the key and the value of the record where the cursor object is. */
+bool tcbdbcurrec(BDBCUR *cur, TCXSTR *kxstr, TCXSTR *vxstr){
+  assert(cur && kxstr && vxstr);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  if(cur->id < 1){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  const char *kbuf, *vbuf;
+  int ksiz, vsiz;
+  bool rv;
+  if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+    tcxstrclear(kxstr);
+    TCXSTRCAT(kxstr, kbuf, ksiz);
+    tcxstrclear(vxstr);
+    TCXSTRCAT(vxstr, vbuf, vsiz);
+    rv = true;
+  } else {
+    rv = false;
+  }
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Set the error code of a B+ tree database object. */
+void tcbdbsetecode(TCBDB *bdb, int ecode, const char *filename, int line, const char *func){
+  assert(bdb && filename && line >= 1 && func);
+  tchdbsetecode(bdb->hdb, ecode, filename, line, func);
+}
+
+
+/* Set the file descriptor for debugging output. */
+void tcbdbsetdbgfd(TCBDB *bdb, int fd){
+  assert(bdb && fd >= 0);
+  tchdbsetdbgfd(bdb->hdb, fd);
+}
+
+
+/* Get the file descriptor for debugging output. */
+int tcbdbdbgfd(TCBDB *bdb){
+  assert(bdb);
+  return tchdbdbgfd(bdb->hdb);
+}
+
+
+/* Synchronize updating contents on memory. */
+bool tcbdbmemsync(TCBDB *bdb, bool phys){
+  assert(bdb);
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  bool err = false;
+  bool clk = BDBLOCKCACHE(bdb);
+  const char *vbuf;
+  int vsiz;
+  TCMAP *leafc = bdb->leafc;
+  tcmapiterinit(leafc);
+  while((vbuf = tcmapiternext(leafc, &vsiz)) != NULL){
+    int rsiz;
+    BDBLEAF *leaf = (BDBLEAF *)tcmapiterval(vbuf, &rsiz);
+    if(leaf->dirty && !tcbdbleafsave(bdb, leaf)) err = true;
+  }
+  TCMAP *nodec = bdb->nodec;
+  tcmapiterinit(nodec);
+  while((vbuf = tcmapiternext(nodec, &vsiz)) != NULL){
+    int rsiz;
+    BDBNODE *node = (BDBNODE *)tcmapiterval(vbuf, &rsiz);
+    if(node->dirty && !tcbdbnodesave(bdb, node)) err = true;
+  }
+  if(clk) BDBUNLOCKCACHE(bdb);
+  tcdumpmeta(bdb);
+  if(!tchdbmemsync(bdb->hdb, phys)) err = true;
+  return !err;
+}
+
+
+/* Get the comparison function of a B+ tree database object. */
+BDBCMP tcbdbcmpfunc(TCBDB *bdb){
+  assert(bdb);
+  return bdb->cmp;
+}
+
+
+/* Get the opaque object for the comparison function of a B+ tree database object. */
+void *tcbdbcmpop(TCBDB *bdb){
+  assert(bdb);
+  return bdb->cmpop;
+}
+
+
+/* Get the maximum number of cached leaf nodes of a B+ tree database object. */
+uint32_t tcbdblmemb(TCBDB *bdb){
+  assert(bdb);
+  return bdb->lmemb;
+}
+
+
+/* Get the maximum number of cached non-leaf nodes of a B+ tree database object. */
+uint32_t tcbdbnmemb(TCBDB *bdb){
+  assert(bdb);
+  return bdb->nmemb;
+}
+
+
+/* Get the number of the leaf nodes of B+ tree database object. */
+uint64_t tcbdblnum(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return bdb->lnum;
+}
+
+
+/* Get the number of the non-leaf nodes of B+ tree database object. */
+uint64_t tcbdbnnum(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return bdb->nnum;
+}
+
+
+/* Get the number of elements of the bucket array of a B+ tree database object. */
+uint64_t tcbdbbnum(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return tchdbbnum(bdb->hdb);
+}
+
+
+/* Get the record alignment of a B+ tree database object. */
+uint32_t tcbdbalign(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return tchdbalign(bdb->hdb);
+}
+
+
+/* Get the maximum number of the free block pool of a B+ tree database object. */
+uint32_t tcbdbfbpmax(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return tchdbfbpmax(bdb->hdb);
+}
+
+
+/* Get the inode number of the database file of a B+ tree database object. */
+uint64_t tcbdbinode(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return tchdbinode(bdb->hdb);
+}
+
+
+/* Get the modification time of the database file of a B+ tree database object. */
+time_t tcbdbmtime(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return tchdbmtime(bdb->hdb);
+}
+
+
+/* Get the additional flags of a B+ tree database object. */
+uint8_t tcbdbflags(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return tchdbflags(bdb->hdb);
+}
+
+
+/* Get the options of a B+ tree database object. */
+uint8_t tcbdbopts(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return bdb->opts;
+}
+
+
+/* Get the pointer to the opaque field of a B+ tree database object. */
+char *tcbdbopaque(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return tchdbopaque(bdb->hdb) + BDBOPAQUESIZ;
+}
+
+
+/* Get the number of used elements of the bucket array of a B+ tree database object. */
+uint64_t tcbdbbnumused(TCBDB *bdb){
+  assert(bdb);
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return tchdbbnumused(bdb->hdb);
+}
+
+
+/* Set the maximum size of each leaf node. */
+bool tcbdbsetlsmax(TCBDB *bdb, uint32_t lsmax){
+  assert(bdb);
+  if(bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  bdb->lsmax = lsmax;
+  return true;
+}
+
+
+/* Set the capacity number of records. */
+bool tcbdbsetcapnum(TCBDB *bdb, uint64_t capnum){
+  assert(bdb);
+  if(bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  bdb->capnum = capnum;
+  return true;
+}
+
+
+/* Store a new record into a B+ tree database object with backward duplication. */
+bool tcbdbputdupback(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(!BDBLOCKMETHOD(bdb, true)) return false;
+  if(!bdb->open || !bdb->wmode){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDDUPB);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Store a new string record into a B+ tree database object with backward duplication. */
+bool tcbdbputdupback2(TCBDB *bdb, const char *kstr, const char *vstr){
+  assert(bdb && kstr && vstr);
+  return tcbdbputdupback(bdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Move a cursor object to the rear of records corresponding a key. */
+bool tcbdbcurjumpback(BDBCUR *cur, const void *kbuf, int ksiz){
+  assert(cur && kbuf && ksiz >= 0);
+  TCBDB *bdb = cur->bdb;
+  if(!BDBLOCKMETHOD(bdb, false)) return false;
+  if(!bdb->open){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    BDBUNLOCKMETHOD(bdb);
+    return false;
+  }
+  bool rv = tcbdbcurjumpimpl(cur, kbuf, ksiz, false);
+  BDBUNLOCKMETHOD(bdb);
+  return rv;
+}
+
+
+/* Move a cursor object to the rear of records corresponding a key string. */
+bool tcbdbcurjumpback2(BDBCUR *cur, const char *kstr){
+  assert(cur && kstr);
+  return tcbdbcurjumpback(cur, kstr, strlen(kstr));
+}
+
+
+/* Compare keys of two records by lexical order. */
+int tcbdbcmplexical(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
+  assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
+  int rv;
+  TCCMPLEXICAL(rv, aptr, asiz, bptr, bsiz);
+  return rv;
+}
+
+
+/* Compare two keys as decimal strings of real numbers. */
+int tcbdbcmpdecimal(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
+  assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
+  int sign;
+  int64_t anum = 0;
+  sign = 1;
+  if(asiz > 0 && *aptr == '-'){
+    aptr++;
+    asiz--;
+    sign = -1;
+  }
+  for(int i = 0; i < asiz; i++){
+    int c = aptr[i];
+    if(c < '0' || c > '9') continue;
+    anum = anum * 10 + c - '0';
+  }
+  anum *= sign;
+  int64_t bnum = 0;
+  sign = 1;
+  if(bsiz > 0 && *bptr == '-'){
+    bptr++;
+    bsiz--;
+    sign = -1;
+  }
+  for(int i = 0; i < bsiz; i++){
+    int c = bptr[i];
+    if(c < '0' || c > '9') continue;
+    bnum = bnum * 10 + c - '0';
+  }
+  bnum *= sign;
+  return (anum < bnum) ? -1 : anum > bnum;
+}
+
+
+/* Compare two keys as 32-bit integers in the native byte order. */
+int tcbdbcmpint32(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
+  assert(aptr && bptr);
+  int32_t anum, bnum;
+  if(asiz == sizeof(int32_t)){
+    memcpy(&anum, aptr, sizeof(int32_t));
+  } else if(asiz < sizeof(int32_t)){
+    memset(&anum, 0, sizeof(int32_t));
+    memcpy(&anum, aptr, asiz);
+  } else {
+    memcpy(&anum, aptr, sizeof(int32_t));
+  }
+  if(bsiz == sizeof(int32_t)){
+    memcpy(&bnum, bptr, sizeof(int32_t));
+  } else if(bsiz < sizeof(int32_t)){
+    memset(&bnum, 0, sizeof(int32_t));
+    memcpy(&bnum, bptr, bsiz);
+  } else {
+    memcpy(&bnum, bptr, sizeof(int32_t));
+  }
+  return (anum < bnum) ? -1 : anum > bnum;
+}
+
+
+/* Compare two keys as 64-bit integers in the native byte order. */
+int tcbdbcmpint64(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
+  assert(aptr && bptr);
+  int64_t anum, bnum;
+  if(asiz == sizeof(int64_t)){
+    memcpy(&anum, aptr, sizeof(int64_t));
+  } else if(asiz < sizeof(int64_t)){
+    memset(&anum, 0, sizeof(int64_t));
+    memcpy(&anum, aptr, asiz);
+  } else {
+    memcpy(&anum, aptr, sizeof(int64_t));
+  }
+  if(bsiz == sizeof(int64_t)){
+    memcpy(&bnum, bptr, sizeof(int64_t));
+  } else if(bsiz < sizeof(int64_t)){
+    memset(&bnum, 0, sizeof(int64_t));
+    memcpy(&bnum, bptr, bsiz);
+  } else {
+    memcpy(&bnum, bptr, sizeof(int64_t));
+  }
+  return (anum < bnum) ? -1 : anum > bnum;
+}
+
+
+
+/*************************************************************************************************
+ * private features
+ *************************************************************************************************/
+
+
+/* Clear all members.
+   `bdb' specifies the B+ tree database object. */
+static void tcbdbclear(TCBDB *bdb){
+  assert(bdb);
+  bdb->mmtx = NULL;
+  bdb->cmtx = NULL;
+  bdb->tmtx = NULL;
+  bdb->hdb = NULL;
+  bdb->opaque = NULL;
+  bdb->open = false;
+  bdb->wmode = false;
+  bdb->lmemb = BDBDEFLMEMB;
+  bdb->nmemb = BDBDEFNMEMB;
+  bdb->opts = 0;
+  bdb->root = 0;
+  bdb->first = 0;
+  bdb->last = 0;
+  bdb->lnum = 0;
+  bdb->nnum = 0;
+  bdb->rnum = 0;
+  bdb->leafc = NULL;
+  bdb->nodec = NULL;
+  bdb->cmp = NULL;
+  bdb->cmpop = NULL;
+  bdb->lcnum = BDBDEFLCNUM;
+  bdb->ncnum = BDBDEFNCNUM;
+  bdb->lsmax = 0;
+  bdb->lschk = 0;
+  bdb->capnum = 0;
+  bdb->hist = NULL;
+  bdb->hnum = 0;
+  bdb->hleaf = 0;
+  bdb->lleaf = 0;
+  bdb->tran = false;
+  bdb->rbopaque = NULL;
+  bdb->cnt_saveleaf = -1;
+  bdb->cnt_loadleaf = -1;
+  bdb->cnt_killleaf = -1;
+  bdb->cnt_adjleafc = -1;
+  bdb->cnt_savenode = -1;
+  bdb->cnt_loadnode = -1;
+  bdb->cnt_adjnodec = -1;
+  TCDODEBUG(bdb->cnt_saveleaf = 0);
+  TCDODEBUG(bdb->cnt_loadleaf = 0);
+  TCDODEBUG(bdb->cnt_killleaf = 0);
+  TCDODEBUG(bdb->cnt_adjleafc = 0);
+  TCDODEBUG(bdb->cnt_savenode = 0);
+  TCDODEBUG(bdb->cnt_loadnode = 0);
+  TCDODEBUG(bdb->cnt_adjnodec = 0);
+}
+
+
+/* Serialize meta data into the opaque field.
+   `bdb' specifies the B+ tree database object. */
+static void tcdumpmeta(TCBDB *bdb){
+  assert(bdb);
+  memset(bdb->opaque, 0, 64);
+  char *wp = bdb->opaque;
+  if(bdb->cmp == tcbdbcmplexical){
+    *(uint8_t *)(wp++) = 0x0;
+  } else if(bdb->cmp == tcbdbcmpdecimal){
+    *(uint8_t *)(wp++) = 0x1;
+  } else if(bdb->cmp == tcbdbcmpint32){
+    *(uint8_t *)(wp++) = 0x2;
+  } else if(bdb->cmp == tcbdbcmpint64){
+    *(uint8_t *)(wp++) = 0x3;
+  } else {
+    *(uint8_t *)(wp++) = 0xff;
+  }
+  wp += 7;
+  uint32_t lnum;
+  lnum = bdb->lmemb;
+  lnum = TCHTOIL(lnum);
+  memcpy(wp, &lnum, sizeof(lnum));
+  wp += sizeof(lnum);
+  lnum = bdb->nmemb;
+  lnum = TCHTOIL(lnum);
+  memcpy(wp, &lnum, sizeof(lnum));
+  wp += sizeof(lnum);
+  uint64_t llnum;
+  llnum = bdb->root;
+  llnum = TCHTOILL(llnum);
+  memcpy(wp, &llnum, sizeof(llnum));
+  wp += sizeof(llnum);
+  llnum = bdb->first;
+  llnum = TCHTOILL(llnum);
+  memcpy(wp, &llnum, sizeof(llnum));
+  wp += sizeof(llnum);
+  llnum = bdb->last;
+  llnum = TCHTOILL(llnum);
+  memcpy(wp, &llnum, sizeof(llnum));
+  wp += sizeof(llnum);
+  llnum = bdb->lnum;
+  llnum = TCHTOILL(llnum);
+  memcpy(wp, &llnum, sizeof(llnum));
+  wp += sizeof(llnum);
+  llnum = bdb->nnum;
+  llnum = TCHTOILL(llnum);
+  memcpy(wp, &llnum, sizeof(llnum));
+  wp += sizeof(llnum);
+  llnum = bdb->rnum;
+  llnum = TCHTOILL(llnum);
+  memcpy(wp, &llnum, sizeof(llnum));
+  wp += sizeof(llnum);
+}
+
+
+/* Deserialize meta data from the opaque field.
+   `bdb' specifies the B+ tree database object. */
+static void tcloadmeta(TCBDB *bdb){
+  const char *rp = bdb->opaque;
+  uint8_t cnum = *(uint8_t *)(rp++);
+  if(cnum == 0x0){
+    bdb->cmp = tcbdbcmplexical;
+  } else if(cnum == 0x1){
+    bdb->cmp = tcbdbcmpdecimal;
+  } else if(cnum == 0x2){
+    bdb->cmp = tcbdbcmpint32;
+  } else if(cnum == 0x3){
+    bdb->cmp = tcbdbcmpint64;
+  }
+  rp += 7;
+  uint32_t lnum;
+  memcpy(&lnum, rp, sizeof(lnum));
+  rp += sizeof(lnum);
+  bdb->lmemb = TCITOHL(lnum);
+  memcpy(&lnum, rp, sizeof(lnum));
+  rp += sizeof(lnum);
+  bdb->nmemb = TCITOHL(lnum);
+  uint64_t llnum;
+  memcpy(&llnum, rp, sizeof(llnum));
+  bdb->root = TCITOHLL(llnum);
+  rp += sizeof(llnum);
+  memcpy(&llnum, rp, sizeof(llnum));
+  bdb->first = TCITOHLL(llnum);
+  rp += sizeof(llnum);
+  memcpy(&llnum, rp, sizeof(llnum));
+  bdb->last = TCITOHLL(llnum);
+  rp += sizeof(llnum);
+  memcpy(&llnum, rp, sizeof(llnum));
+  bdb->lnum = TCITOHLL(llnum);
+  rp += sizeof(llnum);
+  memcpy(&llnum, rp, sizeof(llnum));
+  bdb->nnum = TCITOHLL(llnum);
+  rp += sizeof(llnum);
+  memcpy(&llnum, rp, sizeof(llnum));
+  bdb->rnum = TCITOHLL(llnum);
+  rp += sizeof(llnum);
+}
+
+
+/* Create a new leaf.
+   `bdb' specifies the B+ tree database object.
+   `prev' specifies the ID number of the previous leaf.
+   `next' specifies the ID number of the next leaf.
+   The return value is the new leaf object. */
+static BDBLEAF *tcbdbleafnew(TCBDB *bdb, uint64_t prev, uint64_t next){
+  assert(bdb);
+  BDBLEAF lent;
+  lent.id = ++bdb->lnum;
+  lent.recs = tclistnew2(bdb->lmemb + 1);
+  lent.prev = prev;
+  lent.next = next;
+  lent.dirty = true;
+  lent.dead = false;
+  tcmapputkeep(bdb->leafc, &(lent.id), sizeof(lent.id), &lent, sizeof(lent));
+  int rsiz;
+  return (BDBLEAF *)tcmapget(bdb->leafc, &(lent.id), sizeof(lent.id), &rsiz);
+}
+
+
+/* Remove a leaf from the cache.
+   `bdb' specifies the B+ tree database object.
+   `leaf' specifies the leaf object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbleafcacheout(TCBDB *bdb, BDBLEAF *leaf){
+  assert(bdb && leaf);
+  bool err = false;
+  if(leaf->dirty && !tcbdbleafsave(bdb, leaf)) err = true;
+  TCLIST *recs = leaf->recs;
+  int ln = TCLISTNUM(recs);
+  for(int i = 0; i < ln; i++){
+    BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+    free(recp->kbuf);
+    free(recp->vbuf);
+    if(recp->rest) tclistdel(recp->rest);
+  }
+  tclistdel(recs);
+  tcmapout(bdb->leafc, &(leaf->id), sizeof(leaf->id));
+  return !err;
+}
+
+
+/* Save a leaf into the internal database.
+   `bdb' specifies the B+ tree database object.
+   `leaf' specifies the leaf object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbleafsave(TCBDB *bdb, BDBLEAF *leaf){
+  assert(bdb && leaf);
+  TCDODEBUG(bdb->cnt_saveleaf++);
+  TCXSTR *rbuf = tcxstrnew3(BDBPAGEBUFSIZ);
+  char hbuf[(sizeof(uint64_t)+1)*3];
+  char *wp = hbuf;
+  uint64_t llnum;
+  int step;
+  llnum = leaf->prev;
+  TCSETVNUMBUF64(step, wp, llnum);
+  wp += step;
+  llnum = leaf->next;
+  TCSETVNUMBUF64(step, wp, llnum);
+  wp += step;
+  TCXSTRCAT(rbuf, hbuf, wp - hbuf);
+  TCLIST *recs = leaf->recs;
+  int ln = TCLISTNUM(recs);
+  for(int i = 0; i < ln; i++){
+    BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+    int lnum;
+    wp = hbuf;
+    lnum = recp->ksiz;
+    TCSETVNUMBUF(step, wp, lnum);
+    wp += step;
+    lnum = recp->vsiz;
+    TCSETVNUMBUF(step, wp, lnum);
+    wp += step;
+    TCLIST *rest = recp->rest;
+    int rnum = rest ? TCLISTNUM(rest) : 0;
+    TCSETVNUMBUF(step, wp, rnum);
+    wp += step;
+    TCXSTRCAT(rbuf, hbuf, wp - hbuf);
+    TCXSTRCAT(rbuf, recp->kbuf, recp->ksiz);
+    TCXSTRCAT(rbuf, recp->vbuf, recp->vsiz);
+    for(int j = 0; j < rnum; j++){
+      int vsiz;
+      const char *vbuf = tclistval(rest, j, &vsiz);
+      TCSETVNUMBUF(step, hbuf, vsiz);
+      TCXSTRCAT(rbuf, hbuf, step);
+      TCXSTRCAT(rbuf, vbuf, vsiz);
+    }
+  }
+  bool err = false;
+  step = sprintf(hbuf, "%llx", (unsigned long long)leaf->id);
+  if(ln < 1 && !tchdbout(bdb->hdb, hbuf, step) && tchdbecode(bdb->hdb) != TCENOREC)
+    err = true;
+  if(!leaf->dead && !tchdbput(bdb->hdb, hbuf, step, TCXSTRPTR(rbuf), TCXSTRSIZE(rbuf)))
+    err = true;
+  tcxstrdel(rbuf);
+  leaf->dirty = false;
+  return !err;
+}
+
+
+/* Load a leaf from the internal database.
+   `bdb' specifies the B+ tree database object.
+   `id' specifies the ID number of the leaf.
+   The return value is the leaf object or `NULL' on failure. */
+static BDBLEAF *tcbdbleafload(TCBDB *bdb, uint64_t id){
+  assert(bdb && id > 0);
+  bool clk = BDBLOCKCACHE(bdb);
+  int rsiz;
+  BDBLEAF *leaf = (BDBLEAF *)tcmapget3(bdb->leafc, &id, sizeof(id), &rsiz);
+  if(leaf){
+    if(clk) BDBUNLOCKCACHE(bdb);
+    return leaf;
+  }
+  if(clk) BDBUNLOCKCACHE(bdb);
+  TCDODEBUG(bdb->cnt_loadleaf++);
+  char hbuf[(sizeof(uint64_t)+1)*3];
+  int step;
+  step = sprintf(hbuf, "%llx", (unsigned long long)id);
+  char *rbuf = NULL;
+  char wbuf[BDBPAGEBUFSIZ];
+  const char *rp = NULL;
+  rsiz = tchdbget3(bdb->hdb, hbuf, step, wbuf, BDBPAGEBUFSIZ);
+  if(rsiz < 1){
+    tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+    return false;
+  } else if(rsiz < BDBPAGEBUFSIZ){
+    rp = wbuf;
+  } else {
+    if(!(rbuf = tchdbget(bdb->hdb, hbuf, step, &rsiz))){
+      tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+      return false;
+    }
+    rp = rbuf;
+  }
+  BDBLEAF lent;
+  lent.id = id;
+  uint64_t llnum;
+  TCREADVNUMBUF64(rp, llnum, step);
+  lent.prev = llnum;
+  rp += step;
+  rsiz -= step;
+  TCREADVNUMBUF64(rp, llnum, step);
+  lent.next = llnum;
+  rp += step;
+  rsiz -= step;
+  lent.dirty = false;
+  lent.dead = false;
+  lent.recs = tclistnew2(bdb->lmemb + 1);
+  bool err = false;
+  while(rsiz >= 3){
+    BDBREC rec;
+    TCREADVNUMBUF(rp, rec.ksiz, step);
+    rp += step;
+    rsiz -= step;
+    TCREADVNUMBUF(rp, rec.vsiz, step);
+    rp += step;
+    rsiz -= step;
+    int rnum;
+    TCREADVNUMBUF(rp, rnum, step);
+    rp += step;
+    rsiz -= step;
+    if(rsiz < rec.ksiz + rec.vsiz + rnum){
+      err = true;
+      break;
+    }
+    TCMEMDUP(rec.kbuf, rp, rec.ksiz);
+    rp += rec.ksiz;
+    rsiz -= rec.ksiz;
+    TCMEMDUP(rec.vbuf, rp, rec.vsiz);
+    rp += rec.vsiz;
+    rsiz -= rec.vsiz;
+    if(rnum > 0){
+      rec.rest = tclistnew2(rnum);
+      while(rnum-- > 0 && rsiz > 0){
+        int vsiz;
+        TCREADVNUMBUF(rp, vsiz, step);
+        rp += step;
+        rsiz -= step;
+        if(rsiz < vsiz){
+          err = true;
+          break;
+        }
+        TCLISTPUSH(rec.rest, rp, vsiz);
+        rp += vsiz;
+        rsiz -= vsiz;
+      }
+    } else {
+      rec.rest = NULL;
+    }
+    TCLISTPUSH(lent.recs, &rec, sizeof(rec));
+  }
+  free(rbuf);
+  if(err || rsiz != 0){
+    tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+    return NULL;
+  }
+  clk = BDBLOCKCACHE(bdb);
+  tcmapputkeep(bdb->leafc, &(lent.id), sizeof(lent.id), &lent, sizeof(lent));
+  leaf = (BDBLEAF *)tcmapget(bdb->leafc, &(lent.id), sizeof(lent.id), &rsiz);
+  if(clk) BDBUNLOCKCACHE(bdb);
+  return leaf;
+}
+
+
+/* Load the historical leaf from the internal database.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is the pointer to the leaf, else, it is `NULL'. */
+static BDBLEAF *tcbdbgethistleaf(TCBDB *bdb, const char *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  BDBLEAF *leaf = tcbdbleafload(bdb, bdb->hleaf);
+  if(!leaf) return NULL;
+  int ln = TCLISTNUM(leaf->recs);
+  if(ln < 2) return NULL;
+  BDBREC *recp = (BDBREC *)TCLISTVALPTR(leaf->recs, 0);
+  int rv;
+  if(bdb->cmp == tcbdbcmplexical){
+    TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+  } else {
+    rv = bdb->cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, bdb->cmpop);
+  }
+  if(rv == 0) return leaf;
+  if(rv < 0) return NULL;
+  recp = (BDBREC *)TCLISTVALPTR(leaf->recs, ln - 1);
+  if(bdb->cmp == tcbdbcmplexical){
+    TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+  } else {
+    rv = bdb->cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, bdb->cmpop);
+  }
+  if(rv <= 0 || leaf->next < 1) return leaf;
+  return NULL;
+}
+
+
+/* Add a record to a leaf.
+   `bdb' specifies the B+ tree database object.
+   `leaf' specifies the leaf object.
+   `dmode' specifies behavior when the key overlaps.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbleafaddrec(TCBDB *bdb, BDBLEAF *leaf, int dmode,
+                            const char *kbuf, int ksiz, const char *vbuf, int vsiz){
+  assert(bdb && leaf && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  BDBCMP cmp = bdb->cmp;
+  void *cmpop = bdb->cmpop;
+  TCLIST *recs = leaf->recs;
+  int ln = TCLISTNUM(recs);
+  int left = 0;
+  int right = ln;
+  int i = (left + right) / 2;
+  while(right >= left && i < ln){
+    BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+    int rv;
+    if(cmp == tcbdbcmplexical){
+      TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+    } else {
+      rv = cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, cmpop);
+    }
+    if(rv == 0){
+      break;
+    } else if(rv <= 0){
+      right = i - 1;
+    } else {
+      left = i + 1;
+    }
+    i = (left + right) / 2;
+  }
+  while(i < ln){
+    BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+    int rv;
+    if(cmp == tcbdbcmplexical){
+      TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+    } else {
+      rv = cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, cmpop);
+    }
+    if(rv == 0){
+      switch(dmode){
+      case BDBPDKEEP:
+        return false;
+      case BDBPDCAT:
+        TCREALLOC(recp->vbuf, recp->vbuf, recp->vsiz + vsiz + 1);
+        memcpy(recp->vbuf + recp->vsiz, vbuf, vsiz);
+        recp->vsiz += vsiz;
+        recp->vbuf[recp->vsiz] = '\0';
+        break;
+      case BDBPDDUP:
+        if(!recp->rest) recp->rest = tclistnew();
+        TCLISTPUSH(recp->rest, vbuf, vsiz);
+        bdb->rnum++;
+        break;
+      case BDBPDDUPB:
+        if(!recp->rest) recp->rest = tclistnew();
+        tclistunshift(recp->rest, recp->vbuf, recp->vsiz);
+        if(vsiz > recp->vsiz) TCREALLOC(recp->vbuf, recp->vbuf, vsiz + 1);
+        memcpy(recp->vbuf, vbuf, vsiz);
+        recp->vbuf[vsiz] = '\0';
+        recp->vsiz = vsiz;
+        bdb->rnum++;
+        break;
+      default:
+        if(vsiz > recp->vsiz) TCREALLOC(recp->vbuf, recp->vbuf, vsiz + 1);
+        memcpy(recp->vbuf, vbuf, vsiz);
+        recp->vbuf[vsiz] = '\0';
+        recp->vsiz = vsiz;
+        break;
+      }
+      break;
+    } else if(rv < 0){
+      BDBREC rec;
+      TCMEMDUP(rec.kbuf, kbuf, ksiz);
+      rec.ksiz = ksiz;
+      TCMEMDUP(rec.vbuf, vbuf, vsiz);
+      rec.vsiz = vsiz;
+      rec.rest = NULL;
+      tclistinsert(recs, i, &rec, sizeof(rec));
+      bdb->rnum++;
+      break;
+    }
+    i++;
+  }
+  if(i >= ln){
+    BDBREC rec;
+    TCMEMDUP(rec.kbuf, kbuf, ksiz);
+    rec.ksiz = ksiz;
+    TCMEMDUP(rec.vbuf, vbuf, vsiz);
+    rec.vsiz = vsiz;
+    rec.rest = NULL;
+    TCLISTPUSH(recs, &rec, sizeof(rec));
+    bdb->rnum++;
+  }
+  leaf->dirty = true;
+  return true;
+}
+
+
+/* Calculate the size of data of a leaf object.
+   `bdb' specifies the B+ tree database object.
+   `leaf' specifies the leaf object.
+   The return value is size of data of the leaf. */
+static int tcbdbleafdatasize(BDBLEAF *leaf){
+  assert(leaf);
+  int sum = 0;
+  TCLIST *recs = leaf->recs;
+  int ln = TCLISTNUM(recs);
+  for(int i = 0; i < ln; i++){
+    BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+    sum += recp->ksiz + recp->vsiz;
+    if(recp->rest){
+      TCLIST *rest = recp->rest;
+      int rnum = TCLISTNUM(rest);
+      for(int j = 0; j < rnum; j++){
+        int vsiz;
+        tclistval(rest, j, &vsiz);
+        sum += vsiz;
+      }
+    }
+  }
+  return sum;
+}
+
+
+/* Divide a leaf into two.
+   `bdb' specifies the B+ tree database object.
+   `leaf' specifies the leaf object.
+   The return value is the new leaf object or `NULL' on failure. */
+static BDBLEAF *tcbdbleafdivide(TCBDB *bdb, BDBLEAF *leaf){
+  assert(bdb && leaf);
+  bdb->hleaf = 0;
+  TCLIST *recs = leaf->recs;
+  int mid = TCLISTNUM(recs) / 2;
+  BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, mid);
+  BDBLEAF *newleaf = tcbdbleafnew(bdb, leaf->id, leaf->next);
+  if(newleaf->next > 0){
+    BDBLEAF *nextleaf = tcbdbleafload(bdb, newleaf->next);
+    if(!nextleaf) return NULL;
+    nextleaf->prev = newleaf->id;
+    nextleaf->dirty = true;
+  }
+  leaf->next = newleaf->id;
+  leaf->dirty = true;
+  int ln = TCLISTNUM(recs);
+  TCLIST *newrecs = newleaf->recs;
+  for(int i = mid; i < ln; i++){
+    recp = (BDBREC *)TCLISTVALPTR(recs, i);
+    TCLISTPUSH(newrecs, recp, sizeof(*recp));
+  }
+  ln = TCLISTNUM(newrecs);
+  for(int i = 0; i < ln; i++){
+    int rsiz;
+    free(tclistpop(recs, &rsiz));
+  }
+  return newleaf;
+}
+
+
+/* Cut off the path to a leaf and mark it dead.
+   `bdb' specifies the B+ tree database object.
+   `leaf' specifies the leaf object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbleafkill(TCBDB *bdb, BDBLEAF *leaf){
+  assert(bdb && leaf);
+  BDBNODE *node = tcbdbnodeload(bdb, bdb->hist[bdb->hnum-1]);
+  if(!node) return false;
+  if(tcbdbnodesubidx(bdb, node, leaf->id)){
+    TCDODEBUG(bdb->cnt_killleaf++);
+    if(bdb->hleaf == leaf->id) bdb->hleaf = 0;
+    if(leaf->prev > 0){
+      BDBLEAF *tleaf = tcbdbleafload(bdb, leaf->prev);
+      if(!tleaf) return false;
+      tleaf->next = leaf->next;
+      tleaf->dirty = true;
+      if(bdb->last == leaf->id) bdb->last = leaf->prev;
+    }
+    if(leaf->next > 0){
+      BDBLEAF *tleaf = tcbdbleafload(bdb, leaf->next);
+      if(!tleaf) return false;
+      tleaf->prev = leaf->prev;
+      tleaf->dirty = true;
+      if(bdb->first == leaf->id) bdb->first = leaf->next;
+    }
+    leaf->dead = true;
+  }
+  return true;
+}
+
+
+/* Create a new node.
+   `bdb' specifies the B+ tree database object.
+   `heir' specifies the ID of the child before the first index.
+   The return value is the new node object. */
+static BDBNODE *tcbdbnodenew(TCBDB *bdb, uint64_t heir){
+  assert(bdb && heir > 0);
+  BDBNODE nent;
+  nent.id = ++bdb->nnum + BDBNODEIDBASE;
+  nent.idxs = tclistnew2(bdb->nmemb + 1);
+  nent.heir = heir;
+  nent.dirty = true;
+  tcmapputkeep(bdb->nodec, &(nent.id), sizeof(nent.id), &nent, sizeof(nent));
+  int rsiz;
+  return (BDBNODE *)tcmapget(bdb->nodec, &(nent.id), sizeof(nent.id), &rsiz);
+}
+
+
+/* Remove a node from the cache.
+   `bdb' specifies the B+ tree database object.
+   `node' specifies the node object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbnodecacheout(TCBDB *bdb, BDBNODE *node){
+  assert(bdb && node);
+  bool err = false;
+  if(node->dirty && !tcbdbnodesave(bdb, node)) err = true;
+  TCLIST *idxs = node->idxs;
+  int ln = TCLISTNUM(idxs);
+  for(int i = 0; i < ln; i++){
+    BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+    free(idxp->kbuf);
+  }
+  tclistdel(idxs);
+  tcmapout(bdb->nodec, &(node->id), sizeof(node->id));
+  return !err;
+}
+
+
+/* Save a node into the internal database.
+   `bdb' specifies the B+ tree database object.
+   `node' specifies the node object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbnodesave(TCBDB *bdb, BDBNODE *node){
+  assert(bdb && node);
+  TCDODEBUG(bdb->cnt_savenode++);
+  TCXSTR *rbuf = tcxstrnew3(BDBPAGEBUFSIZ);
+  char hbuf[(sizeof(uint64_t)+1)*2];
+  uint64_t llnum;
+  int step;
+  llnum = node->heir;
+  TCSETVNUMBUF64(step, hbuf, llnum);
+  TCXSTRCAT(rbuf, hbuf, step);
+  TCLIST *idxs = node->idxs;
+  int ln = TCLISTNUM(idxs);
+  for(int i = 0; i < ln; i++){
+    BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+    char *wp = hbuf;
+    llnum = idxp->pid;
+    TCSETVNUMBUF64(step, wp, llnum);
+    wp += step;
+    uint32_t lnum = idxp->ksiz;
+    TCSETVNUMBUF(step, wp, lnum);
+    wp += step;
+    TCXSTRCAT(rbuf, hbuf, wp - hbuf);
+    TCXSTRCAT(rbuf, idxp->kbuf, idxp->ksiz);
+  }
+  bool err = false;
+  step = sprintf(hbuf, "#%llx", (unsigned long long)(node->id - BDBNODEIDBASE));
+  if(!tchdbput(bdb->hdb, hbuf, step, TCXSTRPTR(rbuf), TCXSTRSIZE(rbuf))) err = true;
+  tcxstrdel(rbuf);
+  node->dirty = false;
+  return !err;
+}
+
+
+/* Load a node from the internal database.
+   `bdb' specifies the B+ tree database object.
+   `id' specifies the ID number of the node.
+   The return value is the node object or `NULL' on failure. */
+static BDBNODE *tcbdbnodeload(TCBDB *bdb, uint64_t id){
+  assert(bdb && id > BDBNODEIDBASE);
+  bool clk = BDBLOCKCACHE(bdb);
+  int rsiz;
+  BDBNODE *node = (BDBNODE *)tcmapget3(bdb->nodec, &id, sizeof(id), &rsiz);
+  if(node){
+    if(clk) BDBUNLOCKCACHE(bdb);
+    return node;
+  }
+  if(clk) BDBUNLOCKCACHE(bdb);
+  TCDODEBUG(bdb->cnt_loadnode++);
+  char hbuf[(sizeof(uint64_t)+1)*2];
+  int step;
+  step = sprintf(hbuf, "#%llx", (unsigned long long)(id - BDBNODEIDBASE));
+  char *rbuf = NULL;
+  char wbuf[BDBPAGEBUFSIZ];
+  const char *rp = NULL;
+  rsiz = tchdbget3(bdb->hdb, hbuf, step, wbuf, BDBPAGEBUFSIZ);
+  if(rsiz < 1){
+    tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+    return false;
+  } else if(rsiz < BDBPAGEBUFSIZ){
+    rp = wbuf;
+  } else {
+    if(!(rbuf = tchdbget(bdb->hdb, hbuf, step, &rsiz))){
+      tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+      return false;
+    }
+    rp = rbuf;
+  }
+  BDBNODE nent;
+  nent.id = id;
+  uint64_t llnum;
+  TCREADVNUMBUF64(rp, llnum, step);
+  nent.heir = llnum;
+  rp += step;
+  rsiz -= step;
+  nent.dirty = false;
+  nent.idxs = tclistnew2(bdb->nmemb + 1);
+  bool err = false;
+  while(rsiz >= 2){
+    BDBIDX idx;
+    TCREADVNUMBUF64(rp, idx.pid, step);
+    rp += step;
+    rsiz -= step;
+    TCREADVNUMBUF(rp, idx.ksiz, step);
+    rp += step;
+    rsiz -= step;
+    if(rsiz < idx.ksiz){
+      err = true;
+      break;
+    }
+    TCMEMDUP(idx.kbuf, rp, idx.ksiz);
+    rp += idx.ksiz;
+    rsiz -= idx.ksiz;
+    TCLISTPUSH(nent.idxs, &idx, sizeof(idx));
+  }
+  free(rbuf);
+  if(err || rsiz != 0){
+    tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+    return NULL;
+  }
+  clk = BDBLOCKCACHE(bdb);
+  tcmapputkeep(bdb->nodec, &(nent.id), sizeof(nent.id), &nent, sizeof(nent));
+  node = (BDBNODE *)tcmapget(bdb->nodec, &(nent.id), sizeof(nent.id), &rsiz);
+  if(clk) BDBUNLOCKCACHE(bdb);
+  return node;
+}
+
+
+/* Add an index to a node.
+   `bdb' specifies the B+ tree database object.
+   `node' specifies the node object.
+   `order' specifies whether the calling sequence is orderd or not.
+   `pid' specifies the ID number of referred page.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key. */
+static void tcbdbnodeaddidx(TCBDB *bdb, BDBNODE *node, bool order, uint64_t pid,
+                            const char *kbuf, int ksiz){
+  assert(bdb && node && pid > 0 && kbuf && ksiz >= 0);
+  BDBIDX idx;
+  idx.pid = pid;
+  TCMEMDUP(idx.kbuf, kbuf, ksiz);
+  idx.ksiz = ksiz;
+  BDBCMP cmp = bdb->cmp;
+  void *cmpop = bdb->cmpop;
+  TCLIST *idxs = node->idxs;
+  if(order){
+    TCLISTPUSH(idxs, &idx, sizeof(idx));
+  } else {
+    int ln = TCLISTNUM(idxs);
+    int left = 0;
+    int right = ln;
+    int i = (left + right) / 2;
+    while(right >= left && i < ln){
+      BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+      int rv;
+      if(cmp == tcbdbcmplexical){
+        TCCMPLEXICAL(rv, kbuf, ksiz, idxp->kbuf, idxp->ksiz);
+      } else {
+        rv = cmp(kbuf, ksiz, idxp->kbuf, idxp->ksiz, cmpop);
+      }
+      if(rv == 0){
+        break;
+      } else if(rv <= 0){
+        right = i - 1;
+      } else {
+        left = i + 1;
+      }
+      i = (left + right) / 2;
+    }
+    while(i < ln){
+      BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+      int rv;
+      if(cmp == tcbdbcmplexical){
+        TCCMPLEXICAL(rv, kbuf, ksiz, idxp->kbuf, idxp->ksiz);
+      } else {
+        rv = cmp(kbuf, ksiz, idxp->kbuf, idxp->ksiz, cmpop);
+      }
+      if(rv < 0){
+        tclistinsert(idxs, i, &idx, sizeof(idx));
+        break;
+      }
+      i++;
+    }
+    if(i >= ln) TCLISTPUSH(idxs, &idx, sizeof(idx));
+  }
+  node->dirty = true;
+}
+
+
+/* Subtract an index from a node.
+   `bdb' specifies the B+ tree database object.
+   `node' specifies the node object.
+   `pid' specifies the ID number of referred page.
+   The return value is whether the subtraction is completed. */
+static bool tcbdbnodesubidx(TCBDB *bdb, BDBNODE *node, uint64_t pid){
+  assert(bdb && node && pid > 0);
+  TCLIST *idxs = node->idxs;
+  int ln = TCLISTNUM(idxs);
+  if(ln < 2) return false;
+  if(node->heir == pid){
+    int rsiz;
+    BDBIDX *idxp = (BDBIDX *)tclistshift(idxs, &rsiz);
+    node->heir = idxp->pid;
+    free(idxp->kbuf);
+    free(idxp);
+    node->dirty = true;
+    return true;
+  } else {
+    int ln = TCLISTNUM(idxs);
+    for(int i = 0; i < ln; i++){
+      BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+      if(idxp->pid == pid){
+        int rsiz;
+        free(idxp->kbuf);
+        free(tclistremove(idxs, i, &rsiz));
+        node->dirty = true;
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+
+/* Search the leaf object corresponding to a key.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   The return value is the ID number of the leaf object or 0 on failure. */
+static uint64_t tcbdbsearchleaf(TCBDB *bdb, const char *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  BDBCMP cmp = bdb->cmp;
+  void *cmpop = bdb->cmpop;
+  uint64_t *hist = bdb->hist;
+  uint64_t pid = bdb->root;
+  int hnum = 0;
+  bdb->hleaf = 0;
+  while(pid > BDBNODEIDBASE){
+    BDBNODE *node = tcbdbnodeload(bdb, pid);
+    if(!node){
+      tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+      return 0;
+    }
+    TCLIST *idxs = node->idxs;
+    int ln = TCLISTNUM(idxs);
+    if(ln < 1){
+      tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+      return 0;
+    }
+    hist[hnum++] = node->id;
+    int left = 0;
+    int right = ln;
+    int i = (left + right) / 2;
+    BDBIDX *idxp = NULL;
+    while(right >= left && i < ln){
+      idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+      int rv;
+      if(cmp == tcbdbcmplexical){
+        TCCMPLEXICAL(rv, kbuf, ksiz, idxp->kbuf, idxp->ksiz);
+      } else {
+        rv = cmp(kbuf, ksiz, idxp->kbuf, idxp->ksiz, cmpop);
+      }
+      if(rv == 0){
+        break;
+      } else if(rv <= 0){
+        right = i - 1;
+      } else {
+        left = i + 1;
+      }
+      i = (left + right) / 2;
+    }
+    if(i > 0) i--;
+    while(i < ln){
+      idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+      int rv;
+      if(cmp == tcbdbcmplexical){
+        TCCMPLEXICAL(rv, kbuf, ksiz, idxp->kbuf, idxp->ksiz);
+      } else {
+        rv = cmp(kbuf, ksiz, idxp->kbuf, idxp->ksiz, cmpop);
+      }
+      if(rv < 0){
+        if(i == 0){
+          pid = node->heir;
+          break;
+        }
+        idxp = (BDBIDX *)TCLISTVALPTR(idxs, i - 1);
+        pid = idxp->pid;
+        break;
+      }
+      i++;
+    }
+    if(i >= ln) pid = idxp->pid;
+  }
+  if(!bdb->mmtx){
+    if(bdb->lleaf == pid) bdb->hleaf = pid;
+    bdb->lleaf = pid;
+  }
+  bdb->hnum = hnum;
+  return pid;
+}
+
+
+/* Search a record of a leaf.
+   `bdb' specifies the B+ tree database object.
+   `leaf' specifies the leaf object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `ip' specifies the pointer to a variable to fetch the index of the correspnding record.
+   The return value is the pointer to a corresponding record or `NULL' on failure. */
+static BDBREC *tcbdbsearchrec(TCBDB *bdb, BDBLEAF *leaf, const char *kbuf, int ksiz, int *ip){
+  assert(bdb && leaf && kbuf && ksiz >= 0);
+  BDBCMP cmp = bdb->cmp;
+  void *cmpop = bdb->cmpop;
+  TCLIST *recs = leaf->recs;
+  int ln = TCLISTNUM(recs);
+  int left = 0;
+  int right = ln;
+  int i = (left + right) / 2;
+  while(right >= left && i < ln){
+    BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+    int rv;
+    if(cmp == tcbdbcmplexical){
+      TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+    } else {
+      rv = cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, cmpop);
+    }
+    if(rv == 0){
+      if(ip) *ip = i;
+      return recp;
+    } else if(rv <= 0){
+      right = i - 1;
+    } else {
+      left = i + 1;
+    }
+    i = (left + right) / 2;
+  }
+  if(ip) *ip = i;
+  return NULL;
+}
+
+
+/* Adjust the caches for leaves and nodes.
+   `bdb' specifies the B+ tree database object.
+   The return value is true if successful, else, it is false. */
+static bool tcbdbcacheadjust(TCBDB *bdb){
+  bool err = false;
+  if(TCMAPRNUM(bdb->leafc) > bdb->lcnum){
+    TCDODEBUG(bdb->cnt_adjleafc++);
+    bool clk = BDBLOCKCACHE(bdb);
+    TCMAP *leafc = bdb->leafc;
+    tcmapiterinit(leafc);
+    for(int i = 0; i < BDBCACHEOUT; i++){
+      int rsiz;
+      if(!tcbdbleafcacheout(bdb, (BDBLEAF *)tcmapiterval(tcmapiternext(leafc, &rsiz), &rsiz)))
+        err = true;
+    }
+    if(clk) BDBUNLOCKCACHE(bdb);
+  }
+  if(TCMAPRNUM(bdb->nodec) > bdb->ncnum){
+    TCDODEBUG(bdb->cnt_adjnodec++);
+    bool clk = BDBLOCKCACHE(bdb);
+    TCMAP *nodec = bdb->nodec;
+    tcmapiterinit(nodec);
+    for(int i = 0; i < BDBCACHEOUT; i++){
+      int rsiz;
+      if(!tcbdbnodecacheout(bdb, (BDBNODE *)tcmapiterval(tcmapiternext(nodec, &rsiz), &rsiz)))
+        err = true;
+    }
+    if(clk) BDBUNLOCKCACHE(bdb);
+  }
+  return !err;
+}
+
+
+/* Purge dirty pages of caches for leaves and nodes.
+   `bdb' specifies the B+ tree database object. */
+static void tcbdbcachepurge(TCBDB *bdb){
+  bool clk = BDBLOCKCACHE(bdb);
+  int tsiz;
+  const char *tmp;
+  tcmapiterinit(bdb->leafc);
+  while((tmp = tcmapiternext(bdb->leafc, &tsiz)) != NULL){
+    int lsiz;
+    BDBLEAF *leaf = (BDBLEAF *)tcmapiterval(tmp, &lsiz);
+    if(!leaf->dirty) continue;
+    TCLIST *recs = leaf->recs;
+    int ln = TCLISTNUM(recs);
+    for(int i = 0; i < ln; i++){
+      BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+      free(recp->kbuf);
+      free(recp->vbuf);
+      if(recp->rest) tclistdel(recp->rest);
+    }
+    tclistdel(recs);
+    tcmapout(bdb->leafc, tmp, tsiz);
+  }
+  tcmapiterinit(bdb->nodec);
+  while((tmp = tcmapiternext(bdb->nodec, &tsiz)) != NULL){
+    int nsiz;
+    BDBNODE *node = (BDBNODE *)tcmapiterval(tmp, &nsiz);
+    if(!node->dirty) continue;
+    TCLIST *idxs = node->idxs;
+    int ln = TCLISTNUM(idxs);
+    for(int i = 0; i < ln; i++){
+      BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+      free(idxp->kbuf);
+    }
+    tclistdel(idxs);
+    tcmapout(bdb->nodec, tmp, tsiz);
+  }
+  if(clk) BDBUNLOCKCACHE(bdb);
+}
+
+
+/* Open a database file and connect a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `path' specifies the path of the internal database file.
+   `omode' specifies the connection mode.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbopenimpl(TCBDB *bdb, const char *path, int omode){
+  assert(bdb && path);
+  int homode = HDBOREADER;
+  if(omode & BDBOWRITER){
+    homode = HDBOWRITER;
+    if(omode & BDBOCREAT) homode |= HDBOCREAT;
+    if(omode & BDBOTRUNC) homode |= HDBOTRUNC;
+    bdb->wmode = true;
+  } else {
+    bdb->wmode = false;
+  }
+  if(omode & BDBONOLCK) homode |= HDBONOLCK;
+  if(omode & BDBOLCKNB) homode |= HDBOLCKNB;
+  tchdbsettype(bdb->hdb, HDBTBTREE);
+  if(!tchdbopen(bdb->hdb, path, homode)) return false;
+  bdb->root = 0;
+  bdb->first = 0;
+  bdb->last = 0;
+  bdb->lnum = 0;
+  bdb->nnum = 0;
+  bdb->rnum = 0;
+  bdb->opaque = tchdbopaque(bdb->hdb);
+  bdb->leafc = tcmapnew2(bdb->lcnum * 2 + 1);
+  bdb->nodec = tcmapnew2(bdb->ncnum * 2 + 1);
+  if(bdb->wmode && tchdbrnum(bdb->hdb) < 1){
+    BDBLEAF *leaf = tcbdbleafnew(bdb, 0, 0);
+    bdb->root = leaf->id;
+    bdb->first = leaf->id;
+    bdb->last = leaf->id;
+    bdb->lnum = 1;
+    bdb->nnum = 0;
+    bdb->rnum = 0;
+    if(!bdb->cmp){
+      bdb->cmp = tcbdbcmplexical;
+      bdb->cmpop = NULL;
+    }
+    tcdumpmeta(bdb);
+    if(!tcbdbleafsave(bdb, leaf)){
+      tcmapdel(bdb->nodec);
+      tcmapdel(bdb->leafc);
+      tchdbclose(bdb->hdb);
+      return false;
+    }
+  }
+  tcloadmeta(bdb);
+  if(!bdb->cmp){
+    tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    tcmapdel(bdb->nodec);
+    tcmapdel(bdb->leafc);
+    tchdbclose(bdb->hdb);
+    return false;
+  }
+  if(bdb->lmemb < BDBMINLMEMB || bdb->nmemb < BDBMINNMEMB ||
+     bdb->root < 1 || bdb->first < 1 || bdb->last < 1 ||
+     bdb->lnum < 0 || bdb->nnum < 0 || bdb->rnum < 0){
+    tcbdbsetecode(bdb, TCEMETA, __FILE__, __LINE__, __func__);
+    tcmapdel(bdb->nodec);
+    tcmapdel(bdb->leafc);
+    tchdbclose(bdb->hdb);
+    return false;
+  }
+  bdb->open = true;
+  uint8_t hopts = tchdbopts(bdb->hdb);
+  uint8_t opts = 0;
+  if(hopts & HDBTLARGE) opts |= BDBTLARGE;
+  if(hopts & HDBTDEFLATE) opts |= BDBTDEFLATE;
+  if(hopts & HDBTTCBS) opts |= BDBTTCBS;
+  bdb->opts = opts;
+  bdb->hleaf = 0;
+  bdb->lleaf = 0;
+  bdb->tran = false;
+  bdb->rbopaque = NULL;
+  return true;
+}
+
+
+/* Close a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbcloseimpl(TCBDB *bdb){
+  assert(bdb);
+  if(bdb->tran){
+    tcbdbcachepurge(bdb);
+    memcpy(bdb->opaque, bdb->rbopaque, BDBOPAQUESIZ);
+    tcloadmeta(bdb);
+    free(bdb->rbopaque);
+    bdb->tran = false;
+    bdb->rbopaque = NULL;
+    BDBUNLOCKTRAN(bdb);
+  }
+  bool err = false;
+  bdb->open = false;
+  const char *vbuf;
+  int vsiz;
+  TCMAP *leafc = bdb->leafc;
+  tcmapiterinit(leafc);
+  while((vbuf = tcmapiternext(leafc, &vsiz)) != NULL){
+    if(!tcbdbleafcacheout(bdb, (BDBLEAF *)tcmapiterval(vbuf, &vsiz))) err = true;
+  }
+  TCMAP *nodec = bdb->nodec;
+  tcmapiterinit(nodec);
+  while((vbuf = tcmapiternext(nodec, &vsiz)) != NULL){
+    if(!tcbdbnodecacheout(bdb, (BDBNODE *)tcmapiterval(vbuf, &vsiz))) err = true;
+  }
+  if(bdb->wmode) tcdumpmeta(bdb);
+  tcmapdel(bdb->nodec);
+  tcmapdel(bdb->leafc);
+  if(!tchdbclose(bdb->hdb)) err = true;
+  return !err;
+}
+
+
+/* Store a record into a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   `dmode' specifies behavior when the key overlaps.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbputimpl(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
+                         int dmode){
+  assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  BDBLEAF *leaf = NULL;
+  if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+    uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+    if(pid < 1) return false;
+    if(!(leaf = tcbdbleafload(bdb, pid))) return false;
+  }
+  if(!tcbdbleafaddrec(bdb, leaf, dmode, kbuf, ksiz, vbuf, vsiz)){
+    tcbdbsetecode(bdb, TCEKEEP, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  int rnum = TCLISTNUM(leaf->recs);
+  if(rnum > bdb->lmemb ||
+     (bdb->lsmax > 0 && rnum > BDBMINLMEMB && (bdb->lschk++ & (0x8 - 1)) == 0 &&
+      tcbdbleafdatasize(leaf) > bdb->lsmax)){
+    bdb->lschk = 0;
+    BDBLEAF *newleaf = tcbdbleafdivide(bdb, leaf);
+    if(!newleaf) return false;
+    if(leaf->id == bdb->last) bdb->last = newleaf->id;
+    uint64_t heir = leaf->id;
+    uint64_t pid = newleaf->id;
+    BDBREC *recp = (BDBREC *)TCLISTVALPTR(newleaf->recs, 0);
+    int ksiz = recp->ksiz;
+    char *kbuf;
+    TCMEMDUP(kbuf, recp->kbuf, ksiz);
+    while(true){
+      BDBNODE *node;
+      if(bdb->hnum < 1){
+        node = tcbdbnodenew(bdb, heir);
+        tcbdbnodeaddidx(bdb, node, true, pid, kbuf, ksiz);
+        bdb->root = node->id;
+        free(kbuf);
+        break;
+      }
+      uint64_t parent = bdb->hist[--bdb->hnum];
+      if(!(node = tcbdbnodeload(bdb, parent))){
+        free(kbuf);
+        return false;
+      }
+      tcbdbnodeaddidx(bdb, node, false, pid, kbuf, ksiz);
+      free(kbuf);
+      TCLIST *idxs = node->idxs;
+      int ln = TCLISTNUM(idxs);
+      if(ln <= bdb->nmemb) break;
+      int mid = ln / 2;
+      BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, mid);
+      BDBNODE *newnode = tcbdbnodenew(bdb, idxp->pid);
+      heir = node->id;
+      pid = newnode->id;
+      TCMEMDUP(kbuf, idxp->kbuf, idxp->ksiz);
+      ksiz = idxp->ksiz;
+      for(int i = mid + 1; i < ln; i++){
+        idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+        tcbdbnodeaddidx(bdb, newnode, true, idxp->pid, idxp->kbuf, idxp->ksiz);
+      }
+      ln = TCLISTNUM(newnode->idxs);
+      for(int i = 0; i < ln; i++){
+        int rsiz;
+        idxp = (BDBIDX *)tclistpop(idxs, &rsiz);
+        free(idxp->kbuf);
+        free(idxp);
+      }
+      node->dirty = true;
+    }
+    if(bdb->capnum > 0 && bdb->rnum > bdb->capnum){
+      uint64_t xnum = bdb->rnum - bdb->capnum;
+      BDBCUR *cur = tcbdbcurnew(bdb);
+      tcbdbcurfirstimpl(cur);
+      while((xnum--) > 0){
+        if(!tcbdbcuroutimpl(cur)){
+          tcbdbcurdel(cur);
+          return false;
+        }
+      }
+      tcbdbcurdel(cur);
+    }
+  }
+  if(!bdb->tran && !tcbdbcacheadjust(bdb)) return false;
+  return true;
+}
+
+
+/* Remove a record of a B+ tree database object.
+   `hdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdboutimpl(TCBDB *bdb, const char *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  BDBLEAF *leaf = NULL;
+  if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+    uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+    if(pid < 1) return false;
+    if(!(leaf = tcbdbleafload(bdb, pid))) return false;
+  }
+  int ri;
+  BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, &ri);
+  if(!recp){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  if(recp->rest){
+    free(recp->vbuf);
+    recp->vbuf = tclistshift(recp->rest, &(recp->vsiz));
+    if(TCLISTNUM(recp->rest) < 1){
+      tclistdel(recp->rest);
+      recp->rest = NULL;
+    }
+  } else {
+    free(recp->vbuf);
+    free(recp->kbuf);
+    int rsiz;
+    free(tclistremove(leaf->recs, ri, &rsiz));
+  }
+  leaf->dirty = true;
+  bdb->rnum--;
+  if(TCLISTNUM(leaf->recs) < 1 && bdb->hnum > 0 && !tcbdbleafkill(bdb, leaf)) return false;
+  if(!bdb->tran && !tcbdbcacheadjust(bdb)) return false;
+  return true;
+}
+
+
+/* Remove records of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdboutlist(TCBDB *bdb, const char *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  BDBLEAF *leaf = NULL;
+  if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+    uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+    if(pid < 1) return false;
+    if(!(leaf = tcbdbleafload(bdb, pid))) return false;
+  }
+  int ri;
+  BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, &ri);
+  if(!recp){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  int rnum = 1;
+  if(recp->rest){
+    rnum += TCLISTNUM(recp->rest);
+    tclistdel(recp->rest);
+  }
+  free(recp->vbuf);
+  free(recp->kbuf);
+  int rsiz;
+  free(tclistremove(leaf->recs, ri, &rsiz));
+  leaf->dirty = true;
+  bdb->rnum -= rnum;
+  if(TCLISTNUM(leaf->recs) < 1 && bdb->hnum > 0 && !tcbdbleafkill(bdb, leaf)) return false;
+  if(!bdb->tran && !tcbdbcacheadjust(bdb)) return false;
+  return true;
+}
+
+
+/* Retrieve a record in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the corresponding
+   record. */
+static const char *tcbdbgetimpl(TCBDB *bdb, const char *kbuf, int ksiz, int *sp){
+  assert(bdb && kbuf && ksiz >= 0 && sp);
+  BDBLEAF *leaf = NULL;
+  if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+    uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+    if(pid < 1) return NULL;
+    if(!(leaf = tcbdbleafload(bdb, pid))) return NULL;
+  }
+  BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, NULL);
+  if(!recp){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return NULL;
+  }
+  *sp = recp->vsiz;
+  return recp->vbuf;
+}
+
+
+/* Get the number of records corresponding a key in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is the number of the corresponding records, else, it is 0. */
+static int tcbdbgetnum(TCBDB *bdb, const char *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  BDBLEAF *leaf = NULL;
+  if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+    uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+    if(pid < 1) return 0;
+    if(!(leaf = tcbdbleafload(bdb, pid))) return 0;
+  }
+  BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, NULL);
+  if(!recp){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return recp->rest ? TCLISTNUM(recp->rest) + 1 : 1;
+}
+
+
+/* Retrieve records in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is a list object of the values of the corresponding records. */
+static TCLIST *tcbdbgetlist(TCBDB *bdb, const char *kbuf, int ksiz){
+  assert(bdb && kbuf && ksiz >= 0);
+  BDBLEAF *leaf = NULL;
+  if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+    uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+    if(pid < 1) return NULL;
+    if(!(leaf = tcbdbleafload(bdb, pid))) return NULL;
+  }
+  BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, NULL);
+  if(!recp){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return NULL;
+  }
+  TCLIST *vals;
+  TCLIST *rest = recp->rest;
+  if(rest){
+    int ln = TCLISTNUM(rest);
+    vals = tclistnew2(ln + 1);
+    TCLISTPUSH(vals, recp->vbuf, recp->vsiz);
+    for(int i = 0; i < ln; i++){
+      int vsiz;
+      const char *vbuf = tclistval(rest, i, &vsiz);
+      TCLISTPUSH(vals, vbuf, vsiz);
+    }
+  } else {
+    vals = tclistnew2(1);
+    TCLISTPUSH(vals, recp->vbuf, recp->vsiz);
+  }
+  return vals;
+}
+
+
+/* Get keys of ranged records in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `bkbuf' specifies the pointer to the region of the key of the beginning border.
+   `bksiz' specifies the size of the region of the beginning key.
+   `binc' specifies whether the beginning border is inclusive or not.
+   `ekbuf' specifies the pointer to the region of the key of the ending border.
+   `eksiz' specifies the size of the region of the ending key.
+   `einc' specifies whether the ending border is inclusive or not.
+   `max' specifies the maximum number of keys to be fetched.
+   `keys' specifies a list object to store the result.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbrangeimpl(TCBDB *bdb, const char *bkbuf, int bksiz, bool binc,
+                           const char *ekbuf, int eksiz, bool einc, int max, TCLIST *keys){
+  assert(bdb && keys);
+  bool err = false;
+  BDBCUR *cur = tcbdbcurnew(bdb);
+  if(bkbuf){
+    tcbdbcurjumpimpl(cur, bkbuf, bksiz, true);
+  } else {
+    tcbdbcurfirstimpl(cur);
+  }
+  BDBCMP cmp = bdb->cmp;
+  void *cmpop = bdb->cmpop;
+  const char *lbuf = NULL;
+  int lsiz = 0;
+  while(cur->id > 0){
+    const char *kbuf, *vbuf;
+    int ksiz, vsiz;
+    if(!tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+      if(tchdbecode(bdb->hdb) != TCEINVALID && tchdbecode(bdb->hdb) != TCENOREC) err = true;
+      break;
+    }
+    if(bkbuf && !binc){
+      if(cmp(kbuf, ksiz, bkbuf, bksiz, cmpop) == 0){
+        tcbdbcurnext(cur);
+        continue;
+      }
+      bkbuf = NULL;
+    }
+    if(ekbuf){
+      if(einc){
+        if(cmp(kbuf, ksiz, ekbuf, eksiz, cmpop) > 0) break;
+      } else {
+        if(cmp(kbuf, ksiz, ekbuf, eksiz, cmpop) >= 0) break;
+      }
+    }
+    if(!lbuf || lsiz != ksiz || memcmp(kbuf, lbuf, ksiz)){
+      TCLISTPUSH(keys, kbuf, ksiz);
+      if(max >= 0 && TCLISTNUM(keys) >= max) break;
+      lbuf = kbuf;
+      lsiz = ksiz;
+    }
+    tcbdbcurnextimpl(cur);
+  }
+  tcbdbcurdel(cur);
+  return !err;
+}
+
+
+/* Get forward matching keys in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `pbuf' specifies the pointer to the region of the prefix.
+   `psiz' specifies the size of the region of the prefix.
+   `max' specifies the maximum number of keys to be fetched.
+   `keys' specifies a list object to store the result.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbrangefwm(TCBDB *bdb, const char *pbuf, int psiz, int max, TCLIST *keys){
+  assert(bdb && pbuf && psiz >= 0 && keys);
+  bool err = false;
+  if(max < 0) max = INT_MAX;
+  BDBCUR *cur = tcbdbcurnew(bdb);
+  tcbdbcurjumpimpl(cur, pbuf, psiz, true);
+  const char *lbuf = NULL;
+  int lsiz = 0;
+  while(cur->id > 0){
+    const char *kbuf, *vbuf;
+    int ksiz, vsiz;
+    if(!tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+      if(tchdbecode(bdb->hdb) != TCEINVALID && tchdbecode(bdb->hdb) != TCENOREC) err = true;
+      break;
+    }
+    if(ksiz < psiz || memcmp(kbuf, pbuf, psiz)) break;
+    if(!lbuf || lsiz != ksiz || memcmp(kbuf, lbuf, ksiz)){
+      TCLISTPUSH(keys, kbuf, ksiz);
+      if(TCLISTNUM(keys) >= max) break;
+      lbuf = kbuf;
+      lsiz = ksiz;
+    }
+    tcbdbcurnextimpl(cur);
+  }
+  tcbdbcurdel(cur);
+  return !err;
+}
+
+
+/* Optimize the file of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `lmemb' specifies the number of members in each leaf page.
+   `nmemb' specifies the number of members in each non-leaf page.
+   `bnum' specifies the number of elements of the bucket array.
+   `apow' specifies the size of record alignment by power of 2.
+   `fpow' specifies the maximum number of elements of the free block pool by power of 2.
+   `opts' specifies options by bitwise or.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdboptimizeimpl(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+                              int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+  assert(bdb);
+  if(lmemb < 1) lmemb = bdb->lmemb;
+  if(nmemb < 1) nmemb = bdb->nmemb;
+  if(bnum < 1) bnum = tchdbrnum(bdb->hdb) * 2 + 1;
+  if(opts == UINT8_MAX) opts = bdb->opts;
+  const char *path = tchdbpath(bdb->hdb);
+  char *tpath = tcsprintf("%s%ctmp%c%llu", path, MYEXTCHR, MYEXTCHR, tchdbinode(bdb->hdb));
+  TCBDB *tbdb = tcbdbnew();
+  tcbdbsetcmpfunc(tbdb, bdb->cmp, bdb->cmpop);
+  tcbdbtune(tbdb, lmemb, nmemb, bnum, apow, fpow, opts);
+  tcbdbsetlsmax(tbdb, bdb->lsmax);
+  if(!tcbdbopen(tbdb, tpath, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){
+    tcbdbsetecode(bdb, tcbdbecode(tbdb), __FILE__, __LINE__, __func__);
+    tcbdbdel(tbdb);
+    free(tpath);
+    return false;
+  }
+  bool err = false;
+  BDBCUR *cur = tcbdbcurnew(bdb);
+  tcbdbcurfirstimpl(cur);
+  const char *kbuf, *vbuf;
+  int ksiz, vsiz;
+  while(!err && cur->id > 0 && tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+    if(!tcbdbputdup(tbdb, kbuf, ksiz, vbuf, vsiz)){
+      tcbdbsetecode(bdb, tcbdbecode(tbdb), __FILE__, __LINE__, __func__);
+      err = true;
+    }
+    tcbdbcurnextimpl(cur);
+  }
+  tcbdbcurdel(cur);
+  if(!tcbdbclose(tbdb)){
+    tcbdbsetecode(bdb, tcbdbecode(tbdb), __FILE__, __LINE__, __func__);
+    err = true;
+  }
+  tcbdbdel(tbdb);
+  if(unlink(path) == -1){
+    tcbdbsetecode(bdb, TCEUNLINK, __FILE__, __LINE__, __func__);
+    err = true;
+  }
+  if(rename(tpath, path) == -1){
+    tcbdbsetecode(bdb, TCERENAME, __FILE__, __LINE__, __func__);
+    err = true;
+  }
+  free(tpath);
+  if(err) return false;
+  tpath = tcstrdup(path);
+  int omode = (tchdbomode(bdb->hdb) & ~BDBOCREAT) & ~BDBOTRUNC;
+  if(!tcbdbcloseimpl(bdb)){
+    free(tpath);
+    return false;
+  }
+  bool rv = tcbdbopenimpl(bdb, tpath, omode);
+  free(tpath);
+  return rv;
+}
+
+
+/* Remove all records of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbvanishimpl(TCBDB *bdb){
+  assert(bdb);
+  char *path = tcstrdup(tchdbpath(bdb->hdb));
+  int omode = tchdbomode(bdb->hdb);
+  bool err = false;
+  if(!tcbdbcloseimpl(bdb)) err = true;
+  if(!tcbdbopenimpl(bdb, path, BDBOTRUNC | omode)) err = true;
+  free(path);
+  return !err;
+}
+
+
+/* Lock a method of the B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `wr' specifies whether the lock is writer or not.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdblockmethod(TCBDB *bdb, bool wr){
+  assert(bdb);
+  if(wr ? pthread_rwlock_wrlock(bdb->mmtx) != 0 : pthread_rwlock_rdlock(bdb->mmtx) != 0){
+    tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  TCTESTYIELD();
+  return true;
+}
+
+
+/* Unlock a method of the B+ tree database object.
+   `hdb' specifies the B+ tree database object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbunlockmethod(TCBDB *bdb){
+  assert(bdb);
+  if(pthread_rwlock_unlock(bdb->mmtx) != 0){
+    tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  TCTESTYIELD();
+  return true;
+}
+
+
+/* Lock the cache of the B+ tree database object.
+   `hdb' specifies the B+ tree database object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdblockcache(TCBDB *bdb){
+  assert(bdb);
+  if(pthread_mutex_lock(bdb->cmtx) != 0){
+    tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  TCTESTYIELD();
+  return true;
+}
+
+
+/* Unlock the cache of the B+ tree database object.
+   `hdb' specifies the B+ tree database object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbunlockcache(TCBDB *bdb){
+  assert(bdb);
+  if(pthread_mutex_unlock(bdb->cmtx) != 0){
+    tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  TCTESTYIELD();
+  return true;
+}
+
+
+/* Lock the transaction of the B+ tree database object.
+   `hdb' specifies the B+ tree database object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdblocktran(TCBDB *bdb){
+  assert(bdb);
+  if(pthread_mutex_lock(bdb->tmtx) != 0){
+    tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  TCTESTYIELD();
+  return true;
+}
+
+
+/* Unlock the transaction of the B+ tree database object.
+   `hdb' specifies the B+ tree database object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbunlocktran(TCBDB *bdb){
+  assert(bdb);
+  if(pthread_mutex_unlock(bdb->tmtx) != 0){
+    tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  TCTESTYIELD();
+  return true;
+}
+
+
+/* Move a cursor object to the first record.
+   `cur' specifies the cursor object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbcurfirstimpl(BDBCUR *cur){
+  assert(cur);
+  cur->id = cur->bdb->first;
+  cur->kidx = 0;
+  cur->vidx = 0;
+  return tcbdbcuradjust(cur, true);
+}
+
+
+/* Move a cursor object to the last record.
+   `cur' specifies the cursor object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbcurlastimpl(BDBCUR *cur){
+  assert(cur);
+  cur->id = cur->bdb->last;
+  cur->kidx = INT_MAX;
+  cur->vidx = INT_MAX;
+  return tcbdbcuradjust(cur, false);
+}
+
+
+/* Move a cursor object to around records corresponding a key.
+   `cur' specifies the cursor object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `forward' specifies whether the cursor is to be the front of records.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbcurjumpimpl(BDBCUR *cur, const char *kbuf, int ksiz, bool forward){
+  assert(cur && kbuf && ksiz >= 0);
+  TCBDB *bdb = cur->bdb;
+  uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+  if(pid < 1){
+    cur->id = 0;
+    cur->kidx = 0;
+    cur->vidx = 0;
+    return false;
+  }
+  BDBLEAF *leaf = tcbdbleafload(bdb, pid);
+  if(!leaf){
+    cur->id = 0;
+    cur->kidx = 0;
+    cur->vidx = 0;
+    return false;
+  }
+  if(TCLISTNUM(leaf->recs) < 1){
+    cur->id = pid;
+    cur->kidx = 0;
+    cur->vidx = 0;
+    return forward ? tcbdbcurnextimpl(cur) : tcbdbcurprevimpl(cur);
+  }
+  int ri;
+  BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, &ri);
+  if(recp){
+    cur->id = pid;
+    cur->kidx = ri;
+    if(forward){
+      cur->vidx = 0;
+    } else {
+      cur->vidx = recp->rest ? TCLISTNUM(recp->rest) : 0;
+    }
+    return true;
+  }
+  cur->id = leaf->id;
+  if(ri > 0 && ri >= TCLISTNUM(leaf->recs)) ri = TCLISTNUM(leaf->recs) - 1;
+  cur->kidx = ri;
+  recp = (BDBREC *)TCLISTVALPTR(leaf->recs, ri);
+  if(forward){
+    int rv;
+    if(bdb->cmp == tcbdbcmplexical){
+      TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+    } else {
+      rv = bdb->cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, bdb->cmpop);
+    }
+    if(rv < 0) return true;
+    cur->vidx = recp->rest ? TCLISTNUM(recp->rest) : 0;
+    return tcbdbcurnextimpl(cur);
+  }
+  int rv;
+  if(bdb->cmp == tcbdbcmplexical){
+    TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+  } else {
+    rv = bdb->cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, bdb->cmpop);
+  }
+  if(rv > 0) return true;
+  cur->vidx = 0;
+  return tcbdbcurprevimpl(cur);
+}
+
+
+/* Adjust a cursor object forward to the suitable record.
+   `cur' specifies the cursor object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbcuradjust(BDBCUR *cur, bool forward){
+  assert(cur);
+  TCBDB *bdb = cur->bdb;
+  while(true){
+    if(cur->id < 1){
+      tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+      cur->id = 0;
+      cur->kidx = 0;
+      cur->vidx = 0;
+      return false;
+    }
+    BDBLEAF *leaf = tcbdbleafload(bdb, cur->id);
+    if(!leaf) return false;
+    TCLIST *recs = leaf->recs;
+    int knum = TCLISTNUM(recs);
+    if(cur->kidx < 0){
+      if(forward){
+        cur->kidx = 0;
+        cur->vidx = 0;
+      } else {
+        cur->id = leaf->prev;
+        cur->kidx = INT_MAX;
+        cur->vidx = INT_MAX;
+      }
+    } else if(cur->kidx >= knum){
+      if(forward){
+        cur->id = leaf->next;
+        cur->kidx = 0;
+        cur->vidx = 0;
+      } else {
+        cur->kidx = knum - 1;
+        cur->vidx = INT_MAX;
+      }
+    } else {
+      BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, cur->kidx);
+      int vnum = recp->rest ? TCLISTNUM(recp->rest) + 1 : 1;
+      if(cur->vidx < 0){
+        if(forward){
+          cur->vidx = 0;
+        } else {
+          cur->kidx--;
+          cur->vidx = INT_MAX;
+        }
+      } else if(cur->vidx >= vnum){
+        if(forward){
+          cur->kidx++;
+          cur->vidx = 0;
+          if(cur->kidx >= knum){
+            cur->id = leaf->next;
+            cur->kidx = 0;
+            cur->vidx = 0;
+          } else {
+            break;
+          }
+        } else {
+          cur->vidx = vnum - 1;
+          if(cur->vidx >= 0) break;
+        }
+      } else {
+        break;
+      }
+    }
+  }
+  return true;
+}
+
+
+/* Move a cursor object to the previous record.
+   `cur' specifies the cursor object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbcurprevimpl(BDBCUR *cur){
+  assert(cur);
+  cur->vidx--;
+  return tcbdbcuradjust(cur, false);
+}
+
+
+/* Move a cursor object to the next record.
+   `cur' specifies the cursor object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbcurnextimpl(BDBCUR *cur){
+  assert(cur);
+  cur->vidx++;
+  return tcbdbcuradjust(cur, true);
+}
+
+
+/* Insert a record around a cursor object.
+   `cur' specifies the cursor object.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   `cpmode' specifies detail adjustment.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbcurputimpl(BDBCUR *cur, const char *vbuf, int vsiz, int cpmode){
+  assert(cur && vbuf && vsiz >= 0);
+  TCBDB *bdb = cur->bdb;
+  BDBLEAF *leaf = tcbdbleafload(bdb, cur->id);
+  if(!leaf) return false;
+  TCLIST *recs = leaf->recs;
+  if(cur->kidx >= TCLISTNUM(recs)){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, cur->kidx);
+  int vnum = recp->rest ? TCLISTNUM(recp->rest) + 1 : 1;
+  if(cur->vidx >= vnum){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  switch(cpmode){
+  case BDBCPCURRENT:
+    if(cur->vidx < 1){
+      if(vsiz > recp->vsiz) TCREALLOC(recp->vbuf, recp->vbuf, vsiz + 1);
+      memcpy(recp->vbuf, vbuf, vsiz);
+      recp->vbuf[vsiz] = '\0';
+      recp->vsiz = vsiz;
+    } else {
+      tclistover(recp->rest, cur->vidx - 1, vbuf, vsiz);
+    }
+    break;
+  case BDBCPBEFORE:
+    if(cur->vidx < 1){
+      if(!recp->rest) recp->rest = tclistnew();
+      tclistunshift(recp->rest, recp->vbuf, recp->vsiz);
+      if(vsiz > recp->vsiz) TCREALLOC(recp->vbuf, recp->vbuf, vsiz + 1);
+      memcpy(recp->vbuf, vbuf, vsiz);
+      recp->vbuf[vsiz] = '\0';
+      recp->vsiz = vsiz;
+    } else {
+      tclistinsert(recp->rest, cur->vidx - 1, vbuf, vsiz);
+    }
+    bdb->rnum++;
+    break;
+  case BDBCPAFTER:
+    if(!recp->rest) recp->rest = tclistnew();
+    tclistinsert(recp->rest, cur->vidx, vbuf, vsiz);
+    cur->vidx++;
+    bdb->rnum++;
+    break;
+  }
+  leaf->dirty = true;
+  return true;
+}
+
+
+/* Delete the record where a cursor object is.
+   `cur' specifies the cursor object.
+   If successful, the return value is true, else, it is false. */
+static bool tcbdbcuroutimpl(BDBCUR *cur){
+  assert(cur);
+  TCBDB *bdb = cur->bdb;
+  BDBLEAF *leaf = tcbdbleafload(bdb, cur->id);
+  if(!leaf) return false;
+  TCLIST *recs = leaf->recs;
+  if(cur->kidx >= TCLISTNUM(recs)){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, cur->kidx);
+  int vnum = recp->rest ? TCLISTNUM(recp->rest) + 1 : 1;
+  if(cur->vidx >= vnum){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  if(recp->rest){
+    if(cur->vidx < 1){
+      free(recp->vbuf);
+      recp->vbuf = tclistshift(recp->rest, &(recp->vsiz));
+    } else {
+      int vsiz;
+      free(tclistremove(recp->rest, cur->vidx - 1, &vsiz));
+    }
+    if(TCLISTNUM(recp->rest) < 1){
+      tclistdel(recp->rest);
+      recp->rest = NULL;
+    }
+  } else {
+    if(TCLISTNUM(recs) < 2 && bdb->hnum > 0){
+      uint64_t pid = tcbdbsearchleaf(bdb, recp->kbuf, recp->ksiz);
+      if(pid < 1) return false;
+      if(!(leaf = tcbdbleafload(bdb, pid))) return false;
+      if(!tcbdbleafkill(bdb, leaf)) return false;
+    }
+    free(recp->vbuf);
+    free(recp->kbuf);
+    int rsiz;
+    free(tclistremove(leaf->recs, cur->kidx, &rsiz));
+  }
+  bdb->rnum--;
+  leaf->dirty = true;
+  return tcbdbcuradjust(cur, true) || tchdbecode(bdb->hdb) == TCENOREC;
+}
+
+
+/* Get the key and the value of the current record of the cursor object.
+   `cur' specifies the cursor object.
+   `kbp' specifies the pointer to the variable into which the pointer to the region of the key is
+   assgined.
+   `ksp' specifies the pointer to the variable into which the size of the key region is assigned.
+   `vbp' specifies the pointer to the variable into which the pointer to the region of the value
+   is assgined.
+   `vsp' specifies the pointer to the variable into which the size of the value region is
+   assigned. */
+static bool tcbdbcurrecimpl(BDBCUR *cur, const char **kbp, int *ksp, const char **vbp, int *vsp){
+  assert(cur && kbp && ksp && vbp && vsp);
+  TCBDB *bdb = cur->bdb;
+  BDBLEAF *leaf = tcbdbleafload(bdb, cur->id);
+  if(!leaf) return false;
+  TCLIST *recs = leaf->recs;
+  if(cur->kidx >= TCLISTNUM(recs)){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, cur->kidx);
+  int vnum = recp->rest ? TCLISTNUM(recp->rest) + 1 : 1;
+  if(cur->vidx >= vnum){
+    tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  *kbp = recp->kbuf;
+  *ksp = recp->ksiz;
+  if(cur->vidx > 0){
+    *vbp = tclistval(recp->rest, cur->vidx - 1, vsp);
+  } else {
+    *vbp = recp->vbuf;
+    *vsp = recp->vsiz;
+  }
+  return true;
+}
+
+
+
+/*************************************************************************************************
+ * debugging functions
+ *************************************************************************************************/
+
+
+/* Print meta data of the header into the debugging output.
+   `bdb' specifies the B+ tree database object. */
+void tcbdbprintmeta(TCBDB *bdb){
+  assert(bdb);
+  int dbgfd = tchdbdbgfd(bdb->hdb);
+  if(dbgfd < 0) return;
+  char buf[BDBPAGEBUFSIZ];
+  char *wp = buf;
+  wp += sprintf(wp, "META:");
+  wp += sprintf(wp, " mmtx=%p", (void *)bdb->mmtx);
+  wp += sprintf(wp, " cmtx=%p", (void *)bdb->cmtx);
+  wp += sprintf(wp, " tmtx=%p", (void *)bdb->tmtx);
+  wp += sprintf(wp, " hdb=%p", (void *)bdb->hdb);
+  wp += sprintf(wp, " opaque=%p", (void *)bdb->opaque);
+  wp += sprintf(wp, " open=%d", bdb->open);
+  wp += sprintf(wp, " wmode=%d", bdb->wmode);
+  wp += sprintf(wp, " lmemb=%u", bdb->lmemb);
+  wp += sprintf(wp, " nmemb=%u", bdb->nmemb);
+  wp += sprintf(wp, " opts=%u", bdb->opts);
+  wp += sprintf(wp, " root=%llu", (unsigned long long)bdb->root);
+  wp += sprintf(wp, " first=%llu", (unsigned long long)bdb->first);
+  wp += sprintf(wp, " last=%llu", (unsigned long long)bdb->last);
+  wp += sprintf(wp, " lnum=%llu", (unsigned long long)bdb->lnum);
+  wp += sprintf(wp, " nnum=%llu", (unsigned long long)bdb->nnum);
+  wp += sprintf(wp, " rnum=%llu", (unsigned long long)bdb->rnum);
+  wp += sprintf(wp, " leafc=%p", (void *)bdb->leafc);
+  wp += sprintf(wp, " nodec=%p", (void *)bdb->nodec);
+  wp += sprintf(wp, " cmp=%p", (void *)(intptr_t)bdb->cmp);
+  wp += sprintf(wp, " cmpop=%p", (void *)bdb->cmpop);
+  wp += sprintf(wp, " lcnum=%u", bdb->lcnum);
+  wp += sprintf(wp, " ncnum=%u", bdb->ncnum);
+  wp += sprintf(wp, " lsmax=%u", bdb->lsmax);
+  wp += sprintf(wp, " lschk=%u", bdb->lschk);
+  wp += sprintf(wp, " capnum=%llu", (unsigned long long)bdb->capnum);
+  wp += sprintf(wp, " hist=%p", (void *)bdb->hist);
+  wp += sprintf(wp, " hnum=%d", bdb->hnum);
+  wp += sprintf(wp, " hleaf=%llu", (unsigned long long)bdb->hleaf);
+  wp += sprintf(wp, " lleaf=%llu", (unsigned long long)bdb->lleaf);
+  wp += sprintf(wp, " tran=%d", bdb->tran);
+  wp += sprintf(wp, " rbopaque=%p", (void *)bdb->rbopaque);
+  wp += sprintf(wp, " cnt_saveleaf=%lld", (long long)bdb->cnt_saveleaf);
+  wp += sprintf(wp, " cnt_loadleaf=%lld", (long long)bdb->cnt_loadleaf);
+  wp += sprintf(wp, " cnt_killleaf=%lld", (long long)bdb->cnt_killleaf);
+  wp += sprintf(wp, " cnt_adjleafc=%lld", (long long)bdb->cnt_adjleafc);
+  wp += sprintf(wp, " cnt_savenode=%lld", (long long)bdb->cnt_savenode);
+  wp += sprintf(wp, " cnt_loadnode=%lld", (long long)bdb->cnt_loadnode);
+  wp += sprintf(wp, " cnt_adjnodec=%lld", (long long)bdb->cnt_adjnodec);
+  *(wp++) = '\n';
+  tcwrite(dbgfd, buf, wp - buf);
+}
+
+
+/* Print records of a leaf object into the debugging output.
+   `bdb' specifies the B+ tree database object.
+   `leaf' specifies the leaf object. */
+void tcbdbprintleaf(TCBDB *bdb, BDBLEAF *leaf){
+  assert(bdb && leaf);
+  int dbgfd = tchdbdbgfd(bdb->hdb);
+  TCLIST *recs = leaf->recs;
+  if(dbgfd < 0) return;
+  char buf[BDBPAGEBUFSIZ];
+  char *wp = buf;
+  wp += sprintf(wp, "LEAF:");
+  wp += sprintf(wp, " id:%llx", (unsigned long long)leaf->id);
+  wp += sprintf(wp, " prev:%llx", (unsigned long long)leaf->prev);
+  wp += sprintf(wp, " next:%llx", (unsigned long long)leaf->next);
+  wp += sprintf(wp, " dirty:%d", leaf->dirty);
+  wp += sprintf(wp, " dead:%d", leaf->dead);
+  wp += sprintf(wp, " rnum:%d", TCLISTNUM(recs));
+  *(wp++) = ' ';
+  for(int i = 0; i < TCLISTNUM(recs); i++){
+    tcwrite(dbgfd, buf, wp - buf);
+    wp = buf;
+    BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+    wp += sprintf(wp, " [%s:%s]", recp->kbuf, recp->vbuf);
+    TCLIST *rest = recp->rest;
+    if(rest){
+      for(int j = 0; j < TCLISTNUM(rest); j++){
+        wp += sprintf(wp, ":%s", (char *)TCLISTVALPTR(rest, j));
+      }
+    }
+  }
+  *(wp++) = '\n';
+  tcwrite(dbgfd, buf, wp - buf);
+}
+
+
+/* Print indexes of a node object into the debugging output.
+   `bdb' specifies the B+ tree database object.
+   `node' specifies the node object. */
+void tcbdbprintnode(TCBDB *bdb, BDBNODE *node){
+  assert(bdb && node);
+  int dbgfd = tchdbdbgfd(bdb->hdb);
+  TCLIST *idxs = node->idxs;
+  if(dbgfd < 0) return;
+  char buf[BDBPAGEBUFSIZ];
+  char *wp = buf;
+  wp += sprintf(wp, "NODE:");
+  wp += sprintf(wp, " id:%llx", (unsigned long long)node->id);
+  wp += sprintf(wp, " heir:%llx", (unsigned long long)node->heir);
+  wp += sprintf(wp, " dirty:%d", node->dirty);
+  wp += sprintf(wp, " rnum:%d", TCLISTNUM(idxs));
+  *(wp++) = ' ';
+  for(int i = 0; i < TCLISTNUM(idxs); i++){
+    tcwrite(dbgfd, buf, wp - buf);
+    wp = buf;
+    BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+    wp += sprintf(wp, " [%llx:%s]", (unsigned long long)idxp->pid, idxp->kbuf);
+  }
+  *(wp++) = '\n';
+  tcwrite(dbgfd, buf, wp - buf);
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcbdb.h b/bacula/src/lib/tokyocabinet/tcbdb.h
new file mode 100644 (file)
index 0000000..e4da2e6
--- /dev/null
@@ -0,0 +1,1027 @@
+/*************************************************************************************************
+ * The B+ tree database API of Tokyo Cabinet
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _TCBDB_H                         /* duplication check */
+#define _TCBDB_H
+
+#if defined(__cplusplus)
+#define __TCBDB_CLINKAGEBEGIN extern "C" {
+#define __TCBDB_CLINKAGEEND }
+#else
+#define __TCBDB_CLINKAGEBEGIN
+#define __TCBDB_CLINKAGEEND
+#endif
+__TCBDB_CLINKAGEBEGIN
+
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <tcutil.h>
+#include <tchdb.h>
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+/* type of the pointer to a comparison function.
+   `aptr' specifies the pointer to the region of one key.
+   `asiz' specifies the size of the region of one key.
+   `bptr' specifies the pointer to the region of the other key.
+   `bsiz' specifies the size of the region of the other key.
+   `op' specifies the pointer to the optional opaque object.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+typedef int (*BDBCMP)(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+
+typedef struct {                         /* type of structure for a B+ tree database */
+  void *mmtx;                            /* mutex for method */
+  void *cmtx;                            /* mutex for cache */
+  void *tmtx;                            /* mutex for transaction */
+  TCHDB *hdb;                            /* internal database object */
+  char *opaque;                          /* opaque buffer */
+  bool open;                             /* whether the internal database is opened */
+  bool wmode;                            /* whether to be writable */
+  uint32_t lmemb;                        /* number of members in each leaf */
+  uint32_t nmemb;                        /* number of members in each node */
+  uint8_t opts;                          /* options */
+  uint64_t root;                         /* ID number of the root page */
+  uint64_t first;                        /* ID number of the first leaf */
+  uint64_t last;                         /* ID number of the last leaf */
+  uint64_t lnum;                         /* number of leaves */
+  uint64_t nnum;                         /* number of nodes */
+  uint64_t rnum;                         /* number of records */
+  TCMAP *leafc;                          /* cache for leaves */
+  TCMAP *nodec;                          /* cache for nodes */
+  BDBCMP cmp;                            /* pointer to the comparison function */
+  void *cmpop;                           /* opaque object for the comparison function */
+  uint32_t lcnum;                        /* max number of cached leaves */
+  uint32_t ncnum;                        /* max number of cached nodes */
+  uint32_t lsmax;                        /* max size of each leaf */
+  uint32_t lschk;                        /* counter for leaf size checking */
+  uint64_t capnum;                       /* capacity number of records */
+  uint64_t *hist;                        /* history array of visited nodes */
+  int hnum;                              /* number of element of the history array */
+  uint64_t hleaf;                        /* ID number of the leaf referred by the history */
+  uint64_t lleaf;                        /* ID number of the last visited leaf */
+  bool tran;                             /* whether in the transaction */
+  char *rbopaque;                        /* opaque for rollback */
+  int64_t cnt_saveleaf;                  /* tesing counter for leaf save times */
+  int64_t cnt_loadleaf;                  /* tesing counter for leaf load times */
+  int64_t cnt_killleaf;                  /* tesing counter for leaf kill times */
+  int64_t cnt_adjleafc;                  /* tesing counter for node cache adjust times */
+  int64_t cnt_savenode;                  /* tesing counter for node save times */
+  int64_t cnt_loadnode;                  /* tesing counter for node load times */
+  int64_t cnt_adjnodec;                  /* tesing counter for node cache adjust times */
+} TCBDB;
+
+enum {                                   /* enumeration for additional flags */
+  BDBFOPEN = HDBFOPEN,                   /* whether opened */
+  BDBFFATAL = HDBFFATAL                  /* whetehr with fatal error */
+};
+
+enum {                                   /* enumeration for tuning options */
+  BDBTLARGE = 1 << 0,                    /* use 64-bit bucket array */
+  BDBTDEFLATE = 1 << 1,                  /* compress each page with Deflate */
+  BDBTTCBS = 1 << 2                      /* compress each page with TCBS */
+};
+
+enum {                                   /* enumeration for open modes */
+  BDBOREADER = 1 << 0,                   /* open as a reader */
+  BDBOWRITER = 1 << 1,                   /* open as a writer */
+  BDBOCREAT = 1 << 2,                    /* writer creating */
+  BDBOTRUNC = 1 << 3,                    /* writer truncating */
+  BDBONOLCK = 1 << 4,                    /* open without locking */
+  BDBOLCKNB = 1 << 5                     /* lock without blocking */
+};
+
+typedef struct {                         /* type of structure for a B+ tree cursor */
+  TCBDB *bdb;                            /* database object */
+  uint64_t id;                           /* ID number of the leaf */
+  int32_t kidx;                          /* number of the key */
+  int32_t vidx;                          /* number of the value */
+} BDBCUR;
+
+enum {                                   /* enumeration for cursor put mode */
+  BDBCPCURRENT,                          /* current */
+  BDBCPBEFORE,                           /* before */
+  BDBCPAFTER                             /* after */
+};
+
+
+/* Get the message string corresponding to an error code.
+   `ecode' specifies the error code.
+   The return value is the message string of the error code. */
+const char *tcbdberrmsg(int ecode);
+
+
+/* Create a B+ tree database object.
+   The return value is the new B+ tree database object. */
+TCBDB *tcbdbnew(void);
+
+
+/* Delete a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   If the database is not closed, it is closed implicitly.  Note that the deleted object and its
+   derivatives can not be used anymore. */
+void tcbdbdel(TCBDB *bdb);
+
+
+/* Get the last happened error code of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the last happened error code.
+   The following error code is defined: `TCESUCCESS' for success, `TCETHREAD' for threading
+   error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no
+   permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN'
+   for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync
+   error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error,
+   `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK'
+   for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for
+   rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for
+   miscellaneous error. */
+int tcbdbecode(TCBDB *bdb);
+
+
+/* Set mutual exclusion control of a B+ tree database object for threading.
+   `bdb' specifies the B+ tree database object which is not opened.
+   If successful, the return value is true, else, it is false.
+   Note that the mutual exclusion control is needed if the object is shared by plural threads and
+   this function should should be called before the database is opened. */
+bool tcbdbsetmutex(TCBDB *bdb);
+
+
+/* Set the custom comparison function of a B+ tree database object.
+   `bdb' specifies the B+ tree database object which is not opened.
+   `cmp' specifies the pointer to the custom comparison function.
+   `cmpop' specifies an arbitrary pointer to be given as a parameter of the comparison function.
+   If it is not needed, `NULL' can be specified.
+   If successful, the return value is true, else, it is false.
+   The default comparison function compares keys of two records by lexical order.  The functions
+   `tcbdbcmplexical' (dafault), `tcbdbcmpdecimal', `tcbdbcmpint32', and `tcbdbcmpint64' are
+   built-in.  Note that the comparison function should be set before the database is opened.
+   Moreover, user-defined comparison functions should be set every time the database is being
+   opened. */
+bool tcbdbsetcmpfunc(TCBDB *bdb, BDBCMP cmp, void *cmpop);
+
+
+/* Set the tuning parameters of a B+ tree database object.
+   `bdb' specifies the B+ tree database object which is not opened.
+   `lmemb' specifies the number of members in each leaf page.  If it is not more than 0, the
+   default value is specified.  The default value is 128.
+   `nmemb' specifies the number of members in each non-leaf page.  If it is not more than 0, the
+   default value is specified.  The default value is 256.
+   `bnum' specifies the number of elements of the bucket array.  If it is not more than 0, the
+   default value is specified.  The default value is 16381.  Suggested size of the bucket array
+   is about from 1 to 4 times of the number of all pages to be stored.
+   `apow' specifies the size of record alignment by power of 2.  If it is negative, the default
+   value is specified.  The default value is 8 standing for 2^8=256.
+   `fpow' specifies the maximum number of elements of the free block pool by power of 2.  If it
+   is negative, the default value is specified.  The default value is 10 standing for 2^10=1024.
+   `opts' specifies options by bitwise or: `BDBTLARGE' specifies that the size of the database
+   can be larger than 2GB by using 64-bit bucket array, `BDBTDEFLATE' specifies that each page
+   is compressed with Deflate encoding, `BDBTTCBS' specifies that each page is compressed with
+   TCBS encoding.
+   If successful, the return value is true, else, it is false.
+   Note that the tuning parameters should be set before the database is opened. */
+bool tcbdbtune(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+               int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
+
+/* Set the caching parameters of a B+ tree database object.
+   `bdb' specifies the B+ tree database object which is not opened.
+   `lcnum' specifies the maximum number of leaf nodes to be cached.  If it is not more than 0,
+   the default value is specified.  The default value is 1024.
+   `ncnum' specifies the maximum number of non-leaf nodes to be cached.  If it is not more than 0,
+   the default value is specified.  The default value is 512.
+   If successful, the return value is true, else, it is false.
+   Note that the caching parameters should be set before the database is opened. */
+bool tcbdbsetcache(TCBDB *bdb, int32_t lcnum, int32_t ncnum);
+
+
+/* Open a database file and connect a B+ tree database object.
+   `bdb' specifies the B+ tree database object which is not opened.
+   `path' specifies the path of the database file.
+   `omode' specifies the connection mode: `BDBOWRITER' as a writer, `BDBOREADER' as a reader.
+   If the mode is `BDBOWRITER', the following may be added by bitwise or: `BDBOCREAT', which
+   means it creates a new database if not exist, `BDBOTRUNC', which means it creates a new database
+   regardless if one exists.  Both of `BDBOREADER' and `BDBOWRITER' can be added to by
+   bitwise or: `BDBONOLCK', which means it opens the database file without file locking, or
+   `BDBOLCKNB', which means locking is performed without blocking.
+   If successful, the return value is true, else, it is false. */
+bool tcbdbopen(TCBDB *bdb, const char *path, int omode);
+
+
+/* Close a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   If successful, the return value is true, else, it is false.
+   Update of a database is assured to be written when the database is closed.  If a writer opens
+   a database but does not close it appropriately, the database will be broken. */
+bool tcbdbclose(TCBDB *bdb);
+
+
+/* Store a record into a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, it is overwritten. */
+bool tcbdbput(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, it is overwritten. */
+bool tcbdbput2(TCBDB *bdb, const char *kstr, const char *vstr);
+
+
+/* Store a new record into a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, this function has no effect. */
+bool tcbdbputkeep(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, this function has no effect. */
+bool tcbdbputkeep2(TCBDB *bdb, const char *kstr, const char *vstr);
+
+
+/* Concatenate a value at the end of the existing record in a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If there is no corresponding record, a new record is created. */
+bool tcbdbputcat(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Concatenate a string value at the end of the existing record in a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If there is no corresponding record, a new record is created. */
+bool tcbdbputcat2(TCBDB *bdb, const char *kstr, const char *vstr);
+
+
+/* Store a record into a B+ tree database object with allowing duplication of keys.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, the new record is placed after the
+   existing one. */
+bool tcbdbputdup(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into a B+ tree database object with allowing duplication of keys.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, the new record is placed after the
+   existing one. */
+bool tcbdbputdup2(TCBDB *bdb, const char *kstr, const char *vstr);
+
+
+/* Store records into a B+ tree database object with allowing duplication of keys.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the common key.
+   `ksiz' specifies the size of the region of the common key.
+   `vals' specifies a list object containing values.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, the new records are placed after the
+   existing one. */
+bool tcbdbputdup3(TCBDB *bdb, const void *kbuf, int ksiz, const TCLIST *vals);
+
+
+/* Remove a record of a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true, else, it is false.
+   If the key of duplicated records is specified, the first one is selected. */
+bool tcbdbout(TCBDB *bdb, const void *kbuf, int ksiz);
+
+
+/* Remove a string record of a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kstr' specifies the string of the key.
+   If successful, the return value is true, else, it is false.
+   If the key of duplicated records is specified, the first one is selected. */
+bool tcbdbout2(TCBDB *bdb, const char *kstr);
+
+
+/* Remove records of a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true, else, it is false.
+   If the key of duplicated records is specified, all of them are removed. */
+bool tcbdbout3(TCBDB *bdb, const void *kbuf, int ksiz);
+
+
+/* Retrieve a record in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the corresponding
+   record.  `NULL' is returned if no record corresponds.
+   If the key of duplicated records is specified, the first one is selected.  Because an
+   additional zero code is appended at the end of the region of the return value, the return
+   value can be treated as a character string.  Because the region of the return value is
+   allocated with the `malloc' call, it should be released with the `free' call when it is no
+   longer in use. */
+void *tcbdbget(TCBDB *bdb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve a string record in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is the string of the value of the corresponding record.
+   `NULL' is returned if no record corresponds.
+   If the key of duplicated records is specified, the first one is selected.  Because the region
+   of the return value is allocated with the `malloc' call, it should be released with the `free'
+   call when it is no longer in use. */
+char *tcbdbget2(TCBDB *bdb, const char *kstr);
+
+
+/* Retrieve a record in a B+ tree database object as a volatile buffer.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the corresponding
+   record.  `NULL' is returned if no record corresponds.
+   If the key of duplicated records is specified, the first one is selected.  Because an
+   additional zero code is appended at the end of the region of the return value, the return
+   value can be treated as a character string.  Because the region of the return value is
+   volatile and it may be spoiled by another operation of the database, the data should be copied
+   into another involatile buffer immediately. */
+const void *tcbdbget3(TCBDB *bdb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve records in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is a list object of the values of the corresponding records.
+   `NULL' is returned if no record corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should
+   be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcbdbget4(TCBDB *bdb, const void *kbuf, int ksiz);
+
+
+/* Get the number of records corresponding a key in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is the number of the corresponding records, else, it is 0. */
+int tcbdbvnum(TCBDB *bdb, const void *kbuf, int ksiz);
+
+
+/* Get the number of records corresponding a string key in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is the number of the corresponding records, else, it is 0. */
+int tcbdbvnum2(TCBDB *bdb, const char *kstr);
+
+
+/* Get the size of the value of a record in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1.
+   If the key of duplicated records is specified, the first one is selected. */
+int tcbdbvsiz(TCBDB *bdb, const void *kbuf, int ksiz);
+
+
+/* Get the size of the value of a string record in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1.
+   If the key of duplicated records is specified, the first one is selected. */
+int tcbdbvsiz2(TCBDB *bdb, const char *kstr);
+
+
+/* Get keys of ranged records in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `bkbuf' specifies the pointer to the region of the key of the beginning border.  If it is
+   `NULL', the first record is specified.
+   `bksiz' specifies the size of the region of the beginning key.
+   `binc' specifies whether the beginning border is inclusive or not.
+   `ekbuf' specifies the pointer to the region of the key of the ending border.  If it is `NULL',
+   the last record is specified.
+   `eksiz' specifies the size of the region of the ending key.
+   `einc' specifies whether the ending border is inclusive or not.
+   `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
+   specified.
+   The return value is a list object of the keys of the corresponding records.  This function
+   does never fail and return an empty list even if no record corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should
+   be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcbdbrange(TCBDB *bdb, const void *bkbuf, int bksiz, bool binc,
+                   const void *ekbuf, int eksiz, bool einc, int max);
+
+
+/* Get string keys of ranged records in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `bkstr' specifies the string of the key of the beginning border.  If it is `NULL', the first
+   record is specified.
+   `binc' specifies whether the beginning border is inclusive or not.
+   `ekstr' specifies the string of the key of the ending border.  If it is `NULL', the last
+   record is specified.
+   `einc' specifies whether the ending border is inclusive or not.
+   `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
+   specified.
+   The return value is a list object of the keys of the corresponding records.  This function
+   does never fail and return an empty list even if no record corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should
+   be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcbdbrange2(TCBDB *bdb, const char *bkstr, bool binc,
+                    const char *ekstr, bool einc, int max);
+
+
+/* Get forward matching keys in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `pbuf' specifies the pointer to the region of the prefix.
+   `psiz' specifies the size of the region of the prefix.
+   `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
+   specified.
+   The return value is a list object of the corresponding keys.  This function does never fail
+   and return an empty list even if no key corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should be
+   deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcbdbfwmkeys(TCBDB *bdb, const void *pbuf, int psiz, int max);
+
+
+/* Get forward matching string keys in a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `pstr' specifies the string of the prefix.
+   `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
+   specified.
+   The return value is a list object of the corresponding keys.  This function does never fail
+   and return an empty list even if no key corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should be
+   deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcbdbfwmkeys2(TCBDB *bdb, const char *pstr, int max);
+
+
+/* Synchronize updated contents of a B+ tree database object with the file and the device.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   If successful, the return value is true, else, it is false.
+   This function is useful when another process connects the same database file. */
+bool tcbdbsync(TCBDB *bdb);
+
+
+/* Optimize the file of a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `lmemb' specifies the number of members in each leaf page.  If it is not more than 0, the
+   current setting is not changed.
+   `nmemb' specifies the number of members in each non-leaf page.  If it is not more than 0, the
+   current setting is not changed.
+   `bnum' specifies the number of elements of the bucket array.  If it is not more than 0, the
+   default value is specified.  The default value is two times of the number of pages.
+   `apow' specifies the size of record alignment by power of 2.  If it is negative, the current
+   setting is not changed.
+   `fpow' specifies the maximum number of elements of the free block pool by power of 2.  If it
+   is negative, the current setting is not changed.
+   `opts' specifies options by bitwise or: `BDBTLARGE' specifies that the size of the database
+   can be larger than 2GB by using 64-bit bucket array, `BDBTDEFLATE' specifies that each record
+   is compressed with Deflate encoding, `BDBTTCBS' specifies that each page is compressed with
+   TCBS encoding.  If it is `UINT8_MAX', the current setting is not changed.
+   If successful, the return value is true, else, it is false.
+   This function is useful to reduce the size of the database file with data fragmentation by
+   successive updating. */
+bool tcbdboptimize(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+                   int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
+
+/* Remove all records of a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   If successful, the return value is true, else, it is false. */
+bool tcbdbvanish(TCBDB *bdb);
+
+
+/* Copy the database file of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `path' specifies the path of the destination file.  If it begins with `@', the trailing
+   substring is executed as a command line.
+   If successful, the return value is true, else, it is false.  False is returned if the executed
+   command returns non-zero code.
+   The database file is assured to be kept synchronized and not modified while the copying or
+   executing operation is in progress.  So, this function is useful to create a backup file of
+   the database file. */
+bool tcbdbcopy(TCBDB *bdb, const char *path);
+
+
+/* Begin the transaction of a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   If successful, the return value is true, else, it is false.
+   The database is locked by the thread while the transaction so that only one transaction can be
+   activated with a database object at the same time.  Thus, the serializable isolation level is
+   assumed if every database operation is performed in the transaction.  If the database is
+   closed during transaction, the transaction is aborted implicitly. */
+bool tcbdbtranbegin(TCBDB *bdb);
+
+
+/* Commit the transaction of a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   If successful, the return value is true, else, it is false.
+   Update in the transaction is fixed when it is committed successfully. */
+bool tcbdbtrancommit(TCBDB *bdb);
+
+
+/* Abort the transaction of a B+ tree database object.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   If successful, the return value is true, else, it is false.
+   Update in the transaction is discarded when it is aborted.  The state of the database is
+   rollbacked to before transaction. */
+bool tcbdbtranabort(TCBDB *bdb);
+
+
+/* Get the file path of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the path of the database file or `NULL' if the object does not connect to
+   any database file. */
+const char *tcbdbpath(TCBDB *bdb);
+
+
+/* Get the number of records of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the number of records or 0 if the object does not connect to any database
+   file. */
+uint64_t tcbdbrnum(TCBDB *bdb);
+
+
+/* Get the size of the database file of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the size of the database file or 0 if the object does not connect to any
+   database file. */
+uint64_t tcbdbfsiz(TCBDB *bdb);
+
+
+/* Create a cursor object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the new cursor object.
+   Note that the cursor is available only after initialization with the `tcbdbcurfirst' or the
+   `tcbdbcurjump' functions and so on.  Moreover, the position of the cursor will be indefinite
+   when the database is updated after the initialization of the cursor. */
+BDBCUR *tcbdbcurnew(TCBDB *bdb);
+
+
+/* Delete a cursor object.
+   `cur' specifies the cursor object. */
+void tcbdbcurdel(BDBCUR *cur);
+
+
+/* Move a cursor object to the first record.
+   `cur' specifies the cursor object.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record in the database. */
+bool tcbdbcurfirst(BDBCUR *cur);
+
+
+/* Move a cursor object to the last record.
+   `cur' specifies the cursor object.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record in the database. */
+bool tcbdbcurlast(BDBCUR *cur);
+
+
+/* Move a cursor object to the front of records corresponding a key.
+   `cur' specifies the cursor object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record corresponding the condition.
+   The cursor is set to the first record corresponding the key or the next substitute if
+   completely matching record does not exist. */
+bool tcbdbcurjump(BDBCUR *cur, const void *kbuf, int ksiz);
+
+
+/* Move a cursor object to the front of records corresponding a key string.
+   `cur' specifies the cursor object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record corresponding the condition.
+   The cursor is set to the first record corresponding the key or the next substitute if
+   completely matching record does not exist. */
+bool tcbdbcurjump2(BDBCUR *cur, const char *kstr);
+
+
+/* Move a cursor object to the previous record.
+   `cur' specifies the cursor object.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no previous record. */
+bool tcbdbcurprev(BDBCUR *cur);
+
+
+/* Move a cursor object to the next record.
+   `cur' specifies the cursor object.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no next record. */
+bool tcbdbcurnext(BDBCUR *cur);
+
+
+/* Insert a record around a cursor object.
+   `cur' specifies the cursor object of writer connection.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   `cpmode' specifies detail adjustment: `BDBCPCURRENT', which means that the value of the
+   current record is overwritten, `BDBCPBEFORE', which means that the new record is inserted
+   before the current record, `BDBCPAFTER', which means that the new record is inserted after the
+   current record.
+   If successful, the return value is true, else, it is false.  False is returned when the cursor
+   is at invalid position.
+   After insertion, the cursor is moved to the inserted record. */
+bool tcbdbcurput(BDBCUR *cur, const void *vbuf, int vsiz, int cpmode);
+
+
+/* Insert a string record around a cursor object.
+   `cur' specifies the cursor object of writer connection.
+   `vstr' specifies the string of the value.
+   `cpmode' specifies detail adjustment: `BDBCPCURRENT', which means that the value of the
+   current record is overwritten, `BDBCPBEFORE', which means that the new record is inserted
+   before the current record, `BDBCPAFTER', which means that the new record is inserted after the
+   current record.
+   If successful, the return value is true, else, it is false.  False is returned when the cursor
+   is at invalid position.
+   After insertion, the cursor is moved to the inserted record. */
+bool tcbdbcurput2(BDBCUR *cur, const char *vstr, int cpmode);
+
+
+/* Delete the record where a cursor object is.
+   `cur' specifies the cursor object of writer connection.
+   If successful, the return value is true, else, it is false.  False is returned when the cursor
+   is at invalid position.
+   After deletion, the cursor is moved to the next record if possible. */
+bool tcbdbcurout(BDBCUR *cur);
+
+
+/* Get the key of the record where the cursor object is.
+   `cur' specifies the cursor object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the key, else, it is `NULL'.
+   `NULL' is returned when the cursor is at invalid position.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when
+   it is no longer in use. */
+char *tcbdbcurkey(BDBCUR *cur, int *sp);
+
+
+/* Get the key string of the record where the cursor object is.
+   `cur' specifies the cursor object.
+   If successful, the return value is the string of the key, else, it is `NULL'.  `NULL' is
+   returned when the cursor is at invalid position.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcbdbcurkey2(BDBCUR *cur);
+
+
+/* Get the key of the record where the cursor object is, as a volatile buffer.
+   `cur' specifies the cursor object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the key, else, it is `NULL'.
+   `NULL' is returned when the cursor is at invalid position.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return value
+   is volatile and it may be spoiled by another operation of the database, the data should be
+   copied into another involatile buffer immediately. */
+const char *tcbdbcurkey3(BDBCUR *cur, int *sp);
+
+
+/* Get the value of the record where the cursor object is.
+   `cur' specifies the cursor object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value, else, it is `NULL'.
+   `NULL' is returned when the cursor is at invalid position.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when
+   it is no longer in use. */
+char *tcbdbcurval(BDBCUR *cur, int *sp);
+
+
+/* Get the value string of the record where the cursor object is.
+   `cur' specifies the cursor object.
+   If successful, the return value is the string of the value, else, it is `NULL'.  `NULL' is
+   returned when the cursor is at invalid position.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcbdbcurval2(BDBCUR *cur);
+
+
+/* Get the value of the record where the cursor object is, as a volatile buffer.
+   `cur' specifies the cursor object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value, else, it is `NULL'.
+   `NULL' is returned when the cursor is at invalid position.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return value
+   is volatile and it may be spoiled by another operation of the database, the data should be
+   copied into another involatile buffer immediately. */
+const char *tcbdbcurval3(BDBCUR *cur, int *sp);
+
+
+/* Get the key and the value of the record where the cursor object is.
+   `cur' specifies the cursor object.
+   `kxstr' specifies the object into which the key is wrote down.
+   `vxstr' specifies the object into which the value is wrote down.
+   If successful, the return value is true, else, it is false.  False is returned when the cursor
+   is at invalid position. */
+bool tcbdbcurrec(BDBCUR *cur, TCXSTR *kxstr, TCXSTR *vxstr);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Set the error code of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   `ecode' specifies the error code.
+   `file' specifies the file name of the code.
+   `line' specifies the line number of the code.
+   `func' specifies the function name of the code. */
+void tcbdbsetecode(TCBDB *bdb, int ecode, const char *filename, int line, const char *func);
+
+
+/* Set the file descriptor for debugging output.
+   `bdb' specifies the B+ tree database object.
+   `fd' specifies the file descriptor for debugging output. */
+void tcbdbsetdbgfd(TCBDB *bdb, int fd);
+
+
+/* Get the file descriptor for debugging output.
+   `bdb' specifies the B+ tree database object.
+   The return value is the file descriptor for debugging output. */
+int tcbdbdbgfd(TCBDB *bdb);
+
+
+/* Synchronize updating contents on memory.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `phys' specifies whether to synchronize physically.
+   If successful, the return value is true, else, it is false. */
+bool tcbdbmemsync(TCBDB *bdb, bool phys);
+
+
+/* Get the comparison function of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the pointer to the comparison function. */
+BDBCMP tcbdbcmpfunc(TCBDB *bdb);
+
+
+/* Get the opaque object for the comparison function of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the opaque object for the comparison function. */
+void *tcbdbcmpop(TCBDB *bdb);
+
+
+/* Get the maximum number of cached leaf nodes of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the maximum number of cached leaf nodes. */
+uint32_t tcbdblmemb(TCBDB *bdb);
+
+
+/* Get the maximum number of cached non-leaf nodes of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the maximum number of cached non-leaf nodes. */
+uint32_t tcbdbnmemb(TCBDB *bdb);
+
+
+/* Get the number of the leaf nodes of B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   If successful, the return value is the number of the leaf nodes or 0 if the object does not
+   connect to any database file. */
+uint64_t tcbdblnum(TCBDB *bdb);
+
+
+/* Get the number of the non-leaf nodes of B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   If successful, the return value is the number of the non-leaf nodes or 0 if the object does
+   not connect to any database file. */
+uint64_t tcbdbnnum(TCBDB *bdb);
+
+
+/* Get the number of elements of the bucket array of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the number of elements of the bucket array or 0 if the object does not
+   connect to any database file. */
+uint64_t tcbdbbnum(TCBDB *bdb);
+
+
+/* Get the record alignment of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the record alignment or 0 if the object does not connect to any database
+   file. */
+uint32_t tcbdbalign(TCBDB *bdb);
+
+
+/* Get the maximum number of the free block pool of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the maximum number of the free block pool or 0 if the object does not
+   connect to any database file. */
+uint32_t tcbdbfbpmax(TCBDB *bdb);
+
+
+/* Get the inode number of the database file of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the inode number of the database file or 0 the object does not connect to
+   any database file. */
+uint64_t tcbdbinode(TCBDB *bdb);
+
+
+/* Get the modification time of the database file of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the inode number of the database file or 0 the object does not connect to
+   any database file. */
+time_t tcbdbmtime(TCBDB *bdb);
+
+
+/* Get the additional flags of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the additional flags. */
+uint8_t tcbdbflags(TCBDB *bdb);
+
+
+/* Get the options of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the options. */
+uint8_t tcbdbopts(TCBDB *bdb);
+
+
+/* Get the pointer to the opaque field of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the pointer to the opaque field whose size is 128 bytes. */
+char *tcbdbopaque(TCBDB *bdb);
+
+
+/* Get the number of used elements of the bucket array of a B+ tree database object.
+   `bdb' specifies the B+ tree database object.
+   The return value is the number of used elements of the bucket array or 0 if the object does not
+   connect to any database file. */
+uint64_t tcbdbbnumused(TCBDB *bdb);
+
+
+/* Set the maximum size of each leaf node.
+   `bdb' specifies the B+ tree database object which is not opened.
+   `lsmax' specifies the maximum size of each leaf node.  If it is not more than 0, the maximum
+   size is unlimited.
+   If successful, the return value is true, else, it is false.
+   Note that the tuning parameters of the database should be set before the database is opened. */
+bool tcbdbsetlsmax(TCBDB *bdb, uint32_t lsmax);
+
+
+/* Set the capacity number of records.
+   `bdb' specifies the B+ tree database object which is not opened.
+   `capnum' specifies the capacity number of records.  If it is not more than 0, the capacity is
+   unlimited.
+   If successful, the return value is true, else, it is false.
+   When the number of records exceeds the capacity, forehand records are removed implicitly.
+   Note that the tuning parameters of the database should be set before the database is opened. */
+bool tcbdbsetcapnum(TCBDB *bdb, uint64_t capnum);
+
+
+/* Store a new record into a B+ tree database object with backward duplication.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, the new record is placed after the
+   existing one. */
+bool tcbdbputdupback(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into a B+ tree database object with backward duplication.
+   `bdb' specifies the B+ tree database object connected as a writer.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, the new record is placed after the
+   existing one. */
+bool tcbdbputdupback2(TCBDB *bdb, const char *kstr, const char *vstr);
+
+
+/* Move a cursor object to the rear of records corresponding a key.
+   `cur' specifies the cursor object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record corresponding the condition.
+   The cursor is set to the last record corresponding the key or the previous substitute if
+   completely matching record does not exist. */
+bool tcbdbcurjumpback(BDBCUR *cur, const void *kbuf, int ksiz);
+
+
+/* Move a cursor object to the rear of records corresponding a key string.
+   `cur' specifies the cursor object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record corresponding the condition.
+   The cursor is set to the last record corresponding the key or the previous substitute if
+   completely matching record does not exist. */
+bool tcbdbcurjumpback2(BDBCUR *cur, const char *kstr);
+
+
+/* Compare two keys by lexical order.
+   `aptr' specifies the pointer to the region of one key.
+   `asiz' specifies the size of the region of one key.
+   `bptr' specifies the pointer to the region of the other key.
+   `bsiz' specifies the size of the region of the other key.
+   `op' specifies the pointer to the optional opaque object.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+int tcbdbcmplexical(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+
+
+/* Compare two keys as decimal strings of real numbers.
+   `aptr' specifies the pointer to the region of one key.
+   `asiz' specifies the size of the region of one key.
+   `bptr' specifies the pointer to the region of the other key.
+   `bsiz' specifies the size of the region of the other key.
+   `op' is ignored.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+int tcbdbcmpdecimal(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+
+
+/* Compare two keys as 32-bit integers in the native byte order.
+   `aptr' specifies the pointer to the region of one key.
+   `asiz' specifies the size of the region of one key.
+   `bptr' specifies the pointer to the region of the other key.
+   `bsiz' specifies the size of the region of the other key.
+   `op' is ignored.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+int tcbdbcmpint32(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+
+
+/* Compare two keys as 64-bit integers in the native byte order.
+   `aptr' specifies the pointer to the region of one key.
+   `asiz' specifies the size of the region of one key.
+   `bptr' specifies the pointer to the region of the other key.
+   `bsiz' specifies the size of the region of the other key.
+   `op' is ignored.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+int tcbdbcmpint64(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+
+
+/* tricks for backward compatibility */
+#define tcbdbrange3 tcbdbfwmkeys2
+
+
+
+__TCBDB_CLINKAGEEND
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */
diff --git a/bacula/src/lib/tokyocabinet/tcbmgr.c b/bacula/src/lib/tokyocabinet/tcbmgr.c
new file mode 100644 (file)
index 0000000..6cb796c
--- /dev/null
@@ -0,0 +1,977 @@
+/*************************************************************************************************
+ * The command line utility of the B+ tree database API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tcbdb.h>
+#include "myconf.h"
+
+
+/* global variables */
+const char *g_progname;                  // program name
+int g_dbgfd;                             // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void printerr(TCBDB *bdb);
+static int printdata(const char *ptr, int size, bool px);
+static char *hextoobj(const char *str, int *sp);
+static char *mygetline(FILE *ifp);
+static int mycmpfunc(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+static int runcreate(int argc, char **argv);
+static int runinform(int argc, char **argv);
+static int runput(int argc, char **argv);
+static int runout(int argc, char **argv);
+static int runget(int argc, char **argv);
+static int runlist(int argc, char **argv);
+static int runoptimize(int argc, char **argv);
+static int runimporttsv(int argc, char **argv);
+static int runversion(int argc, char **argv);
+static int proccreate(const char *path, int lmemb, int nmemb,
+                      int bnum, int apow, int fpow, BDBCMP cmp, int opts);
+static int procinform(const char *path, int omode);
+static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                   BDBCMP cmp, int omode, int dmode);
+static int procout(const char *path, const char *kbuf, int ksiz, BDBCMP cmp, int omode);
+static int procget(const char *path, const char *kbuf, int ksiz, BDBCMP cmp, int omode,
+                   bool px, bool pz);
+static int proclist(const char *path, BDBCMP cmp, int omode, int max, bool pv, bool px, bool bk,
+                    const char *jstr, const char *bstr, const char *estr, const char *fmstr);
+static int procoptimize(const char *path, int lmemb, int nmemb,
+                        int bnum, int apow, int fpow, BDBCMP cmp, int opts, int omode);
+static int procimporttsv(const char *path, const char *file, int omode, bool sc);
+static int procversion(void);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  g_dbgfd = -1;
+  const char *ebuf = getenv("TCDBGFD");
+  if(ebuf) g_dbgfd = atoi(ebuf);
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "create")){
+    rv = runcreate(argc, argv);
+  } else if(!strcmp(argv[1], "inform")){
+    rv = runinform(argc, argv);
+  } else if(!strcmp(argv[1], "put")){
+    rv = runput(argc, argv);
+  } else if(!strcmp(argv[1], "out")){
+    rv = runout(argc, argv);
+  } else if(!strcmp(argv[1], "get")){
+    rv = runget(argc, argv);
+  } else if(!strcmp(argv[1], "list")){
+    rv = runlist(argc, argv);
+  } else if(!strcmp(argv[1], "optimize")){
+    rv = runoptimize(argc, argv);
+  } else if(!strcmp(argv[1], "importtsv")){
+    rv = runimporttsv(argc, argv);
+  } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){
+    rv = runversion(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: the command line utility of the B+ tree database API\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s create [-cd|-ci|-cj] [-tl] [-td|-tb] path"
+          " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
+  fprintf(stderr, "  %s inform [-nl|-nb] path\n", g_progname);
+  fprintf(stderr, "  %s put [-cd|-ci|-cj] [-nl|-nb] [-sx] [-dk|-dc|-dd|-db] path"
+          " key value\n", g_progname);
+  fprintf(stderr, "  %s out [-cd|-ci|-cj] [-nl|-nb] [-sx] path key\n", g_progname);
+  fprintf(stderr, "  %s get [-cd|-ci|-cj] [-nl|-nb] [-sx] [-px] [-pz] path key\n", g_progname);
+  fprintf(stderr, "  %s list [-cd|-ci|-cj] [-nl|-nb] [-m num] [-bk] [-pv] [-px] [-j str]"
+          " [-rb bkey ekey] [-fm str] path\n", g_progname);
+  fprintf(stderr, "  %s optimize [-cd|-ci|-cj] [-tl] [-td|-tb] [-tz] [-nl|-nb] path"
+          " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
+  fprintf(stderr, "  %s importtsv [-nl|-nb] [-sc] path [file]\n", g_progname);
+  fprintf(stderr, "  %s version\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print error information */
+static void printerr(TCBDB *bdb){
+  const char *path = tcbdbpath(bdb);
+  int ecode = tcbdbecode(bdb);
+  fprintf(stderr, "%s: %s: %d: %s\n", g_progname, path ? path : "-", ecode, tcbdberrmsg(ecode));
+}
+
+
+/* print record data */
+static int printdata(const char *ptr, int size, bool px){
+  int len = 0;
+  while(size-- > 0){
+    if(px){
+      if(len > 0) putchar(' ');
+      len += printf("%02X", *(unsigned char *)ptr);
+    } else {
+      putchar(*ptr);
+      len++;
+    }
+    ptr++;
+  }
+  return len;
+}
+
+
+/* create a binary object from a hexadecimal string */
+static char *hextoobj(const char *str, int *sp){
+  int len = strlen(str);
+  char *buf = tcmalloc(len + 1);
+  int j = 0;
+  for(int i = 0; i < len; i += 2){
+    while(strchr(" \n\r\t\f\v", str[i])){
+      i++;
+    }
+    char mbuf[3];
+    if((mbuf[0] = str[i]) == '\0') break;
+    if((mbuf[1] = str[i+1]) == '\0') break;
+    mbuf[2] = '\0';
+    buf[j++] = (char)strtol(mbuf, NULL, 16);
+  }
+  buf[j] = '\0';
+  *sp = j;
+  return buf;
+}
+
+
+/* read a line from a file descriptor */
+static char *mygetline(FILE *ifp){
+  char *buf;
+  int c, len, blen;
+  buf = NULL;
+  len = 0;
+  blen = 256;
+  while((c = fgetc(ifp)) != EOF){
+    if(blen <= len) blen *= 2;
+    buf = tcrealloc(buf, blen + 1);
+    if(c == '\n' || c == '\r') c = '\0';
+    buf[len++] = c;
+    if(c == '\0') break;
+  }
+  if(!buf) return NULL;
+  buf[len] = '\0';
+  return buf;
+}
+
+
+/* dummy comparison function */
+static int mycmpfunc(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
+  return 0;
+}
+
+
+/* parse arguments of create command */
+static int runcreate(int argc, char **argv){
+  char *path = NULL;
+  char *lmstr = NULL;
+  char *nmstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  BDBCMP cmp = NULL;
+  int opts = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-tl")){
+        opts |= BDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= BDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= BDBTTCBS;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!lmstr){
+      lmstr = argv[i];
+    } else if(!nmstr){
+      nmstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int lmemb = lmstr ? atoi(lmstr) : -1;
+  int nmemb = nmstr ? atoi(nmstr) : -1;
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = proccreate(path, lmemb, nmemb, bnum, apow, fpow, cmp, opts);
+  return rv;
+}
+
+
+/* parse arguments of inform command */
+static int runinform(int argc, char **argv){
+  char *path = NULL;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int rv = procinform(path, omode);
+  return rv;
+}
+
+
+/* parse arguments of put command */
+static int runput(int argc, char **argv){
+  char *path = NULL;
+  char *key = NULL;
+  char *value = NULL;
+  BDBCMP cmp = NULL;
+  int omode = 0;
+  int dmode = 0;
+  bool sx = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-dk")){
+        dmode = -1;
+      } else if(!strcmp(argv[i], "-dc")){
+        dmode = 1;
+      } else if(!strcmp(argv[i], "-dd")){
+        dmode = 2;
+      } else if(!strcmp(argv[i], "-db")){
+        dmode = 3;
+      } else if(!strcmp(argv[i], "-sx")){
+        sx = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else if(!value){
+      value = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !key || !value) usage();
+  int ksiz, vsiz;
+  char *kbuf, *vbuf;
+  if(sx){
+    kbuf = hextoobj(key, &ksiz);
+    vbuf = hextoobj(value, &vsiz);
+  } else {
+    ksiz = strlen(key);
+    kbuf = tcmemdup(key, ksiz);
+    vsiz = strlen(value);
+    vbuf = tcmemdup(value, vsiz);
+  }
+  int rv = procput(path, kbuf, ksiz, vbuf, vsiz, cmp, omode, dmode);
+  free(vbuf);
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of out command */
+static int runout(int argc, char **argv){
+  char *path = NULL;
+  char *key = NULL;
+  BDBCMP cmp = NULL;
+  int omode = 0;
+  bool sx = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-sx")){
+        sx = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !key) usage();
+  int ksiz;
+  char *kbuf;
+  if(sx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    ksiz = strlen(key);
+    kbuf = tcmemdup(key, ksiz);
+  }
+  int rv = procout(path, kbuf, ksiz, cmp, omode);
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of get command */
+static int runget(int argc, char **argv){
+  char *path = NULL;
+  char *key = NULL;
+  BDBCMP cmp = NULL;
+  int omode = 0;
+  bool sx = false;
+  bool px = false;
+  bool pz = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-sx")){
+        sx = true;
+      } else if(!strcmp(argv[i], "-px")){
+        px = true;
+      } else if(!strcmp(argv[i], "-pz")){
+        pz = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !key) usage();
+  int ksiz;
+  char *kbuf;
+  if(sx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    ksiz = strlen(key);
+    kbuf = tcmemdup(key, ksiz);
+  }
+  int rv = procget(path, kbuf, ksiz, cmp, omode, px, pz);
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+static int runlist(int argc, char **argv){
+  char *path = NULL;
+  BDBCMP cmp = NULL;
+  int omode = 0;
+  int max = -1;
+  bool pv = false;
+  bool px = false;
+  bool bk = false;
+  char *jstr = NULL;
+  char *bstr = NULL;
+  char *estr = NULL;
+  char *fmstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-m")){
+        if(++i >= argc) usage();
+        max = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-bk")){
+        bk = true;
+      } else if(!strcmp(argv[i], "-pv")){
+        pv = true;
+      } else if(!strcmp(argv[i], "-px")){
+        px = true;
+      } else if(!strcmp(argv[i], "-j")){
+        if(++i >= argc) usage();
+        jstr = argv[i];
+      } else if(!strcmp(argv[i], "-rb")){
+        if(++i >= argc) usage();
+        bstr = argv[i];
+        if(++i >= argc) usage();
+        estr = argv[i];
+      } else if(!strcmp(argv[i], "-fm")){
+        if(++i >= argc) usage();
+        fmstr = argv[i];
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int rv = proclist(path, cmp, omode, max, pv, px, bk, jstr, bstr, estr, fmstr);
+  return rv;
+}
+
+
+/* parse arguments of optimize command */
+static int runoptimize(int argc, char **argv){
+  char *path = NULL;
+  char *lmstr = NULL;
+  char *nmstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  BDBCMP cmp = NULL;
+  int opts = UINT8_MAX;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-tl")){
+        if(opts == UINT8_MAX) opts = 0;
+        opts |= BDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        if(opts == UINT8_MAX) opts = 0;
+        opts |= BDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        if(opts == UINT8_MAX) opts = 0;
+        opts |= BDBTTCBS;
+      } else if(!strcmp(argv[i], "-tz")){
+        if(opts == UINT8_MAX) opts = 0;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!lmstr){
+      lmstr = argv[i];
+    } else if(!nmstr){
+      nmstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int lmemb = lmstr ? atoi(lmstr) : -1;
+  int nmemb = nmstr ? atoi(nmstr) : -1;
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = procoptimize(path, lmemb, nmemb, bnum, apow, fpow, cmp, opts, omode);
+  return rv;
+}
+
+
+/* parse arguments of importtsv command */
+static int runimporttsv(int argc, char **argv){
+  char *path = NULL;
+  char *file = NULL;
+  int omode = 0;
+  bool sc = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-sc")){
+        sc = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int rv = procimporttsv(path, file, omode, sc);
+  return rv;
+}
+
+
+/* parse arguments of version command */
+static int runversion(int argc, char **argv){
+  int rv = procversion();
+  return rv;
+}
+
+
+/* perform create command */
+static int proccreate(const char *path, int lmemb, int nmemb,
+                      int bnum, int apow, int fpow, BDBCMP cmp, int opts){
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+  if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+    printerr(bdb);
+    tcbdbdel(bdb);
+    return 1;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){
+    printerr(bdb);
+    tcbdbdel(bdb);
+    return 1;
+  }
+  bool err = false;
+  if(!tcbdbclose(bdb)){
+    printerr(bdb);
+    err = true;
+  }
+  tcbdbdel(bdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform inform command */
+static int procinform(const char *path, int omode){
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  tcbdbsetcmpfunc(bdb, mycmpfunc, NULL);
+  if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
+    printerr(bdb);
+    tcbdbdel(bdb);
+    return 1;
+  }
+  bool err = false;
+  const char *npath = tcbdbpath(bdb);
+  if(!npath) npath = "(unknown)";
+  printf("path: %s\n", npath);
+  printf("database type: btree\n");
+  uint8_t flags = tcbdbflags(bdb);
+  printf("additional flags:");
+  if(flags & BDBFOPEN) printf(" open");
+  if(flags & BDBFFATAL) printf(" fatal");
+  printf("\n");
+  BDBCMP cmp = tcbdbcmpfunc(bdb);
+  printf("comparison function: ");
+  if(cmp == tcbdbcmplexical){
+    printf("lexical");
+  } else if(cmp == tcbdbcmpdecimal){
+    printf("decimal");
+  } else if(cmp == tcbdbcmpint32){
+    printf("int32");
+  } else if(cmp == tcbdbcmpint64){
+    printf("int64");
+  } else {
+    printf("custom");
+  }
+  printf("\n");
+  printf("max leaf member: %d\n", tcbdblmemb(bdb));
+  printf("max node member: %d\n", tcbdbnmemb(bdb));
+  printf("leaf number: %llu\n", (unsigned long long)tcbdblnum(bdb));
+  printf("node number: %llu\n", (unsigned long long)tcbdbnnum(bdb));
+  printf("bucket number: %llu\n", (unsigned long long)tcbdbbnum(bdb));
+  if(bdb->hdb->cnt_writerec >= 0)
+    printf("used bucket number: %lld\n", (long long)tcbdbbnumused(bdb));
+  printf("alignment: %u\n", tcbdbalign(bdb));
+  printf("free block pool: %u\n", tcbdbfbpmax(bdb));
+  printf("inode number: %lld\n", (long long)tcbdbinode(bdb));
+  char date[48];
+  tcdatestrwww(tcbdbmtime(bdb), INT_MAX, date);
+  printf("modified time: %s\n", date);
+  uint8_t opts = tcbdbopts(bdb);
+  printf("options:");
+  if(opts & BDBTLARGE) printf(" large");
+  if(opts & BDBTDEFLATE) printf(" deflate");
+  if(opts & BDBTTCBS) printf(" tcbs");
+  printf("\n");
+  printf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  printf("file size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  if(!tcbdbclose(bdb)){
+    if(!err) printerr(bdb);
+    err = true;
+  }
+  tcbdbdel(bdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform put command */
+static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                   BDBCMP cmp, int omode, int dmode){
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+  if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+    printerr(bdb);
+    tcbdbdel(bdb);
+    return 1;
+  }
+  bool err = false;
+  switch(dmode){
+  case -1:
+    if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(bdb);
+      err = true;
+    }
+    break;
+  case 1:
+    if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(bdb);
+      err = true;
+    }
+    break;
+  case 2:
+    if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(bdb);
+      err = true;
+    }
+    break;
+  case 3:
+    if(!tcbdbputdupback(bdb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(bdb);
+      err = true;
+    }
+    break;
+  default:
+    if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(bdb);
+      err = true;
+    }
+    break;
+  }
+  if(!tcbdbclose(bdb)){
+    if(!err) printerr(bdb);
+    err = true;
+  }
+  tcbdbdel(bdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform out command */
+static int procout(const char *path, const char *kbuf, int ksiz, BDBCMP cmp, int omode){
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+  if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+    printerr(bdb);
+    tcbdbdel(bdb);
+    return 1;
+  }
+  bool err = false;
+  if(!tcbdbout(bdb, kbuf, ksiz)){
+    printerr(bdb);
+    err = true;
+  }
+  if(!tcbdbclose(bdb)){
+    if(!err) printerr(bdb);
+    err = true;
+  }
+  tcbdbdel(bdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform get command */
+static int procget(const char *path, const char *kbuf, int ksiz, BDBCMP cmp, int omode,
+                   bool px, bool pz){
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+  if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
+    printerr(bdb);
+    tcbdbdel(bdb);
+    return 1;
+  }
+  bool err = false;
+  int vsiz;
+  char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+  if(vbuf){
+    printdata(vbuf, vsiz, px);
+    if(!pz) putchar('\n');
+    free(vbuf);
+  } else {
+    printerr(bdb);
+    err = true;
+  }
+  if(!tcbdbclose(bdb)){
+    if(!err) printerr(bdb);
+    err = true;
+  }
+  tcbdbdel(bdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform list command */
+static int proclist(const char *path, BDBCMP cmp, int omode, int max, bool pv, bool px, bool bk,
+                    const char *jstr, const char *bstr, const char *estr, const char *fmstr){
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+  if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
+    printerr(bdb);
+    tcbdbdel(bdb);
+    return 1;
+  }
+  bool err = false;
+  if(bstr || fmstr){
+    TCLIST *keys = fmstr ? tcbdbfwmkeys2(bdb, fmstr, max) :
+      tcbdbrange(bdb, bstr, strlen(bstr), true, estr, strlen(estr), true, max);
+    int cnt = 0;
+    for(int i = 0; i < tclistnum(keys); i++){
+      int ksiz;
+      const char *kbuf = tclistval(keys, i, &ksiz);
+      if(pv){
+        TCLIST *vals = tcbdbget4(bdb, kbuf, ksiz);
+        if(vals){
+          for(int j = 0; j < tclistnum(vals); j++){
+            int vsiz;
+            const char *vbuf = tclistval(vals, j, &vsiz);
+            printdata(kbuf, ksiz, px);
+            putchar('\t');
+            printdata(vbuf, vsiz, px);
+            putchar('\n');
+            if(max >= 0 && ++cnt >= max) break;
+          }
+          tclistdel(vals);
+        }
+      } else {
+        int num = tcbdbvnum(bdb, kbuf, ksiz);
+        for(int j = 0; j < num; j++){
+          printdata(kbuf, ksiz, px);
+          putchar('\n');
+          if(max >= 0 && ++cnt >= max) break;
+        }
+      }
+      if(max >= 0 && cnt >= max) break;
+    }
+    tclistdel(keys);
+  } else {
+    BDBCUR *cur = tcbdbcurnew(bdb);
+    if(bk){
+      if(jstr){
+        if(!tcbdbcurjumpback(cur, jstr, strlen(jstr)) && tcbdbecode(bdb) != TCENOREC){
+          printerr(bdb);
+          err = true;
+        }
+      } else {
+        if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){
+          printerr(bdb);
+          err = true;
+        }
+      }
+    } else {
+      if(jstr){
+        if(!tcbdbcurjump(cur, jstr, strlen(jstr)) && tcbdbecode(bdb) != TCENOREC){
+          printerr(bdb);
+          err = true;
+        }
+      } else {
+        if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+          printerr(bdb);
+          err = true;
+        }
+      }
+    }
+    TCXSTR *key = tcxstrnew();
+    TCXSTR *val = tcxstrnew();
+    int cnt = 0;
+    while(tcbdbcurrec(cur, key, val)){
+      printdata(tcxstrptr(key), tcxstrsize(key), px);
+      if(pv){
+        putchar('\t');
+        printdata(tcxstrptr(val), tcxstrsize(val), px);
+      }
+      putchar('\n');
+      if(bk){
+        if(!tcbdbcurprev(cur) && tcbdbecode(bdb) != TCENOREC){
+          printerr(bdb);
+          err = true;
+        }
+      } else {
+        if(!tcbdbcurnext(cur) && tcbdbecode(bdb) != TCENOREC){
+          printerr(bdb);
+          err = true;
+        }
+      }
+      if(max >= 0 && ++cnt >= max) break;
+    }
+    tcxstrdel(val);
+    tcxstrdel(key);
+    tcbdbcurdel(cur);
+  }
+  if(!tcbdbclose(bdb)){
+    if(!err) printerr(bdb);
+    err = true;
+  }
+  tcbdbdel(bdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform optimize command */
+static int procoptimize(const char *path, int lmemb, int nmemb,
+                        int bnum, int apow, int fpow, BDBCMP cmp, int opts, int omode){
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+  if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+    printerr(bdb);
+    tcbdbdel(bdb);
+    return 1;
+  }
+  bool err = false;
+  if(!tcbdboptimize(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+    printerr(bdb);
+    err = true;
+  }
+  if(!tcbdbclose(bdb)){
+    if(!err) printerr(bdb);
+    err = true;
+  }
+  tcbdbdel(bdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform importtsv command */
+static int procimporttsv(const char *path, const char *file, int omode, bool sc){
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  FILE *ifp = file ? fopen(file, "rb") : stdin;
+  if(!ifp){
+    fprintf(stderr, "%s: could not open\n", file ? file : "(stdin)");
+    tcbdbdel(bdb);
+    return 1;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | omode)){
+    printerr(bdb);
+    tcbdbdel(bdb);
+    return 1;
+  }
+  bool err = false;
+  char *line;
+  int cnt = 0;
+  while(!err && (line = mygetline(ifp)) != NULL){
+    char *pv = strchr(line, '\t');
+    if(!pv) continue;
+    *pv = '\0';
+    if(sc) tcstrtolower(line);
+    if(!tcbdbputdup2(bdb, line, pv + 1) && tcbdbecode(bdb) != TCEKEEP){
+      printerr(bdb);
+      err = true;
+    }
+    free(line);
+    if(cnt > 0 && cnt % 100 == 0){
+      putchar('.');
+      fflush(stdout);
+      if(cnt % 5000 == 0) printf(" (%08d)\n", cnt);
+    }
+    cnt++;
+  }
+  printf(" (%08d)\n", cnt);
+  if(!tcbdbclose(bdb)){
+    if(!err) printerr(bdb);
+    err = true;
+  }
+  tcbdbdel(bdb);
+  if(ifp != stdin) fclose(ifp);
+  return err ? 1 : 0;
+}
+
+
+/* perform version command */
+static int procversion(void){
+  printf("Tokyo Cabinet version %s (%d:%s)\n", tcversion, _TC_LIBVER, _TC_FORMATVER);
+  printf("Copyright (C) 2006-2008 Mikio Hirabayashi\n");
+  return 0;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcbmttest.c b/bacula/src/lib/tokyocabinet/tcbmttest.c
new file mode 100644 (file)
index 0000000..5f8ae01
--- /dev/null
@@ -0,0 +1,1325 @@
+/*************************************************************************************************
+ * The test cases of the B+ tree database API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tcbdb.h>
+#include "myconf.h"
+
+#define RECBUFSIZ      32                // buffer for records
+
+typedef struct {                         // type of structure for write thread
+  TCBDB *bdb;
+  int rnum;
+  bool rnd;
+  int id;
+} TARGWRITE;
+
+typedef struct {                         // type of structure for read thread
+  TCBDB *bdb;
+  int rnum;
+  bool wb;
+  bool rnd;
+  int id;
+} TARGREAD;
+
+typedef struct {                         // type of structure for remove thread
+  TCBDB *bdb;
+  int rnum;
+  bool rnd;
+  int id;
+} TARGREMOVE;
+
+typedef struct {                         // type of structure for wicked thread
+  TCBDB *bdb;
+  int rnum;
+  bool nc;
+  int id;
+  TCMAP *map;
+} TARGWICKED;
+
+typedef struct {                         // type of structure for typical thread
+  TCBDB *bdb;
+  int rnum;
+  bool nc;
+  int rratio;
+  int id;
+} TARGTYPICAL;
+
+
+/* global variables */
+const char *g_progname;                  // program name
+int g_dbgfd;                             // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(TCBDB *bdb, const char *func);
+static void mprint(TCBDB *bdb);
+static int myrand(int range);
+static int myrandnd(int range);
+static int runwrite(int argc, char **argv);
+static int runread(int argc, char **argv);
+static int runremove(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int runtypical(int argc, char **argv);
+static int procwrite(const char *path, int tnum, int rnum, int lmemb, int nmemb,
+                     int bnum, int apow, int fpow, int opts, int omode, bool rnd);
+static int procread(const char *path, int tnum, int omode, bool wb, bool rnd);
+static int procremove(const char *path, int tnum, int omode, bool rnd);
+static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc);
+static int proctypical(const char *path, int tnum, int rnum, int lmemb, int nmemb,
+                       int bnum, int apow, int fpow, int opts, int omode, bool nc, int rratio);
+static void *threadwrite(void *targ);
+static void *threadread(void *targ);
+static void *threadremove(void *targ);
+static void *threadwicked(void *targ);
+static void *threadtypical(void *targ);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  g_dbgfd = -1;
+  const char *ebuf = getenv("TCDBGFD");
+  if(ebuf) g_dbgfd = atoi(ebuf);
+  srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "write")){
+    rv = runwrite(argc, argv);
+  } else if(!strcmp(argv[1], "read")){
+    rv = runread(argc, argv);
+  } else if(!strcmp(argv[1], "remove")){
+    rv = runremove(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else if(!strcmp(argv[1], "typical")){
+    rv = runtypical(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: test cases of the B+ tree database API of Tokyo Cabinet\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write [-tl] [-td|-tb] [-nl|-nb] [-rnd] path tnum rnum"
+          " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
+  fprintf(stderr, "  %s read [-nl|-nb] [-wb] [-rnd] path tnum\n", g_progname);
+  fprintf(stderr, "  %s remove [-nl|-nb] [-rnd] path tnum\n", g_progname);
+  fprintf(stderr, "  %s wicked [-tl] [-td|-tb] [-nl|-nb] [-nc] path tnum rnum\n",
+          g_progname);
+  fprintf(stderr, "  %s typical [-tl] [-td|-tb] [-nl|-nb] [-nc] [-rr] path tnum rnum"
+          " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+  va_list ap;
+  va_start(ap, format);
+  vprintf(format, ap);
+  fflush(stdout);
+  va_end(ap);
+}
+
+
+/* print error message of hash database */
+static void eprint(TCBDB *bdb, const char *func){
+  const char *path = tcbdbpath(bdb);
+  int ecode = tcbdbecode(bdb);
+  fprintf(stderr, "%s: %s: %s: error: %d: %s\n",
+          g_progname, path ? path : "-", func, ecode, tcbdberrmsg(ecode));
+}
+
+
+/* print members of hash database */
+static void mprint(TCBDB *bdb){
+  if(bdb->hdb->cnt_writerec < 0) return;
+  iprintf("max leaf member: %d\n", tcbdblmemb(bdb));
+  iprintf("max node member: %d\n", tcbdbnmemb(bdb));
+  iprintf("leaf number: %d\n", tcbdblnum(bdb));
+  iprintf("node number: %d\n", tcbdbnnum(bdb));
+  iprintf("bucket number: %lld\n", (long long)tcbdbbnum(bdb));
+  iprintf("used bucket number: %lld\n", (long long)tcbdbbnumused(bdb));
+  iprintf("cnt_saveleaf: %lld\n", (long long)bdb->cnt_saveleaf);
+  iprintf("cnt_loadleaf: %lld\n", (long long)bdb->cnt_loadleaf);
+  iprintf("cnt_killleaf: %lld\n", (long long)bdb->cnt_killleaf);
+  iprintf("cnt_adjleafc: %lld\n", (long long)bdb->cnt_adjleafc);
+  iprintf("cnt_savenode: %lld\n", (long long)bdb->cnt_savenode);
+  iprintf("cnt_loadnode: %lld\n", (long long)bdb->cnt_loadnode);
+  iprintf("cnt_adjnodec: %lld\n", (long long)bdb->cnt_adjnodec);
+  iprintf("cnt_writerec: %lld\n", (long long)bdb->hdb->cnt_writerec);
+  iprintf("cnt_reuserec: %lld\n", (long long)bdb->hdb->cnt_reuserec);
+  iprintf("cnt_moverec: %lld\n", (long long)bdb->hdb->cnt_moverec);
+  iprintf("cnt_readrec: %lld\n", (long long)bdb->hdb->cnt_readrec);
+  iprintf("cnt_searchfbp: %lld\n", (long long)bdb->hdb->cnt_searchfbp);
+  iprintf("cnt_insertfbp: %lld\n", (long long)bdb->hdb->cnt_insertfbp);
+  iprintf("cnt_splicefbp: %lld\n", (long long)bdb->hdb->cnt_splicefbp);
+  iprintf("cnt_dividefbp: %lld\n", (long long)bdb->hdb->cnt_dividefbp);
+  iprintf("cnt_mergefbp: %lld\n", (long long)bdb->hdb->cnt_mergefbp);
+  iprintf("cnt_reducefbp: %lld\n", (long long)bdb->hdb->cnt_reducefbp);
+  iprintf("cnt_appenddrp: %lld\n", (long long)bdb->hdb->cnt_appenddrp);
+  iprintf("cnt_deferdrp: %lld\n", (long long)bdb->hdb->cnt_deferdrp);
+  iprintf("cnt_flushdrp: %lld\n", (long long)bdb->hdb->cnt_flushdrp);
+  iprintf("cnt_adjrecc: %lld\n", (long long)bdb->hdb->cnt_adjrecc);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+  return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* get a random number based on normal distribution */
+static int myrandnd(int range){
+  int num = (int)tcdrandnd(range >> 1, range / 10);
+  return (num < 0 || num >= range) ? 0 : num;
+}
+
+
+/* parse arguments of write command */
+static int runwrite(int argc, char **argv){
+  char *path = NULL;
+  char *tstr = NULL;
+  char *rstr = NULL;
+  char *lmstr = NULL;
+  char *nmstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  int opts = 0;
+  int omode = 0;
+  bool rnd = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-tl")){
+        opts |= BDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= BDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= BDBTTCBS;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-rnd")){
+        rnd = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!tstr){
+      tstr = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!lmstr){
+      lmstr = argv[i];
+    } else if(!nmstr){
+      nmstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !tstr || !rstr) usage();
+  int tnum = atoi(tstr);
+  int rnum = atoi(rstr);
+  if(tnum < 1 || rnum < 1) usage();
+  int lmemb = lmstr ? atoi(lmstr) : -1;
+  int nmemb = nmstr ? atoi(nmstr) : -1;
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = procwrite(path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, rnd);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+static int runread(int argc, char **argv){
+  char *path = NULL;
+  char *tstr = NULL;
+  int omode = 0;
+  bool rnd = false;
+  bool wb = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-wb")){
+        wb = true;
+      } else if(!strcmp(argv[i], "-rnd")){
+        rnd = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!tstr){
+      tstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !tstr) usage();
+  int tnum = atoi(tstr);
+  if(tnum < 1) usage();
+  int rv = procread(path, tnum, omode, wb, rnd);
+  return rv;
+}
+
+
+/* parse arguments of remove command */
+static int runremove(int argc, char **argv){
+  char *path = NULL;
+  char *tstr = NULL;
+  int omode = 0;
+  bool rnd = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-rnd")){
+        rnd = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!tstr){
+      tstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !tstr) usage();
+  int tnum = atoi(tstr);
+  if(tnum < 1) usage();
+  int rv = procremove(path, tnum, omode, rnd);
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+  char *path = NULL;
+  char *tstr = NULL;
+  char *rstr = NULL;
+  int opts = 0;
+  int omode = 0;
+  bool nc = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-tl")){
+        opts |= BDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= BDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= BDBTTCBS;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-nc")){
+        nc = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!tstr){
+      tstr = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !tstr || !rstr) usage();
+  int tnum = atoi(tstr);
+  int rnum = atoi(rstr);
+  if(tnum < 1 || rnum < 1) usage();
+  int rv = procwicked(path, tnum, rnum, opts, omode, nc);
+  return rv;
+}
+
+
+/* parse arguments of typical command */
+static int runtypical(int argc, char **argv){
+  char *path = NULL;
+  char *tstr = NULL;
+  char *rstr = NULL;
+  char *lmstr = NULL;
+  char *nmstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  int opts = 0;
+  int omode = 0;
+  int rratio = -1;
+  bool nc = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-tl")){
+        opts |= BDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= BDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= BDBTTCBS;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-nc")){
+        nc = true;
+      } else if(!strcmp(argv[i], "-rr")){
+        if(++i >= argc) usage();
+        rratio = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!tstr){
+      tstr = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!lmstr){
+      lmstr = argv[i];
+    } else if(!nmstr){
+      nmstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !tstr || !rstr) usage();
+  int tnum = atoi(tstr);
+  int rnum = atoi(rstr);
+  if(tnum < 1 || rnum < 1) usage();
+  int lmemb = lmstr ? atoi(lmstr) : -1;
+  int nmemb = nmstr ? atoi(nmstr) : -1;
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = proctypical(path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, nc, rratio);
+  return rv;
+}
+
+
+/* perform write command */
+static int procwrite(const char *path, int tnum, int rnum, int lmemb, int nmemb,
+                     int bnum, int apow, int fpow, int opts, int omode, bool rnd){
+  iprintf("<Writing Test>\n  path=%s  tnum=%d  rnum=%d  lmemb=%d  nmemb=%d"
+          "  bnum=%d  apow=%d  fpow=%d  opts=%d  omode=%d  rnd=%d\n\n",
+          path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, rnd);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(!tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+    eprint(bdb, "tcbdbtune");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  TARGWRITE targs[tnum];
+  pthread_t threads[tnum];
+  if(tnum == 1){
+    targs[0].bdb = bdb;
+    targs[0].rnum = rnum;
+    targs[0].rnd = rnd;
+    targs[0].id = 0;
+    if(threadwrite(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].bdb = bdb;
+      targs[i].rnum = rnum;
+      targs[i].rnd = rnd;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){
+        eprint(bdb, "pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint(bdb, "pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform read command */
+static int procread(const char *path, int tnum, int omode, bool wb, bool rnd){
+  iprintf("<Reading Test>\n  path=%s  tnum=%d  omode=%d  wb=%d  rnd=%d\n\n",
+          path, tnum, omode, wb, rnd);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(!tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  int rnum = tcbdbrnum(bdb) / tnum;
+  TARGREAD targs[tnum];
+  pthread_t threads[tnum];
+  if(tnum == 1){
+    targs[0].bdb = bdb;
+    targs[0].rnum = rnum;
+    targs[0].wb = wb;
+    targs[0].rnd = rnd;
+    targs[0].id = 0;
+    if(threadread(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].bdb = bdb;
+      targs[i].rnum = rnum;
+      targs[i].wb = wb;
+      targs[i].rnd = rnd;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){
+        eprint(bdb, "pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint(bdb, "pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform remove command */
+static int procremove(const char *path, int tnum, int omode, bool rnd){
+  iprintf("<Removing Test>\n  path=%s  tnum=%d  omode=%d  rnd=%d\n\n", path, tnum, omode, rnd);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(!tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  int rnum = tcbdbrnum(bdb) / tnum;
+  TARGREMOVE targs[tnum];
+  pthread_t threads[tnum];
+  if(tnum == 1){
+    targs[0].bdb = bdb;
+    targs[0].rnum = rnum;
+    targs[0].rnd = rnd;
+    targs[0].id = 0;
+    if(threadremove(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].bdb = bdb;
+      targs[i].rnum = rnum;
+      targs[i].rnd = rnd;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){
+        eprint(bdb, "pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint(bdb, "pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc){
+  iprintf("<Writing Test>\n  path=%s  tnum=%d  rnum=%d  opts=%d  omode=%d  nc=%d\n\n",
+          path, tnum, rnum, opts, omode, nc);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(!tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(!tcbdbtune(bdb, 10, 10, rnum / 50, 10, -1, opts)){
+    eprint(bdb, "tcbdbtune");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  TARGWICKED targs[tnum];
+  pthread_t threads[tnum];
+  TCMAP *map = tcmapnew();
+  if(tnum == 1){
+    targs[0].bdb = bdb;
+    targs[0].rnum = rnum;
+    targs[0].nc = nc;
+    targs[0].id = 0;
+    targs[0].map = map;
+    if(threadwicked(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].bdb = bdb;
+      targs[i].rnum = rnum;
+      targs[i].nc = nc;
+      targs[i].id = i;
+      targs[i].map = map;
+      targs[i].map = map;
+      if(pthread_create(threads + i, NULL, threadwicked, targs + i) != 0){
+        eprint(bdb, "pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint(bdb, "pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  if(!nc){
+    if(!tcbdbsync(bdb)){
+      eprint(bdb, "tcbdbsync");
+      err = true;
+    }
+    if(tcbdbrnum(bdb) != tcmaprnum(map)){
+      eprint(bdb, "(validation)");
+      err = true;
+    }
+    int end = rnum * tnum;
+    for(int i = 1; i <= end && !err; i++){
+      char kbuf[RECBUFSIZ];
+      int ksiz = sprintf(kbuf, "%d", i - 1);
+      int vsiz;
+      const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
+      int rsiz;
+      char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
+      if(vbuf){
+        putchar('.');
+        if(!rbuf){
+          eprint(bdb, "tcbdbget");
+          err = true;
+        } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+          eprint(bdb, "(validation)");
+          err = true;
+        }
+      } else {
+        putchar('*');
+        if(rbuf || tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "(validation)");
+          err = true;
+        }
+      }
+      free(rbuf);
+      if(i % 50 == 0) iprintf(" (%08d)\n", i);
+    }
+    if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+  }
+  tcmapdel(map);
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform typical command */
+static int proctypical(const char *path, int tnum, int rnum, int lmemb, int nmemb,
+                       int bnum, int apow, int fpow, int opts, int omode, bool nc, int rratio){
+  iprintf("<Typical Access Test>\n  path=%s  tnum=%d  rnum=%d  lmemb=%d  nmemb=%d"
+          "  bnum=%d  apow=%d  fpow=%d  opts=%d  omode=%d  nc=%d  rratio=%d\n\n",
+          path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, nc, rratio);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(!tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+    eprint(bdb, "tcbdbtune");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  TARGTYPICAL targs[tnum];
+  pthread_t threads[tnum];
+  if(tnum == 1){
+    targs[0].bdb = bdb;
+    targs[0].rnum = rnum;
+    targs[0].nc = nc;
+    targs[0].rratio = rratio;
+    targs[0].id = 0;
+    if(threadtypical(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].bdb = bdb;
+      targs[i].rnum = rnum;
+      targs[i].nc = nc;
+      targs[i].rratio = rratio;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){
+        eprint(bdb, "pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint(bdb, "pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* thread the write function */
+static void *threadwrite(void *targ){
+  TCBDB *bdb = ((TARGWRITE *)targ)->bdb;
+  int rnum = ((TARGWRITE *)targ)->rnum;
+  bool rnd = ((TARGWRITE *)targ)->rnd;
+  int id = ((TARGWRITE *)targ)->id;
+  bool err = false;
+  int base = id * rnum;
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i));
+    if(!tcbdbput(bdb, buf, len, buf, len)){
+      eprint(bdb, "tcbdbput");
+      err = true;
+      break;
+    }
+    if(id <= 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  return err ? "error" : NULL;
+}
+
+
+/* thread the read function */
+static void *threadread(void *targ){
+  TCBDB *bdb = ((TARGREAD *)targ)->bdb;
+  int rnum = ((TARGREAD *)targ)->rnum;
+  bool wb = ((TARGREAD *)targ)->wb;
+  bool rnd = ((TARGREAD *)targ)->rnd;
+  int id = ((TARGREAD *)targ)->id;
+  bool err = false;
+  int base = id * rnum;
+  for(int i = 1; i <= rnum && !err; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrandnd(i) : i));
+    int vsiz;
+    if(wb){
+      int vsiz;
+      const char *vbuf = tcbdbget3(bdb, kbuf, ksiz, &vsiz);
+      if(!vbuf && (!rnd || tcbdbecode(bdb) != TCENOREC)){
+        eprint(bdb, "tcbdbget3");
+        err = true;
+      }
+    } else {
+      char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+      if(!vbuf && (!rnd || tcbdbecode(bdb) != TCENOREC)){
+        eprint(bdb, "tcbdbget");
+        err = true;
+      }
+      free(vbuf);
+    }
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  return err ? "error" : NULL;
+}
+
+
+/* thread the remove function */
+static void *threadremove(void *targ){
+  TCBDB *bdb = ((TARGREMOVE *)targ)->bdb;
+  int rnum = ((TARGREMOVE *)targ)->rnum;
+  bool rnd = ((TARGREMOVE *)targ)->rnd;
+  int id = ((TARGREMOVE *)targ)->id;
+  bool err = false;
+  int base = id * rnum;
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i + 1) : i));
+    if(!tcbdbout(bdb, kbuf, ksiz) && (!rnd || tcbdbecode(bdb) != TCENOREC)){
+      eprint(bdb, "tcbdbout");
+      err = true;
+      break;
+    }
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  return err ? "error" : NULL;
+}
+
+
+/* thread the wicked function */
+static void *threadwicked(void *targ){
+  TCBDB *bdb = ((TARGWICKED *)targ)->bdb;
+  int rnum = ((TARGWICKED *)targ)->rnum;
+  bool nc = ((TARGWICKED *)targ)->nc;
+  int id = ((TARGWICKED *)targ)->id;
+  TCMAP *map = ((TARGWICKED *)targ)->map;
+  BDBCUR *cur = tcbdbcurnew(bdb);
+  bool err = false;
+  for(int i = 1; i <= rnum && !err; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum * (id + 1)));
+    char vbuf[RECBUFSIZ];
+    int vsiz = myrand(RECBUFSIZ);
+    memset(vbuf, '*', vsiz);
+    vbuf[vsiz] = '\0';
+    char *rbuf;
+    if(!nc) tcglobalmutexlock();
+    switch(myrand(16)){
+    case 0:
+      if(id == 0) putchar('0');
+      if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(bdb, "tcbdbput");
+        err = true;
+      }
+      if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 1:
+      if(id == 0) putchar('1');
+      if(!tcbdbput2(bdb, kbuf, vbuf)){
+        eprint(bdb, "tcbdbput2");
+        err = true;
+      }
+      if(!nc) tcmapput2(map, kbuf, vbuf);
+      break;
+    case 2:
+      if(id == 0) putchar('2');
+      if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){
+        eprint(bdb, "tcbdbputkeep");
+        err = true;
+      }
+      if(!nc) tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 3:
+      if(id == 0) putchar('3');
+      if(!tcbdbputkeep2(bdb, kbuf, vbuf) && tcbdbecode(bdb) != TCEKEEP){
+        eprint(bdb, "tcbdbputkeep2");
+        err = true;
+      }
+      if(!nc) tcmapputkeep2(map, kbuf, vbuf);
+      break;
+    case 4:
+      if(id == 0) putchar('4');
+      if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(bdb, "tcbdbputcat");
+        err = true;
+      }
+      if(!nc) tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 5:
+      if(id == 0) putchar('5');
+      if(!tcbdbputcat2(bdb, kbuf, vbuf)){
+        eprint(bdb, "tcbdbputcat2");
+        err = true;
+      }
+      if(!nc) tcmapputcat2(map, kbuf, vbuf);
+      break;
+    case 6:
+      if(id == 0) putchar('6');
+      if(nc){
+        if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){
+          eprint(bdb, "tcbdbputdup");
+          err = true;
+        }
+      }
+      break;
+    case 7:
+      if(id == 0) putchar('7');
+      if(nc){
+        if(!tcbdbputdup2(bdb, kbuf, vbuf)){
+          eprint(bdb, "tcbdbputdup2");
+          err = true;
+        }
+      }
+      break;
+    case 8:
+      if(id == 0) putchar('8');
+      if(myrand(10) == 0){
+        if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbout");
+          err = true;
+        }
+        if(!nc) tcmapout(map, kbuf, ksiz);
+      }
+      break;
+    case 9:
+      if(id == 0) putchar('9');
+      if(myrand(10) == 0){
+        if(!tcbdbout2(bdb, kbuf) && tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbout2");
+          err = true;
+        }
+        if(!nc) tcmapout2(map, kbuf);
+      }
+      break;
+    case 10:
+      if(id == 0) putchar('A');
+      if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz))){
+        if(tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbget");
+          err = true;
+        }
+        rbuf = tcsprintf("[%d]", myrand(i + 1));
+        vsiz = strlen(rbuf);
+      }
+      vsiz += myrand(vsiz);
+      if(myrand(3) == 0) vsiz += PATH_MAX;
+      rbuf = tcrealloc(rbuf, vsiz + 1);
+      for(int j = 0; j < vsiz; j++){
+        rbuf[j] = myrand(0x100);
+      }
+      if(!tcbdbput(bdb, kbuf, ksiz, rbuf, vsiz)){
+        eprint(bdb, "tcbdbput");
+        err = true;
+      }
+      if(!nc) tcmapput(map, kbuf, ksiz, rbuf, vsiz);
+      free(rbuf);
+      break;
+    case 11:
+      if(id == 0) putchar('B');
+      if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz)) && tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbget");
+        err = true;
+      }
+      free(rbuf);
+      break;
+    case 12:
+      if(id == 0) putchar('C');
+      if(!(rbuf = tcbdbget2(bdb, kbuf)) && tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbget");
+        err = true;
+      }
+      free(rbuf);
+      break;
+    case 13:
+      if(id == 0) putchar('D');
+      if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz) && tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbget");
+        err = true;
+      }
+      break;
+    case 14:
+      if(id == 0) putchar('E');
+      if(myrand(rnum / 50) == 0){
+        switch(myrand(5)){
+        case 0:
+          if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+            eprint(bdb, "tcbdbcurfirst");
+            err = true;
+          }
+          break;
+        case 1:
+          if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){
+            eprint(bdb, "tcbdbcurfirst");
+            err = true;
+          }
+          break;
+        default:
+          if(!tcbdbcurjump(cur, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+            eprint(bdb, "tcbdbcurjump");
+            err = true;
+          }
+          break;
+        }
+      }
+      TCXSTR *ikey = tcxstrnew();
+      TCXSTR *ival = tcxstrnew();
+      for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
+        if(j % 3 == 0){
+          if(!tcbdbcurrec(cur, ikey, ival)){
+            int ecode = tcbdbecode(bdb);
+            if(ecode != TCEINVALID && ecode != TCENOREC){
+              eprint(bdb, "tcbdbcurrec");
+              err = true;
+            }
+          }
+        } else {
+          int iksiz;
+          if(!tcbdbcurkey3(cur, &iksiz)){
+            int ecode = tcbdbecode(bdb);
+            if(ecode != TCEINVALID && ecode != TCENOREC){
+              eprint(bdb, "tcbdbcurkey3");
+              err = true;
+            }
+          }
+        }
+        if(myrand(5) == 0){
+          if(!tcbdbcurprev(cur)){
+            int ecode = tcbdbecode(bdb);
+            if(ecode != TCEINVALID && ecode != TCENOREC){
+              eprint(bdb, "tcbdbcurprev");
+              err = true;
+            }
+          }
+        } else {
+          if(!tcbdbcurnext(cur)){
+            int ecode = tcbdbecode(bdb);
+            if(ecode != TCEINVALID && ecode != TCENOREC){
+              eprint(bdb, "tcbdbcurnext");
+              err = true;
+            }
+          }
+        }
+      }
+      tcxstrdel(ival);
+      tcxstrdel(ikey);
+      break;
+    default:
+      if(id == 0) putchar('@');
+      if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+      break;
+    }
+    if(!nc) tcglobalmutexunlock();
+    if(id == 0){
+      if(i % 50 == 0) iprintf(" (%08d)\n", i);
+      if(id == 0 && i == rnum / 4){
+        if(!tcbdboptimize(bdb, -1, -1, -1, -1, -1, -1)){
+          eprint(bdb, "tcbdboptimize");
+          err = true;
+        }
+        if(!tcbdbcurfirst(cur)){
+          eprint(bdb, "tcbdbcurfirst");
+          err = true;
+        }
+      }
+    }
+  }
+  tcbdbcurdel(cur);
+  return err ? "error" : NULL;
+}
+
+
+/* thread the typical function */
+static void *threadtypical(void *targ){
+  TCBDB *bdb = ((TARGTYPICAL *)targ)->bdb;
+  int rnum = ((TARGTYPICAL *)targ)->rnum;
+  bool nc = ((TARGTYPICAL *)targ)->nc;
+  int rratio = ((TARGTYPICAL *)targ)->rratio;
+  int id = ((TARGTYPICAL *)targ)->id;
+  bool err = false;
+  TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL;
+  int base = id * rnum;
+  int mrange = tclmax(50 + rratio, 100);
+  BDBCUR *cur = tcbdbcurnew(bdb);
+  for(int i = 1; !err && i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", base + myrandnd(i));
+    int rnd = myrand(mrange);
+    if(rnd < 10){
+      if(!tcbdbput(bdb, buf, len, buf, len)){
+        eprint(bdb, "tcbdbput");
+        err = true;
+      }
+      if(map) tcmapput(map, buf, len, buf, len);
+    } else if(rnd < 15){
+      if(!tcbdbputkeep(bdb, buf, len, buf, len) && tcbdbecode(bdb) != TCEKEEP){
+        eprint(bdb, "tcbdbputkeep");
+        err = true;
+      }
+      if(map) tcmapputkeep(map, buf, len, buf, len);
+    } else if(rnd < 20){
+      if(!tcbdbputcat(bdb, buf, len, buf, len)){
+        eprint(bdb, "tcbdbputcat");
+        err = true;
+      }
+      if(map) tcmapputcat(map, buf, len, buf, len);
+    } else if(rnd < 25){
+      if(!tcbdbout(bdb, buf, len) && tcbdbecode(bdb) && tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbout");
+        err = true;
+      }
+      if(map) tcmapout(map, buf, len);
+    } else if(rnd < 27){
+      switch(myrand(3)){
+      case 0:
+        if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbcurfirst");
+          err = true;
+        }
+        for(int j = 0; !err && j < 10; j++){
+          int ksiz;
+          char *kbuf = tcbdbcurkey(cur, &ksiz);
+          if(kbuf){
+            int vsiz;
+            char *vbuf = tcbdbcurval(cur, &vsiz);
+            if(vbuf){
+              free(vbuf);
+            } else if(tcbdbecode(bdb) != TCENOREC){
+              eprint(bdb, "tcbdbcurval");
+              err = true;
+            }
+            free(kbuf);
+          } else if(tcbdbecode(bdb) != TCENOREC){
+            eprint(bdb, "tcbdbcurkey");
+            err = true;
+          }
+          tcbdbcurnext(cur);
+        }
+        break;
+      case 1:
+        if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbcurlast");
+          err = true;
+        }
+        for(int j = 0; !err && j < 10; j++){
+          int ksiz;
+          char *kbuf = tcbdbcurkey(cur, &ksiz);
+          if(kbuf){
+            int vsiz;
+            char *vbuf = tcbdbcurval(cur, &vsiz);
+            if(vbuf){
+              free(vbuf);
+            } else if(tcbdbecode(bdb) != TCENOREC){
+              eprint(bdb, "tcbdbcurval");
+              err = true;
+            }
+            free(kbuf);
+          } else if(tcbdbecode(bdb) != TCENOREC){
+            eprint(bdb, "tcbdbcurkey");
+            err = true;
+          }
+          tcbdbcurprev(cur);
+        }
+        break;
+      case 2:
+        if(!tcbdbcurjump(cur, buf, len) && tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbcurjump");
+          err = true;
+        }
+        for(int j = 0; !err && j < 10; j++){
+          int ksiz;
+          char *kbuf = tcbdbcurkey(cur, &ksiz);
+          if(kbuf){
+            int vsiz;
+            char *vbuf = tcbdbcurval(cur, &vsiz);
+            if(vbuf){
+              free(vbuf);
+            } else if(tcbdbecode(bdb) != TCENOREC){
+              eprint(bdb, "tcbdbcurval");
+              err = true;
+            }
+            free(kbuf);
+          } else if(tcbdbecode(bdb) != TCENOREC){
+            eprint(bdb, "tcbdbcurkey");
+            err = true;
+          }
+          tcbdbcurnext(cur);
+        }
+        break;
+      }
+    } else {
+      int vsiz;
+      char *vbuf = tcbdbget(bdb, buf, len, &vsiz);
+      if(vbuf){
+        if(map){
+          int msiz;
+          const char *mbuf = tcmapget(map, buf, len, &msiz);
+          if(msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+            eprint(bdb, "(validation)");
+            err = true;
+          }
+        }
+        free(vbuf);
+      } else {
+        if(tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbget");
+          err = true;
+        }
+        if(map && tcmapget(map, buf, len, &vsiz)){
+          eprint(bdb, "(validation)");
+          err = true;
+        }
+      }
+    }
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  tcbdbcurdel(cur);
+  if(map){
+    tcmapiterinit(map);
+    int ksiz;
+    const char *kbuf;
+    while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
+      int vsiz;
+      char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+      if(vbuf){
+        int msiz;
+        const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz);
+        if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+          eprint(bdb, "(validation)");
+          err = true;
+        }
+        free(vbuf);
+      } else {
+        eprint(bdb, "(validation)");
+        err = true;
+      }
+    }
+    tcmapdel(map);
+  }
+  return err ? "error" : NULL;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcbtest.c b/bacula/src/lib/tokyocabinet/tcbtest.c
new file mode 100644 (file)
index 0000000..dd31991
--- /dev/null
@@ -0,0 +1,2030 @@
+/*************************************************************************************************
+ * The test cases of the B+ tree database API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tcbdb.h>
+#include "myconf.h"
+
+#define RECBUFSIZ      32                // buffer for records
+
+
+/* global variables */
+const char *g_progname;                  // program name
+int g_dbgfd;                             // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(TCBDB *bdb, const char *func);
+static void mprint(TCBDB *bdb);
+static int myrand(int range);
+static int runwrite(int argc, char **argv);
+static int runread(int argc, char **argv);
+static int runremove(int argc, char **argv);
+static int runrcat(int argc, char **argv);
+static int runqueue(int argc, char **argv);
+static int runmisc(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int procwrite(const char *path, int rnum, int lmemb, int nmemb, int bnum,
+                     int apow, int fpow, bool mt, BDBCMP cmp, int opts,
+                     int lcnum, int ncnum, int lsmax, int capnum, int omode);
+static int procread(const char *path, bool mt, BDBCMP cmp, int lcnum, int ncnum,
+                    int omode, bool wb);
+static int procremove(const char *path, bool mt, BDBCMP cmp, int lcnum, int ncnum, int omode);
+static int procrcat(const char *path, int rnum,
+                    int lmemb, int nmemb, int bnum, int apow, int fpow,
+                    bool mt, BDBCMP cmp, int opts, int lcnum, int ncnum, int lsmax, int capnum,
+                    int omode, int pnum, bool rl);
+static int procqueue(const char *path, int rnum, int lmemb, int nmemb, int bnum,
+                     int apow, int fpow, bool mt, BDBCMP cmp, int opts,
+                     int lcnum, int ncnum, int lsmax, int capnum, int omode);
+static int procmisc(const char *path, int rnum, bool mt, int opts, int omode);
+static int procwicked(const char *path, int rnum, bool mt, int opts, int omode);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  g_dbgfd = -1;
+  const char *ebuf = getenv("TCDBGFD");
+  if(ebuf) g_dbgfd = atoi(ebuf);
+  srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "write")){
+    rv = runwrite(argc, argv);
+  } else if(!strcmp(argv[1], "read")){
+    rv = runread(argc, argv);
+  } else if(!strcmp(argv[1], "remove")){
+    rv = runremove(argc, argv);
+  } else if(!strcmp(argv[1], "rcat")){
+    rv = runrcat(argc, argv);
+  } else if(!strcmp(argv[1], "queue")){
+    rv = runqueue(argc, argv);
+  } else if(!strcmp(argv[1], "misc")){
+    rv = runmisc(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: test cases of the B+ tree database API of Tokyo Cabinet\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb] [-lc num] [-nc num]"
+          " [-ls num] [-ca num] [-nl|-nb] path rnum [lmemb [nmemb [bnum [apow [fpow]]]]]\n",
+          g_progname);
+  fprintf(stderr, "  %s read [-mt] [-cd|-ci|-cj] [-lc num] [-nc num] [-nl|-nb] [-wb]"
+          " path\n", g_progname);
+  fprintf(stderr, "  %s remove [-mt] [-cd|-ci|-cj] [-lc num] [-nc num] [-nl|-nb]"
+          " path\n", g_progname);
+  fprintf(stderr, "  %s rcat [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb] [-lc num] [-nc num]"
+          " [-ls num] [-ca num] [-nl|-nb] [-pn num] [-rl] path rnum"
+          " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
+  fprintf(stderr, "  %s queue [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb] [-lc num] [-nc num]"
+          " [-ls num] [-ca num] [-nl|-nb] path rnum [lmemb [nmemb [bnum [apow [fpow]]]]]\n",
+          g_progname);
+  fprintf(stderr, "  %s misc [-mt] [-tl] [-td|-tb] [-nl|-nb] path rnum\n", g_progname);
+  fprintf(stderr, "  %s wicked [-mt] [-tl] [-td|-tb] [-nl|-nb] path rnum\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+  va_list ap;
+  va_start(ap, format);
+  vprintf(format, ap);
+  fflush(stdout);
+  va_end(ap);
+}
+
+
+/* print error message of hash database */
+static void eprint(TCBDB *bdb, const char *func){
+  const char *path = tcbdbpath(bdb);
+  int ecode = tcbdbecode(bdb);
+  fprintf(stderr, "%s: %s: %s: error: %d: %s\n",
+          g_progname, path ? path : "-", func, ecode, tcbdberrmsg(ecode));
+}
+
+
+/* print members of B+ tree database */
+static void mprint(TCBDB *bdb){
+  if(bdb->hdb->cnt_writerec < 0) return;
+  iprintf("max leaf member: %d\n", tcbdblmemb(bdb));
+  iprintf("max node member: %d\n", tcbdbnmemb(bdb));
+  iprintf("leaf number: %d\n", tcbdblnum(bdb));
+  iprintf("node number: %d\n", tcbdbnnum(bdb));
+  iprintf("bucket number: %lld\n", (long long)tcbdbbnum(bdb));
+  iprintf("used bucket number: %lld\n", (long long)tcbdbbnumused(bdb));
+  iprintf("cnt_saveleaf: %lld\n", (long long)bdb->cnt_saveleaf);
+  iprintf("cnt_loadleaf: %lld\n", (long long)bdb->cnt_loadleaf);
+  iprintf("cnt_killleaf: %lld\n", (long long)bdb->cnt_killleaf);
+  iprintf("cnt_adjleafc: %lld\n", (long long)bdb->cnt_adjleafc);
+  iprintf("cnt_savenode: %lld\n", (long long)bdb->cnt_savenode);
+  iprintf("cnt_loadnode: %lld\n", (long long)bdb->cnt_loadnode);
+  iprintf("cnt_adjnodec: %lld\n", (long long)bdb->cnt_adjnodec);
+  iprintf("cnt_writerec: %lld\n", (long long)bdb->hdb->cnt_writerec);
+  iprintf("cnt_reuserec: %lld\n", (long long)bdb->hdb->cnt_reuserec);
+  iprintf("cnt_moverec: %lld\n", (long long)bdb->hdb->cnt_moverec);
+  iprintf("cnt_readrec: %lld\n", (long long)bdb->hdb->cnt_readrec);
+  iprintf("cnt_searchfbp: %lld\n", (long long)bdb->hdb->cnt_searchfbp);
+  iprintf("cnt_insertfbp: %lld\n", (long long)bdb->hdb->cnt_insertfbp);
+  iprintf("cnt_splicefbp: %lld\n", (long long)bdb->hdb->cnt_splicefbp);
+  iprintf("cnt_dividefbp: %lld\n", (long long)bdb->hdb->cnt_dividefbp);
+  iprintf("cnt_mergefbp: %lld\n", (long long)bdb->hdb->cnt_mergefbp);
+  iprintf("cnt_reducefbp: %lld\n", (long long)bdb->hdb->cnt_reducefbp);
+  iprintf("cnt_appenddrp: %lld\n", (long long)bdb->hdb->cnt_appenddrp);
+  iprintf("cnt_deferdrp: %lld\n", (long long)bdb->hdb->cnt_deferdrp);
+  iprintf("cnt_flushdrp: %lld\n", (long long)bdb->hdb->cnt_flushdrp);
+  iprintf("cnt_adjrecc: %lld\n", (long long)bdb->hdb->cnt_adjrecc);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+  return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* parse arguments of write command */
+static int runwrite(int argc, char **argv){
+  char *path = NULL;
+  char *rstr = NULL;
+  char *lmstr = NULL;
+  char *nmstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  bool mt = false;
+  BDBCMP cmp = NULL;
+  int opts = 0;
+  int lcnum = 0;
+  int ncnum = 0;
+  int lsmax = 0;
+  int capnum = 0;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-tl")){
+        opts |= BDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= BDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= BDBTTCBS;
+      } else if(!strcmp(argv[i], "-lc")){
+        if(++i >= argc) usage();
+        lcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nc")){
+        if(++i >= argc) usage();
+        ncnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-ls")){
+        if(++i >= argc) usage();
+        lsmax = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-ca")){
+        if(++i >= argc) usage();
+        capnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!lmstr){
+      lmstr = argv[i];
+    } else if(!nmstr){
+      nmstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int lmemb = lmstr ? atoi(lmstr) : -1;
+  int nmemb = nmstr ? atoi(nmstr) : -1;
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = procwrite(path, rnum, lmemb, nmemb, bnum, apow, fpow,
+                     mt, cmp, opts, lcnum, ncnum, lsmax, capnum, omode);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+static int runread(int argc, char **argv){
+  char *path = NULL;
+  bool mt = false;
+  BDBCMP cmp = NULL;
+  int lcnum = 0;
+  int ncnum = 0;
+  int omode = 0;
+  bool wb = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-lc")){
+        if(++i >= argc) usage();
+        lcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nc")){
+        if(++i >= argc) usage();
+        ncnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-wb")){
+        wb = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int rv = procread(path, mt, cmp, lcnum, ncnum, omode, wb);
+  return rv;
+}
+
+
+/* parse arguments of remove command */
+static int runremove(int argc, char **argv){
+  char *path = NULL;
+  bool mt = false;
+  BDBCMP cmp = NULL;
+  int lcnum = 0;
+  int ncnum = 0;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-lc")){
+        if(++i >= argc) usage();
+        lcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nc")){
+        if(++i >= argc) usage();
+        ncnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int rv = procremove(path, mt, cmp, lcnum, ncnum, omode);
+  return rv;
+}
+
+
+/* parse arguments of rcat command */
+static int runrcat(int argc, char **argv){
+  char *path = NULL;
+  char *rstr = NULL;
+  char *lmstr = NULL;
+  char *nmstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  bool mt = false;
+  BDBCMP cmp = NULL;
+  int opts = 0;
+  int lcnum = 0;
+  int ncnum = 0;
+  int lsmax = 0;
+  int capnum = 0;
+  int omode = 0;
+  int pnum = 0;
+  bool rl = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-tl")){
+        opts |= BDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= BDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= BDBTTCBS;
+      } else if(!strcmp(argv[i], "-lc")){
+        if(++i >= argc) usage();
+        lcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nc")){
+        if(++i >= argc) usage();
+        ncnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-ls")){
+        if(++i >= argc) usage();
+        lsmax = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-ca")){
+        if(++i >= argc) usage();
+        capnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else if(!strcmp(argv[i], "-pn")){
+        if(++i >= argc) usage();
+        pnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-rl")){
+        rl = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!lmstr){
+      lmstr = argv[i];
+    } else if(!nmstr){
+      nmstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int lmemb = lmstr ? atoi(lmstr) : -1;
+  int nmemb = nmstr ? atoi(nmstr) : -1;
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = procrcat(path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, cmp, opts,
+                    lcnum, ncnum, lsmax, capnum, omode, pnum, rl);
+  return rv;
+}
+
+
+/* parse arguments of queue command */
+static int runqueue(int argc, char **argv){
+  char *path = NULL;
+  char *rstr = NULL;
+  char *lmstr = NULL;
+  char *nmstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  bool mt = false;
+  BDBCMP cmp = NULL;
+  int opts = 0;
+  int lcnum = 0;
+  int ncnum = 0;
+  int lsmax = 0;
+  int capnum = 0;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-cd")){
+        cmp = tcbdbcmpdecimal;
+      } else if(!strcmp(argv[i], "-ci")){
+        cmp = tcbdbcmpint32;
+      } else if(!strcmp(argv[i], "-cj")){
+        cmp = tcbdbcmpint64;
+      } else if(!strcmp(argv[i], "-tl")){
+        opts |= BDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= BDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= BDBTTCBS;
+      } else if(!strcmp(argv[i], "-lc")){
+        if(++i >= argc) usage();
+        lcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nc")){
+        if(++i >= argc) usage();
+        ncnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-ls")){
+        if(++i >= argc) usage();
+        lsmax = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-ca")){
+        if(++i >= argc) usage();
+        capnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!lmstr){
+      lmstr = argv[i];
+    } else if(!nmstr){
+      nmstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int lmemb = lmstr ? atoi(lmstr) : -1;
+  int nmemb = nmstr ? atoi(nmstr) : -1;
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = procqueue(path, rnum, lmemb, nmemb, bnum, apow, fpow,
+                     mt, cmp, opts, lcnum, ncnum, lsmax, capnum, omode);
+  return rv;
+}
+
+
+/* parse arguments of misc command */
+static int runmisc(int argc, char **argv){
+  char *path = NULL;
+  char *rstr = NULL;
+  bool mt = false;
+  int opts = 0;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-tl")){
+        opts |= BDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= BDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= BDBTTCBS;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procmisc(path, rnum, mt, opts, omode);
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+  char *path = NULL;
+  char *rstr = NULL;
+  bool mt = false;
+  int opts = 0;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-tl")){
+        opts |= BDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= BDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= BDBTTCBS;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= BDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= BDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procwicked(path, rnum, mt, opts, omode);
+  return rv;
+}
+
+
+/* perform write command */
+static int procwrite(const char *path, int rnum, int lmemb, int nmemb, int bnum,
+                     int apow, int fpow, bool mt, BDBCMP cmp, int opts,
+                     int lcnum, int ncnum, int lsmax, int capnum, int omode){
+  iprintf("<Writing Test>\n  path=%s  rnum=%d  lmemb=%d  nmemb=%d  bnum=%d  apow=%d  fpow=%d"
+          "  mt=%d  cmp=%p  opts=%d  lcnum=%d  ncnum=%d  lsmax=%d  capnum=%d  omode=%d\n\n",
+          path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, (void *)(intptr_t)cmp,
+          opts, lcnum, ncnum, lsmax, capnum, omode);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(mt && !tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){
+    eprint(bdb, "tcbdbsetcmpfunc");
+    err = true;
+  }
+  if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+    eprint(bdb, "tcbdbtune");
+    err = true;
+  }
+  if(!tcbdbsetcache(bdb, lcnum, ncnum)){
+    eprint(bdb, "tcbdbsetcache");
+    err = true;
+  }
+  if(!tcbdbsetlsmax(bdb, lsmax)){
+    eprint(bdb, "tcbdbsetlsmax");
+    err = true;
+  }
+  if(!tcbdbsetcapnum(bdb, capnum)){
+    eprint(bdb, "tcbdbsetcapnum");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len;
+    if(cmp == tcbdbcmpdecimal){
+      len = sprintf(buf, "%d", i);
+    } else if(cmp == tcbdbcmpint32){
+      int32_t lnum = i;
+      memcpy(buf, &lnum, sizeof(lnum));
+      len = sizeof(lnum);
+    } else if(cmp == tcbdbcmpint64){
+      int64_t llnum = i;
+      memcpy(buf, &llnum, sizeof(llnum));
+      len = sizeof(llnum);
+    } else {
+      len = sprintf(buf, "%08d", i);
+    }
+    if(!tcbdbput(bdb, buf, len, buf, len)){
+      eprint(bdb, "tcbdbput");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform read command */
+static int procread(const char *path, bool mt, BDBCMP cmp, int lcnum, int ncnum,
+                    int omode, bool wb){
+  iprintf("<Reading Test>\n  path=%s  mt=%d  cmp=%p  lcnum=%d  ncnum=%d  omode=%d  wb=%d\n",
+          path, mt, (void *)(intptr_t)cmp, lcnum, ncnum, omode, wb);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(mt && !tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){
+    eprint(bdb, "tcbdbsetcmpfunc");
+    err = true;
+  }
+  if(!tcbdbsetcache(bdb, lcnum, ncnum)){
+    eprint(bdb, "tcbdbsetcache");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  int rnum = tcbdbrnum(bdb);
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz;
+    if(cmp == tcbdbcmpdecimal){
+      ksiz = sprintf(kbuf, "%d", i);
+    } else if(cmp == tcbdbcmpint32){
+      int32_t lnum = i;
+      memcpy(kbuf, &lnum, sizeof(lnum));
+      ksiz = sizeof(lnum);
+    } else if(cmp == tcbdbcmpint64){
+      int64_t llnum = i;
+      memcpy(kbuf, &llnum, sizeof(llnum));
+      ksiz = sizeof(llnum);
+    } else {
+      ksiz = sprintf(kbuf, "%08d", i);
+    }
+    int vsiz;
+    if(wb){
+      int vsiz;
+      const char *vbuf = tcbdbget3(bdb, kbuf, ksiz, &vsiz);
+      if(!vbuf){
+        eprint(bdb, "tcbdbget3");
+        err = true;
+        break;
+      }
+    } else {
+      char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+      if(!vbuf){
+        eprint(bdb, "tcbdbget");
+        err = true;
+        break;
+      }
+      free(vbuf);
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform remove command */
+static int procremove(const char *path, bool mt, BDBCMP cmp, int lcnum, int ncnum, int omode){
+  iprintf("<Removing Test>\n  path=%s  mt=%d  cmp=%p  lcnum=%d  ncnum=%d  omode=%d\n",
+          path, mt, (void *)(intptr_t)cmp, lcnum, ncnum, omode);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(mt && !tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){
+    eprint(bdb, "tcbdbsetcmpfunc");
+    err = true;
+  }
+  if(!tcbdbsetcache(bdb, lcnum, ncnum)){
+    eprint(bdb, "tcbdbsetcache");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  int rnum = tcbdbrnum(bdb);
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz;
+    if(cmp == tcbdbcmpdecimal){
+      ksiz = sprintf(kbuf, "%d", i);
+    } else if(cmp == tcbdbcmpint32){
+      int32_t lnum = i;
+      memcpy(kbuf, &lnum, sizeof(lnum));
+      ksiz = sizeof(lnum);
+    } else if(cmp == tcbdbcmpint64){
+      int64_t llnum = i;
+      memcpy(kbuf, &llnum, sizeof(llnum));
+      ksiz = sizeof(llnum);
+    } else {
+      ksiz = sprintf(kbuf, "%08d", i);
+    }
+    if(!tcbdbout(bdb, kbuf, ksiz)){
+      eprint(bdb, "tcbdbout");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform rcat command */
+static int procrcat(const char *path, int rnum,
+                    int lmemb, int nmemb, int bnum, int apow, int fpow,
+                    bool mt, BDBCMP cmp, int opts, int lcnum, int ncnum, int lsmax, int capnum,
+                    int omode, int pnum, bool rl){
+  iprintf("<Random Concatenating Test>\n"
+          "  path=%s  rnum=%d  lmemb=%d  nmemb=%d  bnum=%d  apow=%d  fpow=%d"
+          "  mt=%d  cmp=%p  opts=%d  lcnum=%d  ncnum=%d  lsmax=%d  capnum=%d"
+          "  omode=%d  pnum=%d  rl=%d\n\n",
+          path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, (void *)(intptr_t)cmp,
+          opts, lcnum, ncnum, lsmax, capnum, omode, pnum, rl);
+  if(pnum < 1) pnum = rnum;
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(mt && !tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+    eprint(bdb, "tcbdbtune");
+    err = true;
+  }
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){
+    eprint(bdb, "tcbdbsetcmpfunc");
+    err = true;
+  }
+  if(!tcbdbsetcache(bdb, lcnum, ncnum)){
+    eprint(bdb, "tcbdbsetcache");
+    err = true;
+  }
+  if(!tcbdbsetlsmax(bdb, lsmax)){
+    eprint(bdb, "tcbdbsetlsmax");
+    err = true;
+  }
+  if(!tcbdbsetcapnum(bdb, capnum)){
+    eprint(bdb, "tcbdbsetcapnum");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz;
+    if(cmp == tcbdbcmpdecimal){
+      ksiz = sprintf(kbuf, "%d", myrand(pnum));
+    } else if(cmp == tcbdbcmpint32){
+      int32_t lnum = myrand(pnum);
+      memcpy(kbuf, &lnum, sizeof(lnum));
+      ksiz = sizeof(lnum);
+    } else if(cmp == tcbdbcmpint64){
+      int64_t llnum = myrand(pnum);
+      memcpy(kbuf, &llnum, sizeof(llnum));
+      ksiz = sizeof(llnum);
+    } else {
+      ksiz = sprintf(kbuf, "%08d", myrand(pnum));
+    }
+    if(rl){
+      char vbuf[PATH_MAX];
+      int vsiz = myrand(PATH_MAX);
+      for(int j = 0; j < vsiz; j++){
+        vbuf[j] = myrand(0x100);
+      }
+      if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(bdb, "tcbdbputcat");
+        err = true;
+        break;
+      }
+    } else {
+      if(!tcbdbputcat(bdb, kbuf, ksiz, kbuf, ksiz)){
+        eprint(bdb, "tcbdbputcat");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform queue command */
+static int procqueue(const char *path, int rnum, int lmemb, int nmemb, int bnum,
+                     int apow, int fpow, bool mt, BDBCMP cmp, int opts,
+                     int lcnum, int ncnum, int lsmax, int capnum, int omode){
+  iprintf("<Queueing Test>\n  path=%s  rnum=%d  lmemb=%d  nmemb=%d  bnum=%d  apow=%d  fpow=%d"
+          "  mt=%d  cmp=%p  opts=%d  lcnum=%d  ncnum=%d  lsmax=%d  capnum=%d  omode=%d\n\n",
+          path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, (void *)(intptr_t)cmp,
+          opts, lcnum, ncnum, lsmax, capnum, omode);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(mt && !tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){
+    eprint(bdb, "tcbdbsetcmpfunc");
+    err = true;
+  }
+  if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+    eprint(bdb, "tcbdbtune");
+    err = true;
+  }
+  if(!tcbdbsetcache(bdb, lcnum, ncnum)){
+    eprint(bdb, "tcbdbsetcache");
+    err = true;
+  }
+  if(!tcbdbsetlsmax(bdb, lsmax)){
+    eprint(bdb, "tcbdbsetlsmax");
+    err = true;
+  }
+  if(!tcbdbsetcapnum(bdb, capnum)){
+    eprint(bdb, "tcbdbsetcapnum");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  int deqfreq = (lmemb > 0) ? lmemb * 2 : 256;
+  BDBCUR *cur = tcbdbcurnew(bdb);
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len;
+    if(cmp == tcbdbcmpdecimal){
+      len = sprintf(buf, "%d", i);
+    } else if(cmp == tcbdbcmpint32){
+      int32_t lnum = i;
+      memcpy(buf, &lnum, sizeof(lnum));
+      len = sizeof(lnum);
+    } else if(cmp == tcbdbcmpint64){
+      int64_t llnum = i;
+      memcpy(buf, &llnum, sizeof(llnum));
+      len = sizeof(llnum);
+    } else {
+      len = sprintf(buf, "%08d", i);
+    }
+    if(!tcbdbput(bdb, buf, len, buf, len)){
+      eprint(bdb, "tcbdbput");
+      err = true;
+      break;
+    }
+    if(myrand(deqfreq) == 0){
+      if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbcurfirst");
+        err = true;
+        break;
+      }
+      int num = myrand(deqfreq * 2 + 1);
+      while(num >= 0){
+        if(tcbdbcurout(cur)){
+          num--;
+        } else {
+          if(tcbdbecode(bdb) != TCENOREC){
+            eprint(bdb, "tcbdbcurout");
+            err = true;
+          }
+          break;
+        }
+      }
+      if(err) break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+    eprint(bdb, "tcbdbcurfirst");
+    err = true;
+  }
+  while(true){
+    if(tcbdbcurout(cur)) continue;
+    if(tcbdbecode(bdb) != TCENOREC){
+      eprint(bdb, "tcbdbcurout");
+      err = true;
+    }
+    break;
+  }
+  tcbdbcurdel(cur);
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform misc command */
+static int procmisc(const char *path, int rnum, bool mt, int opts, int omode){
+  iprintf("<Miscellaneous Test>\n  path=%s  rnum=%d  mt=%d  opts=%d  omode=%d\n\n",
+          path, rnum, mt, opts, omode);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(mt && !tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(!tcbdbtune(bdb, 10, 10, rnum / 50, 100, -1, opts)){
+    eprint(bdb, "tcbdbtune");
+    err = true;
+  }
+  if(!tcbdbsetcache(bdb, 128, 256)){
+    eprint(bdb, "tcbdbsetcache");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  iprintf("writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    if(!tcbdbputkeep(bdb, buf, len, buf, len)){
+      eprint(bdb, "tcbdbputkeep");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("reading:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", i);
+    int vsiz;
+    char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+    if(!vbuf){
+      eprint(bdb, "tcbdbget");
+      err = true;
+      break;
+    } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){
+      eprint(bdb, "(validation)");
+      err = true;
+      free(vbuf);
+      break;
+    }
+    free(vbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(tcbdbrnum(bdb) != rnum){
+    eprint(bdb, "(validation)");
+    err = true;
+  }
+  iprintf("random writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    char vbuf[RECBUFSIZ];
+    int vsiz = myrand(RECBUFSIZ);
+    memset(vbuf, '*', vsiz);
+    if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+      eprint(bdb, "tcbdbput");
+      err = true;
+      break;
+    }
+    int rsiz;
+    char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
+    if(!rbuf){
+      eprint(bdb, "tcbdbget");
+      err = true;
+      break;
+    }
+    if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+      eprint(bdb, "(validation)");
+      err = true;
+      free(rbuf);
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+    free(rbuf);
+  }
+  iprintf("word writing:\n");
+  const char *words[] = {
+    "a", "A", "bb", "BB", "ccc", "CCC", "dddd", "DDDD", "eeeee", "EEEEEE",
+    "mikio", "hirabayashi", "tokyo", "cabinet", "hyper", "estraier", "19780211", "birth day",
+    "one", "first", "two", "second", "three", "third", "four", "fourth", "five", "fifth",
+    "_[1]_", "uno", "_[2]_", "dos", "_[3]_", "tres", "_[4]_", "cuatro", "_[5]_", "cinco",
+    "[\xe5\xb9\xb3\xe6\x9e\x97\xe5\xb9\xb9\xe9\x9b\x84]", "[\xe9\xa6\xac\xe9\xb9\xbf]", NULL
+  };
+  for(int i = 0; words[i] != NULL; i += 2){
+    const char *kbuf = words[i];
+    int ksiz = strlen(kbuf);
+    const char *vbuf = words[i+1];
+    int vsiz = strlen(vbuf);
+    if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz)){
+      eprint(bdb, "tcbdbputkeep");
+      err = true;
+      break;
+    }
+    if(rnum > 250) putchar('.');
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", sizeof(words) / sizeof(*words));
+  iprintf("random erasing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+      eprint(bdb, "tcbdbout");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "[%d]", i);
+    char vbuf[RECBUFSIZ];
+    int vsiz = i % RECBUFSIZ;
+    memset(vbuf, '*', vsiz);
+    if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz)){
+      eprint(bdb, "tcbdbputkeep");
+      err = true;
+      break;
+    }
+    if(vsiz < 1){
+      char tbuf[PATH_MAX];
+      for(int j = 0; j < PATH_MAX; j++){
+        tbuf[j] = myrand(0x100);
+      }
+      if(!tcbdbput(bdb, kbuf, ksiz, tbuf, PATH_MAX)){
+        eprint(bdb, "tcbdbput");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("erasing:\n");
+  for(int i = 1; i <= rnum; i++){
+    if(i % 2 == 1){
+      char kbuf[RECBUFSIZ];
+      int ksiz = sprintf(kbuf, "[%d]", i);
+      if(!tcbdbout(bdb, kbuf, ksiz)){
+        eprint(bdb, "tcbdbout");
+        err = true;
+        break;
+      }
+      if(tcbdbout(bdb, kbuf, ksiz) || tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbout");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("random writing and reopening:\n");
+  for(int i = 1; i <= rnum; i++){
+    if(myrand(10) == 0){
+      int ksiz, vsiz;
+      char *kbuf, *vbuf;
+      ksiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ);
+      kbuf = tcmalloc(ksiz + 1);
+      for(int j = 0; j < ksiz; j++){
+        kbuf[j] = 128 + myrand(128);
+      }
+      vsiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ);
+      vbuf = tcmalloc(vsiz + 1);
+      for(int j = 0; j < vsiz; j++){
+        vbuf[j] = myrand(256);
+      }
+      switch(myrand(5)){
+      case 0:
+        if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+          eprint(bdb, "tcbdbput");
+          err = true;
+        }
+        break;
+      case 1:
+        if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
+          eprint(bdb, "tcbdbputcat");
+          err = true;
+        }
+        break;
+      case 2:
+        if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){
+          eprint(bdb, "tcbdbputdup");
+          err = true;
+        }
+        break;
+      case 3:
+        if(!tcbdbputdupback(bdb, kbuf, ksiz, vbuf, vsiz)){
+          eprint(bdb, "tcbdbputdupback");
+          err = true;
+        }
+        break;
+      default:
+        if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbout");
+          err = true;
+        }
+        break;
+      }
+      free(vbuf);
+      free(kbuf);
+    } else {
+      char kbuf[RECBUFSIZ];
+      int ksiz = myrand(RECBUFSIZ);
+      memset(kbuf, '@', ksiz);
+      char vbuf[RECBUFSIZ];
+      int vsiz = myrand(RECBUFSIZ);
+      memset(vbuf, '@', vsiz);
+      if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(bdb, "tcbdbputcat");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  iprintf("checking:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "[%d]", i);
+    int vsiz;
+    char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+    if(i % 2 == 0){
+      if(!vbuf){
+        eprint(bdb, "tcbdbget");
+        err = true;
+        break;
+      }
+      if(vsiz != i % RECBUFSIZ && vsiz != PATH_MAX){
+        eprint(bdb, "(validation)");
+        err = true;
+        free(vbuf);
+        break;
+      }
+    } else {
+      if(vbuf || tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "(validation)");
+        err = true;
+        free(vbuf);
+        break;
+      }
+    }
+    free(vbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    if(!tcbdbput(bdb, buf, len, buf, len)){
+      eprint(bdb, "tcbdbput");
+      err = true;
+      break;
+    }
+    if(i % 10 == 0){
+      TCLIST *vals = tclistnew();
+      for(int j = myrand(5) + 1; j >= 0; j--){
+        tclistpush(vals, buf, len);
+      }
+      if(!tcbdbputdup3(bdb, buf, len, vals)){
+        eprint(bdb, "tcbdbput3");
+        err = true;
+        break;
+      }
+      tclistdel(vals);
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("reading:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", i);
+    int vsiz;
+    char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+    if(!vbuf){
+      eprint(bdb, "tcbdbget");
+      err = true;
+      break;
+    } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){
+      eprint(bdb, "(validation)");
+      err = true;
+      free(vbuf);
+      break;
+    }
+    free(vbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("word checking:\n");
+  for(int i = 0; words[i] != NULL; i += 2){
+    const char *kbuf = words[i];
+    int ksiz = strlen(kbuf);
+    const char *vbuf = words[i+1];
+    int vsiz = strlen(vbuf);
+    int rsiz;
+    char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
+    if(!rbuf){
+      eprint(bdb, "tcbdbget");
+      err = true;
+      break;
+    } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+      eprint(bdb, "(validation)");
+      err = true;
+      free(rbuf);
+      break;
+    }
+    free(rbuf);
+    if(rnum > 250) putchar('.');
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", sizeof(words) / sizeof(*words));
+  iprintf("cursor checking:\n");
+  BDBCUR *cur = tcbdbcurnew(bdb);
+  if(!tcbdbcurfirst(cur)){
+    eprint(bdb, "tcbdbcurfirst");
+    err = true;
+  }
+  char *kbuf;
+  int ksiz;
+  int inum = 0;
+  for(int i = 1; (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){
+    int vsiz;
+    char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+    if(!vbuf){
+      eprint(bdb, "tcbdbget");
+      err = true;
+      free(kbuf);
+      break;
+    }
+    free(vbuf);
+    free(kbuf);
+    tcbdbcurnext(cur);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", inum);
+  if(tcbdbecode(bdb) != TCENOREC || inum != tcbdbrnum(bdb)){
+    eprint(bdb, "(validation)");
+    err = true;
+  }
+  iprintf("cursor updating:\n");
+  if(!tcbdbcurfirst(cur)){
+    eprint(bdb, "tcbdbcurfirst");
+    err = true;
+  }
+  inum = 0;
+  for(int i = 1; !err && (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){
+    switch(myrand(6)){
+    case 0:
+      if(!tcbdbputdup(bdb, kbuf, ksiz, "0123456789", 10)){
+        eprint(bdb, "tcbdbputcat");
+        err = true;
+      }
+      break;
+    case 1:
+      if(!tcbdbout(bdb, kbuf, ksiz)){
+        eprint(bdb, "tcbdbout");
+        err = true;
+      }
+      break;
+    case 2:
+      if(!tcbdbcurput(cur, kbuf, ksiz, BDBCPCURRENT)){
+        eprint(bdb, "tcbdbcurput");
+        err = true;
+      }
+      break;
+    case 3:
+      if(!tcbdbcurput(cur, kbuf, ksiz, BDBCPBEFORE)){
+        eprint(bdb, "tcbdbcurput");
+        err = true;
+      }
+      break;
+    case 4:
+      if(!tcbdbcurput(cur, kbuf, ksiz, BDBCPAFTER)){
+        eprint(bdb, "tcbdbcurput");
+        err = true;
+      }
+      break;
+    default:
+      if(!tcbdbcurout(cur) && tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbcurout");
+        err = true;
+      }
+      break;
+    }
+    free(kbuf);
+    tcbdbcurnext(cur);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", inum);
+  if(tcbdbecode(bdb) != TCENOREC){
+    eprint(bdb, "(validation)");
+    err = true;
+  }
+  if(!tcbdbsync(bdb)){
+    eprint(bdb, "tcbdbsync");
+    err = true;
+  }
+  iprintf("cursor updating from empty:\n");
+  tcbdbcurfirst(cur);
+  inum = 0;
+  for(int i = 1; (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){
+    free(kbuf);
+    if(!tcbdbcurout(cur) && tcbdbecode(bdb) != TCENOREC){
+      eprint(bdb, "tcbdbcurout");
+      err = true;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", inum);
+  if(tcbdbrnum(bdb) != 0){
+    eprint(bdb, "(validation)");
+    err = true;
+  }
+  if(!tcbdbput2(bdb, "one", "first")){
+    eprint(bdb, "tcbdbput");
+    err = true;
+  }
+  if(!tcbdbcurlast(cur)){
+    eprint(bdb, "tcbdbcurlast");
+    err = true;
+  }
+  if(!tcbdbcurput2(cur, "second", BDBCPCURRENT) || !tcbdbcurput2(cur, "first", BDBCPBEFORE) ||
+     !tcbdbcurput2(cur, "zero", BDBCPBEFORE) || !tcbdbcurput2(cur, "top", BDBCPBEFORE)){
+    eprint(bdb, "tcbdbcurput2");
+    err = true;
+  }
+  if(!tcbdbcurlast(cur)){
+    eprint(bdb, "tcbdbcurlast");
+    err = true;
+  }
+  if(!tcbdbcurput2(cur, "third", BDBCPAFTER) || !tcbdbcurput2(cur, "fourth", BDBCPAFTER) ||
+     !tcbdbcurput2(cur, "end", BDBCPCURRENT) ||  !tcbdbcurput2(cur, "bottom", BDBCPAFTER)){
+    eprint(bdb, "tcbdbcurput2");
+    err = true;
+  }
+  iprintf("checking transaction commit:\n");
+  if(!tcbdbtranbegin(bdb)){
+    eprint(bdb, "tcbdbtranbegin");
+    err = true;
+  }
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    if(!tcbdbputdup(bdb, kbuf, ksiz, kbuf, ksiz)){
+      eprint(bdb, "tcbdbputdup");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(!tcbdbtrancommit(bdb)){
+    eprint(bdb, "tcbdbtrancommit");
+    err = true;
+  }
+  iprintf("checking transaction abort:\n");
+  uint64_t ornum = tcbdbrnum(bdb);
+  if(!tcbdbtranbegin(bdb)){
+    eprint(bdb, "tcbdbtranbegin");
+    err = true;
+  }
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+      eprint(bdb, "tcbdbout");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(!tcbdbtranabort(bdb)){
+    eprint(bdb, "tcbdbtranabort");
+    err = true;
+  }
+  if(tcbdbrnum(bdb) != ornum){
+    eprint(bdb, "(validation)");
+    err = true;
+  }
+  if(ornum > 1000){
+    if(!tcbdbcurfirst(cur)){
+      eprint(bdb, "tcbdbcurfirst");
+      err = true;
+    }
+    for(int i = 1; i < 500 && !err && (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++){
+      int vsiz;
+      if(myrand(20) == 0){
+        if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz)){
+          eprint(bdb, "tcbdbget3");
+          err = true;
+        }
+        if(myrand(2) == 0 && !tcbdbout(bdb, kbuf, ksiz)){
+          eprint(bdb, "tcbdbget3");
+          err = true;
+        }
+        if(myrand(2) == 0 && !tcbdbputdup(bdb, kbuf, ksiz, kbuf, ksiz)){
+          eprint(bdb, "tcbdbput");
+          err = true;
+        }
+      } else {
+        if(!tcbdbcurout(cur)){
+          eprint(bdb, "tcbdbcurout");
+          err = true;
+        }
+      }
+      free(kbuf);
+      if(myrand(30) == 0 && !tcbdbcurfirst(cur)){
+        eprint(bdb, "tcbdbcurfirst");
+        err = true;
+      }
+    }
+  }
+  if(!tcbdbvanish(bdb)){
+    eprint(bdb, "tcbdbvanish");
+    err = true;
+  }
+  if(!tcbdbtranbegin(bdb)){
+    eprint(bdb, "tcbdbtranbegin");
+    err = true;
+  }
+  if(!tcbdbput2(bdb, "mikio", "hirabayashi")){
+    eprint(bdb, "tcbdbput2");
+    err = true;
+  }
+  tcbdbcurdel(cur);
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(const char *path, int rnum, bool mt, int opts, int omode){
+  iprintf("<Wicked Writing Test>\n  path=%s  rnum=%d  mt=%d  opts=%d  omode=%d\n\n",
+          path, rnum, mt, opts, omode);
+  bool err = false;
+  double stime = tctime();
+  TCBDB *bdb = tcbdbnew();
+  if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+  if(mt && !tcbdbsetmutex(bdb)){
+    eprint(bdb, "tcbdbsetmutex");
+    err = true;
+  }
+  if(!tcbdbtune(bdb, 10, 10, rnum / 50, 100, -1, opts)){
+    eprint(bdb, "tcbdbtune");
+    err = true;
+  }
+  if(!tcbdbsetcache(bdb, 128, 256)){
+    eprint(bdb, "tcbdbsetcache");
+    err = true;
+  }
+  if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+    eprint(bdb, "tcbdbopen");
+    err = true;
+  }
+  BDBCUR *cur = tcbdbcurnew(bdb);
+  if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+    eprint(bdb, "tcbdbcurfirst");
+    err = true;
+  }
+  TCMAP *map = tcmapnew2(rnum / 5);
+  for(int i = 1; i <= rnum && !err; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    char vbuf[RECBUFSIZ];
+    int vsiz = myrand(RECBUFSIZ);
+    memset(vbuf, '*', vsiz);
+    vbuf[vsiz] = '\0';
+    char *rbuf;
+    switch(myrand(16)){
+    case 0:
+      putchar('0');
+      if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(bdb, "tcbdbput");
+        err = true;
+      }
+      tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 1:
+      putchar('1');
+      if(!tcbdbput2(bdb, kbuf, vbuf)){
+        eprint(bdb, "tcbdbput2");
+        err = true;
+      }
+      tcmapput2(map, kbuf, vbuf);
+      break;
+    case 2:
+      putchar('2');
+      if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){
+        eprint(bdb, "tcbdbputkeep");
+        err = true;
+      }
+      tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 3:
+      putchar('3');
+      if(!tcbdbputkeep2(bdb, kbuf, vbuf) && tcbdbecode(bdb) != TCEKEEP){
+        eprint(bdb, "tcbdbputkeep2");
+        err = true;
+      }
+      tcmapputkeep2(map, kbuf, vbuf);
+      break;
+    case 4:
+      putchar('4');
+      if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(bdb, "tcbdbputcat");
+        err = true;
+      }
+      tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 5:
+      putchar('5');
+      if(!tcbdbputcat2(bdb, kbuf, vbuf)){
+        eprint(bdb, "tcbdbputcat2");
+        err = true;
+      }
+      tcmapputcat2(map, kbuf, vbuf);
+      break;
+    case 6:
+      putchar('6');
+      if(myrand(10) == 0){
+        if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbout");
+          err = true;
+        }
+        tcmapout(map, kbuf, ksiz);
+      }
+      break;
+    case 7:
+      putchar('7');
+      if(myrand(10) == 0){
+        if(!tcbdbout2(bdb, kbuf) && tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbout2");
+          err = true;
+        }
+        tcmapout2(map, kbuf);
+      }
+      break;
+    case 8:
+      putchar('8');
+      if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz))){
+        if(tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbget");
+          err = true;
+        }
+        rbuf = tcsprintf("[%d]", myrand(i + 1));
+        vsiz = strlen(rbuf);
+      }
+      vsiz += myrand(vsiz);
+      if(myrand(3) == 0) vsiz += PATH_MAX;
+      rbuf = tcrealloc(rbuf, vsiz + 1);
+      for(int j = 0; j < vsiz; j++){
+        rbuf[j] = myrand(0x100);
+      }
+      if(!tcbdbput(bdb, kbuf, ksiz, rbuf, vsiz)){
+        eprint(bdb, "tcbdbput");
+        err = true;
+      }
+      tcmapput(map, kbuf, ksiz, rbuf, vsiz);
+      free(rbuf);
+      break;
+    case 9:
+      putchar('9');
+      if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz)) && tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbget");
+        err = true;
+      }
+      free(rbuf);
+      break;
+    case 10:
+      putchar('A');
+      if(!(rbuf = tcbdbget2(bdb, kbuf)) && tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbget");
+        err = true;
+      }
+      free(rbuf);
+      break;
+    case 11:
+      putchar('B');
+      if(myrand(1) == 0) vsiz = 1;
+      if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz) && tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbget");
+        err = true;
+      }
+      break;
+    case 12:
+      putchar('C');
+      if(myrand(rnum / 50) == 0){
+        if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbcurfirst");
+          err = true;
+        }
+      }
+      TCXSTR *ikey = tcxstrnew();
+      TCXSTR *ival = tcxstrnew();
+      for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
+        if(j % 3 == 0){
+          if(tcbdbcurrec(cur, ikey, ival)){
+            if(tcbdbvnum(bdb, tcxstrptr(ikey), tcxstrsize(ikey)) != 1){
+              eprint(bdb, "(validation)");
+              err = true;
+            }
+            if(tcxstrsize(ival) != tcbdbvsiz(bdb, tcxstrptr(ikey), tcxstrsize(ikey))){
+              eprint(bdb, "(validation)");
+              err = true;
+            }
+          } else {
+            int ecode = tcbdbecode(bdb);
+            if(ecode != TCEINVALID && ecode != TCENOREC){
+              eprint(bdb, "tcbdbcurrec");
+              err = true;
+            }
+          }
+        } else {
+          int iksiz;
+          char *ikbuf = tcbdbcurkey(cur, &iksiz);
+          if(ikbuf){
+            free(ikbuf);
+          } else {
+            int ecode = tcbdbecode(bdb);
+            if(ecode != TCEINVALID && ecode != TCENOREC){
+              eprint(bdb, "tcbdbcurkey");
+              err = true;
+            }
+          }
+        }
+        tcbdbcurnext(cur);
+      }
+      tcxstrdel(ival);
+      tcxstrdel(ikey);
+      break;
+    default:
+      putchar('@');
+      if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+      if(myrand(rnum / 32 + 1) == 0){
+        if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+          eprint(bdb, "tcbdbcurfirst");
+          err = true;
+        }
+        int cnt = myrand(30);
+        for(int j = 0; j < rnum && !err; j++){
+          ksiz = sprintf(kbuf, "%d", i + j);
+          if(myrand(4) == 0){
+            if(tcbdbout3(bdb, kbuf, ksiz)){
+              cnt--;
+            } else if(tcbdbecode(bdb) != TCENOREC){
+              eprint(bdb, "tcbdbout3");
+              err = true;
+            }
+            tcmapout(map, kbuf, ksiz);
+          } else if(myrand(30) == 0){
+            int tksiz;
+            char *tkbuf = tcbdbcurkey(cur, &tksiz);
+            if(tkbuf){
+              if(tcbdbcurout(cur)){
+                cnt--;
+              } else {
+                eprint(bdb, "tcbdbcurout");
+                err = true;
+              }
+              tcmapout(map, tkbuf, tksiz);
+              free(tkbuf);
+            } else if(tcbdbecode(bdb) != TCENOREC){
+              eprint(bdb, "tcbdbcurfirst");
+              err = true;
+            }
+          } else {
+            if(tcbdbout(bdb, kbuf, ksiz)){
+              cnt--;
+            } else if(tcbdbecode(bdb) != TCENOREC){
+              eprint(bdb, "tcbdbout");
+              err = true;
+            }
+            tcmapout(map, kbuf, ksiz);
+          }
+          if(cnt < 0) break;
+        }
+      }
+      break;
+    }
+    if(i % 50 == 0) iprintf(" (%08d)\n", i);
+    if(i == rnum / 2){
+      if(!tcbdbclose(bdb)){
+        eprint(bdb, "tcbdbclose");
+        err = true;
+      }
+      if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+        eprint(bdb, "tcbdbopen");
+        err = true;
+      }
+    } else if(i == rnum / 4){
+      char *npath = tcsprintf("%s-tmp", path);
+      if(!tcbdbcopy(bdb, npath)){
+        eprint(bdb, "tcbdbcopy");
+        err = true;
+      }
+      TCBDB *nbdb = tcbdbnew();
+      if(!tcbdbopen(nbdb, npath, BDBOREADER | omode)){
+        eprint(nbdb, "tcbdbopen");
+        err = true;
+      }
+      tcbdbdel(nbdb);
+      unlink(npath);
+      free(npath);
+      if(!tcbdboptimize(bdb, -1, -1, -1, -1, -1, -1)){
+        eprint(bdb, "tcbdboptimize");
+        err = true;
+      }
+      if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "tcbdbcurfirst");
+        err = true;
+      }
+    } else if(i == rnum / 8){
+      if(!tcbdbtranbegin(bdb)){
+        eprint(bdb, "tcbdbtranbegin");
+        err = true;
+      }
+    } else if(i == rnum / 8 + rnum / 16){
+      if(!tcbdbtrancommit(bdb)){
+        eprint(bdb, "tcbdbtrancommit");
+        err = true;
+      }
+    }
+  }
+  if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+  if(!tcbdbsync(bdb)){
+    eprint(bdb, "tcbdbsync");
+    err = true;
+  }
+  if(tcbdbrnum(bdb) != tcmaprnum(map)){
+    eprint(bdb, "(validation)");
+    err = true;
+  }
+  for(int i = 1; i <= rnum && !err; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", i - 1);
+    int vsiz;
+    const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
+    int rsiz;
+    char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
+    if(vbuf){
+      putchar('.');
+      if(!rbuf){
+        eprint(bdb, "tcbdbget");
+        err = true;
+      } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+        eprint(bdb, "(validation)");
+        err = true;
+      }
+    } else {
+      putchar('*');
+      if(rbuf || tcbdbecode(bdb) != TCENOREC){
+        eprint(bdb, "(validation)");
+        err = true;
+      }
+    }
+    free(rbuf);
+    if(i % 50 == 0) iprintf(" (%08d)\n", i);
+  }
+  if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+  tcmapiterinit(map);
+  int ksiz;
+  const char *kbuf;
+  for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){
+    putchar('+');
+    int vsiz;
+    const char *vbuf = tcmapiterval(kbuf, &vsiz);
+    int rsiz;
+    char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
+    if(!rbuf){
+      eprint(bdb, "tcbdbget");
+      err = true;
+    } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+      eprint(bdb, "(validation)");
+      err = true;
+    }
+    free(rbuf);
+    if(!tcbdbout(bdb, kbuf, ksiz)){
+      eprint(bdb, "tcbdbout");
+      err = true;
+    }
+    if(i % 50 == 0) iprintf(" (%08d)\n", i);
+  }
+  int mrnum = tcmaprnum(map);
+  if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum);
+  if(tcbdbrnum(bdb) != 0){
+    eprint(bdb, "(validation)");
+    err = true;
+  }
+  tcbdbcurdel(cur);
+  iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+  iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+  mprint(bdb);
+  tcmapdel(map);
+  if(!tcbdbclose(bdb)){
+    eprint(bdb, "tcbdbclose");
+    err = true;
+  }
+  tcbdbdel(bdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tchdb.c b/bacula/src/lib/tokyocabinet/tchdb.c
new file mode 100644 (file)
index 0000000..c13956a
--- /dev/null
@@ -0,0 +1,3093 @@
+/*************************************************************************************************
+ * The hash database API of Tokyo Cabinet
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "tcutil.h"
+#include "tchdb.h"
+#include "myconf.h"
+
+#define HDBFILEMODE    00644             // permission of created files
+#define HDBIOBUFSIZ    8192              // size of an I/O buffer
+
+#define HDBMAGICDATA   "ToKyO CaBiNeT"   // magic data for identification
+#define HDBHEADSIZ     256               // size of the reagion of the header
+#define HDBTYPEOFF     32                // offset of the region for the database type
+#define HDBFLAGSOFF    33                // offset of the region for the additional flags
+#define HDBAPOWOFF     34                // offset of the region for the alignment power
+#define HDBFPOWOFF     35                // offset of the region for the free block pool power
+#define HDBOPTSOFF     36                // offset of the region for the options
+#define HDBBNUMOFF     40                // offset of the region for the bucket number
+#define HDBRNUMOFF     48                // offset of the region for the record number
+#define HDBFSIZOFF     56                // offset of the region for the file size
+#define HDBFRECOFF     64                // offset of the region for the first record offset
+#define HDBOPAQUEOFF   128               // offset of the region for the opaque field
+
+#define HDBDEFBNUM     16381             // default bucket number
+#define HDBDEFAPOW     4                 // default alignment power
+#define HDBMAXAPOW     16                // maximum alignment power
+#define HDBDEFFPOW     10                // default free block pool power
+#define HDBMAXFPOW     20                // maximum free block pool power
+#define HDBMINRUNIT    48                // minimum record reading unit
+#define HDBMAXHSIZ     32                // maximum record header size
+#define HDBFBPBSIZ     64                // base region size of the free block pool
+#define HDBFBPESIZ     4                 // size of each region of the free block pool
+#define HDBFBPMGFREQ   256               // frequency to merge the free block pool
+#define HDBDRPUNIT     65536             // unit size of the delayed record pool
+#define HDBDRPLAT      2048              // latitude size of the delayed record pool
+#define HDBCACHEOUT    128               // number of records in a process of cacheout
+
+typedef struct {                         // type of structure for a record
+  uint64_t off;                          // offset of the record
+  uint32_t rsiz;                         // size of the whole record
+  uint8_t magic;                         // magic number
+  uint8_t hash;                          // second hash value
+  uint64_t left;                         // offset of the left child record
+  uint64_t right;                        // offset of the right child record
+  uint32_t ksiz;                         // size of the key
+  uint32_t vsiz;                         // size of the value
+  uint16_t psiz;                         // size of the padding
+  const char *kbuf;                      // pointer to the key
+  const char *vbuf;                      // pointer to the value
+  uint64_t boff;                         // offset of the body
+  char *bbuf;                            // buffer of the body
+} TCHREC;
+
+typedef struct {                         // type of structure for a free block
+  uint64_t off;                          // offset of the block
+  uint32_t rsiz;                         // size of the block
+} HDBFB;
+
+enum {                                   // enumeration for magic data
+  HDBMAGICREC = 0xc8,                    // for data block
+  HDBMAGICFB = 0xb0                      // for free block
+};
+
+enum {                                   // enumeration for duplication behavior
+  HDBPDOVER,                             // overwrite an existing value
+  HDBPDKEEP,                             // keep the existing value
+  HDBPDCAT                               // concatenate values
+};
+
+
+/* private macros */
+#define HDBLOCKMETHOD(TC_hdb, TC_wr) \
+  ((TC_hdb)->mmtx ? tchdblockmethod((TC_hdb), (TC_wr)) : true)
+#define HDBUNLOCKMETHOD(TC_hdb) \
+  ((TC_hdb)->mmtx ? tchdbunlockmethod(TC_hdb) : true)
+#define HDBLOCKDRP(TC_hdb) \
+  ((TC_hdb)->mmtx ? tchdblockdrp(TC_hdb) : true)
+#define HDBUNLOCKDRP(TC_hdb) \
+  ((TC_hdb)->mmtx ? tchdbunlockdrp(TC_hdb) : true)
+
+
+/* private function prototypes */
+static bool tcseekwrite(TCHDB *hdb, off_t off, const void *buf, size_t size);
+static bool tcseekread(TCHDB *hdb, off_t off, void *buf, size_t size);
+static void tcdumpmeta(TCHDB *hdb, char *hbuf);
+static void tcloadmeta(TCHDB *hdb, const char *hbuf);
+static uint64_t tcgetprime(uint64_t num);
+static void tchdbclear(TCHDB *hdb);
+static int32_t tchdbpadsize(TCHDB *hdb);
+static void tchdbsetflag(TCHDB *hdb, int flag, bool sign);
+static uint64_t tchdbbidx(TCHDB *hdb, const char *kbuf, int ksiz, uint8_t *hp);
+static off_t tchdbgetbucket(TCHDB *hdb, uint64_t bidx);
+static void tchdbsetbucket(TCHDB *hdb, uint64_t bidx, uint64_t off);
+static bool tchdbsavefbp(TCHDB *hdb);
+static bool tchdbloadfbp(TCHDB *hdb);
+static void tcfbpsortbyoff(HDBFB *fbpool, int fbpnum);
+static void tcfbpsortbyrsiz(HDBFB *fbpool, int fbpnum);
+static void tchdbfbpmerge(TCHDB *hdb);
+static void tchdbfbpinsert(TCHDB *hdb, uint64_t off, uint32_t rsiz);
+static bool tchdbfbpsearch(TCHDB *hdb, TCHREC *rec);
+static bool tchdbfbpsplice(TCHDB *hdb, TCHREC *rec, uint32_t nsiz);
+static bool tchdbwritefb(TCHDB *hdb, uint64_t off, uint32_t rsiz);
+static bool tchdbwriterec(TCHDB *hdb, TCHREC *rec, uint64_t bidx, off_t entoff);
+static bool tchdbreadrec(TCHDB *hdb, TCHREC *rec, char *rbuf);
+static bool tchdbreadrecbody(TCHDB *hdb, TCHREC *rec);
+static int tcreckeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz);
+static bool tchdbflushdrp(TCHDB *hdb);
+static void tchdbcacheadjust(TCHDB *hdb);
+static bool tchdbopenimpl(TCHDB *hdb, const char *path, int omode);
+static bool tchdbcloseimpl(TCHDB *hdb);
+static bool tchdbputimpl(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                         int dmode);
+static void tchdbdrpappend(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                           uint8_t hash);
+static bool tchdbputasyncimpl(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz);
+static bool tchdboutimpl(TCHDB *hdb, const char *kbuf, int ksiz);
+static char *tchdbgetimpl(TCHDB *hdb, const char *kbuf, int ksiz, int *sp);
+static int tchdbgetintobuf(TCHDB *hdb, const char *kbuf, int ksiz, char *vbuf, int max);
+static int tchdbvsizimpl(TCHDB *hdb, const char *kbuf, int ksiz);
+static bool tchdbiterinitimpl(TCHDB *hdb);
+static char *tchdbiternextimpl(TCHDB *hdb, int *sp);
+static bool tchdbiternextintoxstr(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr);
+static bool tchdboptimizeimpl(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+static bool tchdbvanishimpl(TCHDB *hdb);
+static bool tchdblockmethod(TCHDB *hdb, bool wr);
+static bool tchdbunlockmethod(TCHDB *hdb);
+static bool tchdblockdrp(TCHDB *hdb);
+static bool tchdbunlockdrp(TCHDB *hdb);
+
+
+/* debugging function prototypes */
+void tchdbprintmeta(TCHDB *hdb);
+void tchdbprintrec(TCHDB *hdb, TCHREC *rec);
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+/* Get the message string corresponding to an error code. */
+const char *tchdberrmsg(int ecode){
+  switch(ecode){
+  case TCESUCCESS: return "success";
+  case TCETHREAD: return "threading error";
+  case TCEINVALID: return "invalid operation";
+  case TCENOFILE: return "file not found";
+  case TCENOPERM: return "no permission";
+  case TCEMETA: return "invalid meta data";
+  case TCERHEAD: return "invalid record header";
+  case TCEOPEN: return "open error";
+  case TCECLOSE: return "close error";
+  case TCETRUNC: return "trunc error";
+  case TCESYNC: return "sync error";
+  case TCESTAT: return "stat error";
+  case TCESEEK: return "seek error";
+  case TCEREAD: return "read error";
+  case TCEWRITE: return "write error";
+  case TCEMMAP: return "mmap error";
+  case TCELOCK: return "lock error";
+  case TCEUNLINK: return "unlink error";
+  case TCERENAME: return "rename error";
+  case TCEMKDIR: return "mkdir error";
+  case TCERMDIR: return "rmdir error";
+  case TCEKEEP: return "existing record";
+  case TCENOREC: return "no record found";
+  case TCEMISC: return "miscellaneous error";
+  }
+  return "unknown error";
+}
+
+
+/* Create a hash database object. */
+TCHDB *tchdbnew(void){
+  TCHDB *hdb;
+  TCMALLOC(hdb, sizeof(*hdb));
+  tchdbclear(hdb);
+  return hdb;
+}
+
+
+/* Delete a hash database object. */
+void tchdbdel(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd >= 0) tchdbclose(hdb);
+  if(hdb->mmtx){
+    pthread_key_delete(*(pthread_key_t *)hdb->eckey);
+    pthread_mutex_destroy(hdb->dmtx);
+    pthread_rwlock_destroy(hdb->mmtx);
+    free(hdb->eckey);
+    free(hdb->dmtx);
+    free(hdb->mmtx);
+  }
+  free(hdb);
+}
+
+
+/* Get the last happened error code of a hash database object. */
+int tchdbecode(TCHDB *hdb){
+  assert(hdb);
+  return hdb->mmtx ?
+    (int)(intptr_t)pthread_getspecific(*(pthread_key_t *)hdb->eckey) : hdb->ecode;
+}
+
+
+/* Set mutual exclusion control of a hash database object for threading. */
+bool tchdbsetmutex(TCHDB *hdb){
+  assert(hdb);
+  if(!TCUSEPTHREAD) return true;
+  if(!tcglobalmutexlock()) return false;
+  if(hdb->mmtx || hdb->fd >= 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    tcglobalmutexunlock();
+    return false;
+  }
+  pthread_mutexattr_t rma;
+  pthread_mutexattr_init(&rma);
+  TCMALLOC(hdb->mmtx, sizeof(pthread_rwlock_t));
+  TCMALLOC(hdb->dmtx, sizeof(pthread_mutex_t));
+  TCMALLOC(hdb->eckey, sizeof(pthread_key_t));
+  if(pthread_mutexattr_settype(&rma, PTHREAD_MUTEX_RECURSIVE) != 0 ||
+     pthread_rwlock_init(hdb->mmtx, NULL) != 0 || pthread_mutex_init(hdb->dmtx, &rma) != 0 ||
+     pthread_key_create(hdb->eckey, NULL) != 0){
+    tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    pthread_mutexattr_destroy(&rma);
+    free(hdb->eckey);
+    free(hdb->dmtx);
+    free(hdb->mmtx);
+    hdb->eckey = NULL;
+    hdb->dmtx = NULL;
+    hdb->mmtx = NULL;
+    tcglobalmutexunlock();
+    return false;
+  }
+  pthread_mutexattr_destroy(&rma);
+  tcglobalmutexunlock();
+  return true;
+}
+
+
+/* Set the tuning parameters of a hash database object. */
+bool tchdbtune(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+  assert(hdb);
+  if(hdb->fd >= 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  hdb->bnum = (bnum > 0) ? tcgetprime(bnum) : HDBDEFBNUM;
+  hdb->apow = (apow >= 0) ? tclmin(apow, HDBMAXAPOW) : HDBDEFAPOW;
+  hdb->fpow = (fpow >= 0) ? tclmin(fpow, HDBMAXFPOW) : HDBDEFFPOW;
+  hdb->opts = opts;
+  if(!_tc_deflate) hdb->opts &= ~HDBTDEFLATE;
+  return true;
+}
+
+
+/* Set the caching parameters of a hash database object. */
+bool tchdbsetcache(TCHDB *hdb, int32_t rcnum){
+  assert(hdb);
+  if(hdb->fd >= 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  hdb->rcnum = (rcnum > 0) ? tclmin(tclmax(rcnum, HDBCACHEOUT * 2), INT_MAX / 4) : 0;
+  return true;
+}
+
+
+/* Open a database file and connect a hash database object. */
+bool tchdbopen(TCHDB *hdb, const char *path, int omode){
+  assert(hdb && path);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd >= 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  bool rv = tchdbopenimpl(hdb, path, omode);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Close a database object. */
+bool tchdbclose(TCHDB *hdb){
+  assert(hdb);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  bool rv = tchdbcloseimpl(hdb);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Store a record into a hash database object. */
+bool tchdbput(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->zmode){
+    char *zbuf;
+    if(hdb->opts & HDBTDEFLATE){
+      zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
+    } else {
+      zbuf = tcbsencode(vbuf, vsiz, &vsiz);
+    }
+    if(!zbuf){
+      tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+      HDBUNLOCKMETHOD(hdb);
+      return false;
+    }
+    bool rv = tchdbputimpl(hdb, kbuf, ksiz, zbuf, vsiz, HDBPDOVER);
+    free(zbuf);
+    HDBUNLOCKMETHOD(hdb);
+    return rv;
+  }
+  bool rv = tchdbputimpl(hdb, kbuf, ksiz, vbuf, vsiz, HDBPDOVER);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Store a string record into a hash database object. */
+bool tchdbput2(TCHDB *hdb, const char *kstr, const char *vstr){
+  assert(hdb && kstr && vstr);
+  return tchdbput(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a new record into a hash database object. */
+bool tchdbputkeep(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->zmode){
+    char *zbuf;
+    if(hdb->opts & HDBTDEFLATE){
+      zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
+    } else {
+      zbuf = tcbsencode(vbuf, vsiz, &vsiz);
+    }
+    if(!zbuf){
+      tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+      HDBUNLOCKMETHOD(hdb);
+      return false;
+    }
+    bool rv = tchdbputimpl(hdb, kbuf, ksiz, zbuf, vsiz, HDBPDKEEP);
+    free(zbuf);
+    HDBUNLOCKMETHOD(hdb);
+    return rv;
+  }
+  bool rv = tchdbputimpl(hdb, kbuf, ksiz, vbuf, vsiz, HDBPDKEEP);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Store a new string record into a hash database object. */
+bool tchdbputkeep2(TCHDB *hdb, const char *kstr, const char *vstr){
+  return tchdbputkeep(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Concatenate a value at the end of the existing record in a hash database object. */
+bool tchdbputcat(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->zmode){
+    char *zbuf;
+    int osiz;
+    char *obuf = tchdbgetimpl(hdb, kbuf, ksiz, &osiz);
+    if(obuf){
+      TCREALLOC(obuf, obuf, osiz + vsiz + 1);
+      memcpy(obuf + osiz, vbuf, vsiz);
+      if(hdb->opts & HDBTDEFLATE){
+        zbuf = _tc_deflate(obuf, osiz + vsiz, &vsiz, _TCZMRAW);
+      } else {
+        zbuf = tcbsencode(obuf, osiz + vsiz, &vsiz);
+      }
+      free(obuf);
+    } else {
+      if(hdb->opts & HDBTDEFLATE){
+        zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
+      } else {
+        zbuf = tcbsencode(vbuf, vsiz, &vsiz);
+      }
+    }
+    if(!zbuf){
+      tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+      HDBUNLOCKMETHOD(hdb);
+      return false;
+    }
+    bool rv = tchdbputimpl(hdb, kbuf, ksiz, zbuf, vsiz, HDBPDOVER);
+    free(zbuf);
+    HDBUNLOCKMETHOD(hdb);
+    return rv;
+  }
+  bool rv = tchdbputimpl(hdb, kbuf, ksiz, vbuf, vsiz, HDBPDCAT);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Concatenate a string value at the end of the existing record in a hash database object. */
+bool tchdbputcat2(TCHDB *hdb, const char *kstr, const char *vstr){
+  assert(hdb && kstr && vstr);
+  return tchdbputcat(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a record into a hash database object in asynchronous fashion. */
+bool tchdbputasync(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  hdb->async = true;
+  if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->zmode){
+    char *zbuf;
+    if(hdb->opts & HDBTDEFLATE){
+      zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
+    } else {
+      zbuf = tcbsencode(vbuf, vsiz, &vsiz);
+    }
+    if(!zbuf){
+      tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+      HDBUNLOCKMETHOD(hdb);
+      return false;
+    }
+    bool rv = tchdbputasyncimpl(hdb, kbuf, ksiz, zbuf, vsiz);
+    free(zbuf);
+    HDBUNLOCKMETHOD(hdb);
+    return rv;
+  }
+  bool rv = tchdbputasyncimpl(hdb, kbuf, ksiz, vbuf, vsiz);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Store a string record into a hash database object in asynchronous fashion. */
+bool tchdbputasync2(TCHDB *hdb, const char *kstr, const char *vstr){
+  assert(hdb && kstr && vstr);
+  return tchdbputasync(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Remove a record of a hash database object. */
+bool tchdbout(TCHDB *hdb, const void *kbuf, int ksiz){
+  assert(hdb && kbuf && ksiz >= 0);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  bool rv = tchdboutimpl(hdb, kbuf, ksiz);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Remove a string record of a hash database object. */
+bool tchdbout2(TCHDB *hdb, const char *kstr){
+  assert(hdb && kstr);
+  return tchdbout(hdb, kstr, strlen(kstr));
+}
+
+
+/* Retrieve a record in a hash database object. */
+void *tchdbget(TCHDB *hdb, const void *kbuf, int ksiz, int *sp){
+  assert(hdb && kbuf && ksiz >= 0 && sp);
+  if(!HDBLOCKMETHOD(hdb, false)) return NULL;
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return NULL;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return NULL;
+  }
+  char *rv = tchdbgetimpl(hdb, kbuf, ksiz, sp);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Retrieve a string record in a hash database object. */
+char *tchdbget2(TCHDB *hdb, const char *kstr){
+  assert(hdb && kstr);
+  int vsiz;
+  return tchdbget(hdb, kstr, strlen(kstr), &vsiz);
+}
+
+
+/* Retrieve a record in a hash database object and write the value into a buffer. */
+int tchdbget3(TCHDB *hdb, const void *kbuf, int ksiz, void *vbuf, int max){
+  assert(hdb && kbuf && ksiz >= 0 && vbuf && max >= 0);
+  if(!HDBLOCKMETHOD(hdb, false)) return -1;
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return -1;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return -1;
+  }
+  int rv = tchdbgetintobuf(hdb, kbuf, ksiz, vbuf, max);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Get the size of the value of a record in a hash database object. */
+int tchdbvsiz(TCHDB *hdb, const void *kbuf, int ksiz){
+  assert(hdb && kbuf && ksiz >= 0);
+  if(!HDBLOCKMETHOD(hdb, false)) return -1;
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return -1;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return -1;
+  }
+  int rv = tchdbvsizimpl(hdb, kbuf, ksiz);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Get the size of the value of a string record in a hash database object. */
+int tchdbvsiz2(TCHDB *hdb, const char *kstr){
+  assert(hdb && kstr);
+  return tchdbvsiz(hdb, kstr, strlen(kstr));
+}
+
+
+/* Initialize the iterator of a hash database object. */
+bool tchdbiterinit(TCHDB *hdb){
+  assert(hdb);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  bool rv = tchdbiterinitimpl(hdb);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Get the next key of the iterator of a hash database object. */
+void *tchdbiternext(TCHDB *hdb, int *sp){
+  assert(hdb && sp);
+  if(!HDBLOCKMETHOD(hdb, true)) return NULL;
+  if(hdb->fd < 0 || hdb->iter < 1){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return NULL;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return NULL;
+  }
+  char *rv = tchdbiternextimpl(hdb, sp);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Get the next key string of the iterator of a hash database object. */
+char *tchdbiternext2(TCHDB *hdb){
+  assert(hdb);
+  int vsiz;
+  return tchdbiternext(hdb, &vsiz);
+}
+
+
+/* Get the next extensible objects of the iterator of a hash database object. */
+bool tchdbiternext3(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr){
+  assert(hdb && kxstr && vxstr);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd < 0 || hdb->iter < 1){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  bool rv = tchdbiternextintoxstr(hdb, kxstr, vxstr);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Get forward matching keys in a hash database object. */
+TCLIST *tchdbfwmkeys(TCHDB *hdb, const void *pbuf, int psiz, int max){
+  assert(hdb && pbuf && psiz >= 0);
+  TCLIST* keys = tclistnew();
+  if(!HDBLOCKMETHOD(hdb, true)) return keys;
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return keys;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return keys;
+  }
+  if(max < 0) max = INT_MAX;
+  uint64_t iter = hdb->iter;
+  tchdbiterinitimpl(hdb);
+  char *kbuf;
+  int ksiz;
+  while(TCLISTNUM(keys) < max && (kbuf = tchdbiternextimpl(hdb, &ksiz)) != NULL){
+    if(ksiz >= psiz && !memcmp(kbuf, pbuf, psiz)){
+      tclistpushmalloc(keys, kbuf, ksiz);
+    } else {
+      free(kbuf);
+    }
+  }
+  hdb->iter = iter;
+  HDBUNLOCKMETHOD(hdb);
+  return keys;
+}
+
+
+/* Get forward matching string keys in a hash database object. */
+TCLIST *tchdbfwmkeys2(TCHDB *hdb, const char *pstr, int max){
+  assert(hdb && pstr);
+  return tchdbfwmkeys(hdb, pstr, strlen(pstr), max);
+}
+
+
+/* Synchronize updated contents of a hash database object with the file and the device. */
+bool tchdbsync(TCHDB *hdb){
+  assert(hdb);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  bool rv = tchdbmemsync(hdb, true);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Optimize the file of a hash database object. */
+bool tchdboptimize(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+  assert(hdb);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  bool rv = tchdboptimizeimpl(hdb, bnum, apow, fpow, opts);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Remove all records of a hash database object. */
+bool tchdbvanish(TCHDB *hdb){
+  assert(hdb);
+  if(!HDBLOCKMETHOD(hdb, true)) return false;
+  if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  bool rv = tchdbvanishimpl(hdb);
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Copy the database file of a hash database object. */
+bool tchdbcopy(TCHDB *hdb, const char *path){
+  assert(hdb && path);
+  if(!HDBLOCKMETHOD(hdb, false)) return false;
+  if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(hdb->async && !tchdbflushdrp(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  if(!HDBLOCKDRP(hdb)){
+    HDBUNLOCKMETHOD(hdb);
+    return false;
+  }
+  bool err = false;
+  hdb->flags &= ~HDBFOPEN;
+  if(hdb->omode & HDBOWRITER){
+    if(!tchdbsavefbp(hdb)) err = true;
+    if(!tchdbmemsync(hdb, false)) err = true;
+  }
+  if(*path == '@'){
+    int len = strlen(hdb->path);
+    char name[len*2+1];
+    char *wp = name;
+    for(int i = 0; i < len; i++){
+      switch(hdb->path[i]){
+      case '\\':
+      case '$':
+        *(wp++) = '\\';
+        *(wp++) = hdb->path[i];
+        break;
+      default:
+        *(wp++) = hdb->path[i];
+        break;
+      }
+    }
+    *wp = '\0';
+    char *cmd = tcsprintf("%s \"%s\" \"%llu\"",
+                          path + 1, name, (unsigned long long)(tctime() * 1000000));
+    if(system(cmd) != 0) err = true;
+    free(cmd);
+  } else {
+    if(!tccopyfile(hdb->path, path)){
+      tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+      err = true;
+    }
+  }
+  hdb->flags |= HDBFOPEN;
+  HDBUNLOCKDRP(hdb);
+  HDBUNLOCKMETHOD(hdb);
+  return !err;
+}
+
+
+/* Get the file path of a hash database object. */
+const char *tchdbpath(TCHDB *hdb){
+  assert(hdb);
+  if(!HDBLOCKMETHOD(hdb, false)) return NULL;
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return NULL;
+  }
+  const char *rv = hdb->path;
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Get the number of records of a hash database object. */
+uint64_t tchdbrnum(TCHDB *hdb){
+  assert(hdb);
+  if(!HDBLOCKMETHOD(hdb, false)) return 0;
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return 0;
+  }
+  uint64_t rv = hdb->rnum;
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+/* Get the size of the database file of a hash database object. */
+uint64_t tchdbfsiz(TCHDB *hdb){
+  assert(hdb);
+  if(!HDBLOCKMETHOD(hdb, false)) return 0;
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    HDBUNLOCKMETHOD(hdb);
+    return 0;
+  }
+  uint64_t rv = hdb->fsiz;
+  HDBUNLOCKMETHOD(hdb);
+  return rv;
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Set the error code of a hash database object. */
+void tchdbsetecode(TCHDB *hdb, int ecode, const char *filename, int line, const char *func){
+  assert(hdb && filename && line >= 1 && func);
+  if(!hdb->fatal){
+    hdb->ecode = ecode;
+    if(hdb->mmtx) pthread_setspecific(*(pthread_key_t *)hdb->eckey, (void *)(intptr_t)ecode);
+  }
+  if(ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC){
+    hdb->fatal = true;
+    if(hdb->omode & HDBOWRITER) tchdbsetflag(hdb, HDBFFATAL, true);
+  }
+  if(hdb->dbgfd >= 0){
+    char obuf[HDBIOBUFSIZ];
+    int osiz = sprintf(obuf, "ERROR:%s:%d:%s:%s:%d:%s\n", filename, line, func,
+                       hdb->path ? hdb->path : "-", ecode, tchdberrmsg(ecode));
+    tcwrite(hdb->dbgfd, obuf, osiz);
+  }
+}
+
+
+/* Set the type of a hash database object. */
+void tchdbsettype(TCHDB *hdb, uint8_t type){
+  assert(hdb);
+  if(hdb->fd >= 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return;
+  }
+  hdb->type = type;
+}
+
+
+/* Set the file descriptor for debugging output. */
+void tchdbsetdbgfd(TCHDB *hdb, int fd){
+  assert(hdb && fd >= 0);
+  hdb->dbgfd = fd;
+}
+
+
+/* Get the file descriptor for debugging output. */
+int tchdbdbgfd(TCHDB *hdb){
+  assert(hdb);
+  return hdb->dbgfd;
+}
+
+
+/* Synchronize updating contents on memory. */
+bool tchdbmemsync(TCHDB *hdb, bool phys){
+  assert(hdb);
+  if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  bool err = false;
+  char hbuf[HDBHEADSIZ];
+  tcdumpmeta(hdb, hbuf);
+  memcpy(hdb->map, hbuf, HDBOPAQUEOFF);
+  if(phys){
+    if(msync(hdb->map, hdb->msiz, MS_SYNC) == -1){
+      tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__);
+      err = true;
+    }
+    if(fsync(hdb->fd) == -1){
+      tchdbsetecode(hdb, TCESYNC, __FILE__, __LINE__, __func__);
+      err = true;
+    }
+  }
+  return !err;
+}
+
+
+/* Get the number of elements of the bucket array of a hash database object. */
+uint64_t tchdbbnum(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return hdb->bnum;
+}
+
+
+/* Get the record alignment a hash database object. */
+uint32_t tchdbalign(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return hdb->align;
+}
+
+
+/* Get the maximum number of the free block pool of a a hash database object. */
+uint32_t tchdbfbpmax(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return hdb->fbpmax;
+}
+
+
+/* Get the inode number of the database file of a hash database object. */
+uint64_t tchdbinode(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return hdb->inode;
+}
+
+
+/* Get the modification time of the database file of a hash database object. */
+time_t tchdbmtime(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return hdb->mtime;
+}
+
+
+/* Get the connection mode of a hash database object. */
+int tchdbomode(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return hdb->omode;
+}
+
+
+/* Get the database type of a hash database object. */
+uint8_t tchdbtype(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return hdb->type;
+}
+
+
+/* Get the additional flags of a hash database object. */
+uint8_t tchdbflags(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return hdb->flags;
+}
+
+
+/* Get the options of a hash database object. */
+uint8_t tchdbopts(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  return hdb->opts;
+}
+
+
+/* Get the pointer to the opaque field of a hash database object. */
+char *tchdbopaque(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return NULL;
+  }
+  return hdb->map + HDBOPAQUEOFF;
+}
+
+
+/* Get the number of used elements of the bucket array of a hash database object. */
+uint64_t tchdbbnumused(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fd < 0){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    return 0;
+  }
+  uint64_t unum = 0;
+  if(hdb->ba64){
+    uint64_t *buckets = hdb->ba64;
+    for(int i = 0; i < hdb->bnum; i++){
+      if(buckets[i]) unum++;
+    }
+  } else {
+    uint32_t *buckets = hdb->ba32;
+    for(int i = 0; i < hdb->bnum; i++){
+      if(buckets[i]) unum++;
+    }
+  }
+  return unum;
+}
+
+
+
+/*************************************************************************************************
+ * private features
+ *************************************************************************************************/
+
+
+/* Seek and read data from a file.
+   `hdb' specifies the hash database object.
+   `off' specifies the offset of the region to seek.
+   `buf' specifies the buffer to store into.
+   `size' specifies the size of the buffer.
+   The return value is true if successful, else, it is false. */
+static bool tcseekwrite(TCHDB *hdb, off_t off, const void *buf, size_t size){
+  assert(hdb && off >= 0 && buf && size >= 0);
+  while(true){
+    int wb = pwrite(hdb->fd, buf, size, off);
+    if(wb >= size){
+      return true;
+    } else if(wb > 0){
+      buf = (char *)buf + wb;
+      size -= wb;
+      off += wb;
+    } else if(wb == -1){
+      if(errno != EINTR){
+        tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
+        return false;
+      }
+    } else {
+      if(size > 0){
+        tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+
+/* Seek and read data from a file.
+   `hdb' specifies the hash database object.
+   `off' specifies the offset of the region to seek.
+   `buf' specifies the buffer to store into.
+   `size' specifies the size of the buffer.
+   The return value is true if successful, else, it is false. */
+static bool tcseekread(TCHDB *hdb, off_t off, void *buf, size_t size){
+  assert(hdb && off >= 0 && buf && size >= 0);
+  while(true){
+    int rb = pread(hdb->fd, buf, size, off);
+    if(rb >= size){
+      break;
+    } else if(rb > 0){
+      buf = (char *)buf + rb;
+      size -= rb;
+      off += rb;
+    } else if(rb == -1){
+      if(errno != EINTR){
+        tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
+        return false;
+      }
+    } else {
+      if(size > 0){
+        tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+
+/* Serialize meta data into a buffer.
+   `hdb' specifies the hash database object.
+   `hbuf' specifies the buffer. */
+static void tcdumpmeta(TCHDB *hdb, char *hbuf){
+  memset(hbuf, 0, HDBHEADSIZ);
+  sprintf(hbuf, "%s\n%s:%d\n", HDBMAGICDATA, _TC_FORMATVER, _TC_LIBVER);
+  memcpy(hbuf + HDBTYPEOFF, &(hdb->type), sizeof(hdb->type));
+  memcpy(hbuf + HDBFLAGSOFF, &(hdb->flags), sizeof(hdb->flags));
+  memcpy(hbuf + HDBAPOWOFF, &(hdb->apow), sizeof(hdb->apow));
+  memcpy(hbuf + HDBFPOWOFF, &(hdb->fpow), sizeof(hdb->fpow));
+  memcpy(hbuf + HDBOPTSOFF, &(hdb->opts), sizeof(hdb->opts));
+  uint64_t llnum;
+  llnum = hdb->bnum;
+  llnum = TCHTOILL(llnum);
+  memcpy(hbuf + HDBBNUMOFF, &llnum, sizeof(llnum));
+  llnum = hdb->rnum;
+  llnum = TCHTOILL(llnum);
+  memcpy(hbuf + HDBRNUMOFF, &llnum, sizeof(llnum));
+  llnum = hdb->fsiz;
+  llnum = TCHTOILL(llnum);
+  memcpy(hbuf + HDBFSIZOFF, &llnum, sizeof(llnum));
+  llnum = hdb->frec;
+  llnum = TCHTOILL(llnum);
+  memcpy(hbuf + HDBFRECOFF, &llnum, sizeof(llnum));
+}
+
+
+/* Deserialize meta data from a buffer.
+   `hdb' specifies the hash database object.
+   `hbuf' specifies the buffer. */
+static void tcloadmeta(TCHDB *hdb, const char *hbuf){
+  memcpy(&(hdb->type), hbuf + HDBTYPEOFF, sizeof(hdb->type));
+  memcpy(&(hdb->flags), hbuf + HDBFLAGSOFF, sizeof(hdb->flags));
+  memcpy(&(hdb->apow), hbuf + HDBAPOWOFF, sizeof(hdb->apow));
+  memcpy(&(hdb->fpow), hbuf + HDBFPOWOFF, sizeof(hdb->fpow));
+  memcpy(&(hdb->opts), hbuf + HDBOPTSOFF, sizeof(hdb->opts));
+  uint64_t llnum;
+  memcpy(&llnum, hbuf + HDBBNUMOFF, sizeof(llnum));
+  hdb->bnum = TCITOHLL(llnum);
+  memcpy(&llnum, hbuf + HDBRNUMOFF, sizeof(llnum));
+  hdb->rnum = TCITOHLL(llnum);
+  memcpy(&llnum, hbuf + HDBFSIZOFF, sizeof(llnum));
+  hdb->fsiz = TCITOHLL(llnum);
+  memcpy(&llnum, hbuf + HDBFRECOFF, sizeof(llnum));
+  hdb->frec = TCITOHLL(llnum);
+}
+
+
+/* Get a natural prime number not less than a floor number.
+   `num' specified the floor number.
+   The return value is a prime number not less than the floor number. */
+static uint64_t tcgetprime(uint64_t num){
+  uint64_t primes[] = {
+    1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 43, 47, 53, 59, 61, 71, 79, 83,
+    89, 103, 109, 113, 127, 139, 157, 173, 191, 199, 223, 239, 251, 283, 317, 349,
+    383, 409, 443, 479, 509, 571, 631, 701, 761, 829, 887, 953, 1021, 1151, 1279,
+    1399, 1531, 1663, 1789, 1913, 2039, 2297, 2557, 2803, 3067, 3323, 3583, 3833,
+    4093, 4603, 5119, 5623, 6143, 6653, 7159, 7673, 8191, 9209, 10223, 11261,
+    12281, 13309, 14327, 15359, 16381, 18427, 20479, 22511, 24571, 26597, 28669,
+    30713, 32749, 36857, 40949, 45053, 49139, 53239, 57331, 61417, 65521, 73727,
+    81919, 90107, 98299, 106487, 114679, 122869, 131071, 147451, 163819, 180221,
+    196597, 212987, 229373, 245759, 262139, 294911, 327673, 360439, 393209, 425977,
+    458747, 491503, 524287, 589811, 655357, 720887, 786431, 851957, 917503, 982981,
+    1048573, 1179641, 1310719, 1441771, 1572853, 1703903, 1835003, 1966079,
+    2097143, 2359267, 2621431, 2883577, 3145721, 3407857, 3670013, 3932153,
+    4194301, 4718579, 5242877, 5767129, 6291449, 6815741, 7340009, 7864301,
+    8388593, 9437179, 10485751, 11534329, 12582893, 13631477, 14680063, 15728611,
+    16777213, 18874367, 20971507, 23068667, 25165813, 27262931, 29360087, 31457269,
+    33554393, 37748717, 41943023, 46137319, 50331599, 54525917, 58720253, 62914549,
+    67108859, 75497467, 83886053, 92274671, 100663291, 109051903, 117440509,
+    125829103, 134217689, 150994939, 167772107, 184549373, 201326557, 218103799,
+    234881011, 251658227, 268435399, 301989881, 335544301, 369098707, 402653171,
+    436207613, 469762043, 503316469, 536870909, 603979769, 671088637, 738197503,
+    805306357, 872415211, 939524087, 1006632947, 1073741789, 1207959503,
+    1342177237, 1476394991, 1610612711, 1744830457, 1879048183, 2013265907,
+    2576980349, 3092376431, 3710851741, 4718021527, 6133428047, 7973456459,
+    10365493393, 13475141413, 17517683831, 22772988923, 29604885677, 38486351381,
+    50032256819, 65041933867, 84554514043, 109920868241, 0
+  };
+  int i;
+  for(i = 0; primes[i] > 0; i++){
+    if(num <= primes[i]) return primes[i];
+  }
+  return primes[i-1];
+}
+
+
+/* Clear all members.
+   `hdb' specifies the hash database object. */
+static void tchdbclear(TCHDB *hdb){
+  assert(hdb);
+  hdb->mmtx = NULL;
+  hdb->dmtx = NULL;
+  hdb->eckey = NULL;
+  hdb->type = HDBTHASH;
+  hdb->flags = 0;
+  hdb->bnum = HDBDEFBNUM;
+  hdb->apow = HDBDEFAPOW;
+  hdb->fpow = HDBDEFFPOW;
+  hdb->opts = 0;
+  hdb->path = NULL;
+  hdb->fd = -1;
+  hdb->omode = 0;
+  hdb->rnum = 0;
+  hdb->fsiz = 0;
+  hdb->frec = 0;
+  hdb->iter = 0;
+  hdb->map = NULL;
+  hdb->msiz = 0;
+  hdb->ba32 = NULL;
+  hdb->ba64 = NULL;
+  hdb->align = 0;
+  hdb->runit = 0;
+  hdb->zmode = false;
+  hdb->fbpmax = 0;
+  hdb->fbpsiz = 0;
+  hdb->fbpool = NULL;
+  hdb->fbpnum = 0;
+  hdb->fbpmis = 0;
+  hdb->async = false;
+  hdb->drpool = NULL;
+  hdb->drpdef = NULL;
+  hdb->drpoff = 0;
+  hdb->recc = NULL;
+  hdb->rcnum = 0;
+  hdb->ecode = TCESUCCESS;
+  hdb->fatal = false;
+  hdb->dbgfd = -1;
+  hdb->cnt_writerec = -1;
+  hdb->cnt_reuserec = -1;
+  hdb->cnt_moverec = -1;
+  hdb->cnt_readrec = -1;
+  hdb->cnt_searchfbp = -1;
+  hdb->cnt_insertfbp = -1;
+  hdb->cnt_splicefbp = -1;
+  hdb->cnt_dividefbp = -1;
+  hdb->cnt_mergefbp = -1;
+  hdb->cnt_reducefbp = -1;
+  hdb->cnt_appenddrp = -1;
+  hdb->cnt_deferdrp = -1;
+  hdb->cnt_flushdrp = -1;
+  hdb->cnt_adjrecc = -1;
+  TCDODEBUG(hdb->cnt_writerec = 0);
+  TCDODEBUG(hdb->cnt_reuserec = 0);
+  TCDODEBUG(hdb->cnt_moverec = 0);
+  TCDODEBUG(hdb->cnt_readrec = 0);
+  TCDODEBUG(hdb->cnt_searchfbp = 0);
+  TCDODEBUG(hdb->cnt_insertfbp = 0);
+  TCDODEBUG(hdb->cnt_splicefbp = 0);
+  TCDODEBUG(hdb->cnt_dividefbp = 0);
+  TCDODEBUG(hdb->cnt_mergefbp = 0);
+  TCDODEBUG(hdb->cnt_reducefbp = 0);
+  TCDODEBUG(hdb->cnt_appenddrp = 0);
+  TCDODEBUG(hdb->cnt_deferdrp = 0);
+  TCDODEBUG(hdb->cnt_flushdrp = 0);
+  TCDODEBUG(hdb->cnt_adjrecc = 0);
+}
+
+
+/* Get the padding size to record alignment.
+   `hdb' specifies the hash database object.
+   The return value is the padding size. */
+static int32_t tchdbpadsize(TCHDB *hdb){
+  assert(hdb);
+  int32_t diff = hdb->fsiz & (hdb->align - 1);
+  return diff > 0 ? hdb->align - diff : 0;
+}
+
+
+/* Set the open flag.
+   `hdb' specifies the hash database object.
+   `flag' specifies the flag value.
+   `sign' specifies the sign. */
+static void tchdbsetflag(TCHDB *hdb, int flag, bool sign){
+  assert(hdb);
+  char *fp = (char *)hdb->map + HDBFLAGSOFF;
+  if(sign){
+    *fp |= (uint8_t)flag;
+  } else {
+    *fp &= ~(uint8_t)flag;
+  }
+  hdb->flags = *fp;
+}
+
+
+/* Get the bucket index of a record.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `hp' specifies the pointer to the variable into which the second hash value is assigned.
+   The return value is the bucket index. */
+static uint64_t tchdbbidx(TCHDB *hdb, const char *kbuf, int ksiz, uint8_t *hp){
+  assert(hdb && kbuf && ksiz >= 0 && hp);
+  uint64_t idx = 19780211;
+  uint32_t hash = 751;
+  const char *rp = kbuf + ksiz;
+  while(ksiz--){
+    idx = (idx << 5) + (idx << 2) + idx + *(uint8_t *)kbuf++;
+    hash = ((hash << 5) - hash) ^ *(uint8_t *)--rp;
+  }
+  *hp = hash;
+  return idx % hdb->bnum;
+}
+
+
+/* Get the offset of the record of a bucket element.
+   `hdb' specifies the hash database object.
+   `bidx' specifies the index of the bucket.
+   The return value is the offset of the record. */
+static off_t tchdbgetbucket(TCHDB *hdb, uint64_t bidx){
+  assert(hdb && bidx >= 0);
+  if(hdb->ba64){
+    uint64_t llnum = hdb->ba64[bidx];
+    return TCITOHLL(llnum) << hdb->apow;
+  }
+  uint32_t lnum = hdb->ba32[bidx];
+  return (off_t)TCITOHL(lnum) << hdb->apow;
+}
+
+
+/* Get the offset of the record of a bucket element.
+   `hdb' specifies the hash database object.
+   `bidx' specifies the index of the record.
+   `off' specifies the offset of the record. */
+static void tchdbsetbucket(TCHDB *hdb, uint64_t bidx, uint64_t off){
+  assert(hdb && bidx >= 0);
+  if(hdb->ba64){
+    uint64_t llnum = off >> hdb->apow;
+    hdb->ba64[bidx] = TCHTOILL(llnum);
+  } else {
+    uint32_t lnum = off >> hdb->apow;
+    hdb->ba32[bidx] = TCHTOIL(lnum);
+  }
+}
+
+
+/* Load the free block pool from the file.
+   The return value is true if successful, else, it is false. */
+static bool tchdbsavefbp(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->fbpnum > (hdb->fbpmax >> 1)){
+    tchdbfbpmerge(hdb);
+  } else if(hdb->fbpnum > 1){
+    tcfbpsortbyoff(hdb->fbpool, hdb->fbpnum);
+  }
+  int bsiz = hdb->fbpsiz;
+  char *buf;
+  TCMALLOC(buf, bsiz);
+  char *wp = buf;
+  HDBFB *cur = hdb->fbpool;
+  HDBFB *end = cur + hdb->fbpnum;
+  uint64_t base = 0;
+  bsiz -= sizeof(HDBFB) + sizeof(uint8_t) + sizeof(uint8_t);
+  while(cur < end && bsiz > 0){
+    uint64_t noff = cur->off >> hdb->apow;
+    int step;
+    uint64_t llnum = noff - base;
+    TCSETVNUMBUF64(step, wp, llnum);
+    wp += step;
+    bsiz -= step;
+    uint32_t lnum = cur->rsiz >> hdb->apow;
+    TCSETVNUMBUF(step, wp, lnum);
+    wp += step;
+    bsiz -= step;
+    base = noff;
+    cur++;
+  }
+  *(wp++) = '\0';
+  *(wp++) = '\0';
+  if(!tcseekwrite(hdb, hdb->msiz, buf, wp - buf)){
+    free(buf);
+    return false;
+  }
+  free(buf);
+  return true;
+}
+
+
+/* Save the free block pool into the file.
+   The return value is true if successful, else, it is false. */
+static bool tchdbloadfbp(TCHDB *hdb){
+  int bsiz = hdb->fbpsiz;
+  char *buf;
+  TCMALLOC(buf, bsiz);
+  if(!tcseekread(hdb, hdb->msiz, buf, bsiz)){
+    free(buf);
+    return false;
+  }
+  const char *rp = buf;
+  HDBFB *cur = hdb->fbpool;
+  HDBFB *end = cur + hdb->fbpmax;
+  uint64_t base = 0;
+  while(cur < end && *rp != '\0'){
+    int step;
+    uint64_t llnum;
+    TCREADVNUMBUF64(rp, llnum, step);
+    base += llnum << hdb->apow;
+    cur->off = base;
+    rp += step;
+    uint32_t lnum;
+    TCREADVNUMBUF(rp, lnum, step);
+    cur->rsiz = lnum << hdb->apow;
+    rp += step;
+    cur++;
+  }
+  hdb->fbpnum = cur - (HDBFB *)hdb->fbpool;
+  free(buf);
+  return true;
+}
+
+
+/* Sort the free block pool by offset.
+   `fbpool' specifies the free block pool.
+   `fbpnum' specifies the number of blocks. */
+static void tcfbpsortbyoff(HDBFB *fbpool, int fbpnum){
+  assert(fbpool && fbpnum >= 0);
+  fbpnum--;
+  int bottom = fbpnum / 2 + 1;
+  int top = fbpnum;
+  while(bottom > 0){
+    bottom--;
+    int mybot = bottom;
+    int i = 2 * mybot;
+    while(i <= top){
+      if(i < top && fbpool[i+1].off > fbpool[i].off) i++;
+      if(fbpool[mybot].off >= fbpool[i].off) break;
+      HDBFB swap = fbpool[mybot];
+      fbpool[mybot] = fbpool[i];
+      fbpool[i] = swap;
+      mybot = i;
+      i = 2 * mybot;
+    }
+  }
+  while(top > 0){
+    HDBFB swap = fbpool[0];
+    fbpool[0] = fbpool[top];
+    fbpool[top] = swap;
+    top--;
+    int mybot = bottom;
+    int i = 2 * mybot;
+    while(i <= top){
+      if(i < top && fbpool[i+1].off > fbpool[i].off) i++;
+      if(fbpool[mybot].off >= fbpool[i].off) break;
+      swap = fbpool[mybot];
+      fbpool[mybot] = fbpool[i];
+      fbpool[i] = swap;
+      mybot = i;
+      i = 2 * mybot;
+    }
+  }
+}
+
+
+/* Sort the free block pool by record size.
+   `fbpool' specifies the free block pool.
+   `fbpnum' specifies the number of blocks. */
+static void tcfbpsortbyrsiz(HDBFB *fbpool, int fbpnum){
+  assert(fbpool && fbpnum >= 0);
+  fbpnum--;
+  int bottom = fbpnum / 2 + 1;
+  int top = fbpnum;
+  while(bottom > 0){
+    bottom--;
+    int mybot = bottom;
+    int i = 2 * mybot;
+    while(i <= top){
+      if(i < top && fbpool[i+1].rsiz > fbpool[i].rsiz) i++;
+      if(fbpool[mybot].rsiz >= fbpool[i].rsiz) break;
+      HDBFB swap = fbpool[mybot];
+      fbpool[mybot] = fbpool[i];
+      fbpool[i] = swap;
+      mybot = i;
+      i = 2 * mybot;
+    }
+  }
+  while(top > 0){
+    HDBFB swap = fbpool[0];
+    fbpool[0] = fbpool[top];
+    fbpool[top] = swap;
+    top--;
+    int mybot = bottom;
+    int i = 2 * mybot;
+    while(i <= top){
+      if(i < top && fbpool[i+1].rsiz > fbpool[i].rsiz) i++;
+      if(fbpool[mybot].rsiz >= fbpool[i].rsiz) break;
+      swap = fbpool[mybot];
+      fbpool[mybot] = fbpool[i];
+      fbpool[i] = swap;
+      mybot = i;
+      i = 2 * mybot;
+    }
+  }
+}
+
+
+/* Merge successive records in the free block pool.
+   `hdb' specifies the hash database object. */
+static void tchdbfbpmerge(TCHDB *hdb){
+  assert(hdb);
+  TCDODEBUG(hdb->cnt_mergefbp++);
+  int32_t onum = hdb->fbpnum;
+  tcfbpsortbyoff(hdb->fbpool, hdb->fbpnum);
+  HDBFB *wp = hdb->fbpool;;
+  HDBFB *cur = wp;
+  HDBFB *end = wp + hdb->fbpnum - 1;
+  while(cur < end){
+    if(cur->off > 0){
+      HDBFB *next = cur + 1;
+      if(cur->off + cur->rsiz == next->off){
+        if(hdb->iter == next->off) hdb->iter += next->rsiz;
+        cur->rsiz += next->rsiz;
+        next->off = 0;
+      }
+      *(wp++) = *cur;
+    }
+    cur++;
+  }
+  if(end->off > 0) *(wp++) = *end;
+  hdb->fbpnum = wp - (HDBFB *)hdb->fbpool;
+  hdb->fbpmis = (hdb->fbpnum < onum) ? 0 : hdb->fbpnum * -2;
+}
+
+
+/* Insert a block into the free block pool.
+   `hdb' specifies the hash database object.
+   `off' specifies the offset of the block.
+   `rsiz' specifies the size of the block. */
+static void tchdbfbpinsert(TCHDB *hdb, uint64_t off, uint32_t rsiz){
+  assert(hdb && off > 0 && rsiz > 0);
+  TCDODEBUG(hdb->cnt_insertfbp++);
+  if(hdb->fpow < 1) return;
+  HDBFB *pv = hdb->fbpool;
+  if(hdb->fbpnum >= hdb->fbpmax){
+    tchdbfbpmerge(hdb);
+    tcfbpsortbyrsiz(hdb->fbpool, hdb->fbpnum);
+    if(hdb->fbpnum >= hdb->fbpmax){
+      TCDODEBUG(hdb->cnt_reducefbp++);
+      int32_t dnum = (hdb->fbpmax >> 2) + 1;
+      memmove(pv, pv + dnum, (hdb->fbpnum - dnum) * sizeof(*pv));
+      hdb->fbpnum -= dnum;
+    }
+    hdb->fbpmis = 0;
+  }
+  pv = pv + hdb->fbpnum;
+  pv->off = off;
+  pv->rsiz = rsiz;
+  hdb->fbpnum++;
+}
+
+
+/* Search the free block pool for the minimum region.
+   `hdb' specifies the hash database object.
+   `rec' specifies the record object to be stored.
+   The return value is true if successful, else, it is false. */
+static bool tchdbfbpsearch(TCHDB *hdb, TCHREC *rec){
+  assert(hdb && rec);
+  TCDODEBUG(hdb->cnt_searchfbp++);
+  if(hdb->fbpnum < 1){
+    rec->off = hdb->fsiz;
+    rec->rsiz = 0;
+    return true;
+  }
+  uint32_t rsiz = rec->rsiz;
+  HDBFB *pv = hdb->fbpool;
+  HDBFB *ep = pv + hdb->fbpnum;
+  while(pv < ep){
+    if(pv->rsiz > rsiz){
+      if(pv->rsiz > (rsiz << 1)){
+        uint64_t fsiz = hdb->fsiz;
+        hdb->fsiz = pv->off + rsiz;
+        uint32_t psiz = tchdbpadsize(hdb);
+        hdb->fsiz = fsiz;
+        uint64_t noff = pv->off + rsiz + psiz;
+        if(pv->rsiz >= ((noff - pv->off) << 1)){
+          TCDODEBUG(hdb->cnt_dividefbp++);
+          rec->off = pv->off;
+          rec->rsiz = noff - pv->off;
+          pv->off = noff;
+          pv->rsiz -= rec->rsiz;
+          return tchdbwritefb(hdb, pv->off, pv->rsiz);
+        }
+      }
+      rec->off = pv->off;
+      rec->rsiz = pv->rsiz;
+      ep--;
+      pv->off = ep->off;
+      pv->rsiz = ep->rsiz;
+      hdb->fbpnum--;
+      return true;
+    }
+    pv++;
+  }
+  rec->off = hdb->fsiz;
+  rec->rsiz = 0;
+  hdb->fbpmis++;
+  if(hdb->fbpmis >= HDBFBPMGFREQ){
+    tchdbfbpmerge(hdb);
+    tcfbpsortbyrsiz(hdb->fbpool, hdb->fbpnum);
+  }
+  return true;
+}
+
+
+/* Splice the trailing free block
+   `hdb' specifies the hash database object.
+   `rec' specifies the record object to be stored.
+   `nsiz' specifies the needed size.
+   The return value is whether splicing succeeded or not. */
+static bool tchdbfbpsplice(TCHDB *hdb, TCHREC *rec, uint32_t nsiz){
+  assert(hdb && rec && nsiz > 0);
+  if(hdb->fbpnum < 1) return false;
+  uint64_t off = rec->off + rec->rsiz;
+  uint32_t rsiz = rec->rsiz;
+  HDBFB *pv = hdb->fbpool;
+  HDBFB *ep = pv + hdb->fbpnum;
+  while(pv < ep){
+    if(pv->off == off && rsiz + pv->rsiz >= nsiz){
+      if(hdb->iter == pv->off) hdb->iter += pv->rsiz;
+      rec->rsiz += pv->rsiz;
+      ep--;
+      pv->off = ep->off;
+      pv->rsiz = ep->rsiz;
+      hdb->fbpnum--;
+      return true;
+    }
+    pv++;
+  }
+  return false;
+}
+
+
+/* Write a free block into the file.
+   `hdb' specifies the hash database object.
+   `off' specifies the offset of the block.
+   `rsiz' specifies the size of the block.
+   The return value is true if successful, else, it is false. */
+static bool tchdbwritefb(TCHDB *hdb, uint64_t off, uint32_t rsiz){
+  assert(hdb && off > 0 && rsiz > 0);
+  char rbuf[HDBMAXHSIZ];
+  char *wp = rbuf;
+  *(uint8_t *)(wp++) = HDBMAGICFB;
+  uint32_t lnum = TCHTOIL(rsiz);
+  memcpy(wp, &lnum, sizeof(lnum));
+  wp += sizeof(lnum);
+  if(!tcseekwrite(hdb, off, rbuf, wp - rbuf)) return false;
+  return true;
+}
+
+
+/* Write a record into the file.
+   `hdb' specifies the hash database object.
+   `rec' specifies the record object.
+   `bidx' specifies the index of the bucket.
+   `entoff' specifies the offset of the tree entry.
+   The return value is true if successful, else, it is false. */
+static bool tchdbwriterec(TCHDB *hdb, TCHREC *rec, uint64_t bidx, off_t entoff){
+  assert(hdb && rec);
+  TCDODEBUG(hdb->cnt_writerec++);
+  uint64_t ofsiz = hdb->fsiz;
+  char stack[HDBIOBUFSIZ];
+  int bsiz = rec->rsiz > 0 ? rec->rsiz : HDBMAXHSIZ + rec->ksiz + rec->vsiz + hdb->align;
+  char *rbuf;
+  if(bsiz <= HDBIOBUFSIZ){
+    rbuf = stack;
+  } else {
+    TCMALLOC(rbuf, bsiz);
+  }
+  char *wp = rbuf;
+  *(uint8_t *)(wp++) = HDBMAGICREC;
+  *(uint8_t *)(wp++) = rec->hash;
+  if(hdb->ba64){
+    uint64_t llnum;
+    llnum = rec->left >> hdb->apow;
+    llnum = TCHTOILL(llnum);
+    memcpy(wp, &llnum, sizeof(llnum));
+    wp += sizeof(llnum);
+    llnum = rec->right >> hdb->apow;
+    llnum = TCHTOILL(llnum);
+    memcpy(wp, &llnum, sizeof(llnum));
+    wp += sizeof(llnum);
+  } else {
+    uint32_t lnum;
+    lnum = rec->left >> hdb->apow;
+    lnum = TCHTOIL(lnum);
+    memcpy(wp, &lnum, sizeof(lnum));
+    wp += sizeof(lnum);
+    lnum = rec->right >> hdb->apow;
+    lnum = TCHTOIL(lnum);
+    memcpy(wp, &lnum, sizeof(lnum));
+    wp += sizeof(lnum);
+  }
+  uint16_t snum;
+  char *pwp = wp;
+  wp += sizeof(snum);
+  int step;
+  TCSETVNUMBUF(step, wp, rec->ksiz);
+  wp += step;
+  TCSETVNUMBUF(step, wp, rec->vsiz);
+  wp += step;
+  int32_t hsiz = wp - rbuf;
+  int32_t rsiz = hsiz + rec->ksiz + rec->vsiz;
+  if(rec->rsiz < 1){
+    hdb->fsiz += rsiz;
+    uint16_t psiz = tchdbpadsize(hdb);
+    rec->rsiz = rsiz + psiz;
+    rec->psiz = psiz;
+    hdb->fsiz += psiz;
+  } else if(rsiz > rec->rsiz){
+    if(rbuf != stack) free(rbuf);
+    if(tchdbfbpsplice(hdb, rec, rsiz)){
+      TCDODEBUG(hdb->cnt_splicefbp++);
+      return tchdbwriterec(hdb, rec, bidx, entoff);
+    }
+    TCDODEBUG(hdb->cnt_moverec++);
+    if(!tchdbwritefb(hdb, rec->off, rec->rsiz)) return false;
+    tchdbfbpinsert(hdb, rec->off, rec->rsiz);
+    rec->rsiz = rsiz;
+    if(!tchdbfbpsearch(hdb, rec)) return false;
+    return tchdbwriterec(hdb, rec, bidx, entoff);
+  } else {
+    TCDODEBUG(hdb->cnt_reuserec++);
+    uint32_t psiz = rec->rsiz - rsiz;
+    if(psiz > UINT16_MAX){
+      TCDODEBUG(hdb->cnt_dividefbp++);
+      uint64_t fsiz = hdb->fsiz;
+      hdb->fsiz = rec->off + rsiz;
+      psiz = tchdbpadsize(hdb);
+      hdb->fsiz = fsiz;
+      uint64_t noff = rec->off + rsiz + psiz;
+      uint32_t nsiz = rec->rsiz - rsiz - psiz;
+      rec->rsiz = noff - rec->off;
+      rec->psiz = psiz;
+      if(!tchdbwritefb(hdb, noff, nsiz)) return false;
+      tchdbfbpinsert(hdb, noff, nsiz);
+    }
+    rec->psiz = psiz;
+  }
+  snum = rec->psiz;
+  snum = TCHTOIS(snum);
+  memcpy(pwp, &snum, sizeof(snum));
+  rsiz = rec->rsiz;
+  rsiz -= hsiz;
+  memcpy(wp, rec->kbuf, rec->ksiz);
+  wp += rec->ksiz;
+  rsiz -= rec->ksiz;
+  memcpy(wp, rec->vbuf, rec->vsiz);
+  wp += rec->vsiz;
+  rsiz -= rec->vsiz;
+  memset(wp, 0, rsiz);
+  if(!tcseekwrite(hdb, rec->off, rbuf, rec->rsiz)){
+    if(rbuf != stack) free(rbuf);
+    hdb->fsiz = ofsiz;
+    return false;
+  }
+  if(hdb->fsiz != ofsiz){
+    uint64_t llnum = hdb->fsiz;
+    llnum = TCHTOILL(llnum);
+    memcpy(hdb->map + HDBFSIZOFF, &llnum, sizeof(llnum));
+  }
+  if(rbuf != stack) free(rbuf);
+  if(entoff > 0){
+    if(hdb->ba64){
+      uint64_t llnum = rec->off >> hdb->apow;
+      llnum = TCHTOILL(llnum);
+      if(!tcseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false;
+    } else {
+      uint32_t lnum = rec->off >> hdb->apow;
+      lnum = TCHTOIL(lnum);
+      if(!tcseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false;
+    }
+  } else {
+    tchdbsetbucket(hdb, bidx, rec->off);
+  }
+  return true;
+}
+
+
+/* Read a record from the file.
+   `hdb' specifies the hash database object.
+   `rec' specifies the record object.
+   `rbuf' specifies the buffer for reading.
+   The return value is true if successful, else, it is false. */
+static bool tchdbreadrec(TCHDB *hdb, TCHREC *rec, char *rbuf){
+  assert(hdb && rec && rbuf);
+  TCDODEBUG(hdb->cnt_readrec++);
+  off_t rsiz = hdb->fsiz - rec->off;
+  if(rsiz > hdb->runit){
+    rsiz = hdb->runit;
+  } else if(rsiz < (sizeof(uint8_t) + sizeof(uint32_t))){
+    tchdbsetecode(hdb, TCERHEAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  if(!tcseekread(hdb, rec->off, rbuf, rsiz)) return false;
+  const char *rp = rbuf;
+  rec->magic = *(uint8_t *)(rp++);
+  if(rec->magic == HDBMAGICFB){
+    uint32_t lnum;
+    memcpy(&lnum, rp, sizeof(lnum));
+    rec->rsiz = TCITOHL(lnum);
+    return true;
+  } else if(rec->magic != HDBMAGICREC){
+    tchdbsetecode(hdb, TCERHEAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  rec->hash = *(uint8_t *)(rp++);
+  if(hdb->ba64){
+    uint64_t llnum;
+    memcpy(&llnum, rp, sizeof(llnum));
+    rec->left = TCITOHLL(llnum) << hdb->apow;
+    rp += sizeof(llnum);
+    memcpy(&llnum, rp, sizeof(llnum));
+    rec->right = TCITOHLL(llnum) << hdb->apow;
+    rp += sizeof(llnum);
+  } else {
+    uint32_t lnum;
+    memcpy(&lnum, rp, sizeof(lnum));
+    rec->left = (uint64_t)TCITOHL(lnum) << hdb->apow;
+    rp += sizeof(lnum);
+    memcpy(&lnum, rp, sizeof(lnum));
+    rec->right = (uint64_t)TCITOHL(lnum) << hdb->apow;
+    rp += sizeof(lnum);
+  }
+  uint16_t snum;
+  memcpy(&snum, rp, sizeof(snum));
+  rec->psiz = TCITOHS(snum);
+  rp += sizeof(snum);
+  uint32_t lnum;
+  int step;
+  TCREADVNUMBUF(rp, lnum, step);
+  rec->ksiz = lnum;
+  rp += step;
+  TCREADVNUMBUF(rp, lnum, step);
+  rec->vsiz = lnum;
+  rp += step;
+  int32_t hsiz = rp - rbuf;
+  rec->rsiz = hsiz + rec->ksiz + rec->vsiz + rec->psiz;
+  rec->kbuf = NULL;
+  rec->vbuf = NULL;
+  rec->boff = rec->off + hsiz;
+  rec->bbuf = NULL;
+  rsiz -= hsiz;
+  if(rsiz >= rec->ksiz){
+    rec->kbuf = rp;
+    rsiz -= rec->ksiz;
+    rp += rec->ksiz;
+    if(rsiz >= rec->vsiz) rec->vbuf = rp;
+  }
+  return true;
+}
+
+
+/* Read the body of a record from the file.
+   `hdb' specifies the hash database object.
+   `rec' specifies the record object.
+   The return value is true if successful, else, it is false. */
+static bool tchdbreadrecbody(TCHDB *hdb, TCHREC *rec){
+  assert(hdb && rec);
+  int32_t bsiz = rec->ksiz + rec->vsiz;
+  TCMALLOC(rec->bbuf, bsiz + 1);
+  if(!tcseekread(hdb, rec->boff, rec->bbuf, bsiz)) return false;
+  rec->kbuf = rec->bbuf;
+  rec->vbuf = rec->bbuf + rec->ksiz;
+  return true;
+}
+
+
+/* Compare keys of two records.
+   `abuf' specifies the pointer to the region of the former.
+   `asiz' specifies the size of the region.
+   `bbuf' specifies the pointer to the region of the latter.
+   `bsiz' specifies the size of the region.
+   The return value is 0 if two equals, positive if the formar is big, else, negative. */
+static int tcreckeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz){
+  assert(abuf && asiz >= 0 && bbuf && bsiz >= 0);
+  if(asiz > bsiz) return 1;
+  if(asiz < bsiz) return -1;
+  return memcmp(abuf, bbuf, asiz);
+}
+
+
+/* Flush the delayed record pool.
+   `hdb' specifies the hash database object.
+   The return value is true if successful, else, it is false. */
+static bool tchdbflushdrp(TCHDB *hdb){
+  assert(hdb);
+  if(!HDBLOCKDRP(hdb)) return false;
+  if(!hdb->drpool){
+    HDBUNLOCKDRP(hdb);
+    return true;
+  }
+  TCDODEBUG(hdb->cnt_flushdrp++);
+  if(!tcseekwrite(hdb, hdb->drpoff, TCXSTRPTR(hdb->drpool), TCXSTRSIZE(hdb->drpool))){
+    HDBUNLOCKDRP(hdb);
+    return false;
+  }
+  const char *rp = TCXSTRPTR(hdb->drpdef);
+  int size = TCXSTRSIZE(hdb->drpdef);
+  while(size > 0){
+    int ksiz, vsiz;
+    memcpy(&ksiz, rp, sizeof(int));
+    rp += sizeof(int);
+    memcpy(&vsiz, rp, sizeof(int));
+    rp += sizeof(int);
+    const char *kbuf = rp;
+    rp += ksiz;
+    const char *vbuf = rp;
+    rp += vsiz;
+    if(!tchdbputimpl(hdb, kbuf, ksiz, vbuf, vsiz, HDBPDOVER)){
+      tcxstrdel(hdb->drpdef);
+      tcxstrdel(hdb->drpool);
+      hdb->drpool = NULL;
+      hdb->drpdef = NULL;
+      hdb->drpoff = 0;
+      HDBUNLOCKDRP(hdb);
+      return false;
+    }
+    size -= sizeof(int) * 2 + ksiz + vsiz;
+  }
+  tcxstrdel(hdb->drpdef);
+  tcxstrdel(hdb->drpool);
+  hdb->drpool = NULL;
+  hdb->drpdef = NULL;
+  hdb->drpoff = 0;
+  uint64_t llnum;
+  llnum = hdb->rnum;
+  llnum = TCHTOILL(llnum);
+  memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum));
+  llnum = hdb->fsiz;
+  llnum = TCHTOILL(llnum);
+  memcpy(hdb->map + HDBFSIZOFF, &llnum, sizeof(llnum));
+  HDBUNLOCKDRP(hdb);
+  return true;
+}
+
+
+/* Adjust the caches for leaves and nodes.
+   `hdb' specifies the hash tree database object. */
+static void tchdbcacheadjust(TCHDB *hdb){
+  assert(hdb);
+  TCDODEBUG(hdb->cnt_adjrecc++);
+  tcmdbcutfront(hdb->recc, HDBCACHEOUT);
+}
+
+
+/* Open a database file and connect a hash database object.
+   `hdb' specifies the hash database object.
+   `path' specifies the path of the database file.
+   `omode' specifies the connection mode.
+   If successful, the return value is true, else, it is false. */
+static bool tchdbopenimpl(TCHDB *hdb, const char *path, int omode){
+  assert(hdb && path);
+  int mode = O_RDONLY;
+  if(omode & HDBOWRITER){
+    mode = O_RDWR;
+    if(omode & HDBOCREAT) mode |= O_CREAT;
+  }
+  int fd = open(path, mode, HDBFILEMODE);
+  if(fd < 0){
+    int ecode = TCEOPEN;
+    switch(errno){
+    case EACCES: ecode = TCENOPERM; break;
+    case ENOENT: ecode = TCENOFILE; break;
+    }
+    tchdbsetecode(hdb, ecode, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  if(!(omode & HDBONOLCK)){
+    if(!tclock(fd, omode & HDBOWRITER, omode & HDBOLCKNB)){
+      tchdbsetecode(hdb, TCELOCK, __FILE__, __LINE__, __func__);
+      close(fd);
+      return false;
+    }
+  }
+  if((omode & HDBOWRITER) && (omode & HDBOTRUNC)){
+    if(ftruncate(fd, 0) == -1){
+      tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__);
+      close(fd);
+      return false;
+    }
+  }
+  struct stat sbuf;
+  if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
+    tchdbsetecode(hdb, TCESTAT, __FILE__, __LINE__, __func__);
+    close(fd);
+    return false;
+  }
+  char hbuf[HDBHEADSIZ];
+  if((omode & HDBOWRITER) && sbuf.st_size < 1){
+    hdb->rnum = 0;
+    uint32_t fbpmax = 1 << hdb->fpow;
+    uint32_t fbpsiz = HDBFBPBSIZ + fbpmax * HDBFBPESIZ;
+    int besiz = (hdb->opts & HDBTLARGE) ? sizeof(int64_t) : sizeof(int32_t);
+    hdb->align = 1 << hdb->apow;
+    hdb->fsiz = HDBHEADSIZ + besiz * hdb->bnum + fbpsiz;
+    uint64_t psiz = tchdbpadsize(hdb);
+    hdb->fsiz += psiz;
+    hdb->frec = hdb->fsiz;
+    tcdumpmeta(hdb, hbuf);
+    psiz += besiz * hdb->bnum + fbpsiz;
+    char pbuf[HDBIOBUFSIZ];
+    memset(pbuf, 0, HDBIOBUFSIZ);
+    bool err = false;
+    if(!tcwrite(fd, hbuf, HDBHEADSIZ)) err = true;
+    while(psiz > 0){
+      if(psiz > HDBIOBUFSIZ){
+        if(!tcwrite(fd, pbuf, HDBIOBUFSIZ)) err = true;
+        psiz -= HDBIOBUFSIZ;
+      } else {
+        if(!tcwrite(fd, pbuf, psiz)) err = true;
+        psiz = 0;
+      }
+    }
+    if(err){
+      tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
+      close(fd);
+      return false;
+    }
+    sbuf.st_size = hdb->fsiz;
+  }
+  if(lseek(fd, 0, SEEK_SET) == -1){
+    tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__);
+    close(fd);
+    return false;
+  }
+  if(!tcread(fd, hbuf, HDBHEADSIZ)){
+    tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
+    close(fd);
+    return false;
+  }
+  int type = hdb->type;
+  tcloadmeta(hdb, hbuf);
+  uint32_t fbpmax = 1 << hdb->fpow;
+  uint32_t fbpsiz = HDBFBPBSIZ + fbpmax * HDBFBPESIZ;
+  if(!(omode & HDBONOLCK)){
+    if(memcmp(hbuf, HDBMAGICDATA, strlen(HDBMAGICDATA)) || hdb->type != type ||
+       hdb->frec < HDBHEADSIZ + fbpsiz || hdb->frec > hdb->fsiz || sbuf.st_size < hdb->fsiz){
+      tchdbsetecode(hdb, TCEMETA, __FILE__, __LINE__, __func__);
+      close(fd);
+      return false;
+    }
+    if(sbuf.st_size > hdb->fsiz) hdb->fsiz = sbuf.st_size;
+  }
+  if((hdb->opts & HDBTDEFLATE) && !_tc_deflate){
+    tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+    close(fd);
+    return false;
+  }
+  int besiz = (hdb->opts & HDBTLARGE) ? sizeof(int64_t) : sizeof(int32_t);
+  size_t msiz = HDBHEADSIZ + hdb->bnum * besiz;
+  void *map = mmap(0, msiz, PROT_READ | ((omode & HDBOWRITER) ? PROT_WRITE : 0),
+                   MAP_SHARED, fd, 0);
+  if(map == MAP_FAILED){
+    tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__);
+    close(fd);
+    return false;
+  }
+  hdb->fbpmax = fbpmax;
+  hdb->fbpsiz = fbpsiz;
+  if(omode & HDBOWRITER){
+    TCMALLOC(hdb->fbpool, fbpmax * sizeof(HDBFB));
+  } else {
+    hdb->fbpool = NULL;
+  }
+  hdb->fbpnum = 0;
+  hdb->fbpmis = 0;
+  hdb->async = false;
+  hdb->drpool = NULL;
+  hdb->drpdef = NULL;
+  hdb->drpoff = 0;
+  hdb->recc = (hdb->rcnum > 0) ? tcmdbnew2(hdb->rcnum * 2 + 1) : NULL;
+  hdb->path = tcstrdup(path);
+  hdb->omode = omode;
+  hdb->iter = 0;
+  hdb->map = map;
+  hdb->msiz = msiz;
+  if(hdb->opts & HDBTLARGE){
+    hdb->ba32 = NULL;
+    hdb->ba64 = (uint64_t *)((char *)map + HDBHEADSIZ);
+  } else {
+    hdb->ba32 = (uint32_t *)((char *)map + HDBHEADSIZ);
+    hdb->ba64 = NULL;
+  }
+  hdb->align = 1 << hdb->apow;
+  hdb->runit = tclmin(tclmax(hdb->align, HDBMINRUNIT), HDBIOBUFSIZ);
+  hdb->zmode = (hdb->opts & HDBTDEFLATE) || (hdb->opts & HDBTTCBS);
+  hdb->ecode = TCESUCCESS;
+  hdb->fatal = false;
+  hdb->fd = fd;
+  if(hdb->omode & HDBOWRITER){
+    bool err = false;
+    if(!(hdb->flags & HDBFOPEN) && !tchdbloadfbp(hdb)) err = true;
+    memset(hbuf, 0, 2);
+    if(!tcseekwrite(hdb, hdb->msiz, hbuf, 2)) err = true;
+    if(err){
+      free(hdb->path);
+      free(hdb->fbpool);
+      munmap(hdb->map, hdb->msiz);
+      close(fd);
+      hdb->fd = -1;
+      return false;
+    }
+    tchdbsetflag(hdb, HDBFOPEN, true);
+  }
+  hdb->inode = (uint64_t)sbuf.st_ino;
+  hdb->mtime = sbuf.st_mtime;
+  return true;
+}
+
+
+/* Close a hash database object.
+   `hdb' specifies the hash database object.
+   If successful, the return value is true, else, it is false. */
+static bool tchdbcloseimpl(TCHDB *hdb){
+  assert(hdb);
+  bool err = false;
+  if(hdb->recc){
+    tcmdbdel(hdb->recc);
+    hdb->recc = NULL;
+  }
+  if(hdb->omode & HDBOWRITER){
+    if(!tchdbflushdrp(hdb)) err = true;
+    if(!tchdbsavefbp(hdb)) err = true;
+    free(hdb->fbpool);
+    tchdbsetflag(hdb, HDBFOPEN, false);
+  }
+  free(hdb->path);
+  if((hdb->omode & HDBOWRITER) && !tchdbmemsync(hdb, false)) err = true;
+  if(munmap(hdb->map, hdb->msiz) == -1){
+    tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__);
+    err = true;
+  }
+  if(close(hdb->fd) == -1){
+    tchdbsetecode(hdb, TCECLOSE, __FILE__, __LINE__, __func__);
+    err = true;
+  }
+  hdb->path = NULL;
+  hdb->fd = -1;
+  return !err;
+}
+
+
+/* Store a record.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   `dmode' specifies behavior when the key overlaps.
+   If successful, the return value is true, else, it is false. */
+static bool tchdbputimpl(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                         int dmode){
+  assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz);
+  uint8_t hash;
+  uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+  off_t off = tchdbgetbucket(hdb, bidx);
+  off_t entoff = 0;
+  TCHREC rec;
+  char rbuf[HDBIOBUFSIZ];
+  while(off > 0){
+    rec.off = off;
+    if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
+    if(hash > rec.hash){
+      off = rec.left;
+      entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
+    } else if(hash < rec.hash){
+      off = rec.right;
+      entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
+        (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
+    } else {
+      if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return false;
+      int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
+      if(kcmp > 0){
+        off = rec.left;
+        free(rec.bbuf);
+        rec.kbuf = NULL;
+        rec.bbuf = NULL;
+        entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
+      } else if(kcmp < 0){
+        off = rec.right;
+        free(rec.bbuf);
+        rec.kbuf = NULL;
+        rec.bbuf = NULL;
+        entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
+          (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
+      } else {
+        switch(dmode){
+        case HDBPDKEEP:
+          free(rec.bbuf);
+          tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__);
+          return false;
+        case HDBPDCAT:
+          if(vsiz < 1){
+            free(rec.bbuf);
+            return true;
+          }
+          if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return false;
+          int nvsiz = rec.vsiz + vsiz;
+          if(rec.bbuf){
+            TCREALLOC(rec.bbuf, rec.bbuf, rec.ksiz + nvsiz);
+            memcpy(rec.bbuf + rec.ksiz + rec.vsiz, vbuf, vsiz);
+            rec.kbuf = rec.bbuf;
+            rec.vbuf = rec.kbuf + rec.ksiz;
+            rec.vsiz = nvsiz;
+          } else {
+            TCMALLOC(rec.bbuf, nvsiz);
+            memcpy(rec.bbuf, rec.vbuf, rec.vsiz);
+            memcpy(rec.bbuf + rec.vsiz, vbuf, vsiz);
+            rec.vbuf = rec.bbuf;
+            rec.vsiz = nvsiz;
+          }
+          bool rv = tchdbwriterec(hdb, &rec, bidx, entoff);
+          free(rec.bbuf);
+          return rv;
+        default:
+          break;
+        }
+        free(rec.bbuf);
+        rec.ksiz = ksiz;
+        rec.vsiz = vsiz;
+        rec.kbuf = kbuf;
+        rec.vbuf = vbuf;
+        return tchdbwriterec(hdb, &rec, bidx, entoff);
+      }
+    }
+  }
+  rec.rsiz = HDBMAXHSIZ + ksiz + vsiz;
+  if(!tchdbfbpsearch(hdb, &rec)) return false;
+  rec.hash = hash;
+  rec.left = 0;
+  rec.right = 0;
+  rec.ksiz = ksiz;
+  rec.vsiz = vsiz;
+  rec.psiz = 0;
+  rec.kbuf = kbuf;
+  rec.vbuf = vbuf;
+  if(!tchdbwriterec(hdb, &rec, bidx, entoff)) return false;
+  hdb->rnum++;
+  uint64_t llnum = hdb->rnum;
+  llnum = TCHTOILL(llnum);
+  memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum));
+  return true;
+}
+
+
+/* Append a record to the delayed record pool.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   `hash' specifies the second hash value. */
+static void tchdbdrpappend(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                           uint8_t hash){
+  assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  TCDODEBUG(hdb->cnt_appenddrp++);
+  char rbuf[HDBIOBUFSIZ];
+  char *wp = rbuf;
+  *(uint8_t *)(wp++) = HDBMAGICREC;
+  *(uint8_t *)(wp++) = hash;
+  if(hdb->ba64){
+    memset(wp, 0, sizeof(uint64_t) * 2);
+    wp += sizeof(uint64_t) * 2;
+  } else {
+    memset(wp, 0, sizeof(uint32_t) * 2);
+    wp += sizeof(uint32_t) * 2;
+  }
+  uint16_t snum;
+  char *pwp = wp;
+  wp += sizeof(snum);
+  int step;
+  TCSETVNUMBUF(step, wp, ksiz);
+  wp += step;
+  TCSETVNUMBUF(step, wp, vsiz);
+  wp += step;
+  int32_t hsiz = wp - rbuf;
+  int32_t rsiz = hsiz + ksiz + vsiz;
+  hdb->fsiz += rsiz;
+  uint16_t psiz = tchdbpadsize(hdb);
+  hdb->fsiz += psiz;
+  snum = TCHTOIS(psiz);
+  memcpy(pwp, &snum, sizeof(snum));
+  TCXSTR *drpool = hdb->drpool;
+  TCXSTRCAT(drpool, rbuf, hsiz);
+  TCXSTRCAT(drpool, kbuf, ksiz);
+  TCXSTRCAT(drpool, vbuf, vsiz);
+  if(psiz > 0){
+    char pbuf[psiz];
+    memset(pbuf, 0, psiz);
+    TCXSTRCAT(drpool, pbuf, psiz);
+  }
+}
+
+
+/* Store a record in asynchronus fashion.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false. */
+static bool tchdbputasyncimpl(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz){
+  assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz);
+  if(!hdb->drpool){
+    hdb->drpool = tcxstrnew3(HDBDRPUNIT + HDBDRPLAT);
+    hdb->drpdef = tcxstrnew3(HDBDRPUNIT);
+    hdb->drpoff = hdb->fsiz;
+  }
+  uint8_t hash;
+  uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+  off_t off = tchdbgetbucket(hdb, bidx);
+  off_t entoff = 0;
+  TCHREC rec;
+  char rbuf[HDBIOBUFSIZ];
+  while(off > 0){
+    if(off >= hdb->drpoff - hdb->runit){
+      TCDODEBUG(hdb->cnt_deferdrp++);
+      TCXSTR *drpdef = hdb->drpdef;
+      TCXSTRCAT(drpdef, &ksiz, sizeof(ksiz));
+      TCXSTRCAT(drpdef, &vsiz, sizeof(vsiz));
+      TCXSTRCAT(drpdef, kbuf, ksiz);
+      TCXSTRCAT(drpdef, vbuf, vsiz);
+      if(TCXSTRSIZE(hdb->drpdef) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false;
+      return true;
+    }
+    rec.off = off;
+    if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
+    if(hash > rec.hash){
+      off = rec.left;
+      entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
+    } else if(hash < rec.hash){
+      off = rec.right;
+      entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
+        (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
+    } else {
+      TCDODEBUG(hdb->cnt_deferdrp++);
+      TCXSTR *drpdef = hdb->drpdef;
+      TCXSTRCAT(drpdef, &ksiz, sizeof(ksiz));
+      TCXSTRCAT(drpdef, &vsiz, sizeof(vsiz));
+      TCXSTRCAT(drpdef, kbuf, ksiz);
+      TCXSTRCAT(drpdef, vbuf, vsiz);
+      if(TCXSTRSIZE(hdb->drpdef) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false;
+      return true;
+    }
+  }
+  if(entoff > 0){
+    if(hdb->ba64){
+      uint64_t llnum = hdb->fsiz >> hdb->apow;
+      llnum = TCHTOILL(llnum);
+      if(!tcseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false;
+    } else {
+      uint32_t lnum = hdb->fsiz >> hdb->apow;
+      lnum = TCHTOIL(lnum);
+      if(!tcseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false;
+    }
+  } else {
+    tchdbsetbucket(hdb, bidx, hdb->fsiz);
+  }
+  tchdbdrpappend(hdb, kbuf, ksiz, vbuf, vsiz, hash);
+  hdb->rnum++;
+  if(TCXSTRSIZE(hdb->drpool) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false;
+  return true;
+}
+
+
+/* Remove a record of a hash database object.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true, else, it is false. */
+static bool tchdboutimpl(TCHDB *hdb, const char *kbuf, int ksiz){
+  assert(hdb && kbuf && ksiz >= 0);
+  if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz);
+  uint8_t hash;
+  uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+  off_t off = tchdbgetbucket(hdb, bidx);
+  off_t entoff = 0;
+  TCHREC rec;
+  char rbuf[HDBIOBUFSIZ];
+  while(off > 0){
+    rec.off = off;
+    if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
+    if(hash > rec.hash){
+      off = rec.left;
+      entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
+    } else if(hash < rec.hash){
+      off = rec.right;
+      entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
+        (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
+    } else {
+      if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return false;
+      int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
+      if(kcmp > 0){
+        off = rec.left;
+        free(rec.bbuf);
+        rec.kbuf = NULL;
+        rec.bbuf = NULL;
+        entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
+      } else if(kcmp < 0){
+        off = rec.right;
+        free(rec.bbuf);
+        rec.kbuf = NULL;
+        rec.bbuf = NULL;
+        entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
+          (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
+      } else {
+        free(rec.bbuf);
+        rec.bbuf = NULL;
+        if(!tchdbwritefb(hdb, rec.off, rec.rsiz)) return false;
+        tchdbfbpinsert(hdb, rec.off, rec.rsiz);
+        uint64_t child;
+        if(rec.left > 0 && rec.right < 1){
+          child = rec.left;
+        } else if(rec.left < 1 && rec.right > 0){
+          child = rec.right;
+        } else if(rec.left < 1 && rec.left < 1){
+          child = 0;
+        } else {
+          child = rec.left;
+          uint64_t right = rec.right;
+          rec.right = child;
+          while(rec.right > 0){
+            rec.off = rec.right;
+            if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
+          }
+          if(hdb->ba64){
+            off_t toff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint64_t));
+            uint64_t llnum = right >> hdb->apow;
+            llnum = TCHTOILL(llnum);
+            if(!tcseekwrite(hdb, toff, &llnum, sizeof(uint64_t))) return false;
+          } else {
+            off_t toff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
+            uint32_t lnum = right >> hdb->apow;
+            lnum = TCHTOIL(lnum);
+            if(!tcseekwrite(hdb, toff, &lnum, sizeof(uint32_t))) return false;
+          }
+        }
+        if(entoff > 0){
+          if(hdb->ba64){
+            uint64_t llnum = child >> hdb->apow;
+            llnum = TCHTOILL(llnum);
+            if(!tcseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false;
+          } else {
+            uint32_t lnum = child >> hdb->apow;
+            lnum = TCHTOIL(lnum);
+            if(!tcseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false;
+          }
+        } else {
+          tchdbsetbucket(hdb, bidx, child);
+        }
+        hdb->rnum--;
+        uint64_t llnum = hdb->rnum;
+        llnum = TCHTOILL(llnum);
+        memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum));
+        return true;
+      }
+    }
+  }
+  tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+  return false;
+}
+
+
+/* Retrieve a record in a hash database object.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the corresponding
+   record. */
+static char *tchdbgetimpl(TCHDB *hdb, const char *kbuf, int ksiz, int *sp){
+  assert(hdb && kbuf && ksiz >= 0 && sp);
+  if(hdb->recc){
+    int tvsiz;
+    char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz);
+    if(tvbuf){
+      if(*tvbuf == '*'){
+        tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+        free(tvbuf);
+        return NULL;
+      }
+      *sp = tvsiz - 1;
+      memmove(tvbuf, tvbuf + 1, tvsiz);
+      return tvbuf;
+    }
+  }
+  uint8_t hash;
+  uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+  off_t off = tchdbgetbucket(hdb, bidx);
+  TCHREC rec;
+  char rbuf[HDBIOBUFSIZ];
+  while(off > 0){
+    rec.off = off;
+    if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL;
+    if(hash > rec.hash){
+      off = rec.left;
+    } else if(hash < rec.hash){
+      off = rec.right;
+    } else {
+      if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return NULL;
+      int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
+      if(kcmp > 0){
+        off = rec.left;
+        free(rec.bbuf);
+        rec.kbuf = NULL;
+        rec.bbuf = NULL;
+      } else if(kcmp < 0){
+        off = rec.right;
+        free(rec.bbuf);
+        rec.kbuf = NULL;
+        rec.bbuf = NULL;
+      } else {
+        if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return NULL;
+        if(hdb->zmode){
+          int zsiz;
+          char *zbuf;
+          if(hdb->opts & HDBTDEFLATE){
+            zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
+          } else {
+            zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
+          }
+          free(rec.bbuf);
+          if(!zbuf){
+            tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+            return NULL;
+          }
+          if(hdb->recc){
+            if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+            tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz);
+          }
+          *sp = zsiz;
+          return zbuf;
+        }
+        if(rec.bbuf){
+          memmove(rec.bbuf, rec.vbuf, rec.vsiz);
+          rec.bbuf[rec.vsiz] = '\0';
+          *sp = rec.vsiz;
+          return rec.bbuf;
+        }
+        if(hdb->recc){
+          if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+          tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz);
+        }
+        *sp = rec.vsiz;
+        char *rv;
+        TCMEMDUP(rv, rec.vbuf, rec.vsiz);
+        return rv;
+      }
+    }
+  }
+  if(hdb->recc){
+    if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+    tcmdbput(hdb->recc, kbuf, ksiz, "*", 1);
+  }
+  tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+  return NULL;
+}
+
+
+/* Retrieve a record in a hash database object and write the value into a buffer.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the buffer into which the value of the corresponding record is
+   written.
+   `max' specifies the size of the buffer.
+   If successful, the return value is the size of the written data, else, it is -1. */
+static int tchdbgetintobuf(TCHDB *hdb, const char *kbuf, int ksiz, char *vbuf, int max){
+  assert(hdb && kbuf && ksiz >= 0 && vbuf && max >= 0);
+  if(hdb->recc){
+    int tvsiz;
+    char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz);
+    if(tvbuf){
+      if(*tvbuf == '*'){
+        tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+        free(tvbuf);
+        return -1;
+      }
+      tvsiz = tclmin(tvsiz - 1, max);
+      memcpy(vbuf, tvbuf + 1, tvsiz);
+      free(tvbuf);
+      return tvsiz;
+    }
+  }
+  uint8_t hash;
+  uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+  off_t off = tchdbgetbucket(hdb, bidx);
+  TCHREC rec;
+  char rbuf[HDBIOBUFSIZ];
+  while(off > 0){
+    rec.off = off;
+    if(!tchdbreadrec(hdb, &rec, rbuf)) return -1;
+    if(hash > rec.hash){
+      off = rec.left;
+    } else if(hash < rec.hash){
+      off = rec.right;
+    } else {
+      if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
+      int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
+      if(kcmp > 0){
+        off = rec.left;
+        free(rec.bbuf);
+        rec.kbuf = NULL;
+        rec.bbuf = NULL;
+      } else if(kcmp < 0){
+        off = rec.right;
+        free(rec.bbuf);
+        rec.kbuf = NULL;
+        rec.bbuf = NULL;
+      } else {
+        if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
+        if(hdb->zmode){
+          int zsiz;
+          char *zbuf;
+          if(hdb->opts & HDBTDEFLATE){
+            zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
+          } else {
+            zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
+          }
+          free(rec.bbuf);
+          if(!zbuf){
+            tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+            return -1;
+          }
+          if(hdb->recc){
+            if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+            tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz);
+          }
+          zsiz = tclmin(zsiz, max);
+          memcpy(vbuf, zbuf, zsiz);
+          free(zbuf);
+          return zsiz;
+        }
+        if(hdb->recc){
+          if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+          tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz);
+        }
+        int vsiz = tclmin(rec.vsiz, max);
+        memcpy(vbuf, rec.vbuf, vsiz);
+        free(rec.bbuf);
+        return vsiz;
+      }
+    }
+  }
+  if(hdb->recc){
+    if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+    tcmdbput(hdb->recc, kbuf, ksiz, "*", 1);
+  }
+  tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+  return -1;
+}
+
+
+/* Get the size of the value of a record in a hash database object.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1. */
+static int tchdbvsizimpl(TCHDB *hdb, const char *kbuf, int ksiz){
+  assert(hdb && kbuf && ksiz >= 0);
+  if(hdb->recc){
+    int tvsiz;
+    char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz);
+    if(tvbuf){
+      if(*tvbuf == '*'){
+        tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+        free(tvbuf);
+        return -1;
+      }
+      free(tvbuf);
+      return tvsiz - 1;
+    }
+  }
+  uint8_t hash;
+  uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+  off_t off = tchdbgetbucket(hdb, bidx);
+  TCHREC rec;
+  char rbuf[HDBIOBUFSIZ];
+  while(off > 0){
+    rec.off = off;
+    if(!tchdbreadrec(hdb, &rec, rbuf)) return -1;
+    if(hash > rec.hash){
+      off = rec.left;
+    } else if(hash < rec.hash){
+      off = rec.right;
+    } else {
+      if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
+      int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
+      if(kcmp > 0){
+        off = rec.left;
+        free(rec.bbuf);
+        rec.kbuf = NULL;
+        rec.bbuf = NULL;
+      } else if(kcmp < 0){
+        off = rec.right;
+        free(rec.bbuf);
+        rec.kbuf = NULL;
+        rec.bbuf = NULL;
+      } else {
+        if(hdb->zmode){
+          if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
+          int zsiz;
+          char *zbuf;
+          if(hdb->opts & HDBTDEFLATE){
+            zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
+          } else {
+            zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
+          }
+          free(rec.bbuf);
+          if(!zbuf){
+            tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+            return -1;
+          }
+          if(hdb->recc){
+            if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+            tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz);
+          }
+          free(zbuf);
+          return zsiz;
+        }
+        if(hdb->recc && rec.vbuf){
+          if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+          tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz);
+        }
+        free(rec.bbuf);
+        return rec.vsiz;
+      }
+    }
+  }
+  if(hdb->recc){
+    if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+    tcmdbput(hdb->recc, kbuf, ksiz, "*", 1);
+  }
+  tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+  return -1;
+}
+
+
+/* Initialize the iterator of a hash database object.
+   `hdb' specifies the hash database object.
+   If successful, the return value is true, else, it is false. */
+static bool tchdbiterinitimpl(TCHDB *hdb){
+  assert(hdb);
+  hdb->iter = hdb->frec;
+  return true;
+}
+
+
+/* Get the next key of the iterator of a hash database object.
+   `hdb' specifies the hash database object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the next key, else, it is
+   `NULL'. */
+static char *tchdbiternextimpl(TCHDB *hdb, int *sp){
+  assert(hdb && sp);
+  TCHREC rec;
+  char rbuf[HDBIOBUFSIZ];
+  while(hdb->iter < hdb->fsiz){
+    rec.off = hdb->iter;
+    if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL;
+    hdb->iter += rec.rsiz;
+    if(rec.magic == HDBMAGICREC){
+      if(rec.kbuf){
+        *sp = rec.ksiz;
+        char *rv;
+        TCMEMDUP(rv, rec.kbuf, rec.ksiz);
+        return rv;
+      }
+      if(!tchdbreadrecbody(hdb, &rec)) return NULL;
+      rec.bbuf[rec.ksiz] = '\0';
+      *sp = rec.ksiz;
+      return rec.bbuf;
+    }
+  }
+  tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+  return NULL;
+}
+
+
+/* Get the next extensible objects of the iterator of a hash database object. */
+static bool tchdbiternextintoxstr(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr){
+  assert(hdb && kxstr && vxstr);
+  TCHREC rec;
+  char rbuf[HDBIOBUFSIZ];
+  while(hdb->iter < hdb->fsiz){
+    rec.off = hdb->iter;
+    if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
+    hdb->iter += rec.rsiz;
+    if(rec.magic == HDBMAGICREC){
+      if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return false;
+      tcxstrclear(kxstr);
+      TCXSTRCAT(kxstr, rec.kbuf, rec.ksiz);
+      tcxstrclear(vxstr);
+      if(hdb->zmode){
+        int zsiz;
+        char *zbuf;
+        if(hdb->opts & HDBTDEFLATE){
+          zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
+        } else {
+          zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
+        }
+        if(!zbuf){
+          tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+          free(rec.bbuf);
+          return false;
+        }
+        TCXSTRCAT(vxstr, zbuf, zsiz);
+        free(zbuf);
+      } else {
+        TCXSTRCAT(vxstr, rec.vbuf, rec.vsiz);
+      }
+      free(rec.bbuf);
+      return true;
+    }
+  }
+  tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+  return false;
+}
+
+
+/* Optimize the file of a hash database object.
+   `hdb' specifies the hash database object.
+   `bnum' specifies the number of elements of the bucket array.
+   `apow' specifies the size of record alignment by power of 2.
+   `fpow' specifies the maximum number of elements of the free block pool by power of 2.
+   `opts' specifies options by bitwise or.
+   If successful, the return value is true, else, it is false. */
+static bool tchdboptimizeimpl(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+  assert(hdb);
+  if(bnum < 1){
+    bnum = hdb->rnum * 2 + 1;
+    if(bnum < HDBDEFBNUM) bnum = HDBDEFBNUM;
+  }
+  if(apow < 0) apow = hdb->apow;
+  if(fpow < 0) fpow = hdb->fpow;
+  if(opts == UINT8_MAX) opts = hdb->opts;
+  char *tpath = tcsprintf("%s%ctmp%c%llu", hdb->path, MYEXTCHR, MYEXTCHR, hdb->inode);
+  TCHDB *thdb = tchdbnew();
+  tchdbtune(thdb, bnum, apow, fpow, opts);
+  if(!tchdbopen(thdb, tpath, HDBOWRITER | HDBOCREAT | HDBOTRUNC)){
+    tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__);
+    tchdbdel(thdb);
+    free(tpath);
+    return false;
+  }
+  bool err = false;
+  if(!tchdbiterinitimpl(hdb)) err = true;
+  TCXSTR *key = tcxstrnew();
+  TCXSTR *val = tcxstrnew();
+  while(!err && tchdbiternextintoxstr(hdb, key, val)){
+    if(!tchdbputkeep(thdb, TCXSTRPTR(key), TCXSTRSIZE(key), TCXSTRPTR(val), TCXSTRSIZE(val))){
+      tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__);
+      err = true;
+    }
+  }
+  tcxstrdel(val);
+  tcxstrdel(key);
+  if(!tchdbclose(thdb)){
+    tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__);
+    err = true;
+  }
+  tchdbdel(thdb);
+  if(unlink(hdb->path) == -1){
+    tchdbsetecode(hdb, TCEUNLINK, __FILE__, __LINE__, __func__);
+    err = true;
+  }
+  if(rename(tpath, hdb->path) == -1){
+    tchdbsetecode(hdb, TCERENAME, __FILE__, __LINE__, __func__);
+    err = true;
+  }
+  free(tpath);
+  if(err) return false;
+  tpath = tcstrdup(hdb->path);
+  int omode = (hdb->omode & ~HDBOCREAT) & ~HDBOTRUNC;
+  if(!tchdbcloseimpl(hdb)){
+    free(tpath);
+    return false;
+  }
+  bool rv = tchdbopenimpl(hdb, tpath, omode);
+  free(tpath);
+  return rv;
+}
+
+
+/* Remove all records of a hash database object.
+   `hdb' specifies the hash database object.
+   If successful, the return value is true, else, it is false. */
+static bool tchdbvanishimpl(TCHDB *hdb){
+  assert(hdb);
+  char *path = tcstrdup(hdb->path);
+  int omode = hdb->omode;
+  bool err = false;
+  if(!tchdbcloseimpl(hdb)) err = true;
+  if(!tchdbopenimpl(hdb, path, HDBOTRUNC | omode)) err = true;
+  free(path);
+  return !err;
+}
+
+
+/* Lock a method of the hash database object.
+   `hdb' specifies the hash database object.
+   `wr' specifies whether the lock is writer or not.
+   If successful, the return value is true, else, it is false. */
+static bool tchdblockmethod(TCHDB *hdb, bool wr){
+  assert(hdb);
+  if(wr ? pthread_rwlock_wrlock(hdb->mmtx) != 0 : pthread_rwlock_rdlock(hdb->mmtx) != 0){
+    tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  TCTESTYIELD();
+  return true;
+}
+
+
+/* Unlock a method of the hash database object.
+   `hdb' specifies the hash database object.
+   If successful, the return value is true, else, it is false. */
+static bool tchdbunlockmethod(TCHDB *hdb){
+  assert(hdb);
+  if(pthread_rwlock_unlock(hdb->mmtx) != 0){
+    tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  TCTESTYIELD();
+  return true;
+}
+
+
+/* Lock the delayed record pool of the hash database object.
+   `hdb' specifies the hash database object.
+   If successful, the return value is true, else, it is false. */
+static bool tchdblockdrp(TCHDB *hdb){
+  assert(hdb);
+  if(pthread_mutex_lock(hdb->dmtx) != 0){
+    tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  TCTESTYIELD();
+  return true;
+}
+
+
+/* Unlock the delayed record pool of the hash database object.
+   `hdb' specifies the hash database object.
+   If successful, the return value is true, else, it is false. */
+static bool tchdbunlockdrp(TCHDB *hdb){
+  assert(hdb);
+  if(pthread_mutex_unlock(hdb->dmtx) != 0){
+    tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
+    return false;
+  }
+  TCTESTYIELD();
+  return true;
+}
+
+
+
+/*************************************************************************************************
+ * debugging functions
+ *************************************************************************************************/
+
+
+/* Print meta data of the header into the debugging output.
+   `hdb' specifies the hash database object. */
+void tchdbprintmeta(TCHDB *hdb){
+  assert(hdb);
+  if(hdb->dbgfd < 0) return;
+  char buf[HDBIOBUFSIZ];
+  char *wp = buf;
+  wp += sprintf(wp, "META:");
+  wp += sprintf(wp, " mmtx=%p", (void *)hdb->mmtx);
+  wp += sprintf(wp, " dmtx=%p", (void *)hdb->dmtx);
+  wp += sprintf(wp, " eckey=%p", (void *)hdb->eckey);
+  wp += sprintf(wp, " type=%02X", hdb->type);
+  wp += sprintf(wp, " flags=%02X", hdb->flags);
+  wp += sprintf(wp, " bnum=%llu", (unsigned long long)hdb->bnum);
+  wp += sprintf(wp, " apow=%u", hdb->apow);
+  wp += sprintf(wp, " fpow=%u", hdb->fpow);
+  wp += sprintf(wp, " opts=%u", hdb->opts);
+  wp += sprintf(wp, " path=%s", hdb->path ? hdb->path : "-");
+  wp += sprintf(wp, " fd=%u", hdb->fd);
+  wp += sprintf(wp, " omode=%u", hdb->omode);
+  wp += sprintf(wp, " rnum=%llu", (unsigned long long)hdb->rnum);
+  wp += sprintf(wp, " fsiz=%llu", (unsigned long long)hdb->fsiz);
+  wp += sprintf(wp, " frec=%llu", (unsigned long long)hdb->frec);
+  wp += sprintf(wp, " iter=%llu", (unsigned long long)hdb->iter);
+  wp += sprintf(wp, " map=%p", (void *)hdb->map);
+  wp += sprintf(wp, " msiz=%llu", (unsigned long long)hdb->msiz);
+  wp += sprintf(wp, " ba32=%p", (void *)hdb->ba32);
+  wp += sprintf(wp, " ba64=%p", (void *)hdb->ba64);
+  wp += sprintf(wp, " align=%u", hdb->align);
+  wp += sprintf(wp, " runit=%u", hdb->runit);
+  wp += sprintf(wp, " zmode=%u", hdb->zmode);
+  wp += sprintf(wp, " fbpmax=%d", hdb->fbpmax);
+  wp += sprintf(wp, " fbpsiz=%d", hdb->fbpsiz);
+  wp += sprintf(wp, " fbpool=%p", (void *)hdb->fbpool);
+  wp += sprintf(wp, " fbpnum=%d", hdb->fbpnum);
+  wp += sprintf(wp, " fbpmis=%d", hdb->fbpmis);
+  wp += sprintf(wp, " drpool=%p", (void *)hdb->drpool);
+  wp += sprintf(wp, " drpdef=%p", (void *)hdb->drpdef);
+  wp += sprintf(wp, " drpoff=%llu", (unsigned long long)hdb->drpoff);
+  wp += sprintf(wp, " recc=%p", (void *)hdb->recc);
+  wp += sprintf(wp, " rcnum=%u", hdb->rcnum);
+  wp += sprintf(wp, " ecode=%d", hdb->ecode);
+  wp += sprintf(wp, " fatal=%u", hdb->fatal);
+  wp += sprintf(wp, " inode=%llu", (unsigned long long)(uint64_t)hdb->inode);
+  wp += sprintf(wp, " mtime=%llu", (unsigned long long)(uint64_t)hdb->mtime);
+  wp += sprintf(wp, " dbgfd=%d", hdb->dbgfd);
+  wp += sprintf(wp, " cnt_writerec=%lld", (long long)hdb->cnt_writerec);
+  wp += sprintf(wp, " cnt_reuserec=%lld", (long long)hdb->cnt_reuserec);
+  wp += sprintf(wp, " cnt_moverec=%lld", (long long)hdb->cnt_moverec);
+  wp += sprintf(wp, " cnt_readrec=%lld", (long long)hdb->cnt_readrec);
+  wp += sprintf(wp, " cnt_searchfbp=%lld", (long long)hdb->cnt_searchfbp);
+  wp += sprintf(wp, " cnt_insertfbp=%lld", (long long)hdb->cnt_insertfbp);
+  wp += sprintf(wp, " cnt_splicefbp=%lld", (long long)hdb->cnt_splicefbp);
+  wp += sprintf(wp, " cnt_dividefbp=%lld", (long long)hdb->cnt_dividefbp);
+  wp += sprintf(wp, " cnt_mergefbp=%lld", (long long)hdb->cnt_mergefbp);
+  wp += sprintf(wp, " cnt_reducefbp=%lld", (long long)hdb->cnt_reducefbp);
+  wp += sprintf(wp, " cnt_appenddrp=%lld", (long long)hdb->cnt_appenddrp);
+  wp += sprintf(wp, " cnt_deferdrp=%lld", (long long)hdb->cnt_deferdrp);
+  wp += sprintf(wp, " cnt_flushdrp=%lld", (long long)hdb->cnt_flushdrp);
+  wp += sprintf(wp, " cnt_adjrecc=%lld", (long long)hdb->cnt_adjrecc);
+  *(wp++) = '\n';
+  tcwrite(hdb->dbgfd, buf, wp - buf);
+}
+
+
+/* Print a record information into the debugging output.
+   `hdb' specifies the hash database object.
+   `rec' specifies the record. */
+void tchdbprintrec(TCHDB *hdb, TCHREC *rec){
+  assert(hdb && rec);
+  if(hdb->dbgfd < 0) return;
+  char buf[HDBIOBUFSIZ];
+  char *wp = buf;
+  wp += sprintf(wp, "REC:");
+  wp += sprintf(wp, " off=%llu", (unsigned long long)rec->off);
+  wp += sprintf(wp, " rsiz=%u", rec->rsiz);
+  wp += sprintf(wp, " magic=%02X", rec->magic);
+  wp += sprintf(wp, " hash=%02X", rec->hash);
+  wp += sprintf(wp, " left=%llu", (unsigned long long)rec->left);
+  wp += sprintf(wp, " right=%llu", (unsigned long long)rec->right);
+  wp += sprintf(wp, " ksiz=%u", rec->ksiz);
+  wp += sprintf(wp, " vsiz=%u", rec->vsiz);
+  wp += sprintf(wp, " psiz=%u", rec->psiz);
+  wp += sprintf(wp, " kbuf=%p", (void *)rec->kbuf);
+  wp += sprintf(wp, " vbuf=%p", (void *)rec->vbuf);
+  wp += sprintf(wp, " boff=%llu", (unsigned long long)rec->boff);
+  wp += sprintf(wp, " bbuf=%p", (void *)rec->bbuf);
+  *(wp++) = '\n';
+  tcwrite(hdb->dbgfd, buf, wp - buf);
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tchdb.h b/bacula/src/lib/tokyocabinet/tchdb.h
new file mode 100644 (file)
index 0000000..9a20762
--- /dev/null
@@ -0,0 +1,649 @@
+/*************************************************************************************************
+ * The hash database API of Tokyo Cabinet
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _TCHDB_H                         /* duplication check */
+#define _TCHDB_H
+
+#if defined(__cplusplus)
+#define __TCHDB_CLINKAGEBEGIN extern "C" {
+#define __TCHDB_CLINKAGEEND }
+#else
+#define __TCHDB_CLINKAGEBEGIN
+#define __TCHDB_CLINKAGEEND
+#endif
+__TCHDB_CLINKAGEBEGIN
+
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <tcutil.h>
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of structure for a hash database */
+  void *mmtx;                            /* mutex for method */
+  void *dmtx;                            /* mutex for DRP */
+  void *eckey;                           /* key for thread specific error code */
+  uint8_t type;                          /* database type */
+  uint8_t flags;                         /* additional flags */
+  uint64_t bnum;                         /* number of the bucket array */
+  uint8_t apow;                          /* power of record alignment */
+  uint8_t fpow;                          /* power of free block pool number */
+  uint8_t opts;                          /* options */
+  char *path;                            /* path of the database file */
+  int fd;                                /* file descriptor of the database file */
+  uint32_t omode;                        /* connection mode */
+  uint64_t rnum;                         /* number of the records */
+  uint64_t fsiz;                         /* size of the database file */
+  uint64_t frec;                         /* offset of the first record */
+  uint64_t lrec;                         /* offset of the last record */
+  uint64_t iter;                         /* offset of the iterator */
+  char *map;                             /* pointer to the mapped memory */
+  uint64_t msiz;                         /* size of the mapped memory */
+  uint32_t *ba32;                        /* 32-bit bucket array */
+  uint64_t *ba64;                        /* 64-bit bucket array */
+  uint32_t align;                        /* record alignment */
+  uint32_t runit;                        /* record reading unit */
+  bool zmode;                            /* whether compression is used */
+  int32_t fbpmax;                        /* maximum number of the free block pool */
+  int32_t fbpsiz;                        /* size of the free block pool */
+  void *fbpool;                          /* free block pool */
+  int32_t fbpnum;                        /* number of the free block pool */
+  int32_t fbpmis;                        /* number of missing retrieval of the free block pool */
+  bool async;                            /* whether asynchronous storing is called */
+  TCXSTR *drpool;                        /* delayed record pool */
+  TCXSTR *drpdef;                        /* deferred records of the delayed record pool */
+  uint64_t drpoff;                       /* offset of the delayed record pool */
+  TCMDB *recc;                           /* cache for records */
+  uint32_t rcnum;                        /* max number of cached records */
+  int ecode;                             /* last happened error code */
+  bool fatal;                            /* whether a fatal error occured */
+  uint64_t inode;                        /* inode number */
+  time_t mtime;                          /* modification time */
+  int dbgfd;                             /* file descriptor for debugging */
+  int64_t cnt_writerec;                  /* tesing counter for record write times */
+  int64_t cnt_reuserec;                  /* tesing counter for record reuse times */
+  int64_t cnt_moverec;                   /* tesing counter for record move times */
+  int64_t cnt_readrec;                   /* tesing counter for record read times */
+  int64_t cnt_searchfbp;                 /* tesing counter for FBP search times */
+  int64_t cnt_insertfbp;                 /* tesing counter for FBP insert times */
+  int64_t cnt_splicefbp;                 /* tesing counter for FBP splice times */
+  int64_t cnt_dividefbp;                 /* tesing counter for FBP divide times */
+  int64_t cnt_mergefbp;                  /* tesing counter for FBP merge times */
+  int64_t cnt_reducefbp;                 /* tesing counter for FBP reduce times */
+  int64_t cnt_appenddrp;                 /* tesing counter for DRP append times */
+  int64_t cnt_deferdrp;                  /* tesing counter for DRP defer times */
+  int64_t cnt_flushdrp;                  /* tesing counter for DRP flush times */
+  int64_t cnt_adjrecc;                   /* tesing counter for record cache adjust times */
+} TCHDB;
+
+enum {                                   /* enumeration for error codes */
+  TCESUCCESS,                            /* success */
+  TCETHREAD,                             /* threading error */
+  TCEINVALID,                            /* invalid operation */
+  TCENOFILE,                             /* file not found */
+  TCENOPERM,                             /* no permission */
+  TCEMETA,                               /* invalid meta data */
+  TCERHEAD,                              /* invalid record header */
+  TCEOPEN,                               /* open error */
+  TCECLOSE,                              /* close error */
+  TCETRUNC,                              /* trunc error */
+  TCESYNC,                               /* sync error */
+  TCESTAT,                               /* stat error */
+  TCESEEK,                               /* seek error */
+  TCEREAD,                               /* read error */
+  TCEWRITE,                              /* write error */
+  TCEMMAP,                               /* mmap error */
+  TCELOCK,                               /* lock error */
+  TCEUNLINK,                             /* unlink error */
+  TCERENAME,                             /* rename error */
+  TCEMKDIR,                              /* mkdir error */
+  TCERMDIR,                              /* rmdir error */
+  TCEKEEP,                               /* existing record */
+  TCENOREC,                              /* no record found */
+  TCEMISC = 9999                         /* miscellaneous error */
+};
+
+enum {                                   /* enumeration for database type */
+  HDBTHASH,                              /* hash table */
+  HDBTBTREE                              /* B+ tree */
+};
+
+enum {                                   /* enumeration for additional flags */
+  HDBFOPEN = 1 << 0,                     /* whether opened */
+  HDBFFATAL = 1 << 1                     /* whetehr with fatal error */
+};
+
+enum {                                   /* enumeration for tuning options */
+  HDBTLARGE = 1 << 0,                    /* use 64-bit bucket array */
+  HDBTDEFLATE = 1 << 1,                  /* compress each record with Deflate */
+  HDBTTCBS = 1 << 2                      /* compress each record with TCBS */
+};
+
+enum {                                   /* enumeration for open modes */
+  HDBOREADER = 1 << 0,                   /* open as a reader */
+  HDBOWRITER = 1 << 1,                   /* open as a writer */
+  HDBOCREAT = 1 << 2,                    /* writer creating */
+  HDBOTRUNC = 1 << 3,                    /* writer truncating */
+  HDBONOLCK = 1 << 4,                    /* open without locking */
+  HDBOLCKNB = 1 << 5                     /* lock without blocking */
+};
+
+
+/* Get the message string corresponding to an error code.
+   `ecode' specifies the error code.
+   The return value is the message string of the error code. */
+const char *tchdberrmsg(int ecode);
+
+
+/* Create a hash database object.
+   The return value is the new hash database object. */
+TCHDB *tchdbnew(void);
+
+
+/* Delete a hash database object.
+   `hdb' specifies the hash database object.
+   If the database is not closed, it is closed implicitly.  Note that the deleted object and its
+   derivatives can not be used anymore. */
+void tchdbdel(TCHDB *hdb);
+
+
+/* Get the last happened error code of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the last happened error code.
+   The following error code is defined: `TCESUCCESS' for success, `TCETHREAD' for threading
+   error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no
+   permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN'
+   for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync
+   error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error,
+   `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK'
+   for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for
+   rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for
+   miscellaneous error. */
+int tchdbecode(TCHDB *hdb);
+
+
+/* Set mutual exclusion control of a hash database object for threading.
+   `hdb' specifies the hash database object which is not opened.
+   If successful, the return value is true, else, it is false.
+   Note that the mutual exclusion control is needed if the object is shared by plural threads and
+   this function should should be called before the database is opened. */
+bool tchdbsetmutex(TCHDB *hdb);
+
+
+/* Set the tuning parameters of a hash database object.
+   `hdb' specifies the hash database object which is not opened.
+   `bnum' specifies the number of elements of the bucket array.  If it is not more than 0, the
+   default value is specified.  The default value is 16381.  Suggested size of the bucket array
+   is about from 0.5 to 4 times of the number of all records to be stored.
+   `apow' specifies the size of record alignment by power of 2.  If it is negative, the default
+   value is specified.  The default value is 4 standing for 2^4=16.
+   `fpow' specifies the maximum number of elements of the free block pool by power of 2.  If it
+   is negative, the default value is specified.  The default value is 10 standing for 2^10=1024.
+   `opts' specifies options by bitwise or: `HDBTLARGE' specifies that the size of the database
+   can be larger than 2GB by using 64-bit bucket array, `HDBTDEFLATE' specifies that each record
+   is compressed with Deflate encoding, `HDBTTCBS' specifies that each record is compressed with
+   TCBS encoding.
+   If successful, the return value is true, else, it is false.
+   Note that the tuning parameters should be set before the database is opened. */
+bool tchdbtune(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
+
+/* Set the caching parameters of a hash database object.
+   `hdb' specifies the hash database object which is not opened.
+   `rcnum' specifies the maximum number of records to be cached.  If it is not more than 0, the
+   record cache is disabled.  It is disabled by default.
+   If successful, the return value is true, else, it is false.
+   Note that the caching parameters should be set before the database is opened. */
+bool tchdbsetcache(TCHDB *hdb, int32_t rcnum);
+
+
+/* Open a database file and connect a hash database object.
+   `hdb' specifies the hash database object which is not opened.
+   `path' specifies the path of the database file.
+   `omode' specifies the connection mode: `HDBOWRITER' as a writer, `HDBOREADER' as a reader.
+   If the mode is `HDBOWRITER', the following may be added by bitwise or: `HDBOCREAT', which
+   means it creates a new database if not exist, `HDBOTRUNC', which means it creates a new
+   database regardless if one exists.  Both of `HDBOREADER' and `HDBOWRITER' can be added to by
+   bitwise or: `HDBONOLCK', which means it opens the database file without file locking, or
+   `HDBOLCKNB', which means locking is performed without blocking.
+   If successful, the return value is true, else, it is false. */
+bool tchdbopen(TCHDB *hdb, const char *path, int omode);
+
+
+/* Close a hash database object.
+   `hdb' specifies the hash database object.
+   If successful, the return value is true, else, it is false.
+   Update of a database is assured to be written when the database is closed.  If a writer opens
+   a database but does not close it appropriately, the database will be broken. */
+bool tchdbclose(TCHDB *hdb);
+
+
+/* Store a record into a hash database object.
+   `hdb' specifies the hash database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, it is overwritten. */
+bool tchdbput(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into a hash database object.
+   `hdb' specifies the hash database object connected as a writer.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, it is overwritten. */
+bool tchdbput2(TCHDB *hdb, const char *kstr, const char *vstr);
+
+
+/* Store a new record into a hash database object.
+   `hdb' specifies the hash database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, this function has no effect. */
+bool tchdbputkeep(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into a hash database object.
+   `hdb' specifies the hash database object connected as a writer.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, this function has no effect. */
+bool tchdbputkeep2(TCHDB *hdb, const char *kstr, const char *vstr);
+
+
+/* Concatenate a value at the end of the existing record in a hash database object.
+   `hdb' specifies the hash database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If there is no corresponding record, a new record is created. */
+bool tchdbputcat(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Concatenate a string value at the end of the existing record in a hash database object.
+   `hdb' specifies the hash database object connected as a writer.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If there is no corresponding record, a new record is created. */
+bool tchdbputcat2(TCHDB *hdb, const char *kstr, const char *vstr);
+
+
+/* Store a record into a hash database object in asynchronous fashion.
+   `hdb' specifies the hash database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, it is overwritten.  Records passed to
+   this function are accumulated into the inner buffer and wrote into the file at a blast. */
+bool tchdbputasync(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into a hash database object in asynchronous fashion.
+   `hdb' specifies the hash database object connected as a writer.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, it is overwritten.  Records passed to
+   this function are accumulated into the inner buffer and wrote into the file at a blast. */
+bool tchdbputasync2(TCHDB *hdb, const char *kstr, const char *vstr);
+
+
+/* Remove a record of a hash database object.
+   `hdb' specifies the hash database object connected as a writer.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true, else, it is false. */
+bool tchdbout(TCHDB *hdb, const void *kbuf, int ksiz);
+
+
+/* Remove a string record of a hash database object.
+   `hdb' specifies the hash database object connected as a writer.
+   `kstr' specifies the string of the key.
+   If successful, the return value is true, else, it is false. */
+bool tchdbout2(TCHDB *hdb, const char *kstr);
+
+
+/* Retrieve a record in a hash database object.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the corresponding
+   record.  `NULL' is returned if no record corresponds.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when
+   it is no longer in use. */
+void *tchdbget(TCHDB *hdb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve a string record in a hash database object.
+   `hdb' specifies the hash database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is the string of the value of the corresponding record.
+   `NULL' is returned if no record corresponds.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tchdbget2(TCHDB *hdb, const char *kstr);
+
+
+/* Retrieve a record in a hash database object and write the value into a buffer.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the buffer into which the value of the corresponding record is
+   written.
+   `max' specifies the size of the buffer.
+   If successful, the return value is the size of the written data, else, it is -1.  -1 is
+   returned if no record corresponds to the specified key.
+   Note that an additional zero code is not appended at the end of the region of the writing
+   buffer. */
+int tchdbget3(TCHDB *hdb, const void *kbuf, int ksiz, void *vbuf, int max);
+
+
+/* Get the size of the value of a record in a hash database object.
+   `hdb' specifies the hash database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1. */
+int tchdbvsiz(TCHDB *hdb, const void *kbuf, int ksiz);
+
+
+/* Get the size of the value of a string record in a hash database object.
+   `hdb' specifies the hash database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1. */
+int tchdbvsiz2(TCHDB *hdb, const char *kstr);
+
+
+/* Initialize the iterator of a hash database object.
+   `hdb' specifies the hash database object.
+   If successful, the return value is true, else, it is false.
+   The iterator is used in order to access the key of every record stored in a database. */
+bool tchdbiterinit(TCHDB *hdb);
+
+
+/* Get the next key of the iterator of a hash database object.
+   `hdb' specifies the hash database object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the next key, else, it is
+   `NULL'.  `NULL' is returned when no record is to be get out of the iterator.
+   Because an additional zero code is appended at the end of the region of the return value, the
+   return value can be treated as a character string.  Because the region of the return value is
+   allocated with the `malloc' call, it should be released with the `free' call when it is no
+   longer in use.  It is possible to access every record by iteration of calling this function.
+   It is allowed to update or remove records whose keys are fetched while the iteration.
+   However, it is not assured if updating the database is occurred while the iteration.  Besides,
+   the order of this traversal access method is arbitrary, so it is not assured that the order of
+   storing matches the one of the traversal access. */
+void *tchdbiternext(TCHDB *hdb, int *sp);
+
+
+/* Get the next key string of the iterator of a hash database object.
+   `hdb' specifies the hash database object.
+   If successful, the return value is the string of the next key, else, it is `NULL'.  `NULL' is
+   returned when no record is to be get out of the iterator.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use.  It is possible to access every
+   record by iteration of calling this function.  However, it is not assured if updating the
+   database is occurred while the iteration.  Besides, the order of this traversal access method
+   is arbitrary, so it is not assured that the order of storing matches the one of the traversal
+   access. */
+char *tchdbiternext2(TCHDB *hdb);
+
+
+/* Get the next extensible objects of the iterator of a hash database object.
+   `hdb' specifies the hash database object.
+   `kxstr' specifies the object into which the next key is wrote down.
+   `vxstr' specifies the object into which the next value is wrote down.
+   If successful, the return value is true, else, it is false.  False is returned when no record
+   is to be get out of the iterator. */
+bool tchdbiternext3(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr);
+
+
+/* Get forward matching keys in a hash database object.
+   `hdb' specifies the hash database object.
+   `pbuf' specifies the pointer to the region of the prefix.
+   `psiz' specifies the size of the region of the prefix.
+   `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
+   specified.
+   The return value is a list object of the corresponding keys.  This function does never fail
+   and return an empty list even if no key corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should be
+   deleted with the function `tclistdel' when it is no longer in use.  Note that this function
+   may be very slow because every key in the database is scanned. */
+TCLIST *tchdbfwmkeys(TCHDB *hdb, const void *pbuf, int psiz, int max);
+
+
+/* Get forward matching string keys in a hash database object.
+   `hdb' specifies the hash database object.
+   `pstr' specifies the string of the prefix.
+   `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
+   specified.
+   The return value is a list object of the corresponding keys.  This function does never fail
+   and return an empty list even if no key corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should be
+   deleted with the function `tclistdel' when it is no longer in use.  Note that this function
+   may be very slow because every key in the database is scanned. */
+TCLIST *tchdbfwmkeys2(TCHDB *hdb, const char *pstr, int max);
+
+
+/* Synchronize updated contents of a hash database object with the file and the device.
+   `hdb' specifies the hash database object connected as a writer.
+   If successful, the return value is true, else, it is false.
+   This function is useful when another process connects the same database file. */
+bool tchdbsync(TCHDB *hdb);
+
+
+/* Optimize the file of a hash database object.
+   `hdb' specifies the hash database object connected as a writer.
+   `bnum' specifies the number of elements of the bucket array.  If it is not more than 0, the
+   default value is specified.  The default value is two times of the number of records.
+   `apow' specifies the size of record alignment by power of 2.  If it is negative, the current
+   setting is not changed.
+   `fpow' specifies the maximum number of elements of the free block pool by power of 2.  If it
+   is negative, the current setting is not changed.
+   `opts' specifies options by bitwise or: `HDBTLARGE' specifies that the size of the database
+   can be larger than 2GB by using 64-bit bucket array, `HDBTDEFLATE' specifies that each record
+   is compressed with Deflate encoding, `HDBTTCBS' specifies that each record is compressed with
+   TCBS encoding.  If it is `UINT8_MAX', the current setting is not changed.
+   If successful, the return value is true, else, it is false.
+   This function is useful to reduce the size of the database file with data fragmentation by
+   successive updating. */
+bool tchdboptimize(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
+
+/* Remove all records of a hash database object.
+   `hdb' specifies the hash database object connected as a writer.
+   If successful, the return value is true, else, it is false. */
+bool tchdbvanish(TCHDB *hdb);
+
+
+/* Copy the database file of a hash database object.
+   `hdb' specifies the hash database object.
+   `path' specifies the path of the destination file.  If it begins with `@', the trailing
+   substring is executed as a command line.
+   If successful, the return value is true, else, it is false.  False is returned if the executed
+   command returns non-zero code.
+   The database file is assured to be kept synchronized and not modified while the copying or
+   executing operation is in progress.  So, this function is useful to create a backup file of
+   the database file. */
+bool tchdbcopy(TCHDB *hdb, const char *path);
+
+
+/* Get the file path of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the path of the database file or `NULL' if the object does not connect to
+   any database file. */
+const char *tchdbpath(TCHDB *hdb);
+
+
+/* Get the number of records of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the number of records or 0 if the object does not connect to any database
+   file. */
+uint64_t tchdbrnum(TCHDB *hdb);
+
+
+/* Get the size of the database file of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the size of the database file or 0 if the object does not connect to any
+   database file. */
+uint64_t tchdbfsiz(TCHDB *hdb);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Set the error code of a hash database object.
+   `hdb' specifies the hash database object.
+   `ecode' specifies the error code.
+   `file' specifies the file name of the code.
+   `line' specifies the line number of the code.
+   `func' specifies the function name of the code. */
+void tchdbsetecode(TCHDB *hdb, int ecode, const char *filename, int line, const char *func);
+
+
+/* Set the type of a hash database object.
+   `hdb' specifies the hash database object.
+   `type' specifies the database type. */
+void tchdbsettype(TCHDB *hdb, uint8_t type);
+
+
+/* Set the file descriptor for debugging output.
+   `hdb' specifies the hash database object.
+   `fd' specifies the file descriptor for debugging output. */
+void tchdbsetdbgfd(TCHDB *hdb, int fd);
+
+
+/* Get the file descriptor for debugging output.
+   `hdb' specifies the hash database object.
+   The return value is the file descriptor for debugging output. */
+int tchdbdbgfd(TCHDB *hdb);
+
+
+/* Synchronize updating contents on memory.
+   `hdb' specifies the hash database object connected as a writer.
+   `phys' specifies whether to synchronize physically.
+   If successful, the return value is true, else, it is false. */
+bool tchdbmemsync(TCHDB *hdb, bool phys);
+
+
+/* Get the number of elements of the bucket array of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the number of elements of the bucket array or 0 if the object does not
+   connect to any database file. */
+uint64_t tchdbbnum(TCHDB *hdb);
+
+
+/* Get the record alignment of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the record alignment or 0 if the object does not connect to any database
+   file. */
+uint32_t tchdbalign(TCHDB *hdb);
+
+
+/* Get the maximum number of the free block pool of a a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the maximum number of the free block pool or 0 if the object does not
+   connect to any database file. */
+uint32_t tchdbfbpmax(TCHDB *hdb);
+
+
+/* Get the inode number of the database file of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the inode number of the database file or 0 the object does not connect to
+   any database file. */
+uint64_t tchdbinode(TCHDB *hdb);
+
+
+/* Get the modification time of the database file of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the inode number of the database file or 0 the object does not connect to
+   any database file. */
+time_t tchdbmtime(TCHDB *hdb);
+
+
+/* Get the connection mode of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the connection mode. */
+int tchdbomode(TCHDB *hdb);
+
+
+/* Get the database type of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the database type. */
+uint8_t tchdbtype(TCHDB *hdb);
+
+
+/* Get the additional flags of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the additional flags. */
+uint8_t tchdbflags(TCHDB *hdb);
+
+
+/* Get the options of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the options. */
+uint8_t tchdbopts(TCHDB *hdb);
+
+
+/* Get the pointer to the opaque field of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the pointer to the opaque field whose size is 128 bytes. */
+char *tchdbopaque(TCHDB *hdb);
+
+
+/* Get the number of used elements of the bucket array of a hash database object.
+   `hdb' specifies the hash database object.
+   The return value is the number of used elements of the bucket array or 0 if the object does not
+   connect to any database file. */
+uint64_t tchdbbnumused(TCHDB *hdb);
+
+
+
+__TCHDB_CLINKAGEEND
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */
diff --git a/bacula/src/lib/tokyocabinet/tchmgr.c b/bacula/src/lib/tokyocabinet/tchmgr.c
new file mode 100644 (file)
index 0000000..98febda
--- /dev/null
@@ -0,0 +1,803 @@
+/*************************************************************************************************
+ * The command line utility of the hash database API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tchdb.h>
+#include "myconf.h"
+
+
+/* global variables */
+const char *g_progname;                  // program name
+int g_dbgfd;                             // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void printerr(TCHDB *hdb);
+static int printdata(const char *ptr, int size, bool px);
+static char *hextoobj(const char *str, int *sp);
+static char *mygetline(FILE *ifp);
+static int runcreate(int argc, char **argv);
+static int runinform(int argc, char **argv);
+static int runput(int argc, char **argv);
+static int runout(int argc, char **argv);
+static int runget(int argc, char **argv);
+static int runlist(int argc, char **argv);
+static int runoptimize(int argc, char **argv);
+static int runimporttsv(int argc, char **argv);
+static int runversion(int argc, char **argv);
+static int proccreate(const char *path, int bnum, int apow, int fpow, int opts);
+static int procinform(const char *path, int omode);
+static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                   int omode, int dmode);
+static int procout(const char *path, const char *kbuf, int ksiz, int omode);
+static int procget(const char *path, const char *kbuf, int ksiz, int omode, bool px, bool pz);
+static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *fmstr);
+static int procoptimize(const char *path, int bnum, int apow, int fpow, int opts, int omode);
+static int procimporttsv(const char *path, const char *file, int omode, bool sc);
+static int procversion(void);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  g_dbgfd = -1;
+  const char *ebuf = getenv("TCDBGFD");
+  if(ebuf) g_dbgfd = atoi(ebuf);
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "create")){
+    rv = runcreate(argc, argv);
+  } else if(!strcmp(argv[1], "inform")){
+    rv = runinform(argc, argv);
+  } else if(!strcmp(argv[1], "put")){
+    rv = runput(argc, argv);
+  } else if(!strcmp(argv[1], "out")){
+    rv = runout(argc, argv);
+  } else if(!strcmp(argv[1], "get")){
+    rv = runget(argc, argv);
+  } else if(!strcmp(argv[1], "list")){
+    rv = runlist(argc, argv);
+  } else if(!strcmp(argv[1], "optimize")){
+    rv = runoptimize(argc, argv);
+  } else if(!strcmp(argv[1], "importtsv")){
+    rv = runimporttsv(argc, argv);
+  } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){
+    rv = runversion(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: the command line utility of the hash database API\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s create [-tl] [-td|-tb] path [bnum [apow [fpow]]]\n", g_progname);
+  fprintf(stderr, "  %s inform [-nl|-nb] path\n", g_progname);
+  fprintf(stderr, "  %s put [-nl|-nb] [-sx] [-dk|-dc] path key value\n", g_progname);
+  fprintf(stderr, "  %s out [-nl|-nb] [-sx] path key\n", g_progname);
+  fprintf(stderr, "  %s get [-nl|-nb] [-sx] [-px] [-pz] path key\n", g_progname);
+  fprintf(stderr, "  %s list [-nl|-nb] [-m num] [-pv] [-px] [-fm str] path\n", g_progname);
+  fprintf(stderr, "  %s optimize [-tl] [-td|-tb] [-tz] [-nl|-nb] path [bnum [apow [fpow]]]\n",
+          g_progname);
+  fprintf(stderr, "  %s importtsv [-nl|-nb] [-sc] path [file]\n", g_progname);
+  fprintf(stderr, "  %s version\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print error information */
+static void printerr(TCHDB *hdb){
+  const char *path = tchdbpath(hdb);
+  int ecode = tchdbecode(hdb);
+  fprintf(stderr, "%s: %s: %d: %s\n", g_progname, path ? path : "-", ecode, tchdberrmsg(ecode));
+}
+
+
+/* print record data */
+static int printdata(const char *ptr, int size, bool px){
+  int len = 0;
+  while(size-- > 0){
+    if(px){
+      if(len > 0) putchar(' ');
+      len += printf("%02X", *(unsigned char *)ptr);
+    } else {
+      putchar(*ptr);
+      len++;
+    }
+    ptr++;
+  }
+  return len;
+}
+
+
+/* create a binary object from a hexadecimal string */
+static char *hextoobj(const char *str, int *sp){
+  int len = strlen(str);
+  char *buf = tcmalloc(len + 1);
+  int j = 0;
+  for(int i = 0; i < len; i += 2){
+    while(strchr(" \n\r\t\f\v", str[i])){
+      i++;
+    }
+    char mbuf[3];
+    if((mbuf[0] = str[i]) == '\0') break;
+    if((mbuf[1] = str[i+1]) == '\0') break;
+    mbuf[2] = '\0';
+    buf[j++] = (char)strtol(mbuf, NULL, 16);
+  }
+  buf[j] = '\0';
+  *sp = j;
+  return buf;
+}
+
+
+/* read a line from a file descriptor */
+static char *mygetline(FILE *ifp){
+  char *buf;
+  int c, len, blen;
+  buf = NULL;
+  len = 0;
+  blen = 256;
+  while((c = fgetc(ifp)) != EOF){
+    if(blen <= len) blen *= 2;
+    buf = tcrealloc(buf, blen + 1);
+    if(c == '\n' || c == '\r') c = '\0';
+    buf[len++] = c;
+    if(c == '\0') break;
+  }
+  if(!buf) return NULL;
+  buf[len] = '\0';
+  return buf;
+}
+
+
+/* parse arguments of create command */
+static int runcreate(int argc, char **argv){
+  char *path = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  int opts = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-tl")){
+        opts |= HDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= HDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= HDBTTCBS;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = proccreate(path, bnum, apow, fpow, opts);
+  return rv;
+}
+
+
+/* parse arguments of inform command */
+static int runinform(int argc, char **argv){
+  char *path = NULL;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int rv = procinform(path, omode);
+  return rv;
+}
+
+
+/* parse arguments of put command */
+static int runput(int argc, char **argv){
+  char *path = NULL;
+  char *key = NULL;
+  char *value = NULL;
+  int omode = 0;
+  int dmode = 0;
+  bool sx = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-dk")){
+        dmode = -1;
+      } else if(!strcmp(argv[i], "-dc")){
+        dmode = 1;
+      } else if(!strcmp(argv[i], "-sx")){
+        sx = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else if(!value){
+      value = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !key || !value) usage();
+  int ksiz, vsiz;
+  char *kbuf, *vbuf;
+  if(sx){
+    kbuf = hextoobj(key, &ksiz);
+    vbuf = hextoobj(value, &vsiz);
+  } else {
+    ksiz = strlen(key);
+    kbuf = tcmemdup(key, ksiz);
+    vsiz = strlen(value);
+    vbuf = tcmemdup(value, vsiz);
+  }
+  int rv = procput(path, kbuf, ksiz, vbuf, vsiz, omode, dmode);
+  free(vbuf);
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of out command */
+static int runout(int argc, char **argv){
+  char *path = NULL;
+  char *key = NULL;
+  int omode = 0;
+  bool sx = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-sx")){
+        sx = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !key) usage();
+  int ksiz;
+  char *kbuf;
+  if(sx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    ksiz = strlen(key);
+    kbuf = tcmemdup(key, ksiz);
+  }
+  int rv = procout(path, kbuf, ksiz, omode);
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of get command */
+static int runget(int argc, char **argv){
+  char *path = NULL;
+  char *key = NULL;
+  int omode = 0;
+  bool sx = false;
+  bool px = false;
+  bool pz = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-sx")){
+        sx = true;
+      } else if(!strcmp(argv[i], "-px")){
+        px = true;
+      } else if(!strcmp(argv[i], "-pz")){
+        pz = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !key) usage();
+  int ksiz;
+  char *kbuf;
+  if(sx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    ksiz = strlen(key);
+    kbuf = tcmemdup(key, ksiz);
+  }
+  int rv = procget(path, kbuf, ksiz, omode, px, pz);
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+static int runlist(int argc, char **argv){
+  char *path = NULL;
+  int omode = 0;
+  int max = -1;
+  bool pv = false;
+  bool px = false;
+  char *fmstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-m")){
+        if(++i >= argc) usage();
+        max = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-pv")){
+        pv = true;
+      } else if(!strcmp(argv[i], "-px")){
+        px = true;
+      } else if(!strcmp(argv[i], "-fm")){
+        if(++i >= argc) usage();
+        fmstr = argv[i];
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int rv = proclist(path, omode, max, pv, px, fmstr);
+  return rv;
+}
+
+
+/* parse arguments of optimize command */
+static int runoptimize(int argc, char **argv){
+  char *path = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  int opts = UINT8_MAX;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-tl")){
+        if(opts == UINT8_MAX) opts = 0;
+        opts |= HDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        if(opts == UINT8_MAX) opts = 0;
+        opts |= HDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        if(opts == UINT8_MAX) opts = 0;
+        opts |= HDBTTCBS;
+      } else if(!strcmp(argv[i], "-tz")){
+        if(opts == UINT8_MAX) opts = 0;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = procoptimize(path, bnum, apow, fpow, opts, omode);
+  return rv;
+}
+
+
+/* parse arguments of importtsv command */
+static int runimporttsv(int argc, char **argv){
+  char *path = NULL;
+  char *file = NULL;
+  int omode = 0;
+  bool sc = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-sc")){
+        sc = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int rv = procimporttsv(path, file, omode, sc);
+  return rv;
+}
+
+
+/* parse arguments of version command */
+static int runversion(int argc, char **argv){
+  int rv = procversion();
+  return rv;
+}
+
+
+/* perform create command */
+static int proccreate(const char *path, int bnum, int apow, int fpow, int opts){
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
+    printerr(hdb);
+    tchdbdel(hdb);
+    return 1;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC)){
+    printerr(hdb);
+    tchdbdel(hdb);
+    return 1;
+  }
+  bool err = false;
+  if(!tchdbclose(hdb)){
+    printerr(hdb);
+    err = true;
+  }
+  tchdbdel(hdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform inform command */
+static int procinform(const char *path, int omode){
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbopen(hdb, path, HDBOREADER | omode)){
+    printerr(hdb);
+    tchdbdel(hdb);
+    return 1;
+  }
+  bool err = false;
+  const char *npath = tchdbpath(hdb);
+  if(!npath) npath = "(unknown)";
+  printf("path: %s\n", npath);
+  const char *type = "(unknown)";
+  switch(tchdbtype(hdb)){
+  case HDBTHASH: type = "hash"; break;
+  case HDBTBTREE: type = "btree"; break;
+  }
+  printf("database type: %s\n", type);
+  uint8_t flags = tchdbflags(hdb);
+  printf("additional flags:");
+  if(flags & HDBFOPEN) printf(" open");
+  if(flags & HDBFFATAL) printf(" fatal");
+  printf("\n");
+  printf("bucket number: %llu\n", (unsigned long long)tchdbbnum(hdb));
+  if(hdb->cnt_writerec >= 0)
+    printf("used bucket number: %lld\n", (long long)tchdbbnumused(hdb));
+  printf("alignment: %u\n", tchdbalign(hdb));
+  printf("free block pool: %u\n", tchdbfbpmax(hdb));
+  printf("inode number: %lld\n", (long long)tchdbinode(hdb));
+  char date[48];
+  tcdatestrwww(tchdbmtime(hdb), INT_MAX, date);
+  printf("modified time: %s\n", date);
+  uint8_t opts = tchdbopts(hdb);
+  printf("options:");
+  if(opts & HDBTLARGE) printf(" large");
+  if(opts & HDBTDEFLATE) printf(" deflate");
+  if(opts & HDBTTCBS) printf(" tcbs");
+  printf("\n");
+  printf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  printf("file size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  if(!tchdbclose(hdb)){
+    if(!err) printerr(hdb);
+    err = true;
+  }
+  tchdbdel(hdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform put command */
+static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                   int omode, int dmode){
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+    printerr(hdb);
+    tchdbdel(hdb);
+    return 1;
+  }
+  bool err = false;
+  switch(dmode){
+  case -1:
+    if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(hdb);
+      err = true;
+    }
+    break;
+  case 1:
+    if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(hdb);
+      err = true;
+    }
+    break;
+  default:
+    if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+      printerr(hdb);
+      err = true;
+    }
+    break;
+  }
+  if(!tchdbclose(hdb)){
+    if(!err) printerr(hdb);
+    err = true;
+  }
+  tchdbdel(hdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform out command */
+static int procout(const char *path, const char *kbuf, int ksiz, int omode){
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+    printerr(hdb);
+    tchdbdel(hdb);
+    return 1;
+  }
+  bool err = false;
+  if(!tchdbout(hdb, kbuf, ksiz)){
+    printerr(hdb);
+    err = true;
+  }
+  if(!tchdbclose(hdb)){
+    if(!err) printerr(hdb);
+    err = true;
+  }
+  tchdbdel(hdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform get command */
+static int procget(const char *path, const char *kbuf, int ksiz, int omode, bool px, bool pz){
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbopen(hdb, path, HDBOREADER | omode)){
+    printerr(hdb);
+    tchdbdel(hdb);
+    return 1;
+  }
+  bool err = false;
+  int vsiz;
+  char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+  if(vbuf){
+    printdata(vbuf, vsiz, px);
+    if(!pz) putchar('\n');
+    free(vbuf);
+  } else {
+    printerr(hdb);
+    err = true;
+  }
+  if(!tchdbclose(hdb)){
+    if(!err) printerr(hdb);
+    err = true;
+  }
+  tchdbdel(hdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform list command */
+static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *fmstr){
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbopen(hdb, path, HDBOREADER | omode)){
+    printerr(hdb);
+    tchdbdel(hdb);
+    return 1;
+  }
+  bool err = false;
+  if(fmstr){
+    TCLIST *keys = tchdbfwmkeys2(hdb, fmstr, max);
+    for(int i = 0; i < tclistnum(keys); i++){
+      int ksiz;
+      const char *kbuf = tclistval(keys, i, &ksiz);
+      printdata(kbuf, ksiz, px);
+      if(pv){
+        int vsiz;
+        char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+        if(vbuf){
+          putchar('\t');
+          printdata(vbuf, vsiz, px);
+          free(vbuf);
+        }
+      }
+      putchar('\n');
+    }
+    tclistdel(keys);
+  } else {
+    if(!tchdbiterinit(hdb)){
+      printerr(hdb);
+      err = true;
+    }
+    TCXSTR *key = tcxstrnew();
+    TCXSTR *val = tcxstrnew();
+    int cnt = 0;
+    while(tchdbiternext3(hdb, key, val)){
+      printdata(tcxstrptr(key), tcxstrsize(key), px);
+      if(pv){
+        putchar('\t');
+        printdata(tcxstrptr(val), tcxstrsize(val), px);
+      }
+      putchar('\n');
+      if(max >= 0 && ++cnt >= max) break;
+    }
+    tcxstrdel(val);
+    tcxstrdel(key);
+  }
+  if(!tchdbclose(hdb)){
+    if(!err) printerr(hdb);
+    err = true;
+  }
+  tchdbdel(hdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform optimize command */
+static int procoptimize(const char *path, int bnum, int apow, int fpow, int opts, int omode){
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+    printerr(hdb);
+    tchdbdel(hdb);
+    return 1;
+  }
+  bool err = false;
+  if(!tchdboptimize(hdb, bnum, apow, fpow, opts)){
+    printerr(hdb);
+    err = true;
+  }
+  if(!tchdbclose(hdb)){
+    if(!err) printerr(hdb);
+    err = true;
+  }
+  tchdbdel(hdb);
+  return err ? 1 : 0;
+}
+
+
+/* perform importtsv command */
+static int procimporttsv(const char *path, const char *file, int omode, bool sc){
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  FILE *ifp = file ? fopen(file, "rb") : stdin;
+  if(!ifp){
+    fprintf(stderr, "%s: could not open\n", file ? file : "(stdin)");
+    tchdbdel(hdb);
+    return 1;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | omode)){
+    printerr(hdb);
+    tchdbdel(hdb);
+    return 1;
+  }
+  bool err = false;
+  char *line;
+  int cnt = 0;
+  while(!err && (line = mygetline(ifp)) != NULL){
+    char *pv = strchr(line, '\t');
+    if(!pv) continue;
+    *pv = '\0';
+    if(sc) tcstrtolower(line);
+    if(!tchdbput2(hdb, line, pv + 1) && tchdbecode(hdb) != TCEKEEP){
+      printerr(hdb);
+      err = true;
+    }
+    free(line);
+    if(cnt > 0 && cnt % 100 == 0){
+      putchar('.');
+      fflush(stdout);
+      if(cnt % 5000 == 0) printf(" (%08d)\n", cnt);
+    }
+    cnt++;
+  }
+  printf(" (%08d)\n", cnt);
+  if(!tchdbclose(hdb)){
+    if(!err) printerr(hdb);
+    err = true;
+  }
+  tchdbdel(hdb);
+  if(ifp != stdin) fclose(ifp);
+  return err ? 1 : 0;
+}
+
+
+/* perform version command */
+static int procversion(void){
+  printf("Tokyo Cabinet version %s (%d:%s)\n", tcversion, _TC_LIBVER, _TC_FORMATVER);
+  printf("Copyright (C) 2006-2008 Mikio Hirabayashi\n");
+  return 0;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tchmttest.c b/bacula/src/lib/tokyocabinet/tchmttest.c
new file mode 100644 (file)
index 0000000..2e64ae9
--- /dev/null
@@ -0,0 +1,1266 @@
+/*************************************************************************************************
+ * The test cases of the hash database API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tchdb.h>
+#include "myconf.h"
+
+#define RECBUFSIZ      32                // buffer for records
+
+typedef struct {                         // type of structure for write thread
+  TCHDB *hdb;
+  int rnum;
+  bool as;
+  bool rnd;
+  int id;
+} TARGWRITE;
+
+typedef struct {                         // type of structure for read thread
+  TCHDB *hdb;
+  int rnum;
+  bool wb;
+  bool rnd;
+  int id;
+} TARGREAD;
+
+typedef struct {                         // type of structure for remove thread
+  TCHDB *hdb;
+  int rnum;
+  bool rnd;
+  int id;
+} TARGREMOVE;
+
+typedef struct {                         // type of structure for wicked thread
+  TCHDB *hdb;
+  int rnum;
+  bool nc;
+  int id;
+  TCMAP *map;
+} TARGWICKED;
+
+typedef struct {                         // type of structure for typical thread
+  TCHDB *hdb;
+  int rnum;
+  bool nc;
+  int rratio;
+  int id;
+} TARGTYPICAL;
+
+
+/* global variables */
+const char *g_progname;                  // program name
+int g_dbgfd;                             // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(TCHDB *hdb, const char *func);
+static void mprint(TCHDB *hdb);
+static int myrand(int range);
+static int myrandnd(int range);
+static int runwrite(int argc, char **argv);
+static int runread(int argc, char **argv);
+static int runremove(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int runtypical(int argc, char **argv);
+static int procwrite(const char *path, int tnum, int rnum, int bnum, int apow, int fpow,
+                     int opts, int omode, bool as, bool rnd);
+static int procread(const char *path, int tnum, int rcnum, int omode, bool wb, bool rnd);
+static int procremove(const char *path, int tnum, int omode, bool rnd);
+static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc);
+static int proctypical(const char *path, int tnum, int rnum, int bnum, int apow, int fpow,
+                       int opts, int rcnum, int omode, bool nc, int rratio);
+static void *threadwrite(void *targ);
+static void *threadread(void *targ);
+static void *threadremove(void *targ);
+static void *threadwicked(void *targ);
+static void *threadtypical(void *targ);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  g_dbgfd = -1;
+  const char *ebuf = getenv("TCDBGFD");
+  if(ebuf) g_dbgfd = atoi(ebuf);
+  srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "write")){
+    rv = runwrite(argc, argv);
+  } else if(!strcmp(argv[1], "read")){
+    rv = runread(argc, argv);
+  } else if(!strcmp(argv[1], "remove")){
+    rv = runremove(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else if(!strcmp(argv[1], "typical")){
+    rv = runtypical(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: test cases of the hash database API of Tokyo Cabinet\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write [-tl] [-td|-tb] [-nl|-nb] [-as] [-rnd] path tnum rnum"
+          " [bnum [apow [fpow]]]\n", g_progname);
+  fprintf(stderr, "  %s read [-rc num] [-nl|-nb] [-wb] [-rnd] path tnum\n", g_progname);
+  fprintf(stderr, "  %s remove [-nl|-nb] [-wb] [-rnd] path tnum\n", g_progname);
+  fprintf(stderr, "  %s wicked [-tl] [-td|-tb] [-nl|-nb] [-nc] path tnum rnum\n",
+          g_progname);
+  fprintf(stderr, "  %s typical [-tl] [-td|-tb] [-rc num] [-nl|-nb] [-nc] [-rr num]"
+          " path tnum rnum [bnum [apow [fpow]]]\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+  va_list ap;
+  va_start(ap, format);
+  vprintf(format, ap);
+  fflush(stdout);
+  va_end(ap);
+}
+
+
+/* print error message of hash database */
+static void eprint(TCHDB *hdb, const char *func){
+  const char *path = tchdbpath(hdb);
+  int ecode = tchdbecode(hdb);
+  fprintf(stderr, "%s: %s: %s: error: %d: %s\n",
+          g_progname, path ? path : "-", func, ecode, tchdberrmsg(ecode));
+}
+
+
+/* print members of hash database */
+static void mprint(TCHDB *hdb){
+  if(hdb->cnt_writerec < 0) return;
+  iprintf("bucket number: %lld\n", (long long)tchdbbnum(hdb));
+  iprintf("used bucket number: %lld\n", (long long)tchdbbnumused(hdb));
+  iprintf("cnt_writerec: %lld\n", (long long)hdb->cnt_writerec);
+  iprintf("cnt_reuserec: %lld\n", (long long)hdb->cnt_reuserec);
+  iprintf("cnt_moverec: %lld\n", (long long)hdb->cnt_moverec);
+  iprintf("cnt_readrec: %lld\n", (long long)hdb->cnt_readrec);
+  iprintf("cnt_searchfbp: %lld\n", (long long)hdb->cnt_searchfbp);
+  iprintf("cnt_insertfbp: %lld\n", (long long)hdb->cnt_insertfbp);
+  iprintf("cnt_splicefbp: %lld\n", (long long)hdb->cnt_splicefbp);
+  iprintf("cnt_dividefbp: %lld\n", (long long)hdb->cnt_dividefbp);
+  iprintf("cnt_mergefbp: %lld\n", (long long)hdb->cnt_mergefbp);
+  iprintf("cnt_reducefbp: %lld\n", (long long)hdb->cnt_reducefbp);
+  iprintf("cnt_appenddrp: %lld\n", (long long)hdb->cnt_appenddrp);
+  iprintf("cnt_deferdrp: %lld\n", (long long)hdb->cnt_deferdrp);
+  iprintf("cnt_flushdrp: %lld\n", (long long)hdb->cnt_flushdrp);
+  iprintf("cnt_adjrecc: %lld\n", (long long)hdb->cnt_adjrecc);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+  return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* get a random number based on normal distribution */
+static int myrandnd(int range){
+  int num = (int)tcdrandnd(range >> 1, range / 10);
+  return (num < 0 || num >= range) ? 0 : num;
+}
+
+
+/* parse arguments of write command */
+static int runwrite(int argc, char **argv){
+  char *path = NULL;
+  char *tstr = NULL;
+  char *rstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  int opts = 0;
+  int omode = 0;
+  bool as = false;
+  bool rnd = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-tl")){
+        opts |= HDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= HDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= HDBTTCBS;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-as")){
+        as = true;
+      } else if(!strcmp(argv[i], "-rnd")){
+        rnd = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!tstr){
+      tstr = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !tstr || !rstr) usage();
+  int tnum = atoi(tstr);
+  int rnum = atoi(rstr);
+  if(tnum < 1 || rnum < 1) usage();
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = procwrite(path, tnum, rnum, bnum, apow, fpow, opts, omode, as, rnd);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+static int runread(int argc, char **argv){
+  char *path = NULL;
+  char *tstr = NULL;
+  int rcnum = 0;
+  int omode = 0;
+  bool wb = false;
+  bool rnd = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-rc")){
+        if(++i >= argc) usage();
+        rcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-wb")){
+        wb = true;
+      } else if(!strcmp(argv[i], "-rnd")){
+        rnd = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!tstr){
+      tstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !tstr) usage();
+  int tnum = atoi(tstr);
+  if(tnum < 1) usage();
+  int rv = procread(path, tnum, rcnum, omode, wb, rnd);
+  return rv;
+}
+
+
+/* parse arguments of remove command */
+static int runremove(int argc, char **argv){
+  char *path = NULL;
+  char *tstr = NULL;
+  int omode = 0;
+  bool rnd = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-rnd")){
+        rnd = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!tstr){
+      tstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !tstr) usage();
+  int tnum = atoi(tstr);
+  if(tnum < 1) usage();
+  int rv = procremove(path, tnum, omode, rnd);
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+  char *path = NULL;
+  char *tstr = NULL;
+  char *rstr = NULL;
+  int opts = 0;
+  int omode = 0;
+  bool nc = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-tl")){
+        opts |= HDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= HDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= HDBTTCBS;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-nc")){
+        nc = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!tstr){
+      tstr = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !tstr || !rstr) usage();
+  int tnum = atoi(tstr);
+  int rnum = atoi(rstr);
+  if(tnum < 1 || rnum < 1) usage();
+  int rv = procwicked(path, tnum, rnum, opts, omode, nc);
+  return rv;
+}
+
+
+/* parse arguments of typical command */
+static int runtypical(int argc, char **argv){
+  char *path = NULL;
+  char *tstr = NULL;
+  char *rstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  int opts = 0;
+  int rcnum = 0;
+  int omode = 0;
+  int rratio = -1;
+  bool nc = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-tl")){
+        opts |= HDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= HDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= HDBTTCBS;
+      } else if(!strcmp(argv[i], "-rc")){
+        if(++i >= argc) usage();
+        rcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-nc")){
+        nc = true;
+      } else if(!strcmp(argv[i], "-rr")){
+        if(++i >= argc) usage();
+        rratio = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!tstr){
+      tstr = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !tstr || !rstr) usage();
+  int tnum = atoi(tstr);
+  int rnum = atoi(rstr);
+  if(tnum < 1 || rnum < 1) usage();
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = proctypical(path, tnum, rnum, bnum, apow, fpow, opts, rcnum, omode, nc, rratio);
+  return rv;
+}
+
+
+/* perform write command */
+static int procwrite(const char *path, int tnum, int rnum, int bnum, int apow, int fpow,
+                     int opts, int omode, bool as, bool rnd){
+  iprintf("<Writing Test>\n  path=%s  tnum=%d  rnum=%d  bnum=%d  apow=%d  fpow=%d"
+          "  opts=%d  omode=%d  as=%d  rnd=%d\n\n",
+          path, tnum, rnum, bnum, apow, fpow, opts, omode, as, rnd);
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
+    eprint(hdb, "tchdbtune");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  TARGWRITE targs[tnum];
+  pthread_t threads[tnum];
+  if(tnum == 1){
+    targs[0].hdb = hdb;
+    targs[0].rnum = rnum;
+    targs[0].as = as;
+    targs[0].rnd = rnd;
+    targs[0].id = 0;
+    if(threadwrite(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].hdb = hdb;
+      targs[i].rnum = rnum;
+      targs[i].as = as;
+      targs[i].rnd = rnd;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){
+        eprint(hdb, "pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint(hdb, "pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform read command */
+static int procread(const char *path, int tnum, int rcnum, int omode, bool wb, bool rnd){
+  iprintf("<Reading Test>\n  path=%s  tnum=%d  rcnum=%d  omode=%d  wb=%d  rnd=%d\n\n",
+          path, tnum, rcnum, omode, wb, rnd);
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbsetcache(hdb, rcnum)){
+    eprint(hdb, "tchdbsetcache");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOREADER | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  int rnum = tchdbrnum(hdb) / tnum;
+  TARGREAD targs[tnum];
+  pthread_t threads[tnum];
+  if(tnum == 1){
+    targs[0].hdb = hdb;
+    targs[0].rnum = rnum;
+    targs[0].wb = wb;
+    targs[0].rnd = rnd;
+    targs[0].id = 0;
+    if(threadread(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].hdb = hdb;
+      targs[i].rnum = rnum;
+      targs[i].wb = wb;
+      targs[i].rnd = rnd;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){
+        eprint(hdb, "pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint(hdb, "pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform remove command */
+static int procremove(const char *path, int tnum, int omode, bool rnd){
+  iprintf("<Removing Test>\n  path=%s  tnum=%d  omode=%d  rnd=%d\n\n", path, tnum, omode, rnd);
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  int rnum = tchdbrnum(hdb) / tnum;
+  TARGREMOVE targs[tnum];
+  pthread_t threads[tnum];
+  if(tnum == 1){
+    targs[0].hdb = hdb;
+    targs[0].rnum = rnum;
+    targs[0].rnd = rnd;
+    targs[0].id = 0;
+    if(threadremove(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].hdb = hdb;
+      targs[i].rnum = rnum;
+      targs[i].rnd = rnd;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){
+        eprint(hdb, "pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint(hdb, "pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc){
+  iprintf("<Writing Test>\n  path=%s  tnum=%d  rnum=%d  opts=%d  omode=%d  nc=%d\n\n",
+          path, tnum, rnum, opts, omode, nc);
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbtune(hdb, rnum / 50, 2, -1, opts)){
+    eprint(hdb, "tchdbtune");
+    err = true;
+  }
+  if(!tchdbsetcache(hdb, rnum / 2)){
+    eprint(hdb, "tchdbsetcache");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  if(!tchdbiterinit(hdb)){
+    eprint(hdb, "tchdbiterinit");
+    err = true;
+  }
+  TARGWICKED targs[tnum];
+  pthread_t threads[tnum];
+  TCMAP *map = tcmapnew();
+  if(tnum == 1){
+    targs[0].hdb = hdb;
+    targs[0].rnum = rnum;
+    targs[0].nc = nc;
+    targs[0].id = 0;
+    targs[0].map = map;
+    if(threadwicked(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].hdb = hdb;
+      targs[i].rnum = rnum;
+      targs[i].nc = nc;
+      targs[i].id = i;
+      targs[i].map = map;
+      targs[i].map = map;
+      if(pthread_create(threads + i, NULL, threadwicked, targs + i) != 0){
+        eprint(hdb, "pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint(hdb, "pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  if(!nc){
+    if(!tchdbsync(hdb)){
+      eprint(hdb, "tchdbsync");
+      err = true;
+    }
+    if(tchdbrnum(hdb) != tcmaprnum(map)){
+      eprint(hdb, "(validation)");
+      err = true;
+    }
+    int end = rnum * tnum;
+    for(int i = 1; i <= end && !err; i++){
+      char kbuf[RECBUFSIZ];
+      int ksiz = sprintf(kbuf, "%d", i - 1);
+      int vsiz;
+      const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
+      int rsiz;
+      char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
+      if(vbuf){
+        putchar('.');
+        if(!rbuf){
+          eprint(hdb, "tchdbget");
+          err = true;
+        } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+          eprint(hdb, "(validation)");
+          err = true;
+        }
+      } else {
+        putchar('*');
+        if(rbuf || tchdbecode(hdb) != TCENOREC){
+          eprint(hdb, "(validation)");
+          err = true;
+        }
+      }
+      free(rbuf);
+      if(i % 50 == 0) iprintf(" (%08d)\n", i);
+    }
+    if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+  }
+  tcmapdel(map);
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform typical command */
+static int proctypical(const char *path, int tnum, int rnum, int bnum, int apow, int fpow,
+                       int opts, int rcnum, int omode, bool nc, int rratio){
+  iprintf("<Typical Access Test>\n  path=%s  tnum=%d  rnum=%d  bnum=%d  apow=%d  fpow=%d"
+          "  opts=%d  rcnum=%d  omode=%d  nc=%d  rratio=%d\n\n",
+          path, tnum, rnum, bnum, apow, fpow, opts, rcnum, omode, nc, rratio);
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(!tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
+    eprint(hdb, "tchdbtune");
+    err = true;
+  }
+  if(!tchdbsetcache(hdb, rcnum)){
+    eprint(hdb, "tchdbsetcache");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  TARGTYPICAL targs[tnum];
+  pthread_t threads[tnum];
+  if(tnum == 1){
+    targs[0].hdb = hdb;
+    targs[0].rnum = rnum;
+    targs[0].nc = nc;
+    targs[0].rratio = rratio;
+    targs[0].id = 0;
+    if(threadtypical(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].hdb = hdb;
+      targs[i].rnum = rnum;
+      targs[i].nc = nc;
+      targs[i].rratio= rratio;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){
+        eprint(hdb, "pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint(hdb, "pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* thread the write function */
+static void *threadwrite(void *targ){
+  TCHDB *hdb = ((TARGWRITE *)targ)->hdb;
+  int rnum = ((TARGWRITE *)targ)->rnum;
+  bool as = ((TARGWRITE *)targ)->as;
+  bool rnd = ((TARGWRITE *)targ)->rnd;
+  int id = ((TARGWRITE *)targ)->id;
+  bool err = false;
+  int base = id * rnum;
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i));
+    if(as){
+      if(!tchdbputasync(hdb, buf, len, buf, len)){
+        eprint(hdb, "tchdbput");
+        err = true;
+        break;
+      }
+    } else {
+      if(!tchdbput(hdb, buf, len, buf, len)){
+        eprint(hdb, "tchdbput");
+        err = true;
+        break;
+      }
+    }
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  return err ? "error" : NULL;
+}
+
+
+/* thread the read function */
+static void *threadread(void *targ){
+  TCHDB *hdb = ((TARGREAD *)targ)->hdb;
+  int rnum = ((TARGREAD *)targ)->rnum;
+  bool wb = ((TARGREAD *)targ)->wb;
+  bool rnd = ((TARGREAD *)targ)->rnd;
+  int id = ((TARGREAD *)targ)->id;
+  bool err = false;
+  int base = id * rnum;
+  for(int i = 1; i <= rnum && !err; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrandnd(i) : i));
+    int vsiz;
+    if(wb){
+      char vbuf[RECBUFSIZ];
+      int vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, RECBUFSIZ);
+      if(vsiz < 0 && (!rnd || tchdbecode(hdb) != TCENOREC)){
+        eprint(hdb, "tchdbget3");
+        err = true;
+      }
+    } else {
+      char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+      if(!vbuf && (!rnd || tchdbecode(hdb) != TCENOREC)){
+        eprint(hdb, "tchdbget");
+        err = true;
+      }
+      free(vbuf);
+    }
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  return err ? "error" : NULL;
+}
+
+
+/* thread the remove function */
+static void *threadremove(void *targ){
+  TCHDB *hdb = ((TARGREMOVE *)targ)->hdb;
+  int rnum = ((TARGREMOVE *)targ)->rnum;
+  bool rnd = ((TARGREMOVE *)targ)->rnd;
+  int id = ((TARGREMOVE *)targ)->id;
+  bool err = false;
+  int base = id * rnum;
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i + 1) : i));
+    if(!tchdbout(hdb, kbuf, ksiz) && (!rnd || tchdbecode(hdb) != TCENOREC)){
+      eprint(hdb, "tchdbout");
+      err = true;
+      break;
+    }
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  return err ? "error" : NULL;
+}
+
+
+/* thread the wicked function */
+static void *threadwicked(void *targ){
+  TCHDB *hdb = ((TARGWICKED *)targ)->hdb;
+  int rnum = ((TARGWICKED *)targ)->rnum;
+  bool nc = ((TARGWICKED *)targ)->nc;
+  int id = ((TARGWICKED *)targ)->id;
+  TCMAP *map = ((TARGWICKED *)targ)->map;
+  bool err = false;
+  for(int i = 1; i <= rnum && !err; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum * (id + 1)));
+    char vbuf[RECBUFSIZ];
+    int vsiz = myrand(RECBUFSIZ);
+    memset(vbuf, '*', vsiz);
+    vbuf[vsiz] = '\0';
+    char *rbuf;
+    if(!nc) tcglobalmutexlock();
+    switch(myrand(16)){
+    case 0:
+      if(id == 0) putchar('0');
+      if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(hdb, "tchdbput");
+        err = true;
+      }
+      if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 1:
+      if(id == 0) putchar('1');
+      if(!tchdbput2(hdb, kbuf, vbuf)){
+        eprint(hdb, "tchdbput2");
+        err = true;
+      }
+      if(!nc) tcmapput2(map, kbuf, vbuf);
+      break;
+    case 2:
+      if(id == 0) putchar('2');
+      if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){
+        eprint(hdb, "tchdbputkeep");
+        err = true;
+      }
+      if(!nc) tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 3:
+      if(id == 0) putchar('3');
+      if(!tchdbputkeep2(hdb, kbuf, vbuf) && tchdbecode(hdb) != TCEKEEP){
+        eprint(hdb, "tchdbputkeep2");
+        err = true;
+      }
+      if(!nc) tcmapputkeep2(map, kbuf, vbuf);
+      break;
+    case 4:
+      if(id == 0) putchar('4');
+      if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(hdb, "tchdbputcat");
+        err = true;
+      }
+      if(!nc) tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 5:
+      if(id == 0) putchar('5');
+      if(!tchdbputcat2(hdb, kbuf, vbuf)){
+        eprint(hdb, "tchdbputcat2");
+        err = true;
+      }
+      if(!nc) tcmapputcat2(map, kbuf, vbuf);
+      break;
+    case 6:
+      if(id == 0) putchar('6');
+      if(i > rnum / 4 * 3){
+        if(!tchdbputasync(hdb, kbuf, ksiz, vbuf, vsiz)){
+          eprint(hdb, "tchdbputasync");
+          err = true;
+        }
+      } else {
+        if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+          eprint(hdb, "tchdbput");
+          err = true;
+        }
+      }
+      if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 7:
+      if(id == 0) putchar('7');
+      if(i > rnum / 4 * 3){
+        if(!tchdbputasync2(hdb, kbuf, vbuf)){
+          eprint(hdb, "tchdbputasync2");
+          err = true;
+        }
+      } else {
+        if(!tchdbput2(hdb, kbuf, vbuf)){
+          eprint(hdb, "tchdbput2");
+          err = true;
+        }
+      }
+      if(!nc) tcmapput2(map, kbuf, vbuf);
+      break;
+    case 8:
+      if(id == 0) putchar('8');
+      if(myrand(10) == 0){
+        if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){
+          eprint(hdb, "tchdbout");
+          err = true;
+        }
+        if(!nc) tcmapout(map, kbuf, ksiz);
+      }
+      break;
+    case 9:
+      if(id == 0) putchar('9');
+      if(myrand(10) == 0){
+        if(!tchdbout2(hdb, kbuf) && tchdbecode(hdb) != TCENOREC){
+          eprint(hdb, "tchdbout2");
+          err = true;
+        }
+        if(!nc) tcmapout2(map, kbuf);
+      }
+      break;
+    case 10:
+      if(id == 0) putchar('A');
+      if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz))){
+        if(tchdbecode(hdb) != TCENOREC){
+          eprint(hdb, "tchdbget");
+          err = true;
+        }
+        rbuf = tcsprintf("[%d]", myrand(i + 1));
+        vsiz = strlen(rbuf);
+      }
+      vsiz += myrand(vsiz);
+      if(myrand(3) == 0) vsiz += PATH_MAX;
+      rbuf = tcrealloc(rbuf, vsiz + 1);
+      for(int j = 0; j < vsiz; j++){
+        rbuf[j] = myrand(0x100);
+      }
+      if(!tchdbput(hdb, kbuf, ksiz, rbuf, vsiz)){
+        eprint(hdb, "tchdbput");
+        err = true;
+      }
+      if(!nc) tcmapput(map, kbuf, ksiz, rbuf, vsiz);
+      free(rbuf);
+      break;
+    case 11:
+      if(id == 0) putchar('B');
+      if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz)) && tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "tchdbget");
+        err = true;
+      }
+      free(rbuf);
+      break;
+    case 12:
+      if(id == 0) putchar('C');
+      if(!(rbuf = tchdbget2(hdb, kbuf)) && tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "tchdbget");
+        err = true;
+      }
+      free(rbuf);
+      break;
+    case 13:
+      if(id == 0) putchar('D');
+      if(myrand(1) == 0) vsiz = 1;
+      if((vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, vsiz)) < 0 && tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "tchdbget3");
+        err = true;
+      }
+      break;
+    case 14:
+      if(id == 0) putchar('E');
+      if(myrand(rnum / 50) == 0){
+        if(!tchdbiterinit(hdb)){
+          eprint(hdb, "tchdbiterinit");
+          err = true;
+        }
+      }
+      TCXSTR *ikey = tcxstrnew();
+      TCXSTR *ival = tcxstrnew();
+      for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
+        if(j % 3 == 0){
+          if(!tchdbiternext3(hdb, ikey, ival)){
+            int ecode = tchdbecode(hdb);
+            if(ecode != TCEINVALID && ecode != TCENOREC){
+              eprint(hdb, "tchdbiternext3");
+              err = true;
+            }
+          }
+        } else {
+          int iksiz;
+          char *ikbuf = tchdbiternext(hdb, &iksiz);
+          if(ikbuf){
+            free(ikbuf);
+          } else {
+            int ecode = tchdbecode(hdb);
+            if(ecode != TCEINVALID && ecode != TCENOREC){
+              eprint(hdb, "tchdbiternext");
+              err = true;
+            }
+          }
+        }
+      }
+      tcxstrdel(ival);
+      tcxstrdel(ikey);
+      break;
+    default:
+      if(id == 0) putchar('@');
+      if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+      break;
+    }
+    if(!nc) tcglobalmutexunlock();
+    if(id == 0){
+      if(i % 50 == 0) iprintf(" (%08d)\n", i);
+      if(id == 0 && i == rnum / 4){
+        if(!tchdboptimize(hdb, rnum / 50, -1, -1, -1)){
+          eprint(hdb, "tchdboptimize");
+          err = true;
+        }
+        if(!tchdbiterinit(hdb)){
+          eprint(hdb, "tchdbiterinit");
+          err = true;
+        }
+      }
+    }
+  }
+  return err ? "error" : NULL;
+}
+
+
+/* thread the typical function */
+static void *threadtypical(void *targ){
+  TCHDB *hdb = ((TARGTYPICAL *)targ)->hdb;
+  int rnum = ((TARGTYPICAL *)targ)->rnum;
+  bool nc = ((TARGTYPICAL *)targ)->nc;
+  int rratio = ((TARGTYPICAL *)targ)->rratio;
+  int id = ((TARGTYPICAL *)targ)->id;
+  bool err = false;
+  TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL;
+  int base = id * rnum;
+  int mrange = tclmax(50 + rratio, 100);
+  for(int i = 1; !err && i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", base + myrandnd(i));
+    int rnd = myrand(mrange);
+    if(rnd < 10){
+      if(!tchdbput(hdb, buf, len, buf, len)){
+        eprint(hdb, "tchdbput");
+        err = true;
+      }
+      if(map) tcmapput(map, buf, len, buf, len);
+    } else if(rnd < 15){
+      if(!tchdbputkeep(hdb, buf, len, buf, len) && tchdbecode(hdb) != TCEKEEP){
+        eprint(hdb, "tchdbputkeep");
+        err = true;
+      }
+      if(map) tcmapputkeep(map, buf, len, buf, len);
+    } else if(rnd < 20){
+      if(!tchdbputcat(hdb, buf, len, buf, len)){
+        eprint(hdb, "tchdbputcat");
+        err = true;
+      }
+      if(map) tcmapputcat(map, buf, len, buf, len);
+    } else if(rnd < 25){
+      if(i > rnum / 10 * 9){
+        if(!tchdbputasync(hdb, buf, len, buf, len)){
+          eprint(hdb, "tchdbputasync");
+          err = true;
+        }
+      } else {
+        if(!tchdbput(hdb, buf, len, buf, len)){
+          eprint(hdb, "tchdbput");
+          err = true;
+        }
+      }
+      if(map) tcmapput(map, buf, len, buf, len);
+    } else if(rnd < 30){
+      if(!tchdbout(hdb, buf, len) && tchdbecode(hdb) && tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "tchdbout");
+        err = true;
+      }
+      if(map) tcmapout(map, buf, len);
+    } else if(rnd < 31){
+      if(myrand(10) == 0 && !tchdbiterinit(hdb) && tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "tchdbiterinit");
+        err = true;
+      }
+      for(int j = 0; !err && j < 10; j++){
+        int ksiz;
+        char *kbuf = tchdbiternext(hdb, &ksiz);
+        if(kbuf){
+          free(kbuf);
+        } else if(tchdbecode(hdb) != TCEINVALID && tchdbecode(hdb) != TCENOREC){
+          eprint(hdb, "tchdbiternext");
+          err = true;
+        }
+      }
+    } else {
+      int vsiz;
+      char *vbuf = tchdbget(hdb, buf, len, &vsiz);
+      if(vbuf){
+        if(map){
+          int msiz;
+          const char *mbuf = tcmapget(map, buf, len, &msiz);
+          if(msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+            eprint(hdb, "(validation)");
+            err = true;
+          }
+        }
+        free(vbuf);
+      } else {
+        if(tchdbecode(hdb) != TCENOREC){
+          eprint(hdb, "tchdbget");
+          err = true;
+        }
+        if(map && tcmapget(map, buf, len, &vsiz)){
+          eprint(hdb, "(validation)");
+          err = true;
+        }
+      }
+    }
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(map){
+    tcmapiterinit(map);
+    int ksiz;
+    const char *kbuf;
+    while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
+      int vsiz;
+      char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+      if(vbuf){
+        int msiz;
+        const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz);
+        if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+          eprint(hdb, "(validation)");
+          err = true;
+        }
+        free(vbuf);
+      } else {
+        eprint(hdb, "(validation)");
+        err = true;
+      }
+    }
+    tcmapdel(map);
+  }
+  return err ? "error" : NULL;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tchtest.c b/bacula/src/lib/tokyocabinet/tchtest.c
new file mode 100644 (file)
index 0000000..9dfffc4
--- /dev/null
@@ -0,0 +1,1439 @@
+/*************************************************************************************************
+ * The test cases of the hash database API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tchdb.h>
+#include "myconf.h"
+
+#define RECBUFSIZ      32                // buffer for records
+
+
+/* global variables */
+const char *g_progname;                  // program name
+int g_dbgfd;                             // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(TCHDB *hdb, const char *func);
+static void mprint(TCHDB *hdb);
+static int myrand(int range);
+static int runwrite(int argc, char **argv);
+static int runread(int argc, char **argv);
+static int runremove(int argc, char **argv);
+static int runrcat(int argc, char **argv);
+static int runmisc(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int procwrite(const char *path, int rnum, int bnum, int apow, int fpow,
+                     bool mt, int opts, int rcnum, int omode, bool as);
+static int procread(const char *path, bool mt, int rcnum, int omode, bool wb);
+static int procremove(const char *path, bool mt, int rcnum, int omode);
+static int procrcat(const char *path, int rnum, int bnum, int apow, int fpow,
+                    bool mt, int opts, int rcnum, int omode, int pnum, bool rl);
+static int procmisc(const char *path, int rnum, bool mt, int opts, int omode);
+static int procwicked(const char *path, int rnum, bool mt, int opts, int omode);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  g_dbgfd = -1;
+  const char *ebuf = getenv("TCDBGFD");
+  if(ebuf) g_dbgfd = atoi(ebuf);
+  srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "write")){
+    rv = runwrite(argc, argv);
+  } else if(!strcmp(argv[1], "read")){
+    rv = runread(argc, argv);
+  } else if(!strcmp(argv[1], "remove")){
+    rv = runremove(argc, argv);
+  } else if(!strcmp(argv[1], "rcat")){
+    rv = runrcat(argc, argv);
+  } else if(!strcmp(argv[1], "misc")){
+    rv = runmisc(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: test cases of the hash database API of Tokyo Cabinet\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write [-mt] [-tl] [-td|-tb] [-rc num] [-nl|-nb] [-as] path rnum"
+          " [bnum [apow [fpow]]]\n", g_progname);
+  fprintf(stderr, "  %s read [-mt] [-rc num] [-nl|-nb] [-wb] path\n", g_progname);
+  fprintf(stderr, "  %s remove [-mt] [-rc num] [-nl|-nb] path\n", g_progname);
+  fprintf(stderr, "  %s rcat [-mt] [-rc num] [-tl] [-td|-tb] [-nl|-nb] [-pn num] [-rl]"
+          " path rnum [bnum [apow [fpow]]]\n", g_progname);
+  fprintf(stderr, "  %s misc [-mt] [-tl] [-td|-tb] [-nl|-nb] path rnum\n", g_progname);
+  fprintf(stderr, "  %s wicked [-mt] [-tl] [-td|-tb] [-nl|-nb] path rnum\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+  va_list ap;
+  va_start(ap, format);
+  vprintf(format, ap);
+  fflush(stdout);
+  va_end(ap);
+}
+
+
+/* print error message of hash database */
+static void eprint(TCHDB *hdb, const char *func){
+  const char *path = tchdbpath(hdb);
+  int ecode = tchdbecode(hdb);
+  fprintf(stderr, "%s: %s: %s: error: %d: %s\n",
+          g_progname, path ? path : "-", func, ecode, tchdberrmsg(ecode));
+}
+
+
+/* print members of hash database */
+static void mprint(TCHDB *hdb){
+  if(hdb->cnt_writerec < 0) return;
+  iprintf("bucket number: %lld\n", (long long)tchdbbnum(hdb));
+  iprintf("used bucket number: %lld\n", (long long)tchdbbnumused(hdb));
+  iprintf("cnt_writerec: %lld\n", (long long)hdb->cnt_writerec);
+  iprintf("cnt_reuserec: %lld\n", (long long)hdb->cnt_reuserec);
+  iprintf("cnt_moverec: %lld\n", (long long)hdb->cnt_moverec);
+  iprintf("cnt_readrec: %lld\n", (long long)hdb->cnt_readrec);
+  iprintf("cnt_searchfbp: %lld\n", (long long)hdb->cnt_searchfbp);
+  iprintf("cnt_insertfbp: %lld\n", (long long)hdb->cnt_insertfbp);
+  iprintf("cnt_splicefbp: %lld\n", (long long)hdb->cnt_splicefbp);
+  iprintf("cnt_dividefbp: %lld\n", (long long)hdb->cnt_dividefbp);
+  iprintf("cnt_mergefbp: %lld\n", (long long)hdb->cnt_mergefbp);
+  iprintf("cnt_reducefbp: %lld\n", (long long)hdb->cnt_reducefbp);
+  iprintf("cnt_appenddrp: %lld\n", (long long)hdb->cnt_appenddrp);
+  iprintf("cnt_deferdrp: %lld\n", (long long)hdb->cnt_deferdrp);
+  iprintf("cnt_flushdrp: %lld\n", (long long)hdb->cnt_flushdrp);
+  iprintf("cnt_adjrecc: %lld\n", (long long)hdb->cnt_adjrecc);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+  return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* parse arguments of write command */
+static int runwrite(int argc, char **argv){
+  char *path = NULL;
+  char *rstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  bool mt = false;
+  int opts = 0;
+  int rcnum = 0;
+  int omode = 0;
+  bool as = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-tl")){
+        opts |= HDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= HDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= HDBTTCBS;
+      } else if(!strcmp(argv[i], "-rc")){
+        if(++i >= argc) usage();
+        rcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-as")){
+        as = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = procwrite(path, rnum, bnum, apow, fpow, mt, opts, rcnum, omode, as);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+static int runread(int argc, char **argv){
+  char *path = NULL;
+  bool mt = false;
+  int rcnum = 0;
+  int omode = 0;
+  bool wb = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-rc")){
+        if(++i >= argc) usage();
+        rcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-wb")){
+        wb = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int rv = procread(path, mt, rcnum, omode, wb);
+  return rv;
+}
+
+
+/* parse arguments of remove command */
+static int runremove(int argc, char **argv){
+  char *path = NULL;
+  bool mt = false;
+  int rcnum = 0;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-rc")){
+        if(++i >= argc) usage();
+        rcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path) usage();
+  int rv = procremove(path, mt, rcnum, omode);
+  return rv;
+}
+
+
+/* parse arguments of rcat command */
+static int runrcat(int argc, char **argv){
+  char *path = NULL;
+  char *rstr = NULL;
+  char *bstr = NULL;
+  char *astr = NULL;
+  char *fstr = NULL;
+  bool mt = false;
+  int opts = 0;
+  int rcnum = 0;
+  int omode = 0;
+  int pnum = 0;
+  bool rl = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-tl")){
+        opts |= HDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= HDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= HDBTTCBS;
+      } else if(!strcmp(argv[i], "-rc")){
+        if(++i >= argc) usage();
+        rcnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else if(!strcmp(argv[i], "-pn")){
+        if(++i >= argc) usage();
+        pnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-rl")){
+        rl = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int bnum = bstr ? atoi(bstr) : -1;
+  int apow = astr ? atoi(astr) : -1;
+  int fpow = fstr ? atoi(fstr) : -1;
+  int rv = procrcat(path, rnum, bnum, apow, fpow, mt, opts, rcnum, omode, pnum, rl);
+  return rv;
+}
+
+
+/* parse arguments of misc command */
+static int runmisc(int argc, char **argv){
+  char *path = NULL;
+  char *rstr = NULL;
+  bool mt = false;
+  int opts = 0;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-tl")){
+        opts |= HDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= HDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= HDBTTCBS;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procmisc(path, rnum, mt, opts, omode);
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+  char *path = NULL;
+  char *rstr = NULL;
+  bool mt = false;
+  int opts = 0;
+  int omode = 0;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-mt")){
+        mt = true;
+      } else if(!strcmp(argv[i], "-tl")){
+        opts |= HDBTLARGE;
+      } else if(!strcmp(argv[i], "-td")){
+        opts |= HDBTDEFLATE;
+      } else if(!strcmp(argv[i], "-tb")){
+        opts |= HDBTTCBS;
+      } else if(!strcmp(argv[i], "-nl")){
+        omode |= HDBONOLCK;
+      } else if(!strcmp(argv[i], "-nb")){
+        omode |= HDBOLCKNB;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!path || !rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procwicked(path, rnum, mt, opts, omode);
+  return rv;
+}
+
+
+/* perform write command */
+static int procwrite(const char *path, int rnum, int bnum, int apow, int fpow,
+                     bool mt, int opts, int rcnum, int omode, bool as){
+  iprintf("<Writing Test>\n  path=%s  rnum=%d  bnum=%d  apow=%d  fpow=%d  mt=%d"
+          "  opts=%d  rcnum=%d  omode=%d  as=%d\n\n",
+          path, rnum, bnum, apow, fpow, mt, opts, rcnum, omode, as);
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(mt && !tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
+    eprint(hdb, "tchdbtune");
+    err = true;
+  }
+  if(!tchdbsetcache(hdb, rcnum)){
+    eprint(hdb, "tchdbsetcache");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    if(as){
+      if(!tchdbputasync(hdb, buf, len, buf, len)){
+        eprint(hdb, "tchdbput");
+        err = true;
+        break;
+      }
+    } else {
+      if(!tchdbput(hdb, buf, len, buf, len)){
+        eprint(hdb, "tchdbput");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform read command */
+static int procread(const char *path, bool mt, int rcnum, int omode, bool wb){
+  iprintf("<Reading Test>\n  path=%s  mt=%d  rcnum=%d  omode=%d  wb=%d\n",
+          path, mt, rcnum, omode, wb);
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(mt && !tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbsetcache(hdb, rcnum)){
+    eprint(hdb, "tchdbsetcache");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOREADER | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  int rnum = tchdbrnum(hdb);
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", i);
+    int vsiz;
+    if(wb){
+      char vbuf[RECBUFSIZ];
+      int vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, RECBUFSIZ);
+      if(vsiz < 0){
+        eprint(hdb, "tchdbget3");
+        err = true;
+        break;
+      }
+    } else {
+      char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+      if(!vbuf){
+        eprint(hdb, "tchdbget");
+        err = true;
+        break;
+      }
+      free(vbuf);
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform remove command */
+static int procremove(const char *path, bool mt, int rcnum, int omode){
+  iprintf("<Removing Test>\n  path=%s  mt=%d  rcnum=%d  omode=%d\n", path, mt, rcnum, omode);
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(mt && !tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbsetcache(hdb, rcnum)){
+    eprint(hdb, "tchdbsetcache");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  int rnum = tchdbrnum(hdb);
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", i);
+    if(!tchdbout(hdb, kbuf, ksiz)){
+      eprint(hdb, "tchdbout");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform rcat command */
+static int procrcat(const char *path, int rnum, int bnum, int apow, int fpow,
+                    bool mt, int opts, int rcnum, int omode, int pnum, bool rl){
+  iprintf("<Random Concatenating Test>\n"
+          "  path=%s  rnum=%d  bnum=%d  apow=%d  fpow=%d  mt=%d  opts=%d  rcnum=%d"
+          "  omode=%d  pnum=%d  rl=%d\n\n",
+          path, rnum, bnum, apow, fpow, mt, opts, rcnum, omode, pnum, rl);
+  if(pnum < 1) pnum = rnum;
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(mt && !tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
+    eprint(hdb, "tchdbtune");
+    err = true;
+  }
+  if(!tchdbsetcache(hdb, rcnum)){
+    eprint(hdb, "tchdbsetcache");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(pnum));
+    if(rl){
+      char vbuf[PATH_MAX];
+      int vsiz = myrand(PATH_MAX);
+      for(int j = 0; j < vsiz; j++){
+        vbuf[j] = myrand(0x100);
+      }
+      if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(hdb, "tchdbputcat");
+        err = true;
+        break;
+      }
+    } else {
+      if(!tchdbputcat(hdb, kbuf, ksiz, kbuf, ksiz)){
+        eprint(hdb, "tchdbputcat");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform misc command */
+static int procmisc(const char *path, int rnum, bool mt, int opts, int omode){
+  iprintf("<Miscellaneous Test>\n  path=%s  rnum=%d  mt=%d  opts=%d  omode=%d\n\n",
+          path, rnum, mt, opts, omode);
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(mt && !tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbtune(hdb, rnum / 50, 2, -1, opts)){
+    eprint(hdb, "tchdbtune");
+    err = true;
+  }
+  if(!tchdbsetcache(hdb, rnum / 10)){
+    eprint(hdb, "tchdbsetcache");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  iprintf("writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    if(i % 3 == 0){
+      if(!tchdbputkeep(hdb, buf, len, buf, len)){
+        eprint(hdb, "tchdbputkeep");
+        err = true;
+        break;
+      }
+    } else {
+      if(!tchdbputasync(hdb, buf, len, buf, len)){
+        eprint(hdb, "tchdbputasync");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("reading:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", i);
+    int vsiz;
+    char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+    if(!vbuf){
+      eprint(hdb, "tchdbget");
+      err = true;
+      break;
+    } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){
+      eprint(hdb, "(validation)");
+      err = true;
+      free(vbuf);
+      break;
+    }
+    free(vbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(tchdbrnum(hdb) != rnum){
+    eprint(hdb, "(validation)");
+    err = true;
+  }
+  iprintf("random writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    char vbuf[RECBUFSIZ];
+    int vsiz = myrand(RECBUFSIZ);
+    memset(vbuf, '*', vsiz);
+    if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+      eprint(hdb, "tchdbput");
+      err = true;
+      break;
+    }
+    int rsiz;
+    char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
+    if(!rbuf){
+      eprint(hdb, "tchdbget");
+      err = true;
+      break;
+    }
+    if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+      eprint(hdb, "(validation)");
+      err = true;
+      free(rbuf);
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+    free(rbuf);
+  }
+  iprintf("word writing:\n");
+  const char *words[] = {
+    "a", "A", "bb", "BB", "ccc", "CCC", "dddd", "DDDD", "eeeee", "EEEEEE",
+    "mikio", "hirabayashi", "tokyo", "cabinet", "hyper", "estraier", "19780211", "birth day",
+    "one", "first", "two", "second", "three", "third", "four", "fourth", "five", "fifth",
+    "_[1]_", "uno", "_[2]_", "dos", "_[3]_", "tres", "_[4]_", "cuatro", "_[5]_", "cinco",
+    "[\xe5\xb9\xb3\xe6\x9e\x97\xe5\xb9\xb9\xe9\x9b\x84]", "[\xe9\xa6\xac\xe9\xb9\xbf]", NULL
+  };
+  for(int i = 0; words[i] != NULL; i += 2){
+    const char *kbuf = words[i];
+    int ksiz = strlen(kbuf);
+    const char *vbuf = words[i+1];
+    int vsiz = strlen(vbuf);
+    if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz)){
+      eprint(hdb, "tchdbputkeep");
+      err = true;
+      break;
+    }
+    if(rnum > 250) putchar('.');
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", sizeof(words) / sizeof(*words));
+  iprintf("random erasing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){
+      eprint(hdb, "tchdbout");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "[%d]", i);
+    char vbuf[RECBUFSIZ];
+    int vsiz = i % RECBUFSIZ;
+    memset(vbuf, '*', vsiz);
+    if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz)){
+      eprint(hdb, "tchdbputkeep");
+      err = true;
+      break;
+    }
+    if(vsiz < 1){
+      char tbuf[PATH_MAX];
+      for(int j = 0; j < PATH_MAX; j++){
+        tbuf[j] = myrand(0x100);
+      }
+      if(!tchdbput(hdb, kbuf, ksiz, tbuf, PATH_MAX)){
+        eprint(hdb, "tchdbput");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("erasing:\n");
+  for(int i = 1; i <= rnum; i++){
+    if(i % 2 == 1){
+      char kbuf[RECBUFSIZ];
+      int ksiz = sprintf(kbuf, "[%d]", i);
+      if(!tchdbout(hdb, kbuf, ksiz)){
+        eprint(hdb, "tchdbout");
+        err = true;
+        break;
+      }
+      if(tchdbout(hdb, kbuf, ksiz) || tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "tchdbout");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("random writing and reopening:\n");
+  for(int i = 1; i <= rnum; i++){
+    if(myrand(10) == 0){
+      int ksiz, vsiz;
+      char *kbuf, *vbuf;
+      ksiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ);
+      kbuf = tcmalloc(ksiz + 1);
+      memset(kbuf, '@', ksiz);
+      vsiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ);
+      vbuf = tcmalloc(vsiz + 1);
+      for(int j = 0; j < vsiz; j++){
+        vbuf[j] = myrand(256);
+      }
+      switch(myrand(4)){
+      case 0:
+        if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+          eprint(hdb, "tchdbput");
+          err = true;
+        }
+        break;
+      case 1:
+        if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
+          eprint(hdb, "tchdbputcat");
+          err = true;
+        }
+        break;
+      case 2:
+        if(!tchdbputasync(hdb, kbuf, ksiz, vbuf, vsiz)){
+          eprint(hdb, "tchdbputasync");
+          err = true;
+        }
+        break;
+      case 3:
+        if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){
+          eprint(hdb, "tchdbout");
+          err = true;
+        }
+        break;
+      }
+      free(vbuf);
+      free(kbuf);
+    } else {
+      char kbuf[RECBUFSIZ];
+      int ksiz = myrand(RECBUFSIZ);
+      memset(kbuf, '@', ksiz);
+      char vbuf[RECBUFSIZ];
+      int vsiz = myrand(RECBUFSIZ);
+      memset(vbuf, '@', vsiz);
+      if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(hdb, "tchdbputcat");
+        err = true;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  iprintf("checking:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "[%d]", i);
+    int vsiz;
+    char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+    if(i % 2 == 0){
+      if(!vbuf){
+        eprint(hdb, "tchdbget");
+        err = true;
+        break;
+      }
+      if(vsiz != i % RECBUFSIZ && vsiz != PATH_MAX){
+        eprint(hdb, "(validation)");
+        err = true;
+        free(vbuf);
+        break;
+      }
+    } else {
+      if(vbuf || tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "(validation)");
+        err = true;
+        free(vbuf);
+        break;
+      }
+    }
+    free(vbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("writing:\n");
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    if(!tchdbput(hdb, buf, len, buf, len)){
+      eprint(hdb, "tchdbput");
+      err = true;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("reading:\n");
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", i);
+    int vsiz;
+    char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+    if(!vbuf){
+      eprint(hdb, "tchdbget");
+      err = true;
+      break;
+    } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){
+      eprint(hdb, "(validation)");
+      err = true;
+      free(vbuf);
+      break;
+    }
+    free(vbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("word checking:\n");
+  for(int i = 0; words[i] != NULL; i += 2){
+    const char *kbuf = words[i];
+    int ksiz = strlen(kbuf);
+    const char *vbuf = words[i+1];
+    int vsiz = strlen(vbuf);
+    int rsiz;
+    char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
+    if(!rbuf){
+      eprint(hdb, "tchdbget");
+      err = true;
+      break;
+    } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+      eprint(hdb, "(validation)");
+      err = true;
+      free(rbuf);
+      break;
+    }
+    free(rbuf);
+    if(rnum > 250) putchar('.');
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", sizeof(words) / sizeof(*words));
+  iprintf("iterator checking:\n");
+  if(!tchdbiterinit(hdb)){
+    eprint(hdb, "tchdbiterinit");
+    err = true;
+  }
+  char *kbuf;
+  int ksiz;
+  int inum = 0;
+  for(int i = 1; (kbuf = tchdbiternext(hdb, &ksiz)) != NULL; i++, inum++){
+    int vsiz;
+    char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+    if(!vbuf){
+      eprint(hdb, "tchdbget");
+      err = true;
+      free(kbuf);
+      break;
+    }
+    free(vbuf);
+    free(kbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", inum);
+  if(tchdbecode(hdb) != TCENOREC || inum != tchdbrnum(hdb)){
+    eprint(hdb, "(validation)");
+    err = true;
+  }
+  iprintf("iteration updating:\n");
+  if(!tchdbiterinit(hdb)){
+    eprint(hdb, "tchdbiterinit");
+    err = true;
+  }
+  inum = 0;
+  for(int i = 1; (kbuf = tchdbiternext(hdb, &ksiz)) != NULL; i++, inum++){
+    if(myrand(2) == 0){
+      if(!tchdbputcat(hdb, kbuf, ksiz, "0123456789", 10)){
+        eprint(hdb, "tchdbputcat");
+        err = true;
+        free(kbuf);
+        break;
+      }
+    } else {
+      if(!tchdbout(hdb, kbuf, ksiz)){
+        eprint(hdb, "tchdbout");
+        err = true;
+        free(kbuf);
+        break;
+      }
+    }
+    free(kbuf);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(rnum > 250) iprintf(" (%08d)\n", inum);
+  if(tchdbecode(hdb) != TCENOREC || inum < tchdbrnum(hdb)){
+    eprint(hdb, "(validation)");
+    err = true;
+  }
+  if(!tchdbsync(hdb)){
+    eprint(hdb, "tchdbsync");
+    err = true;
+  }
+  if(!tchdbvanish(hdb)){
+    eprint(hdb, "tchdbvanish");
+    err = true;
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(const char *path, int rnum, bool mt, int opts, int omode){
+  iprintf("<Wicked Writing Test>\n  path=%s  rnum=%d  mt=%d  opts=%d  omode=%d\n\n",
+          path, rnum, mt, opts, omode);
+  bool err = false;
+  double stime = tctime();
+  TCHDB *hdb = tchdbnew();
+  if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+  if(mt && !tchdbsetmutex(hdb)){
+    eprint(hdb, "tchdbsetmutex");
+    err = true;
+  }
+  if(!tchdbtune(hdb, rnum / 50, 2, -1, opts)){
+    eprint(hdb, "tchdbtune");
+    err = true;
+  }
+  if(!tchdbsetcache(hdb, rnum / 10)){
+    eprint(hdb, "tchdbsetcache");
+    err = true;
+  }
+  if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+    eprint(hdb, "tchdbopen");
+    err = true;
+  }
+  if(!tchdbiterinit(hdb)){
+    eprint(hdb, "tchdbiterinit");
+    err = true;
+  }
+  TCMAP *map = tcmapnew2(rnum / 5);
+  for(int i = 1; i <= rnum && !err; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+    char vbuf[RECBUFSIZ];
+    int vsiz = myrand(RECBUFSIZ);
+    memset(vbuf, '*', vsiz);
+    vbuf[vsiz] = '\0';
+    char *rbuf;
+    switch(myrand(16)){
+    case 0:
+      putchar('0');
+      if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(hdb, "tchdbput");
+        err = true;
+      }
+      tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 1:
+      putchar('1');
+      if(!tchdbput2(hdb, kbuf, vbuf)){
+        eprint(hdb, "tchdbput2");
+        err = true;
+      }
+      tcmapput2(map, kbuf, vbuf);
+      break;
+    case 2:
+      putchar('2');
+      if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){
+        eprint(hdb, "tchdbputkeep");
+        err = true;
+      }
+      tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 3:
+      putchar('3');
+      if(!tchdbputkeep2(hdb, kbuf, vbuf) && tchdbecode(hdb) != TCEKEEP){
+        eprint(hdb, "tchdbputkeep2");
+        err = true;
+      }
+      tcmapputkeep2(map, kbuf, vbuf);
+      break;
+    case 4:
+      putchar('4');
+      if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(hdb, "tchdbputcat");
+        err = true;
+      }
+      tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 5:
+      putchar('5');
+      if(!tchdbputcat2(hdb, kbuf, vbuf)){
+        eprint(hdb, "tchdbputcat2");
+        err = true;
+      }
+      tcmapputcat2(map, kbuf, vbuf);
+      break;
+    case 6:
+      putchar('6');
+      if(!tchdbputasync(hdb, kbuf, ksiz, vbuf, vsiz)){
+        eprint(hdb, "tchdbputasync");
+        err = true;
+      }
+      tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 7:
+      putchar('7');
+      if(!tchdbputasync2(hdb, kbuf, vbuf)){
+        eprint(hdb, "tchdbputasync2");
+        err = true;
+      }
+      tcmapput2(map, kbuf, vbuf);
+      break;
+    case 8:
+      putchar('8');
+      if(myrand(10) == 0){
+        if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){
+          eprint(hdb, "tchdbout");
+          err = true;
+        }
+        tcmapout(map, kbuf, ksiz);
+      }
+      break;
+    case 9:
+      putchar('9');
+      if(myrand(10) == 0){
+        if(!tchdbout2(hdb, kbuf) && tchdbecode(hdb) != TCENOREC){
+          eprint(hdb, "tchdbout2");
+          err = true;
+        }
+        tcmapout2(map, kbuf);
+      }
+      break;
+    case 10:
+      putchar('A');
+      if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz))){
+        if(tchdbecode(hdb) != TCENOREC){
+          eprint(hdb, "tchdbget");
+          err = true;
+        }
+        rbuf = tcsprintf("[%d]", myrand(i + 1));
+        vsiz = strlen(rbuf);
+      }
+      vsiz += myrand(vsiz);
+      if(myrand(3) == 0) vsiz += PATH_MAX;
+      rbuf = tcrealloc(rbuf, vsiz + 1);
+      for(int j = 0; j < vsiz; j++){
+        rbuf[j] = myrand(0x100);
+      }
+      if(!tchdbput(hdb, kbuf, ksiz, rbuf, vsiz)){
+        eprint(hdb, "tchdbput");
+        err = true;
+      }
+      tcmapput(map, kbuf, ksiz, rbuf, vsiz);
+      free(rbuf);
+      break;
+    case 11:
+      putchar('B');
+      if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz)) && tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "tchdbget");
+        err = true;
+      }
+      free(rbuf);
+      break;
+    case 12:
+      putchar('C');
+      if(!(rbuf = tchdbget2(hdb, kbuf)) && tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "tchdbget");
+        err = true;
+      }
+      free(rbuf);
+      break;
+    case 13:
+      putchar('D');
+      if(myrand(1) == 0) vsiz = 1;
+      if((vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, vsiz)) < 0 && tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "tchdbget");
+        err = true;
+      }
+      break;
+    case 14:
+      putchar('E');
+      if(myrand(rnum / 50) == 0){
+        if(!tchdbiterinit(hdb)){
+          eprint(hdb, "tchdbiterinit");
+          err = true;
+        }
+      }
+      TCXSTR *ikey = tcxstrnew();
+      TCXSTR *ival = tcxstrnew();
+      for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
+        if(j % 3 == 0){
+          if(tchdbiternext3(hdb, ikey, ival)){
+            if(tcxstrsize(ival) != tchdbvsiz(hdb, tcxstrptr(ikey), tcxstrsize(ikey))){
+              eprint(hdb, "(validation)");
+              err = true;
+            }
+          } else {
+            int ecode = tchdbecode(hdb);
+            if(ecode != TCEINVALID && ecode != TCENOREC){
+              eprint(hdb, "tchdbiternext3");
+              err = true;
+            }
+          }
+        } else {
+          int iksiz;
+          char *ikbuf = tchdbiternext(hdb, &iksiz);
+          if(ikbuf){
+            free(ikbuf);
+          } else {
+            int ecode = tchdbecode(hdb);
+            if(ecode != TCEINVALID && ecode != TCENOREC){
+              eprint(hdb, "tchdbiternext");
+              err = true;
+            }
+          }
+        }
+      }
+      tcxstrdel(ival);
+      tcxstrdel(ikey);
+      break;
+    default:
+      putchar('@');
+      if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+      if(myrand(rnum / 16 + 1) == 0){
+        int cnt = myrand(30);
+        for(int j = 0; j < rnum && !err; j++){
+          ksiz = sprintf(kbuf, "%d", i + j);
+          if(tchdbout(hdb, kbuf, ksiz)){
+            cnt--;
+          } else if(tchdbecode(hdb) != TCENOREC){
+            eprint(hdb, "tcbdbout");
+            err = true;
+          }
+          tcmapout(map, kbuf, ksiz);
+          if(cnt < 0) break;
+        }
+      }
+      break;
+    }
+    if(i % 50 == 0) iprintf(" (%08d)\n", i);
+    if(i == rnum / 2){
+      if(!tchdbclose(hdb)){
+        eprint(hdb, "tchdbclose");
+        err = true;
+      }
+      if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+        eprint(hdb, "tchdbopen");
+        err = true;
+      }
+    } else if(i == rnum / 4){
+      char *npath = tcsprintf("%s-tmp", path);
+      if(!tchdbcopy(hdb, npath)){
+        eprint(hdb, "tchdbcopy");
+        err = true;
+      }
+      TCHDB *nhdb = tchdbnew();
+      if(!tchdbopen(nhdb, npath, HDBOREADER | omode)){
+        eprint(nhdb, "tchdbopen");
+        err = true;
+      }
+      tchdbdel(nhdb);
+      unlink(npath);
+      free(npath);
+      if(!tchdboptimize(hdb, rnum / 50, -1, -1, -1)){
+        eprint(hdb, "tchdboptimize");
+        err = true;
+      }
+      if(!tchdbiterinit(hdb)){
+        eprint(hdb, "tchdbiterinit");
+        err = true;
+      }
+    }
+  }
+  if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+  if(!tchdbsync(hdb)){
+    eprint(hdb, "tchdbsync");
+    err = true;
+  }
+  if(tchdbrnum(hdb) != tcmaprnum(map)){
+    eprint(hdb, "(validation)");
+    err = true;
+  }
+  for(int i = 1; i <= rnum && !err; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", i - 1);
+    int vsiz;
+    const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
+    int rsiz;
+    char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
+    if(vbuf){
+      putchar('.');
+      if(!rbuf){
+        eprint(hdb, "tchdbget");
+        err = true;
+      } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+        eprint(hdb, "(validation)");
+        err = true;
+      }
+    } else {
+      putchar('*');
+      if(rbuf || tchdbecode(hdb) != TCENOREC){
+        eprint(hdb, "(validation)");
+        err = true;
+      }
+    }
+    free(rbuf);
+    if(i % 50 == 0) iprintf(" (%08d)\n", i);
+  }
+  if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+  tcmapiterinit(map);
+  int ksiz;
+  const char *kbuf;
+  for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){
+    putchar('+');
+    int vsiz;
+    const char *vbuf = tcmapiterval(kbuf, &vsiz);
+    int rsiz;
+    char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
+    if(!rbuf){
+      eprint(hdb, "tchdbget");
+      err = true;
+    } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+      eprint(hdb, "(validation)");
+      err = true;
+    }
+    free(rbuf);
+    if(!tchdbout(hdb, kbuf, ksiz)){
+      eprint(hdb, "tchdbout");
+      err = true;
+    }
+    if(i % 50 == 0) iprintf(" (%08d)\n", i);
+  }
+  int mrnum = tcmaprnum(map);
+  if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum);
+  if(tchdbrnum(hdb) != 0){
+    eprint(hdb, "(validation)");
+    err = true;
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+  iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+  mprint(hdb);
+  tcmapdel(map);
+  if(!tchdbclose(hdb)){
+    eprint(hdb, "tchdbclose");
+    err = true;
+  }
+  tchdbdel(hdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcucodec.c b/bacula/src/lib/tokyocabinet/tcucodec.c
new file mode 100644 (file)
index 0000000..7a9d933
--- /dev/null
@@ -0,0 +1,865 @@
+/*************************************************************************************************
+ * Popular encoders and decoders of the utility API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include "myconf.h"
+
+
+/* global variables */
+const char *g_progname;                  // program name
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void eprintf(const char *format, ...);
+static int runurl(int argc, char **argv);
+static int runbase(int argc, char **argv);
+static int runquote(int argc, char **argv);
+static int runmime(int argc, char **argv);
+static int runpack(int argc, char **argv);
+static int runtcbs(int argc, char **argv);
+static int runzlib(int argc, char **argv);
+static int runxml(int argc, char **argv);
+static int runucs(int argc, char **argv);
+static int rundate(int argc, char **argv);
+static int runconf(int argc, char **argv);
+static int procurl(const char *ibuf, int isiz, bool dec, bool br, const char *base);
+static int procbase(const char *ibuf, int isiz, bool dec);
+static int procquote(const char *ibuf, int isiz, bool dec);
+static int procmime(const char *ibuf, int isiz, bool dec, const char *ename, bool qb, bool on);
+static int procpack(const char *ibuf, int isiz, bool dec, bool bwt);
+static int proctcbs(const char *ibuf, int isiz, bool dec);
+static int proczlib(const char *ibuf, int isiz, bool dec, bool gz);
+static int procxml(const char *ibuf, int isiz, bool dec, bool br);
+static int procucs(const char *ibuf, int isiz, bool dec);
+static int procdate(const char *str, int jl, bool wf, bool rf);
+static int procconf(int mode);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "url")){
+    rv = runurl(argc, argv);
+  } else if(!strcmp(argv[1], "base")){
+    rv = runbase(argc, argv);
+  } else if(!strcmp(argv[1], "quote")){
+    rv = runquote(argc, argv);
+  } else if(!strcmp(argv[1], "mime")){
+    rv = runmime(argc, argv);
+  } else if(!strcmp(argv[1], "pack")){
+    rv = runpack(argc, argv);
+  } else if(!strcmp(argv[1], "tcbs")){
+    rv = runtcbs(argc, argv);
+  } else if(!strcmp(argv[1], "zlib")){
+    rv = runzlib(argc, argv);
+  } else if(!strcmp(argv[1], "xml")){
+    rv = runxml(argc, argv);
+  } else if(!strcmp(argv[1], "ucs")){
+    rv = runucs(argc, argv);
+  } else if(!strcmp(argv[1], "date")){
+    rv = rundate(argc, argv);
+  } else if(!strcmp(argv[1], "conf")){
+    rv = runconf(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: popular encoders and decoders of Tokyo Cabinet\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s url [-d] [-br] [-rs base] [file]\n", g_progname);
+  fprintf(stderr, "  %s base [-d] [file]\n", g_progname);
+  fprintf(stderr, "  %s quote [-d] [file]\n", g_progname);
+  fprintf(stderr, "  %s mime [-d] [-en name] [-q] [file]\n", g_progname);
+  fprintf(stderr, "  %s pack [-d] [-bwt] [file]\n", g_progname);
+  fprintf(stderr, "  %s tcbs [-d] [file]\n", g_progname);
+  fprintf(stderr, "  %s zlib [-d] [-gz] [file]\n", g_progname);
+  fprintf(stderr, "  %s xml [-d] [-br] [file]\n", g_progname);
+  fprintf(stderr, "  %s ucs [-d] [file]\n", g_progname);
+  fprintf(stderr, "  %s date [-ds str] [-jl num] [-wf] [-rf]\n", g_progname);
+  fprintf(stderr, "  %s conf [-v|-i|-l|-p]\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print formatted error string */
+static void eprintf(const char *format, ...){
+  va_list ap;
+  va_start(ap, format);
+  fprintf(stderr, "%s: ", g_progname);
+  vfprintf(stderr, format, ap);
+  fprintf(stderr, "\n");
+  va_end(ap);
+}
+
+
+/* parse arguments of url command */
+static int runurl(int argc, char **argv){
+  char *path = NULL;
+  bool dec = false;
+  bool br = false;
+  char *base = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = true;
+      } else if(!strcmp(argv[i], "-br")){
+        br = true;
+      } else if(!strcmp(argv[i], "-rs")){
+        if(++i >= argc) usage();
+        base = argv[i];
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  char *ibuf;
+  int isiz;
+  if(path && path[0] == '@'){
+    isiz = strlen(path) - 1;
+    ibuf = tcmemdup(path + 1, isiz);
+  } else {
+    ibuf = tcreadfile(path, -1, &isiz);
+  }
+  if(!ibuf){
+    eprintf("%s: cannot open", path ? path : "(stdin)");
+    return 1;
+  }
+  int rv = procurl(ibuf, isiz, dec, br, base);
+  if(path && path[0] == '@' && !br) printf("\n");
+  free(ibuf);
+  return rv;
+}
+
+
+/* parse arguments of base command */
+static int runbase(int argc, char **argv){
+  char *path = NULL;
+  bool dec = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  char *ibuf;
+  int isiz;
+  if(path && path[0] == '@'){
+    isiz = strlen(path) - 1;
+    ibuf = tcmemdup(path + 1, isiz);
+  } else {
+    ibuf = tcreadfile(path, -1, &isiz);
+  }
+  if(!ibuf){
+    eprintf("%s: cannot open", path ? path : "(stdin)");
+    return 1;
+  }
+  int rv = procbase(ibuf, isiz, dec);
+  if(path && path[0] == '@') printf("\n");
+  free(ibuf);
+  return rv;
+}
+
+
+/* parse arguments of quote command */
+static int runquote(int argc, char **argv){
+  char *path = NULL;
+  bool dec = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  char *ibuf;
+  int isiz;
+  if(path && path[0] == '@'){
+    isiz = strlen(path) - 1;
+    ibuf = tcmemdup(path + 1, isiz);
+  } else {
+    ibuf = tcreadfile(path, -1, &isiz);
+  }
+  if(!ibuf){
+    eprintf("%s: cannot open", path ? path : "(stdin)");
+    return 1;
+  }
+  int rv = procquote(ibuf, isiz, dec);
+  if(path && path[0] == '@') printf("\n");
+  free(ibuf);
+  return rv;
+}
+
+
+/* parse arguments of mime command */
+static int runmime(int argc, char **argv){
+  char *path = NULL;
+  bool dec = false;
+  char *ename = NULL;
+  bool qb = false;
+  bool on = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = true;
+      } else if(!strcmp(argv[i], "-en")){
+        if(++i >= argc) usage();
+        ename = argv[i];
+      } else if(!strcmp(argv[i], "-q")){
+        qb = true;
+      } else if(!strcmp(argv[i], "-on")){
+        on = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  char *ibuf;
+  int isiz;
+  if(path && path[0] == '@'){
+    isiz = strlen(path) - 1;
+    ibuf = tcmemdup(path + 1, isiz);
+  } else {
+    ibuf = tcreadfile(path, -1, &isiz);
+  }
+  if(!ibuf){
+    eprintf("%s: cannot open", path ? path : "(stdin)");
+    return 1;
+  }
+  if(!ename) ename = "UTF-8";
+  int rv = procmime(ibuf, isiz, dec, ename, qb, on);
+  if(path && path[0] == '@') printf("\n");
+  free(ibuf);
+  return rv;
+}
+
+
+/* parse arguments of pack command */
+static int runpack(int argc, char **argv){
+  char *path = NULL;
+  bool dec = false;
+  bool bwt = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = true;
+      } else if(!strcmp(argv[i], "-bwt")){
+        bwt = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  char *ibuf;
+  int isiz;
+  if(path && path[0] == '@'){
+    isiz = strlen(path) - 1;
+    ibuf = tcmemdup(path + 1, isiz);
+  } else {
+    ibuf = tcreadfile(path, -1, &isiz);
+  }
+  if(!ibuf){
+    eprintf("%s: cannot open", path ? path : "(stdin)");
+    return 1;
+  }
+  int rv = procpack(ibuf, isiz, dec, bwt);
+  if(path && path[0] == '@') printf("\n");
+  free(ibuf);
+  return rv;
+}
+
+
+/* parse arguments of tcbs command */
+static int runtcbs(int argc, char **argv){
+  char *path = NULL;
+  bool dec = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  char *ibuf;
+  int isiz;
+  if(path && path[0] == '@'){
+    isiz = strlen(path) - 1;
+    ibuf = tcmemdup(path + 1, isiz);
+  } else {
+    ibuf = tcreadfile(path, -1, &isiz);
+  }
+  if(!ibuf){
+    eprintf("%s: cannot open", path ? path : "(stdin)");
+    return 1;
+  }
+  int rv = proctcbs(ibuf, isiz, dec);
+  if(path && path[0] == '@') printf("\n");
+  free(ibuf);
+  return rv;
+}
+
+
+/* parse arguments of zlib command */
+static int runzlib(int argc, char **argv){
+  char *path = NULL;
+  bool dec = false;
+  bool gz = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = true;
+      } else if(!strcmp(argv[i], "-gz")){
+        gz = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  char *ibuf;
+  int isiz;
+  if(path && path[0] == '@'){
+    isiz = strlen(path) - 1;
+    ibuf = tcmemdup(path + 1, isiz);
+  } else {
+    ibuf = tcreadfile(path, -1, &isiz);
+  }
+  if(!ibuf){
+    eprintf("%s: cannot open", path ? path : "(stdin)");
+    return 1;
+  }
+  int rv = proczlib(ibuf, isiz, dec, gz);
+  if(path && path[0] == '@') printf("\n");
+  free(ibuf);
+  return rv;
+}
+
+
+/* parse arguments of xml command */
+static int runxml(int argc, char **argv){
+  char *path = NULL;
+  bool dec = false;
+  bool br = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = true;
+      } else if(!strcmp(argv[i], "-br")){
+        br = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  char *ibuf;
+  int isiz;
+  if(path && path[0] == '@'){
+    isiz = strlen(path) - 1;
+    ibuf = tcmemdup(path + 1, isiz);
+  } else {
+    ibuf = tcreadfile(path, -1, &isiz);
+  }
+  if(!ibuf){
+    eprintf("%s: cannot open", path ? path : "(stdin)");
+    return 1;
+  }
+  int rv = procxml(ibuf, isiz, dec, br);
+  if(path && path[0] == '@') printf("\n");
+  free(ibuf);
+  return rv;
+}
+
+
+/* parse arguments of ucs command */
+static int runucs(int argc, char **argv){
+  char *path = NULL;
+  bool dec = false;
+  for(int i = 2; i < argc; i++){
+    if(!path && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = true;
+      } else {
+        usage();
+      }
+    } else if(!path){
+      path = argv[i];
+    } else {
+      usage();
+    }
+  }
+  char *ibuf;
+  int isiz;
+  if(path && path[0] == '@'){
+    isiz = strlen(path) - 1;
+    ibuf = tcmemdup(path + 1, isiz);
+  } else {
+    ibuf = tcreadfile(path, -1, &isiz);
+  }
+  if(!ibuf){
+    eprintf("%s: cannot open", path ? path : "(stdin)");
+    return 1;
+  }
+  int rv = procucs(ibuf, isiz, dec);
+  if(path && path[0] == '@') printf("\n");
+  free(ibuf);
+  return rv;
+}
+
+
+/* parse arguments of date command */
+static int rundate(int argc, char **argv){
+  char *str = NULL;
+  int jl = INT_MAX;
+  bool wf = false;
+  bool rf = false;
+  for(int i = 2; i < argc; i++){
+    if(argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-ds")){
+        if(++i >= argc) usage();
+        str = argv[i];
+      } else if(!strcmp(argv[i], "-jl")){
+        if(++i >= argc) usage();
+        jl = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-wf")){
+        wf = true;
+      } else if(!strcmp(argv[i], "-rf")){
+        rf = true;
+      } else {
+        usage();
+      }
+    } else {
+      usage();
+    }
+  }
+  int rv = procdate(str, jl, wf, rf);
+  return rv;
+}
+
+
+/* parse arguments of conf command */
+static int runconf(int argc, char **argv){
+  int mode = 0;
+  for(int i = 2; i < argc; i++){
+    if(argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-v")){
+        mode = 'v';
+      } else if(!strcmp(argv[i], "-i")){
+        mode = 'i';
+      } else if(!strcmp(argv[i], "-l")){
+        mode = 'l';
+      } else if(!strcmp(argv[i], "-p")){
+        mode = 'p';
+      } else {
+        usage();
+      }
+    } else {
+      usage();
+    }
+  }
+  int rv = procconf(mode);
+  return rv;
+}
+
+
+/* perform url command */
+static int procurl(const char *ibuf, int isiz, bool dec, bool br, const char *base){
+  if(base){
+    char *obuf = tcurlresolve(base, ibuf);
+    printf("%s", obuf);
+    free(obuf);
+  } else if(br){
+    TCMAP *elems = tcurlbreak(ibuf);
+    const char *elem;
+    if((elem = tcmapget2(elems, "self")) != NULL) printf("self: %s\n", elem);
+    if((elem = tcmapget2(elems, "scheme")) != NULL) printf("scheme: %s\n", elem);
+    if((elem = tcmapget2(elems, "host")) != NULL) printf("host: %s\n", elem);
+    if((elem = tcmapget2(elems, "port")) != NULL) printf("port: %s\n", elem);
+    if((elem = tcmapget2(elems, "authority")) != NULL) printf("authority: %s\n", elem);
+    if((elem = tcmapget2(elems, "path")) != NULL) printf("path: %s\n", elem);
+    if((elem = tcmapget2(elems, "file")) != NULL) printf("file: %s\n", elem);
+    if((elem = tcmapget2(elems, "query")) != NULL) printf("query: %s\n", elem);
+    if((elem = tcmapget2(elems, "fragment")) != NULL) printf("fragment: %s\n", elem);
+    tcmapdel(elems);
+  } else if(dec){
+    int osiz;
+    char *obuf = tcurldecode(ibuf, &osiz);
+    fwrite(obuf, osiz, 1, stdout);
+    free(obuf);
+  } else {
+    char *obuf = tcurlencode(ibuf, isiz);
+    fwrite(obuf, strlen(obuf), 1, stdout);
+    free(obuf);
+  }
+  return 0;
+}
+
+
+/* perform base command */
+static int procbase(const char *ibuf, int isiz, bool dec){
+  if(dec){
+    int osiz;
+    char *obuf = tcbasedecode(ibuf, &osiz);
+    fwrite(obuf, osiz, 1, stdout);
+    free(obuf);
+  } else {
+    char *obuf = tcbaseencode(ibuf, isiz);
+    fwrite(obuf, strlen(obuf), 1, stdout);
+    free(obuf);
+  }
+  return 0;
+}
+
+
+/* perform quote command */
+static int procquote(const char *ibuf, int isiz, bool dec){
+  if(dec){
+    int osiz;
+    char *obuf = tcquotedecode(ibuf, &osiz);
+    fwrite(obuf, osiz, 1, stdout);
+    free(obuf);
+  } else {
+    char *obuf = tcquoteencode(ibuf, isiz);
+    fwrite(obuf, strlen(obuf), 1, stdout);
+    free(obuf);
+  }
+  return 0;
+}
+
+
+/* perform mime command */
+static int procmime(const char *ibuf, int isiz, bool dec, const char *ename, bool qb, bool on){
+  if(dec){
+    char ebuf[32];
+    char *obuf = tcmimedecode(ibuf, ebuf);
+    if(on){
+      fwrite(ebuf, strlen(ebuf), 1, stdout);
+    } else {
+      fwrite(obuf, strlen(obuf), 1, stdout);
+    }
+    free(obuf);
+  } else {
+    char *obuf = tcmimeencode(ibuf, ename, !qb);
+    fwrite(obuf, strlen(obuf), 1, stdout);
+    free(obuf);
+  }
+  return 0;
+}
+
+
+/* perform pack command */
+static int procpack(const char *ibuf, int isiz, bool dec, bool bwt){
+  if(dec){
+    int osiz;
+    char *obuf = tcpackdecode(ibuf, isiz, &osiz);
+    if(bwt && osiz > 0){
+      int idx, step;
+      TCREADVNUMBUF(obuf, idx, step);
+      char *tbuf = tcbwtdecode(obuf + step, osiz - step, idx);
+      fwrite(tbuf, osiz - step, 1, stdout);
+      free(tbuf);
+    } else {
+      fwrite(obuf, osiz, 1, stdout);
+    }
+    free(obuf);
+  } else {
+    char *tbuf = NULL;
+    if(bwt){
+      int idx;
+      tbuf = tcbwtencode(ibuf, isiz, &idx);
+      char vnumbuf[sizeof(int)+1];
+      int step;
+      TCSETVNUMBUF(step, vnumbuf, idx);
+      tbuf = tcrealloc(tbuf, isiz + step + 1);
+      memmove(tbuf + step, tbuf, isiz);
+      memcpy(tbuf, vnumbuf, step);
+      isiz += step;
+      ibuf = tbuf;
+    }
+    int osiz;
+    char *obuf = tcpackencode(ibuf, isiz, &osiz);
+    fwrite(obuf, osiz, 1, stdout);
+    free(obuf);
+    free(tbuf);
+  }
+  return 0;
+}
+
+
+/* perform tcbs command */
+static int proctcbs(const char *ibuf, int isiz, bool dec){
+  if(dec){
+    int osiz;
+    char *obuf = tcbsdecode(ibuf, isiz, &osiz);
+    fwrite(obuf, osiz, 1, stdout);
+    free(obuf);
+  } else {
+    int osiz;
+    char *obuf = tcbsencode(ibuf, isiz, &osiz);
+    fwrite(obuf, osiz, 1, stdout);
+    free(obuf);
+  }
+  return 0;
+}
+
+
+/* perform zlib command */
+static int proczlib(const char *ibuf, int isiz, bool dec, bool gz){
+  if(dec){
+    int osiz;
+    char *obuf = gz ? tcgzipdecode(ibuf, isiz, &osiz) : tcinflate(ibuf, isiz, &osiz);
+    if(obuf){
+      fwrite(obuf, osiz, 1, stdout);
+      free(obuf);
+    } else {
+      eprintf("inflate failure");
+    }
+  } else {
+    int osiz;
+    char *obuf = gz ? tcgzipencode(ibuf, isiz, &osiz) : tcdeflate(ibuf, isiz, &osiz);
+    if(obuf){
+      fwrite(obuf, osiz, 1, stdout);
+      free(obuf);
+    } else {
+      eprintf("deflate failure");
+    }
+  }
+  return 0;
+}
+
+
+/* perform xml command */
+static int procxml(const char *ibuf, int isiz, bool dec, bool br){
+  if(br){
+    TCLIST *elems = tcxmlbreak(ibuf);
+    for(int i = 0; i < tclistnum(elems); i++){
+      int esiz;
+      const char *elem = tclistval(elems, i, &esiz);
+      char *estr = tcmemdup(elem, esiz);
+      tcstrsubchr(estr, "\t\n\r", "  ");
+      tcstrtrim(estr);
+      if(*estr != '\0'){
+        if(*elem == '<'){
+          if(tcstrfwm(estr, "<!--")){
+            printf("COMMENT\t%s\n", estr);
+          } else if(tcstrfwm(estr, "<![CDATA[")){
+            printf("CDATA\t%s\n", estr);
+          } else if(tcstrfwm(estr, "<!")){
+            printf("DECL\t%s\n", estr);
+          } else if(tcstrfwm(estr, "<?")){
+            printf("XMLDECL\t%s\n", estr);
+          } else {
+            TCMAP *attrs = tcxmlattrs(estr);
+            tcmapiterinit(attrs);
+            const char *name;
+            if((name = tcmapget2(attrs, "")) != NULL){
+              if(tcstrfwm(estr, "</")){
+                printf("END");
+              } else if(tcstrbwm(estr, "/>")){
+                printf("EMPTY");
+              } else {
+                printf("BEGIN");
+              }
+              printf("\t%s", name);
+              while((name = tcmapiternext2(attrs)) != NULL){
+                if(*name == '\0') continue;
+                printf("\t%s\t%s", name, tcmapiterval2(name));
+              }
+              printf("\n");
+            }
+            tcmapdel(attrs);
+          }
+        } else {
+          char *dstr = tcxmlunescape(estr);
+          printf("TEXT\t%s\n", dstr);
+          free(dstr);
+        }
+      }
+      free(estr);
+    }
+    tclistdel(elems);
+  } else if(dec){
+    char *obuf = tcxmlunescape(ibuf);
+    fwrite(obuf, strlen(obuf), 1, stdout);
+    free(obuf);
+  } else {
+    char *obuf = tcxmlescape(ibuf);
+    fwrite(obuf, strlen(obuf), 1, stdout);
+    free(obuf);
+  }
+  return 0;
+}
+
+
+/* perform ucs command */
+static int procucs(const char *ibuf, int isiz, bool dec){
+  if(dec){
+    uint16_t *ary = tcmalloc(isiz + 1);
+    int anum = 0;
+    for(int i = 0; i < isiz; i += 2){
+      ary[anum++] = (((unsigned char *)ibuf)[i] << 8) + ((unsigned char *)ibuf)[i+1];
+    }
+    char *str = tcmalloc(isiz * 3 + 1);
+    tcstrucstoutf(ary, anum, str);
+    printf("%s", str);
+    free(str);
+    free(ary);
+  } else {
+    uint16_t *ary = tcmalloc(isiz * sizeof(uint16_t) + 1);
+    int anum;
+    tcstrutftoucs(ibuf, ary, &anum);
+    for(int i = 0; i < anum; i++){
+      int c = ary[i];
+      putchar(c >> 8);
+      putchar(c & 0xff);
+    }
+    free(ary);
+  }
+  return 0;
+}
+
+
+/* perform date command */
+static int procdate(const char *str, int jl, bool wf, bool rf){
+  int64_t t = str ? tcstrmktime(str) : time(NULL);
+  if(wf){
+    char buf[48];
+    tcdatestrwww(t, jl, buf);
+    printf("%s\n", buf);
+  } else if(rf){
+    char buf[48];
+    tcdatestrhttp(t, jl, buf);
+    printf("%s\n", buf);
+  } else {
+    printf("%lld\n", (long long int)t);
+  }
+  return 0;
+}
+
+
+/* perform conf command */
+static int procconf(int mode){
+  switch(mode){
+  case 'v':
+    printf("%s\n", tcversion);
+    break;
+  case 'i':
+    printf("%s\n", _TC_APPINC);
+    break;
+  case 'l':
+    printf("%s\n", _TC_APPLIBS);
+    break;
+  case 'p':
+    printf("%s\n", _TC_BINDIR);
+    break;
+  default:
+    printf("myconf(version): %s\n", tcversion);
+    printf("myconf(libver): %d\n", _TC_LIBVER);
+    printf("myconf(formatver): %s\n", _TC_FORMATVER);
+    printf("myconf(prefix): %s\n", _TC_PREFIX);
+    printf("myconf(includedir): %s\n", _TC_INCLUDEDIR);
+    printf("myconf(libdir): %s\n", _TC_LIBDIR);
+    printf("myconf(bindir): %s\n", _TC_BINDIR);
+    printf("myconf(libexecdir): %s\n", _TC_LIBEXECDIR);
+    printf("myconf(appinc): %s\n", _TC_APPINC);
+    printf("myconf(applibs): %s\n", _TC_APPLIBS);
+    printf("myconf(bigend): %d\n", TCBIGEND);
+    printf("myconf(usezlib): %d\n", TCUSEZLIB);
+    printf("sizeof(bool): %d\n", sizeof(bool));
+    printf("sizeof(char): %d\n", sizeof(char));
+    printf("sizeof(short): %d\n", sizeof(short));
+    printf("sizeof(int): %d\n", sizeof(int));
+    printf("sizeof(long): %d\n", sizeof(long));
+    printf("sizeof(long long): %d\n", sizeof(long long));
+    printf("sizeof(float): %d\n", sizeof(float));
+    printf("sizeof(double): %d\n", sizeof(double));
+    printf("sizeof(long double): %d\n", sizeof(long double));
+    printf("sizeof(size_t): %d\n", sizeof(size_t));
+    printf("sizeof(time_t): %d\n", sizeof(time_t));
+    printf("sizeof(off_t): %d\n", sizeof(off_t));
+    printf("sizeof(ino_t): %d\n", sizeof(ino_t));
+    printf("sizeof(intptr_t): %d\n", sizeof(intptr_t));
+    printf("sizeof(void *): %d\n", sizeof(void *));
+    printf("macro(CHAR_MAX): %llu\n", (unsigned long long)CHAR_MAX);
+    printf("macro(SHRT_MAX): %llu\n", (unsigned long long)SHRT_MAX);
+    printf("macro(INT_MAX): %llu\n", (unsigned long long)INT_MAX);
+    printf("macro(LONG_MAX): %llu\n", (unsigned long long)LONG_MAX);
+    printf("macro(LLONG_MAX): %llu\n", (unsigned long long)LLONG_MAX);
+    printf("macro(PATH_MAX): %llu\n", (unsigned long long)PATH_MAX);
+    printf("macro(RAND_MAX): %llu\n", (unsigned long long)RAND_MAX);
+    printf("sysconf(_SC_CLK_TCK): %ld\n", sysconf(_SC_CLK_TCK));
+    printf("sysconf(_SC_OPEN_MAX): %ld\n", sysconf(_SC_OPEN_MAX));
+    printf("sysconf(_SC_PAGESIZE): %ld\n", sysconf(_SC_PAGESIZE));
+    struct stat sbuf;
+    if(stat(MYCDIRSTR, &sbuf) == 0){
+      printf("stat(st_uid): %d\n", (int)sbuf.st_uid);
+      printf("stat(st_gid): %d\n", (int)sbuf.st_gid);
+      printf("stat(st_blksize): %d\n", (int)sbuf.st_blksize);
+    }
+  }
+  return 0;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcumttest.c b/bacula/src/lib/tokyocabinet/tcumttest.c
new file mode 100644 (file)
index 0000000..4712377
--- /dev/null
@@ -0,0 +1,499 @@
+/*************************************************************************************************
+ * The test cases of the on-memory database API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include "myconf.h"
+
+#define RECBUFSIZ      32                // buffer for records
+
+typedef struct {                         // type of structure for combo thread
+  TCMDB *mdb;
+  int rnum;
+  bool rnd;
+  int id;
+} TARGCOMBO;
+
+typedef struct {                         // type of structure for typical thread
+  TCMDB *mdb;
+  int rnum;
+  bool nc;
+  int rratio;
+  int id;
+} TARGTYPICAL;
+
+
+/* global variables */
+const char *g_progname;                  // program name
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(const char *func);
+static int myrand(int range);
+static int myrandnd(int range);
+static int runcombo(int argc, char **argv);
+static int runtypical(int argc, char **argv);
+static int proccombo(int tnum, int rnum, int bnum, bool rnd);
+static int proctypical(int tnum, int rnum, int bnum, bool nc, int rratio);
+static void *threadwrite(void *targ);
+static void *threadread(void *targ);
+static void *threadremove(void *targ);
+static void *threadtypical(void *targ);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "combo")){
+    rv = runcombo(argc, argv);
+  } else if(!strcmp(argv[1], "typical")){
+    rv = runtypical(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: test cases of the on-memory database API of Tokyo Cabinet\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s combo [-rnd] tnum rnum [bnum]\n", g_progname);
+  fprintf(stderr, "  %s typical [-nc] [-rr num] tnum rnum [bnum]\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+  va_list ap;
+  va_start(ap, format);
+  vprintf(format, ap);
+  fflush(stdout);
+  va_end(ap);
+}
+
+
+/* print error message of on-memory database */
+static void eprint(const char *func){
+  fprintf(stderr, "%s: %s: error\n", g_progname, func);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+  return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* get a random number based on normal distribution */
+static int myrandnd(int range){
+  int num = (int)tcdrandnd(range >> 1, range / 10);
+  return (num < 0 || num >= range) ? 0 : num;
+}
+
+
+/* parse arguments of combo command */
+static int runcombo(int argc, char **argv){
+  char *tstr = NULL;
+  char *rstr = NULL;
+  char *bstr = NULL;
+  bool rnd = false;
+  for(int i = 2; i < argc; i++){
+    if(!tstr && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-rnd")){
+        rnd = true;
+      } else {
+        usage();
+      }
+    } else if(!tstr){
+      tstr = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!tstr || !rstr) usage();
+  int tnum = atoi(tstr);
+  int rnum = atoi(rstr);
+  if(tnum < 1 || rnum < 1) usage();
+  int bnum = bstr ? atoi(bstr) : -1;
+  int rv = proccombo(tnum, rnum, bnum, rnd);
+  return rv;
+}
+
+
+/* parse arguments of typical command */
+static int runtypical(int argc, char **argv){
+  char *tstr = NULL;
+  char *rstr = NULL;
+  char *bstr = NULL;
+  int rratio = -1;
+  bool nc = false;
+  for(int i = 2; i < argc; i++){
+    if(!tstr && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nc")){
+        nc = true;
+      } else if(!strcmp(argv[i], "-rr")){
+        if(++i >= argc) usage();
+        rratio = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!tstr){
+      tstr = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!tstr || !rstr) usage();
+  int tnum = atoi(tstr);
+  int rnum = atoi(rstr);
+  if(tnum < 1 || rnum < 1) usage();
+  int bnum = bstr ? atoi(bstr) : -1;
+  int rv = proctypical(tnum, rnum, bnum, nc, rratio);
+  return rv;
+}
+
+
+/* perform combo command */
+static int proccombo(int tnum, int rnum, int bnum, bool rnd){
+  iprintf("<Combination Test>\n  tnum=%d  rnum=%d  bnum=%d  rnd=%d\n\n", tnum, rnum, bnum, rnd);
+  bool err = false;
+  double stime = tctime();
+  TCMDB *mdb = (bnum > 0) ? tcmdbnew2(bnum) : tcmdbnew();
+  TARGCOMBO targs[tnum];
+  pthread_t threads[tnum];
+  if(tnum == 1){
+    targs[0].mdb = mdb;
+    targs[0].rnum = rnum;
+    targs[0].rnd = rnd;
+    targs[0].id = 0;
+    if(threadwrite(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].mdb = mdb;
+      targs[i].rnum = rnum;
+      targs[i].rnd = rnd;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){
+        eprint("pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint("pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  if(tnum == 1){
+    targs[0].mdb = mdb;
+    targs[0].rnum = rnum;
+    targs[0].rnd = rnd;
+    targs[0].id = 0;
+    if(threadread(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].mdb = mdb;
+      targs[i].rnum = rnum;
+      targs[i].rnd = rnd;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){
+        eprint("pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint("pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  if(tnum == 1){
+    targs[0].mdb = mdb;
+    targs[0].rnum = rnum;
+    targs[0].rnd = rnd;
+    targs[0].id = 0;
+    if(threadremove(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].mdb = mdb;
+      targs[i].rnum = rnum;
+      targs[i].rnd = rnd;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){
+        eprint("pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint("pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcmdbrnum(mdb));
+  tcmdbdel(mdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* perform typical command */
+static int proctypical(int tnum, int rnum, int bnum, bool nc, int rratio){
+  iprintf("<Typical Access Test>\n  tnum=%d  rnum=%d  bnum=%d  nc=%d  rratio=%d\n\n",
+          tnum, rnum, bnum, nc, rratio);
+  bool err = false;
+  double stime = tctime();
+  TCMDB *mdb = (bnum > 0) ? tcmdbnew2(bnum) : tcmdbnew();
+  TARGTYPICAL targs[tnum];
+  pthread_t threads[tnum];
+  if(tnum == 1){
+    targs[0].mdb = mdb;
+    targs[0].rnum = rnum;
+    targs[0].nc = nc;
+    targs[0].rratio = rratio;
+    targs[0].id = 0;
+    if(threadtypical(targs) != NULL) err = true;
+  } else {
+    for(int i = 0; i < tnum; i++){
+      targs[i].mdb = mdb;
+      targs[i].rnum = rnum;
+      targs[i].nc = nc;
+      targs[i].rratio= rratio;
+      targs[i].id = i;
+      if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){
+        eprint("pthread_create");
+        targs[i].id = -1;
+        err = true;
+      }
+    }
+    for(int i = 0; i < tnum; i++){
+      if(targs[i].id == -1) continue;
+      void *rv;
+      if(pthread_join(threads[i], &rv) != 0){
+        eprint("pthread_join");
+        err = true;
+      } else if(rv){
+        err = true;
+      }
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcmdbrnum(mdb));
+  tcmdbdel(mdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("%s\n\n", err ? "error" : "ok");
+  return err ? 1 : 0;
+}
+
+
+/* thread the write function */
+static void *threadwrite(void *targ){
+  TCMDB *mdb = ((TARGCOMBO *)targ)->mdb;
+  int rnum = ((TARGCOMBO *)targ)->rnum;
+  bool rnd = ((TARGCOMBO *)targ)->rnd;
+  int id = ((TARGCOMBO *)targ)->id;
+  double stime = tctime();
+  if(id == 0) iprintf("writing:\n");
+  int base = id * rnum;
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i));
+    tcmdbput(mdb, buf, len, buf, len);
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(id == 0) iprintf("time: %.3f\n", tctime() - stime);
+  return NULL;
+}
+
+
+/* thread the read function */
+static void *threadread(void *targ){
+  TCMDB *mdb = ((TARGCOMBO *)targ)->mdb;
+  int rnum = ((TARGCOMBO *)targ)->rnum;
+  bool rnd = ((TARGCOMBO *)targ)->rnd;
+  int id = ((TARGCOMBO *)targ)->id;
+  double stime = tctime();
+  if(id == 0) iprintf("reading:\n");
+  int base = id * rnum;
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i) : i));
+    int vsiz;
+    char *vbuf = tcmdbget(mdb, kbuf, ksiz, &vsiz);
+    if(vbuf) free(vbuf);
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(id == 0) iprintf("time: %.3f\n", tctime() - stime);
+  return NULL;
+}
+
+
+/* thread the remove function */
+static void *threadremove(void *targ){
+  TCMDB *mdb = ((TARGCOMBO *)targ)->mdb;
+  int rnum = ((TARGCOMBO *)targ)->rnum;
+  bool rnd = ((TARGCOMBO *)targ)->rnd;
+  int id = ((TARGCOMBO *)targ)->id;
+  double stime = tctime();
+  if(id == 0) iprintf("removing:\n");
+  int base = id * rnum;
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i));
+    tcmdbout(mdb, buf, len);
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(id == 0) iprintf("time: %.3f\n", tctime() - stime);
+  return NULL;
+}
+
+
+/* thread the typical function */
+static void *threadtypical(void *targ){
+  TCMDB *mdb = ((TARGTYPICAL *)targ)->mdb;
+  int rnum = ((TARGTYPICAL *)targ)->rnum;
+  bool nc = ((TARGTYPICAL *)targ)->nc;
+  int rratio = ((TARGTYPICAL *)targ)->rratio;
+  int id = ((TARGTYPICAL *)targ)->id;
+  bool err = false;
+  TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL;
+  int base = id * rnum;
+  int mrange = tclmax(50 + rratio, 100);
+  for(int i = 1; !err && i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", base + myrandnd(i));
+    int rnd = myrand(mrange);
+    if(rnd < 10){
+      tcmdbput(mdb, buf, len, buf, len);
+      if(map) tcmapput(map, buf, len, buf, len);
+    } else if(rnd < 15){
+      tcmdbputkeep(mdb, buf, len, buf, len);
+      if(map) tcmapputkeep(map, buf, len, buf, len);
+    } else if(rnd < 20){
+      tcmdbputcat(mdb, buf, len, buf, len);
+      if(map) tcmapputcat(map, buf, len, buf, len);
+    } else if(rnd < 30){
+      tcmdbout(mdb, buf, len);
+      if(map) tcmapout(map, buf, len);
+    } else if(rnd < 31){
+      if(myrand(10) == 0) tcmdbiterinit(mdb);
+      for(int j = 0; !err && j < 10; j++){
+        int ksiz;
+        char *kbuf = tcmdbiternext(mdb, &ksiz);
+        if(kbuf) free(kbuf);
+      }
+    } else {
+      int vsiz;
+      char *vbuf = tcmdbget(mdb, buf, len, &vsiz);
+      if(vbuf){
+        if(map){
+          int msiz;
+          const char *mbuf = tcmapget(map, buf, len, &msiz);
+          if(msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+            eprint("(validation)");
+            err = true;
+          }
+        }
+        free(vbuf);
+      } else {
+        if(map && tcmapget(map, buf, len, &vsiz)){
+          eprint("(validation)");
+          err = true;
+        }
+      }
+    }
+    if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  if(map){
+    tcmapiterinit(map);
+    int ksiz;
+    const char *kbuf;
+    while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
+      int vsiz;
+      char *vbuf = tcmdbget(mdb, kbuf, ksiz, &vsiz);
+      if(vbuf){
+        int msiz;
+        const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz);
+        if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+          eprint("(validation)");
+          err = true;
+        }
+        free(vbuf);
+      } else {
+        eprint("(validation)");
+        err = true;
+      }
+    }
+    tcmapdel(map);
+  }
+  return err ? "error" : NULL;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcutest.c b/bacula/src/lib/tokyocabinet/tcutest.c
new file mode 100644 (file)
index 0000000..75fbf48
--- /dev/null
@@ -0,0 +1,946 @@
+/*************************************************************************************************
+ * The test cases of the utility API
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include "myconf.h"
+
+#define RECBUFSIZ      32                // buffer for records
+
+
+/* global variables */
+const char *g_progname;                  // program name
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static int myrand(int range);
+static int runxstr(int argc, char **argv);
+static int runlist(int argc, char **argv);
+static int runmap(int argc, char **argv);
+static int runmdb(int argc, char **argv);
+static int runmisc(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int procxstr(int rnum);
+static int proclist(int rnum, int anum);
+static int procmap(int rnum, int bnum);
+static int procmdb(int rnum, int bnum);
+static int procmisc(int rnum);
+static int procwicked(int rnum);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  g_progname = argv[0];
+  srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+  if(argc < 2) usage();
+  int rv = 0;
+  if(!strcmp(argv[1], "xstr")){
+    rv = runxstr(argc, argv);
+  } else if(!strcmp(argv[1], "list")){
+    rv = runlist(argc, argv);
+  } else if(!strcmp(argv[1], "map")){
+    rv = runmap(argc, argv);
+  } else if(!strcmp(argv[1], "mdb")){
+    rv = runmdb(argc, argv);
+  } else if(!strcmp(argv[1], "misc")){
+    rv = runmisc(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+  fprintf(stderr, "%s: test cases of the utility API of Tokyo Cabinet\n", g_progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s xstr rnum\n", g_progname);
+  fprintf(stderr, "  %s list rnum [anum]\n", g_progname);
+  fprintf(stderr, "  %s map rnum [bnum]\n", g_progname);
+  fprintf(stderr, "  %s mdb rnum [bnum]\n", g_progname);
+  fprintf(stderr, "  %s misc rnum\n", g_progname);
+  fprintf(stderr, "  %s wicked rnum\n", g_progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+  va_list ap;
+  va_start(ap, format);
+  vprintf(format, ap);
+  fflush(stdout);
+  va_end(ap);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+  return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* parse arguments of xstr command */
+static int runxstr(int argc, char **argv){
+  char *rstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!rstr && argv[i][0] == '-'){
+      usage();
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procxstr(rnum);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+static int runlist(int argc, char **argv){
+  char *rstr = NULL;
+  char *astr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!rstr && argv[i][0] == '-'){
+      usage();
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int anum = astr ? atoi(astr) : -1;
+  int rv = proclist(rnum, anum);
+  return rv;
+}
+
+
+/* parse arguments of map command */
+static int runmap(int argc, char **argv){
+  char *rstr = NULL;
+  char *bstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!rstr && argv[i][0] == '-'){
+      usage();
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int bnum = bstr ? atoi(bstr) : -1;
+  int rv = procmap(rnum, bnum);
+  return rv;
+}
+
+
+/* parse arguments of mdb command */
+static int runmdb(int argc, char **argv){
+  char *rstr = NULL;
+  char *bstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!rstr && argv[i][0] == '-'){
+      usage();
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int bnum = bstr ? atoi(bstr) : -1;
+  int rv = procmdb(rnum, bnum);
+  return rv;
+}
+
+
+/* parse arguments of misc command */
+static int runmisc(int argc, char **argv){
+  char *rstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!rstr && argv[i][0] == '-'){
+      usage();
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procmisc(rnum);
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+  char *rstr = NULL;
+  for(int i = 2; i < argc; i++){
+    if(!rstr && argv[i][0] == '-'){
+      usage();
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  int rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  int rv = procwicked(rnum);
+  return rv;
+}
+
+
+/* perform xstr command */
+static int procxstr(int rnum){
+  iprintf("<Extensible String Writing Test>\n  rnum=%d\n\n", rnum);
+  double stime = tctime();
+  TCXSTR *xstr = tcxstrnew();
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    tcxstrcat(xstr, buf, len);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  tcxstrdel(xstr);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("ok\n\n");
+  return 0;
+}
+
+
+/* perform list command */
+static int proclist(int rnum, int anum){
+  iprintf("<List Writing Test>\n  rnum=%d  anum=%d\n\n", rnum, anum);
+  double stime = tctime();
+  TCLIST *list = (anum > 0) ? tclistnew2(anum) : tclistnew();
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    tclistpush(list, buf, len);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  tclistdel(list);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("ok\n\n");
+  return 0;
+}
+
+
+/* perform map command */
+static int procmap(int rnum, int bnum){
+  iprintf("<Map Writing Test>\n  rnum=%d\n\n", rnum);
+  double stime = tctime();
+  TCMAP *map = (bnum > 0) ? tcmapnew2(bnum) : tcmapnew();
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    tcmapput(map, buf, len, buf, len);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  tcmapdel(map);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("ok\n\n");
+  return 0;
+}
+
+
+/* perform mdb command */
+static int procmdb(int rnum, int bnum){
+  iprintf("<On-memory Database Writing Test>\n  rnum=%d\n\n", rnum);
+  double stime = tctime();
+  TCMDB *mdb = (bnum > 0) ? tcmdbnew2(bnum) : tcmdbnew();
+  for(int i = 1; i <= rnum; i++){
+    char buf[RECBUFSIZ];
+    int len = sprintf(buf, "%08d", i);
+    tcmdbput(mdb, buf, len, buf, len);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("record number: %llu\n", (unsigned long long)tcmdbrnum(mdb));
+  iprintf("size: %llu\n", (unsigned long long)tcmdbmsiz(mdb));
+  tcmdbdel(mdb);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("ok\n\n");
+  return 0;
+}
+
+
+/* perform misc command */
+static int procmisc(int rnum){
+  iprintf("<Miscellaneous Test>\n  rnum=%d\n\n", rnum);
+  double stime = tctime();
+  bool err = false;
+  for(int i = 1; i <= rnum && !err; i++){
+    const char *str = "5%2+3-1=4 \"Yes/No\" <a&b>";
+    int slen = strlen(str);
+    char *buf, *dec;
+    int bsiz, dsiz, jl;
+    time_t date, ddate;
+    TCXSTR *xstr;
+    TCLIST *list;
+    TCMAP *map;
+    buf = tcmemdup(str, slen);
+    xstr = tcxstrfrommalloc(buf, slen);
+    buf = tcxstrtomalloc(xstr);
+    if(strcmp(buf, str)) err = true;
+    free(buf);
+    if(tclmax(1, 2) != 2) err = true;
+    if(tclmin(1, 2) != 1) err = true;
+    tclrand();
+    if(tcdrand() >= 1.0) err = true;
+    tcdrandnd(50, 10);
+    if(tcstricmp("abc", "ABC") != 0) err = true;
+    if(!tcstrfwm("abc", "ab") || !tcstrifwm("abc", "AB")) err = true;
+    if(!tcstrbwm("abc", "bc") || !tcstribwm("abc", "BC")) err = true;
+    if(tcstrdist("abcde", "abdfgh") != 4 || tcstrdist("abdfgh", "abcde") != 4) err = true;
+    if(tcstrdistutf("abcde", "abdfgh") != 4 || tcstrdistutf("abdfgh", "abcde") != 4) err = true;
+    buf = tcmemdup("abcde", 5);
+    tcstrtoupper(buf);
+    if(strcmp(buf, "ABCDE")) err = true;
+    tcstrtolower(buf);
+    if(strcmp(buf, "abcde")) err = true;
+    free(buf);
+    buf = tcmemdup("  ab  cd  ", 10);
+    tcstrtrim(buf);
+    if(strcmp(buf, "ab  cd")) err = true;
+    tcstrsqzspc(buf);
+    if(strcmp(buf, "ab cd")) err = true;
+    tcstrsubchr(buf, "cd", "C");
+    if(strcmp(buf, "ab C")) err = true;
+    if(tcstrcntutf(buf) != 4) err = true;
+    tcstrcututf(buf, 2);
+    if(strcmp(buf, "ab")) err = true;
+    free(buf);
+    if(i % 10 == 1){
+      int anum = myrand(30);
+      uint16_t ary[anum+1];
+      for(int j = 0; j < anum; j++){
+        ary[j] = myrand(65535) + 1;
+      }
+      char ustr[anum*3+1];
+      tcstrucstoutf(ary, anum, ustr);
+      uint16_t dary[anum+1];
+      int danum;
+      tcstrutftoucs(ustr, dary, &danum);
+      if(danum != anum){
+        err = true;
+      } else {
+        for(int j = 0; j < danum; j++){
+          if(dary[j] != dary[j]) err = true;
+        }
+      }
+      list = tcstrsplit(",a,b..c,d,", ",.");
+      if(tclistnum(list) != 7) err = true;
+      buf = tcstrjoin(list, ':');
+      if(strcmp(buf, ":a:b::c:d:")) err = true;
+      free(buf);
+      tclistdel(list);
+      if(!tcregexmatch("ABCDEFGHI", "*(b)c[d-f]*g(h)")) err = true;
+      buf = tcregexreplace("ABCDEFGHI", "*(b)c[d-f]*g(h)", "[\\1][\\2][&]");
+      if(strcmp(buf, "A[B][H][BCDEFGH]I")) err = true;
+      free(buf);
+    }
+    buf = tcmalloc(48);
+    date = myrand(INT_MAX - 1000000);
+    jl = 3600 * (myrand(23) - 11);
+    tcdatestrwww(date, jl, buf);
+    ddate = tcstrmktime(buf);
+    if(ddate != date) err = true;
+    tcdatestrhttp(date, jl, buf);
+    ddate = tcstrmktime(buf);
+    if(ddate != date) err = true;
+    free(buf);
+    if(i % 100){
+      map = tcmapnew();
+      for(int j = 0; j < 10; j++){
+        char kbuf[RECBUFSIZ];
+        int ksiz = sprintf(kbuf, "%d", myrand(10));
+        tcmapaddint(map, kbuf, ksiz, 1);
+        const char *vbuf = tcmapget2(map, kbuf);
+        if(*(int *)vbuf < 1) err = true;
+      }
+      tcmapdel(map);
+    }
+    buf = tcurlencode(str, slen);
+    if(strcmp(buf, "5%252%2B3-1%3D4%20%22Yes%2FNo%22%20%3Ca%26b%3E")) err = true;
+    dec = tcurldecode(buf, &dsiz);
+    if(dsiz != slen || strcmp(dec, str)) err = true;
+    free(dec);
+    free(buf);
+    if(i % 10 == 1){
+      map = tcurlbreak("http://mikio:oikim@estraier.net:1978/foo/bar/baz.cgi?ab=cd&ef=jkl#quux");
+      const char *elem;
+      if(!(elem = tcmapget2(map, "self")) ||
+         strcmp(elem, "http://mikio:oikim@estraier.net:1978/foo/bar/baz.cgi?ab=cd&ef=jkl#quux"))
+        err = true;
+      if(!(elem = tcmapget2(map, "scheme")) || strcmp(elem, "http")) err = true;
+      if(!(elem = tcmapget2(map, "host")) || strcmp(elem, "estraier.net")) err = true;
+      if(!(elem = tcmapget2(map, "port")) || strcmp(elem, "1978")) err = true;
+      if(!(elem = tcmapget2(map, "authority")) || strcmp(elem, "mikio:oikim")) err = true;
+      if(!(elem = tcmapget2(map, "path")) || strcmp(elem, "/foo/bar/baz.cgi")) err = true;
+      if(!(elem = tcmapget2(map, "file")) || strcmp(elem, "baz.cgi")) err = true;
+      if(!(elem = tcmapget2(map, "query")) || strcmp(elem, "ab=cd&ef=jkl")) err = true;
+      if(!(elem = tcmapget2(map, "fragment")) || strcmp(elem, "quux")) err = true;
+      tcmapdel(map);
+      buf = tcurlresolve("http://a:b@c.d:1/e/f/g.h?i=j#k", "http://A:B@C.D:1/E/F/G.H?I=J#K");
+      if(strcmp(buf, "http://A:B@c.d:1/E/F/G.H?I=J#K")) err = true;
+      free(buf);
+      buf = tcurlresolve("http://a:b@c.d:1/e/f/g.h?i=j#k", "/E/F/G.H?I=J#K");
+      if(strcmp(buf, "http://a:b@c.d:1/E/F/G.H?I=J#K")) err = true;
+      free(buf);
+      buf = tcurlresolve("http://a:b@c.d:1/e/f/g.h?i=j#k", "G.H?I=J#K");
+      if(strcmp(buf, "http://a:b@c.d:1/e/f/G.H?I=J#K")) err = true;
+      free(buf);
+      buf = tcurlresolve("http://a:b@c.d:1/e/f/g.h?i=j#k", "?I=J#K");
+      if(strcmp(buf, "http://a:b@c.d:1/e/f/g.h?I=J#K")) err = true;
+      free(buf);
+      buf = tcurlresolve("http://a:b@c.d:1/e/f/g.h?i=j#k", "#K");
+      if(strcmp(buf, "http://a:b@c.d:1/e/f/g.h?i=j#K")) err = true;
+      free(buf);
+    }
+    buf = tcbaseencode(str, slen);
+    if(strcmp(buf, "NSUyKzMtMT00ICJZZXMvTm8iIDxhJmI+")) err = true;
+    dec = tcbasedecode(buf, &dsiz);
+    if(dsiz != slen || strcmp(dec, str)) err = true;
+    free(dec);
+    free(buf);
+    buf = tcquoteencode(str, slen);
+    if(strcmp(buf, "5%2+3-1=3D4 \"Yes/No\" <a&b>")) err = true;
+    dec = tcquotedecode(buf, &dsiz);
+    if(dsiz != slen || strcmp(dec, str)) err = true;
+    free(dec);
+    free(buf);
+    buf = tcmimeencode(str, "UTF-8", true);
+    if(strcmp(buf, "=?UTF-8?B?NSUyKzMtMT00ICJZZXMvTm8iIDxhJmI+?=")) err = true;
+    char encname[32];
+    dec = tcmimedecode(buf, encname);
+    if(strcmp(dec, str) || strcmp(encname, "UTF-8")) err = true;
+    free(dec);
+    free(buf);
+    buf = tcpackencode(str, slen, &bsiz);
+    dec = tcpackdecode(buf, bsiz, &dsiz);
+    if(dsiz != slen || strcmp(dec, str)) err = true;
+    free(dec);
+    free(buf);
+    buf = tcbsencode(str, slen, &bsiz);
+    dec = tcbsdecode(buf, bsiz, &dsiz);
+    if(dsiz != slen || strcmp(dec, str)) err = true;
+    free(dec);
+    free(buf);
+    int idx;
+    buf = tcbwtencode(str, slen, &idx);
+    if(memcmp(buf, "4\"o 5a23s-%+=> 1b/\"<&YNe", slen) || idx != 13) err = true;
+    dec = tcbwtdecode(buf, slen, idx);
+    if(memcmp(dec, str, slen)) err = true;
+    free(dec);
+    free(buf);
+    if(_tc_deflate){
+      if((buf = tcdeflate(str, slen, &bsiz)) != NULL){
+        if((dec = tcinflate(buf, bsiz, &dsiz)) != NULL){
+          if(slen != dsiz || memcmp(str, dec, dsiz)) err = true;
+          free(dec);
+        } else {
+          err = true;
+        }
+        free(buf);
+      } else {
+        err = true;
+      }
+      if((buf = tcgzipencode(str, slen, &bsiz)) != NULL){
+        if((dec = tcgzipdecode(buf, bsiz, &dsiz)) != NULL){
+          if(slen != dsiz || memcmp(str, dec, dsiz)) err = true;
+          free(dec);
+        } else {
+          err = true;
+        }
+        free(buf);
+      } else {
+        err = true;
+      }
+      if(tcgetcrc("hoge", 4) % 10000 != 7034) err = true;
+    }
+    int anum = myrand(50)+1;
+    unsigned int ary[anum];
+    for(int j = 0; j < anum; j++){
+      ary[j] = myrand(INT_MAX);
+    }
+    buf = tcberencode(ary, anum, &bsiz);
+    int dnum;
+    unsigned int *dary = tcberdecode(buf, bsiz, &dnum);
+    if(anum != dnum || memcmp(ary, dary, sizeof(*dary) * dnum)) err = true;
+    free(dary);
+    free(buf);
+    buf = tcxmlescape(str);
+    if(strcmp(buf, "5%2+3-1=4 &quot;Yes/No&quot; &lt;a&amp;b&gt;")) err = true;
+    dec = tcxmlunescape(buf);
+    if(strcmp(dec, str)) err = true;
+    free(dec);
+    free(buf);
+    if(i % 10 == 1){
+      list = tcxmlbreak("<abc de=\"foo&amp;\" gh='&lt;bar&gt;'>xyz<br>\na<!--<mikio>--></abc>");
+      for(int j = 0; j < tclistnum(list); j++){
+        const char *elem = tclistval2(list, j);
+        TCMAP *attrs = tcxmlattrs(elem);
+        tcmapdel(attrs);
+      }
+      tclistdel(list);
+    }
+    if(i % 10 == 1){
+      for(int16_t j = 1; j <= 0x2000; j *= 2){
+        for(int16_t num = j - 1; num <= j + 1; num++){
+          int16_t nnum = TCHTOIS(num);
+          if(num != TCITOHS(nnum)) err = true;
+        }
+      }
+      for(int32_t j = 1; j <= 0x20000000; j *= 2){
+        for(int32_t num = j - 1; num <= j + 1; num++){
+          int32_t nnum = TCHTOIL(num);
+          if(num != TCITOHL(nnum)) err = true;
+          char buf[TCNUMBUFSIZ];
+          int step, nstep;
+          TCSETVNUMBUF(step, buf, num);
+          TCREADVNUMBUF(buf, nnum, nstep);
+          if(num != nnum || step != nstep) err = true;
+        }
+      }
+      for(int64_t j = 1; j <= 0x2000000000000000; j *= 2){
+        for(int64_t num = j - 1; num <= j + 1; num++){
+          int64_t nnum = TCHTOILL(num);
+          if(num != TCITOHLL(nnum)) err = true;
+          char buf[TCNUMBUFSIZ];
+          int step, nstep;
+          TCSETVNUMBUF64(step, buf, num);
+          TCREADVNUMBUF64(buf, nnum, nstep);
+          if(num != nnum || step != nstep) err = true;
+        }
+      }
+      char *bitmap = TCBITMAPNEW(100);
+      for(int j = 0; j < 100; j++){
+        if(j % 3 == 0) TCBITMAPON(bitmap, j);
+        if(j % 5 == 0) TCBITMAPOFF(bitmap, j);
+      }
+      for(int j = 0; j < 100; j++){
+        if(j % 5 == 0){
+          if(TCBITMAPCHECK(bitmap, j)) err = true;
+        } else if(j % 3 == 0){
+          if(!TCBITMAPCHECK(bitmap, j)) err = true;
+        }
+      }
+      TCBITMAPDEL(bitmap);
+      buf = tcmalloc(i / 8 + 2);
+      TCBITSTRM strm;
+      TCBITSTRMINITW(strm, buf);
+      for(int j = 0; j < i; j++){
+        int sign = j % 3 == 0 || j % 7 == 0;
+        TCBITSTRMCAT(strm, sign);
+      }
+      TCBITSTRMSETEND(strm);
+      int bnum = TCBITSTRMNUM(strm);
+      if(bnum != i) err = true;
+      TCBITSTRMINITR(strm, buf, bsiz);
+      for(int j = 0; j < i; j++){
+        int sign;
+        TCBITSTRMREAD(strm, sign);
+        if(sign != (j % 3 == 0 || j % 7 == 0)) err = true;
+      }
+      free(buf);
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+    }
+  }
+  iprintf("time: %.3f\n", tctime() - stime);
+  if(err){
+    iprintf("error\n\n");
+    return 1;
+  }
+  iprintf("ok\n\n");
+  return 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(int rnum){
+  iprintf("<Wicked Writing Test>\n  rnum=%d\n\n", rnum);
+  double stime = tctime();
+  TCMPOOL *mpool = tcmpoolglobal();
+  TCXSTR *xstr = myrand(2) > 0 ? tcxstrnew() : tcxstrnew2("hello world");
+  tcmpoolputxstr(mpool, xstr);
+  TCLIST *list = myrand(2) > 0 ? tclistnew() : tclistnew2(myrand(rnum) + rnum / 2);
+  tcmpoolputlist(mpool, list);
+  TCMAP *map = myrand(2) > 0 ? tcmapnew() : tcmapnew2(myrand(rnum) + rnum / 2);
+  tcmpoolputmap(mpool, map);
+  TCMDB *mdb = myrand(2) > 0 ? tcmdbnew() : tcmdbnew2(myrand(rnum) + rnum / 2);
+  tcmpoolput(mpool, mdb, (void (*)(void*))tcmdbdel);
+  for(int i = 1; i <= rnum; i++){
+    char kbuf[RECBUFSIZ];
+    int ksiz = sprintf(kbuf, "%d", myrand(i));
+    char vbuf[RECBUFSIZ];
+    int vsiz = sprintf(vbuf, "%d", myrand(i));
+    char *tmp;
+    switch(myrand(69)){
+    case 0:
+      putchar('0');
+      tcxstrcat(xstr, kbuf, ksiz);
+      break;
+    case 1:
+      putchar('1');
+      tcxstrcat2(xstr, kbuf);
+      break;
+    case 2:
+      putchar('2');
+      if(myrand(rnum / 100 + 1) == 0) tcxstrclear(xstr);
+      break;
+    case 3:
+      putchar('3');
+      tcxstrprintf(xstr, "[%s:%d]", kbuf, i);
+      break;
+    case 4:
+      putchar('4');
+      tclistpush(list, kbuf, ksiz);
+      break;
+    case 5:
+      putchar('5');
+      tclistpush2(list, kbuf);
+      break;
+    case 6:
+      putchar('6');
+      tmp = tcmemdup(kbuf, ksiz);
+      tclistpushmalloc(list, tmp, strlen(tmp));
+      break;
+    case 7:
+      putchar('7');
+      if(myrand(10) == 0) free(tclistpop(list, &ksiz));
+      break;
+    case 8:
+      putchar('8');
+      if(myrand(10) == 0) free(tclistpop2(list));
+      break;
+    case 9:
+      putchar('9');
+      tclistunshift(list, kbuf, ksiz);
+      break;
+    case 10:
+      putchar('A');
+      tclistunshift2(list, kbuf);
+      break;
+    case 11:
+      putchar('B');
+      if(myrand(10) == 0) free(tclistshift(list, &ksiz));
+      break;
+    case 12:
+      putchar('C');
+      if(myrand(10) == 0) free(tclistshift2(list));
+      break;
+    case 13:
+      putchar('D');
+      tclistinsert(list, i / 10, kbuf, ksiz);
+      break;
+    case 14:
+      putchar('E');
+      tclistinsert2(list, i / 10, kbuf);
+      break;
+    case 15:
+      putchar('F');
+      if(myrand(10) == 0) free(tclistremove(list, i / 10, &ksiz));
+      break;
+    case 16:
+      putchar('G');
+      if(myrand(10) == 0) free(tclistremove2(list, i / 10));
+      break;
+    case 17:
+      putchar('H');
+      tclistover(list, i / 10, kbuf, ksiz);
+      break;
+    case 18:
+      putchar('I');
+      tclistover2(list, i / 10, kbuf);
+      break;
+    case 19:
+      putchar('J');
+      if(myrand(rnum / 1000 + 1) == 0) tclistsort(list);
+      break;
+    case 20:
+      putchar('K');
+      if(myrand(rnum / 1000 + 1) == 0) tclistsortci(list);
+      break;
+    case 21:
+      putchar('L');
+      if(myrand(rnum / 1000 + 1) == 0) tclistlsearch(list, kbuf, ksiz);
+      break;
+    case 22:
+      putchar('M');
+      if(myrand(rnum / 1000 + 1) == 0) tclistbsearch(list, kbuf, ksiz);
+      break;
+    case 23:
+      putchar('N');
+      if(myrand(rnum / 100 + 1) == 0) tclistclear(list);
+      break;
+    case 24:
+      putchar('O');
+      if(myrand(rnum / 100 + 1) == 0){
+        int dsiz;
+        char *dbuf = tclistdump(list, &dsiz);
+        tclistdel(tclistload(dbuf, dsiz));
+        free(dbuf);
+      }
+      break;
+    case 25:
+      putchar('P');
+      if(myrand(100) == 0){
+        if(myrand(2) == 0){
+          for(int j = 0; j < tclistnum(list); j++){
+            int rsiz;
+            tclistval(list, j, &rsiz);
+          }
+        } else {
+          for(int j = 0; j < tclistnum(list); j++){
+            tclistval2(list, j);
+          }
+        }
+      }
+      break;
+    case 26:
+      putchar('Q');
+      tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 27:
+      putchar('R');
+      tcmapput2(map, kbuf, vbuf);
+      break;
+    case 28:
+      putchar('S');
+      tcmapput3(map, kbuf, ksiz, vbuf, vsiz, vbuf, vsiz);
+      break;
+    case 29:
+      putchar('T');
+      tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 30:
+      putchar('U');
+      tcmapputkeep2(map, kbuf, vbuf);
+      break;
+    case 31:
+      putchar('V');
+      tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 32:
+      putchar('W');
+      tcmapputcat2(map, kbuf, vbuf);
+      break;
+    case 33:
+      putchar('X');
+      if(myrand(10) == 0) tcmapout(map, kbuf, ksiz);
+      break;
+    case 34:
+      putchar('Y');
+      if(myrand(10) == 0) tcmapout2(map, kbuf);
+      break;
+    case 35:
+      putchar('Z');
+      tcmapget3(map, kbuf, ksiz, &vsiz);
+      break;
+    case 36:
+      putchar('a');
+      tcmapmove(map, kbuf, ksiz, true);
+      break;
+    case 37:
+      putchar('b');
+      tcmapmove(map, kbuf, ksiz, false);
+      break;
+    case 38:
+      putchar('c');
+      tcmapmove2(map, kbuf, true);
+      break;
+    case 39:
+      putchar('d');
+      if(myrand(100) == 0) tcmapiterinit(map);
+      break;
+    case 40:
+      putchar('e');
+      tcmapiternext(map, &vsiz);
+      break;
+    case 41:
+      putchar('f');
+      tcmapiternext2(map);
+      break;
+    case 42:
+      putchar('g');
+      if(myrand(100) == 0){
+        if(myrand(2) == 0){
+          tclistdel(tcmapkeys(map));
+        } else {
+          tclistdel(tcmapvals(map));
+        }
+      }
+      break;
+    case 43:
+      putchar('h');
+      if(myrand(rnum / 100 + 1) == 0) tcmapclear(map);
+      break;
+    case 44:
+      putchar('i');
+      if(myrand(20) == 0) tcmapcutfront(map, myrand(10));
+      break;
+    case 45:
+      putchar('j');
+      if(myrand(rnum / 100 + 1) == 0){
+        int dsiz;
+        char *dbuf = tcmapdump(map, &dsiz);
+        free(tcmaploadone(dbuf, dsiz, kbuf, ksiz, &vsiz));
+        tcmapdel(tcmapload(dbuf, dsiz));
+        free(dbuf);
+      }
+      break;
+    case 46:
+      putchar('k');
+      tcmdbput(mdb, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 47:
+      putchar('l');
+      tcmdbput2(mdb, kbuf, vbuf);
+      break;
+    case 48:
+      putchar('m');
+      tcmdbputkeep(mdb, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 49:
+      putchar('n');
+      tcmdbputkeep2(mdb, kbuf, vbuf);
+      break;
+    case 50:
+      putchar('o');
+      tcmdbputcat(mdb, kbuf, ksiz, vbuf, vsiz);
+      break;
+    case 51:
+      putchar('p');
+      tcmdbputcat2(mdb, kbuf, vbuf);
+      break;
+    case 52:
+      putchar('q');
+      if(myrand(10) == 0) tcmdbout(mdb, kbuf, ksiz);
+      break;
+    case 53:
+      putchar('r');
+      if(myrand(10) == 0) tcmdbout2(mdb, kbuf);
+      break;
+    case 54:
+      putchar('s');
+      free(tcmdbget(mdb, kbuf, ksiz, &vsiz));
+      break;
+    case 55:
+      putchar('t');
+      free(tcmdbget3(mdb, kbuf, ksiz, &vsiz));
+      break;
+    case 56:
+      putchar('u');
+      if(myrand(100) == 0) tcmdbiterinit(mdb);
+      break;
+    case 57:
+      putchar('v');
+      free(tcmdbiternext(mdb, &vsiz));
+      break;
+    case 58:
+      putchar('w');
+      tmp = tcmdbiternext2(mdb);
+      free(tmp);
+      break;
+    case 59:
+      putchar('x');
+      if(myrand(rnum / 100 + 1) == 0) tcmdbvanish(mdb);
+      break;
+    case 60:
+      putchar('y');
+      if(myrand(200) == 0) tcmdbcutfront(mdb, myrand(100));
+      break;
+    case 61:
+      putchar('+');
+      if(myrand(100) == 0) tcmpoolmalloc(mpool, 1);
+      break;
+    case 62:
+      putchar('+');
+      if(myrand(100) == 0) tcmpoolxstrnew(mpool);
+      break;
+    case 63:
+      putchar('+');
+      if(myrand(100) == 0) tcmpoollistnew(mpool);
+      break;
+    case 64:
+      putchar('+');
+      if(myrand(100) == 0) tcmpoolmapnew(mpool);
+      break;
+    default:
+      putchar('@');
+      if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+      break;
+    }
+    if(i % 50 == 0) iprintf(" (%08d)\n", i);
+  }
+  if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+  iprintf("time: %.3f\n", tctime() - stime);
+  iprintf("ok\n\n");
+  return 0;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcutil.c b/bacula/src/lib/tokyocabinet/tcutil.c
new file mode 100644 (file)
index 0000000..19cba35
--- /dev/null
@@ -0,0 +1,5014 @@
+/*************************************************************************************************
+ * The utility API of Tokyo Cabinet
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "tcutil.h"
+#include "myconf.h"
+
+
+/*************************************************************************************************
+ * basic utilities
+ *************************************************************************************************/
+
+
+/* String containing the version information. */
+const char *tcversion = _TC_VERSION;
+
+
+/* Call back function for handling a fatal error. */
+void (*tcfatalfunc)(const char *message) = NULL;
+
+
+/* Allocate a region on memory. */
+void *tcmalloc(size_t size){
+  assert(size > 0 && size < INT_MAX);
+  char *p = malloc(size);
+  if(!p) tcmyfatal("out of memory");
+  return p;
+}
+
+
+/* Allocate a nullified region on memory. */
+void *tccalloc(size_t nmemb, size_t size){
+  assert(nmemb > 0 && size < INT_MAX && size > 0 && size < INT_MAX);
+  char *p = calloc(nmemb, size);
+  if(!p) tcmyfatal("out of memory");
+  return p;
+}
+
+
+/* Re-allocate a region on memory. */
+void *tcrealloc(void *ptr, size_t size){
+  assert(size >= 0 && size < INT_MAX);
+  char *p = realloc(ptr, size);
+  if(!p) tcmyfatal("out of memory");
+  return p;
+}
+
+
+/* Duplicate a region on memory. */
+void *tcmemdup(const void *ptr, size_t size){
+  assert(ptr && size >= 0);
+  char *p;
+  TCMALLOC(p, size + 1);
+  memcpy(p, ptr, size);
+  p[size] = '\0';
+  return p;
+}
+
+
+/* Duplicate a string on memory. */
+char *tcstrdup(const void *str){
+  assert(str);
+  int size = strlen(str);
+  char *p;
+  TCMALLOC(p, size + 1);
+  memcpy(p, str, size);
+  p[size] = '\0';
+  return p;
+}
+
+
+/* Free a region on memory. */
+void tcfree(void *ptr){
+  free(ptr);
+}
+
+
+
+/*************************************************************************************************
+ * extensible string
+ *************************************************************************************************/
+
+
+#define TCXSTRUNIT     12                // allocation unit size of an extensible string
+
+
+/* private function prototypes */
+static void tcvxstrprintf(TCXSTR *xstr, const char *format, va_list ap);
+
+
+/* Create an extensible string object. */
+TCXSTR *tcxstrnew(void){
+  TCXSTR *xstr;
+  TCMALLOC(xstr, sizeof(*xstr));
+  TCMALLOC(xstr->ptr, TCXSTRUNIT);
+  xstr->size = 0;
+  xstr->asize = TCXSTRUNIT;
+  xstr->ptr[0] = '\0';
+  return xstr;
+}
+
+
+/* Create an extensible string object from a character string. */
+TCXSTR *tcxstrnew2(const char *str){
+  assert(str);
+  TCXSTR *xstr;
+  TCMALLOC(xstr, sizeof(*xstr));
+  int size = strlen(str);
+  int asize = tclmax(size + 1, TCXSTRUNIT);
+  TCMALLOC(xstr->ptr, asize);
+  xstr->size = size;
+  xstr->asize = asize;
+  memcpy(xstr->ptr, str, size + 1);
+  return xstr;
+}
+
+
+/* Create an extensible string object with the initial allocation size. */
+TCXSTR *tcxstrnew3(int asiz){
+  assert(asiz >= 0);
+  asiz = tclmax(asiz, TCXSTRUNIT);
+  TCXSTR *xstr;
+  TCMALLOC(xstr, sizeof(*xstr));
+  TCMALLOC(xstr->ptr, asiz);
+  xstr->size = 0;
+  xstr->asize = asiz;
+  xstr->ptr[0] = '\0';
+  return xstr;
+}
+
+
+/* Copy an extensible string object. */
+TCXSTR *tcxstrdup(const TCXSTR *xstr){
+  assert(xstr);
+  TCXSTR *nxstr;
+  TCMALLOC(nxstr, sizeof(*nxstr));
+  int asize = tclmax(xstr->size + 1, TCXSTRUNIT);
+  TCMALLOC(nxstr->ptr, asize);
+  nxstr->size = xstr->size;
+  nxstr->asize = asize;
+  memcpy(nxstr->ptr, xstr->ptr, xstr->size + 1);
+  return nxstr;
+}
+
+
+/* Delete an extensible string object. */
+void tcxstrdel(TCXSTR *xstr){
+  assert(xstr);
+  free(xstr->ptr);
+  free(xstr);
+}
+
+
+/* Concatenate a region to the end of an extensible string object. */
+void tcxstrcat(TCXSTR *xstr, const void *ptr, int size){
+  assert(xstr && ptr && size >= 0);
+  int nsize = xstr->size + size + 1;
+  if(xstr->asize < nsize){
+    while(xstr->asize < nsize){
+      xstr->asize *= 2;
+      if(xstr->asize < nsize) xstr->asize = nsize;
+    }
+    TCREALLOC(xstr->ptr, xstr->ptr, xstr->asize);
+  }
+  memcpy(xstr->ptr + xstr->size, ptr, size);
+  xstr->size += size;
+  xstr->ptr[xstr->size] = '\0';
+}
+
+
+/* Concatenate a character string to the end of an extensible string object. */
+void tcxstrcat2(TCXSTR *xstr, const char *str){
+  assert(xstr && str);
+  int size = strlen(str);
+  int nsize = xstr->size + size + 1;
+  if(xstr->asize < nsize){
+    while(xstr->asize < nsize){
+      xstr->asize *= 2;
+      if(xstr->asize < nsize) xstr->asize = nsize;
+    }
+    TCREALLOC(xstr->ptr, xstr->ptr, xstr->asize);
+  }
+  memcpy(xstr->ptr + xstr->size, str, size + 1);
+  xstr->size += size;
+}
+
+
+/* Get the pointer of the region of an extensible string object. */
+const void *tcxstrptr(const TCXSTR *xstr){
+  assert(xstr);
+  return xstr->ptr;
+}
+
+
+/* Get the size of the region of an extensible string object. */
+int tcxstrsize(const TCXSTR *xstr){
+  assert(xstr);
+  return xstr->size;
+}
+
+
+/* Clear an extensible string object. */
+void tcxstrclear(TCXSTR *xstr){
+  assert(xstr);
+  xstr->size = 0;
+  xstr->ptr[0] = '\0';
+}
+
+
+/* Convert an extensible string object into a usual allocated region. */
+void *tcxstrtomalloc(TCXSTR *xstr){
+  assert(xstr);
+  char *ptr;
+  ptr = xstr->ptr;
+  free(xstr);
+  return ptr;
+}
+
+
+/* Create an extensible string object from an allocated region. */
+TCXSTR *tcxstrfrommalloc(void *ptr, int size){
+  TCXSTR *xstr;
+  TCMALLOC(xstr, sizeof(*xstr));
+  TCREALLOC(xstr->ptr, ptr, size + 1);
+  xstr->ptr[size] = '\0';
+  xstr->size = size;
+  xstr->asize = size;
+  return xstr;
+}
+
+
+/* Perform formatted output into an extensible string object. */
+void tcxstrprintf(TCXSTR *xstr, const char *format, ...){
+  assert(xstr && format);
+  va_list ap;
+  va_start(ap, format);
+  tcvxstrprintf(xstr, format, ap);
+  va_end(ap);
+}
+
+
+/* Allocate a formatted string on memory. */
+char *tcsprintf(const char *format, ...){
+  assert(format);
+  TCXSTR *xstr = tcxstrnew();
+  va_list ap;
+  va_start(ap, format);
+  tcvxstrprintf(xstr, format, ap);
+  va_end(ap);
+  return tcxstrtomalloc(xstr);
+}
+
+
+/* Perform formatted output into an extensible string object. */
+static void tcvxstrprintf(TCXSTR *xstr, const char *format, va_list ap){
+  assert(xstr && format);
+  while(*format != '\0'){
+    if(*format == '%'){
+      char cbuf[TCNUMBUFSIZ];
+      cbuf[0] = '%';
+      int cblen = 1;
+      int lnum = 0;
+      format++;
+      while(strchr("0123456789 .+-hlLz", *format) && *format != '\0' &&
+            cblen < TCNUMBUFSIZ - 1){
+        if(*format == 'l' || *format == 'L') lnum++;
+        cbuf[cblen++] = *(format++);
+      }
+      cbuf[cblen++] = *format;
+      cbuf[cblen] = '\0';
+      int tlen;
+      char *tmp, tbuf[TCNUMBUFSIZ*2];
+      switch(*format){
+      case 's':
+        tmp = va_arg(ap, char *);
+        if(!tmp) tmp = "(null)";
+        tcxstrcat2(xstr, tmp);
+        break;
+      case 'd':
+        if(lnum >= 2){
+          tlen = sprintf(tbuf, cbuf, va_arg(ap, long long));
+        } else if(lnum >= 1){
+          tlen = sprintf(tbuf, cbuf, va_arg(ap, long));
+        } else {
+          tlen = sprintf(tbuf, cbuf, va_arg(ap, int));
+        }
+        TCXSTRCAT(xstr, tbuf, tlen);
+        break;
+      case 'o': case 'u': case 'x': case 'X': case 'c':
+        if(lnum >= 2){
+          tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long long));
+        } else if(lnum >= 1){
+          tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long));
+        } else {
+          tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned int));
+        }
+        TCXSTRCAT(xstr, tbuf, tlen);
+        break;
+      case 'e': case 'E': case 'f': case 'g': case 'G':
+        if(lnum >= 1){
+          tlen = sprintf(tbuf, cbuf, va_arg(ap, long double));
+        } else {
+          tlen = sprintf(tbuf, cbuf, va_arg(ap, double));
+        }
+        TCXSTRCAT(xstr, tbuf, tlen);
+        break;
+      case '@':
+        tmp = va_arg(ap, char *);
+        if(!tmp) tmp = "(null)";
+        while(*tmp){
+          switch(*tmp){
+          case '&': TCXSTRCAT(xstr, "&amp;", 5); break;
+          case '<': TCXSTRCAT(xstr, "&lt;", 4); break;
+          case '>': TCXSTRCAT(xstr, "&gt;", 4); break;
+          case '"': TCXSTRCAT(xstr, "&quot;", 6); break;
+          default:
+            if(!((*tmp >= 0 && *tmp <= 0x8) || (*tmp >= 0x0e && *tmp <= 0x1f)))
+              TCXSTRCAT(xstr, tmp, 1);
+            break;
+          }
+          tmp++;
+        }
+        break;
+      case '?':
+        tmp = va_arg(ap, char *);
+        if(!tmp) tmp = "(null)";
+        while(*tmp){
+          unsigned char c = *(unsigned char *)tmp;
+          if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+             (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.", c))){
+            TCXSTRCAT(xstr, tmp, 1);
+          } else {
+            tlen = sprintf(tbuf, "%%%02X", c);
+            TCXSTRCAT(xstr, tbuf, tlen);
+          }
+          tmp++;
+        }
+        break;
+      case '%':
+        TCXSTRCAT(xstr, "%", 1);
+        break;
+      }
+    } else {
+      TCXSTRCAT(xstr, format, 1);
+    }
+    format++;
+  }
+}
+
+
+
+/*************************************************************************************************
+ * array list
+ *************************************************************************************************/
+
+
+#define TCLISTUNIT     64                // allocation unit number of a list handle
+
+
+/* private function prototypes */
+static int tclistelemcmp(const void *a, const void *b);
+static int tclistelemcmpci(const void *a, const void *b);
+
+
+/* Create a list object. */
+TCLIST *tclistnew(void){
+  TCLIST *list;
+  TCMALLOC(list, sizeof(*list));
+  list->anum = TCLISTUNIT;
+  TCMALLOC(list->array, sizeof(list->array[0]) * list->anum);
+  list->start = 0;
+  list->num = 0;
+  return list;
+}
+
+
+/* Create a list object. */
+TCLIST *tclistnew2(int anum){
+  TCLIST *list;
+  TCMALLOC(list, sizeof(*list));
+  if(anum < 1) anum = 1;
+  list->anum = anum;
+  TCMALLOC(list->array, sizeof(list->array[0]) * list->anum);
+  list->start = 0;
+  list->num = 0;
+  return list;
+}
+
+
+/* Copy a list object. */
+TCLIST *tclistdup(const TCLIST *list){
+  assert(list);
+  int num = list->num;
+  if(num < 1) tclistnew();
+  const TCLISTDATUM *array = list->array + list->start;
+  TCLIST *nlist;
+  TCMALLOC(nlist, sizeof(*nlist));
+  TCLISTDATUM *narray;
+  TCMALLOC(narray, sizeof(list->array[0]) * tclmax(num, 1));
+  for(int i = 0; i < num; i++){
+    int size = array[i].size;
+    TCMALLOC(narray[i].ptr, tclmax(size + 1, TCXSTRUNIT));
+    memcpy(narray[i].ptr, array[i].ptr, size + 1);
+    narray[i].size = array[i].size;
+  }
+  nlist->anum = num;
+  nlist->array = narray;
+  nlist->start = 0;
+  nlist->num = num;
+  return nlist;
+}
+
+
+/* Delete a list object. */
+void tclistdel(TCLIST *list){
+  assert(list);
+  TCLISTDATUM *array = list->array;
+  int end = list->start + list->num;
+  for(int i = list->start; i < end; i++){
+    free(array[i].ptr);
+  }
+  free(list->array);
+  free(list);
+}
+
+
+/* Get the number of elements of a list object. */
+int tclistnum(const TCLIST *list){
+  assert(list);
+  return list->num;
+}
+
+
+/* Get the pointer to the region of an element of a list object. */
+const void *tclistval(const TCLIST *list, int index, int *sp){
+  assert(list && index >= 0 && sp);
+  if(index >= list->num) return NULL;
+  index += list->start;
+  *sp = list->array[index].size;
+  return list->array[index].ptr;
+}
+
+
+/* Get the string of an element of a list object. */
+const char *tclistval2(const TCLIST *list, int index){
+  assert(list && index >= 0);
+  if(index >= list->num) return NULL;
+  index += list->start;
+  return list->array[index].ptr;
+}
+
+
+/* Add an element at the end of a list object. */
+void tclistpush(TCLIST *list, const void *ptr, int size){
+  assert(list && ptr && size >= 0);
+  int index = list->start + list->num;
+  if(index >= list->anum){
+    list->anum += list->num + 1;
+    TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+  }
+  TCLISTDATUM *array = list->array;
+  TCMALLOC(array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+  memcpy(array[index].ptr, ptr, size);
+  array[index].ptr[size] = '\0';
+  array[index].size = size;
+  list->num++;
+}
+
+
+/* Add a string element at the end of a list object. */
+void tclistpush2(TCLIST *list, const char *str){
+  assert(list && str);
+  int index = list->start + list->num;
+  if(index >= list->anum){
+    list->anum += list->num + 1;
+    TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+  }
+  int size = strlen(str);
+  TCLISTDATUM *array = list->array;
+  TCMALLOC(array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+  memcpy(array[index].ptr, str, size + 1);
+  array[index].size = size;
+  list->num++;
+}
+
+
+/* Add an allocated element at the end of a list object. */
+void tclistpushmalloc(TCLIST *list, void *ptr, int size){
+  assert(list && ptr && size >= 0);
+  int index = list->start + list->num;
+  if(index >= list->anum){
+    list->anum += list->num + 1;
+    TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+  }
+  TCLISTDATUM *array = list->array;
+  TCREALLOC(array[index].ptr, ptr, size + 1);
+  array[index].ptr[size] = '\0';
+  array[index].size = size;
+  list->num++;
+}
+
+
+/* Remove an element of the end of a list object. */
+void *tclistpop(TCLIST *list, int *sp){
+  assert(list && sp);
+  if(list->num < 1) return NULL;
+  int index = list->start + list->num - 1;
+  list->num--;
+  *sp = list->array[index].size;
+  return list->array[index].ptr;
+}
+
+
+/* Remove a string element of the end of a list object. */
+char *tclistpop2(TCLIST *list){
+  assert(list);
+  if(list->num < 1) return NULL;
+  int index = list->start + list->num - 1;
+  list->num--;
+  return list->array[index].ptr;
+}
+
+
+/* Add an element at the top of a list object. */
+void tclistunshift(TCLIST *list, const void *ptr, int size){
+  assert(list && ptr && size >= 0);
+  if(list->start < 1){
+    if(list->start + list->num >= list->anum){
+      list->anum += list->num + 1;
+      TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+    }
+    list->start = list->anum - list->num;
+    memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0]));
+  }
+  int index = list->start - 1;
+  TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+  memcpy(list->array[index].ptr, ptr, size);
+  list->array[index].ptr[size] = '\0';
+  list->array[index].size = size;
+  list->start--;
+  list->num++;
+}
+
+
+/* Add a string element at the top of a list object. */
+void tclistunshift2(TCLIST *list, const char *str){
+  assert(list && str);
+  if(list->start < 1){
+    if(list->start + list->num >= list->anum){
+      list->anum += list->num + 1;
+      TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+    }
+    list->start = list->anum - list->num;
+    memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0]));
+  }
+  int index = list->start - 1;
+  int size = strlen(str);
+  TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+  memcpy(list->array[index].ptr, str, size + 1);
+  list->array[index].size = size;
+  list->start--;
+  list->num++;
+}
+
+
+/* Remove an element of the top of a list object. */
+void *tclistshift(TCLIST *list, int *sp){
+  assert(list && sp);
+  if(list->num < 1) return NULL;
+  int index = list->start;
+  list->start++;
+  list->num--;
+  *sp = list->array[index].size;
+  void *rv = list->array[index].ptr;
+  if((list->start & 0xff) == 0 && list->start > (list->num >> 1)){
+    memmove(list->array, list->array + list->start, list->num * sizeof(list->array[0]));
+    list->start = 0;
+  }
+  return rv;
+}
+
+
+/* Remove a string element of the top of a list object. */
+char *tclistshift2(TCLIST *list){
+  assert(list);
+  if(list->num < 1) return NULL;
+  int index = list->start;
+  list->start++;
+  list->num--;
+  void *rv = list->array[index].ptr;
+  if((list->start & 0xff) == 0 && list->start > (list->num >> 1)){
+    memmove(list->array, list->array + list->start, list->num * sizeof(list->array[0]));
+    list->start = 0;
+  }
+  return rv;
+}
+
+
+/* Add an element at the specified location of a list object. */
+void tclistinsert(TCLIST *list, int index, const void *ptr, int size){
+  assert(list && index >= 0 && ptr && size >= 0);
+  if(index > list->num) return;
+  index += list->start;
+  if(list->start + list->num >= list->anum){
+    list->anum += list->num + 1;
+    TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+  }
+  memmove(list->array + index + 1, list->array + index,
+          sizeof(list->array[0]) * (list->start + list->num - index));
+  TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+  memcpy(list->array[index].ptr, ptr, size);
+  list->array[index].ptr[size] = '\0';
+  list->array[index].size = size;
+  list->num++;
+}
+
+
+/* Add a string element at the specified location of a list object. */
+void tclistinsert2(TCLIST *list, int index, const char *str){
+  assert(list && index >= 0 && str);
+  if(index > list->num) return;
+  index += list->start;
+  if(list->start + list->num >= list->anum){
+    list->anum += list->num + 1;
+    TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+  }
+  memmove(list->array + index + 1, list->array + index,
+          sizeof(list->array[0]) * (list->start + list->num - index));
+  int size = strlen(str);
+  TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+  memcpy(list->array[index].ptr, str, size);
+  list->array[index].ptr[size] = '\0';
+  list->array[index].size = size;
+  list->num++;
+}
+
+
+/* Remove an element at the specified location of a list object. */
+void *tclistremove(TCLIST *list, int index, int *sp){
+  assert(list && index >= 0 && sp);
+  if(index >= list->num) return NULL;
+  index += list->start;
+  char *ptr = list->array[index].ptr;
+  *sp = list->array[index].size;
+  list->num--;
+  memmove(list->array + index, list->array + index + 1,
+          sizeof(list->array[0]) * (list->start + list->num - index));
+  return ptr;
+}
+
+
+/* Remove a string element at the specified location of a list object. */
+char *tclistremove2(TCLIST *list, int index){
+  assert(list && index >= 0);
+  if(index >= list->num) return NULL;
+  index += list->start;
+  char *ptr = list->array[index].ptr;
+  list->num--;
+  memmove(list->array + index, list->array + index + 1,
+          sizeof(list->array[0]) * (list->start + list->num - index));
+  return ptr;
+}
+
+
+/* Overwrite an element at the specified location of a list object. */
+void tclistover(TCLIST *list, int index, const void *ptr, int size){
+  assert(list && index >= 0 && ptr && size >= 0);
+  if(index >= list->num) return;
+  index += list->start;
+  if(size > list->array[index].size)
+    TCREALLOC(list->array[index].ptr, list->array[index].ptr, size + 1);
+  memcpy(list->array[index].ptr, ptr, size);
+  list->array[index].size = size;
+  list->array[index].ptr[size] = '\0';
+}
+
+
+/* Overwrite a string element at the specified location of a list object. */
+void tclistover2(TCLIST *list, int index, const char *str){
+  assert(list && index >= 0 && str);
+  if(index >= list->num) return;
+  index += list->start;
+  int size = strlen(str);
+  if(size > list->array[index].size)
+    TCREALLOC(list->array[index].ptr, list->array[index].ptr, size + 1);
+  memcpy(list->array[index].ptr, str, size + 1);
+  list->array[index].size = size;
+}
+
+
+/* Sort elements of a list object in lexical order. */
+void tclistsort(TCLIST *list){
+  assert(list);
+  qsort(list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmp);
+}
+
+
+/* Sort elements of a list object in case-insensitive lexical order. */
+void tclistsortci(TCLIST *list){
+  assert(list);
+  qsort(list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmpci);
+}
+
+
+/* Sort elements of a list object by an arbitrary comparison function. */
+void tclistsortex(TCLIST *list, int (*cmp)(const TCLISTDATUM *, const TCLISTDATUM *)){
+  assert(list && cmp);
+  qsort(list->array + list->start, list->num, sizeof(list->array[0]),
+        (int (*)(const void *, const void *))cmp);
+}
+
+
+/* Search a list object for an element using liner search. */
+int tclistlsearch(const TCLIST *list, const void *ptr, int size){
+  assert(list && ptr && size >= 0);
+  int end = list->start + list->num;
+  for(int i = list->start; i < end; i++){
+    if(list->array[i].size == size && !memcmp(list->array[i].ptr, ptr, size))
+      return i - list->start;
+  }
+  return -1;
+}
+
+
+/* Search a list object for an element using binary search. */
+int tclistbsearch(const TCLIST *list, const void *ptr, int size){
+  assert(list && ptr && size >= 0);
+  TCLISTDATUM key;
+  key.ptr = (char *)ptr;
+  key.size = size;
+  TCLISTDATUM *res = bsearch(&key, list->array + list->start,
+                             list->num, sizeof(list->array[0]), tclistelemcmp);
+  return res ? res - list->array - list->start : -1;
+}
+
+
+/* Clear a list object. */
+void tclistclear(TCLIST *list){
+  assert(list);
+  TCLISTDATUM *array = list->array;
+  int end = list->start + list->num;
+  for(int i = list->start; i < end; i++){
+    free(array[i].ptr);
+  }
+  list->start = 0;
+  list->num = 0;
+}
+
+
+/* Serialize a list object into a byte array. */
+void *tclistdump(const TCLIST *list, int *sp){
+  assert(list && sp);
+  const TCLISTDATUM *array = list->array;
+  int end = list->start + list->num;
+  int tsiz = 0;
+  for(int i = list->start; i < end; i++){
+    tsiz += array[i].size + sizeof(int);
+  }
+  char *buf;
+  TCMALLOC(buf, tsiz + 1);
+  char *wp = buf;
+  for(int i = list->start; i < end; i++){
+    int step;
+    TCSETVNUMBUF(step, wp, array[i].size);
+    wp += step;
+    memcpy(wp, array[i].ptr, array[i].size);
+    wp += array[i].size;
+  }
+  *sp = wp - buf;
+  return buf;
+}
+
+
+/* Create a list object from a serialized byte array. */
+TCLIST *tclistload(const void *ptr, int size){
+  assert(ptr && size >= 0);
+  TCLIST *list;
+  TCMALLOC(list, sizeof(*list));
+  int anum = size / sizeof(int) + 1;
+  TCLISTDATUM *array;
+  TCMALLOC(array, sizeof(array[0]) * anum);
+  int num = 0;
+  const char *rp = ptr;
+  const char *ep = (char *)ptr + size;
+  while(rp < ep){
+    int step, vsiz;
+    TCREADVNUMBUF(rp, vsiz, step);
+    rp += step;
+    if(num >= anum){
+      anum *= 2;
+      TCREALLOC(array, array, anum * sizeof(array[0]));
+    }
+    TCMALLOC(array[num].ptr, tclmax(vsiz + 1, TCXSTRUNIT));
+    memcpy(array[num].ptr, rp, vsiz);
+    array[num].ptr[vsiz] = '\0';
+    array[num].size = vsiz;
+    num++;
+    rp += vsiz;
+  }
+  list->anum = anum;
+  list->array = array;
+  list->start = 0;
+  list->num = num;
+  return list;
+}
+
+
+/* Compare two list elements in lexical order.
+   `a' specifies the pointer to one element.
+   `b' specifies the pointer to the other element.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+static int tclistelemcmp(const void *a, const void *b){
+  assert(a && b);
+  unsigned char *ao = (unsigned char *)((TCLISTDATUM *)a)->ptr;
+  unsigned char *bo = (unsigned char *)((TCLISTDATUM *)b)->ptr;
+  int size = (((TCLISTDATUM *)a)->size < ((TCLISTDATUM *)b)->size) ?
+    ((TCLISTDATUM *)a)->size : ((TCLISTDATUM *)b)->size;
+  for(int i = 0; i < size; i++){
+    if(ao[i] > bo[i]) return 1;
+    if(ao[i] < bo[i]) return -1;
+  }
+  return ((TCLISTDATUM *)a)->size - ((TCLISTDATUM *)b)->size;
+}
+
+
+/* Compare two list elements in case-insensitive lexical order..
+   `a' specifies the pointer to one element.
+   `b' specifies the pointer to the other element.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+static int tclistelemcmpci(const void *a, const void *b){
+  assert(a && b);
+  TCLISTDATUM *ap = (TCLISTDATUM *)a;
+  TCLISTDATUM *bp = (TCLISTDATUM *)b;
+  unsigned char *ao = (unsigned char *)ap->ptr;
+  unsigned char *bo = (unsigned char *)bp->ptr;
+  int size = (ap->size < bp->size) ? ap->size : bp->size;
+  for(int i = 0; i < size; i++){
+    int ac = ao[i];
+    bool ab = false;
+    if(ac >= 'A' && ac <= 'Z'){
+      ac += 'a' - 'A';
+      ab = true;
+    }
+    int bc = bo[i];
+    bool bb = false;
+    if(bc >= 'A' && bc <= 'Z'){
+      bc += 'a' - 'A';
+      bb = true;
+    }
+    if(ac > bc) return 1;
+    if(ac < bc) return -1;
+    if(!ab && bb) return 1;
+    if(ab && !bb) return -1;
+  }
+  return ap->size - bp->size;
+}
+
+
+
+/*************************************************************************************************
+ * hash map
+ *************************************************************************************************/
+
+
+#define TCMAPBNUM      4093              // allocation unit number of a map
+#define TCMAPCSUNIT    52                // small allocation unit size of map concatenation
+#define TCMAPCBUNIT    252               // big allocation unit size of map concatenation
+
+/* get the first hash value */
+#define TCMAPHASH1(TC_res, TC_kbuf, TC_ksiz) \
+  do { \
+    const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf); \
+    int _TC_ksiz = TC_ksiz; \
+    for((TC_res) = 19780211; _TC_ksiz--;){ \
+      (TC_res) = ((TC_res) << 5) + ((TC_res) << 2) + (TC_res) + *(_TC_p)++; \
+    } \
+  } while(false)
+
+/* get the second hash value */
+#define TCMAPHASH2(TC_res, TC_kbuf, TC_ksiz) \
+  do { \
+    const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf) + TC_ksiz - 1; \
+    int _TC_ksiz = TC_ksiz; \
+    for((TC_res) = 0x13579bdf; _TC_ksiz--;){ \
+      (TC_res) = ((TC_res) << 5) - (TC_res) + *(_TC_p)--; \
+    } \
+  } while(false)
+
+/* get the size of padding bytes for pointer alignment */
+#define TCALIGNPAD(TC_hsiz) \
+  (((TC_hsiz | ~-(int)sizeof(void *)) + 1) - TC_hsiz)
+
+/* compare two keys */
+#define TCKEYCMP(TC_abuf, TC_asiz, TC_bbuf, TC_bsiz) \
+  ((TC_asiz > TC_bsiz) ? 1 : (TC_asiz < TC_bsiz) ? -1 : memcmp(TC_abuf, TC_bbuf, TC_asiz))
+
+
+/* Create a map object. */
+TCMAP *tcmapnew(void){
+  return tcmapnew2(TCMAPBNUM);
+}
+
+
+/* Create a map object with specifying the number of the buckets. */
+TCMAP *tcmapnew2(uint32_t bnum){
+  if(bnum < 1) bnum = 1;
+  TCMAP *map;
+  TCMALLOC(map, sizeof(*map));
+  TCMAPREC **buckets;
+  TCCALLOC(buckets, bnum, sizeof(map->buckets[0]));
+  map->buckets = buckets;
+  map->first = NULL;
+  map->last = NULL;
+  map->cur = NULL;
+  map->bnum = bnum;
+  map->rnum = 0;
+  map->msiz = 0;
+  return map;
+}
+
+
+/* Copy a map object. */
+TCMAP *tcmapdup(const TCMAP *map){
+  assert(map);
+  TCMAP *nmap = tcmapnew2(tclmax(tclmax(map->bnum, map->rnum), TCMAPBNUM));
+  TCMAPREC *cur = map->cur;
+  const char *kbuf;
+  int ksiz;
+  ((TCMAP *)map)->cur = map->first;
+  while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
+    int vsiz;
+    const char *vbuf = tcmapiterval(kbuf, &vsiz);
+    tcmapputkeep(nmap, kbuf, ksiz, vbuf, vsiz);
+  }
+  ((TCMAP *)map)->cur = cur;
+  return nmap;
+}
+
+
+/* Close a map object. */
+void tcmapdel(TCMAP *map){
+  assert(map);
+  TCMAPREC *rec = map->first;
+  while(rec){
+    TCMAPREC *next = rec->next;
+    free(rec);
+    rec = next;
+  }
+  free(map->buckets);
+  free(map);
+}
+
+
+/* Store a record into a map object. */
+void tcmapput(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  unsigned int hash;
+  TCMAPHASH1(hash, kbuf, ksiz);
+  int bidx = hash % map->bnum;
+  TCMAPREC *rec = map->buckets[bidx];
+  TCMAPREC **entp = map->buckets + bidx;
+  TCMAPHASH2(hash, kbuf, ksiz);
+  while(rec){
+    if(hash > rec->hash){
+      entp = &(rec->left);
+      rec = rec->left;
+    } else if(hash < rec->hash){
+      entp = &(rec->right);
+      rec = rec->right;
+    } else {
+      char *dbuf = (char *)rec + sizeof(*rec);
+      int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+      if(kcmp < 0){
+        entp = &(rec->left);
+        rec = rec->left;
+      } else if(kcmp > 0){
+        entp = &(rec->right);
+        rec = rec->right;
+      } else {
+        map->msiz += vsiz - rec->vsiz;
+        int psiz = TCALIGNPAD(ksiz);
+        if(vsiz > rec->vsiz){
+          TCMAPREC *old = rec;
+          TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
+          if(rec != old){
+            if(map->first == old) map->first = rec;
+            if(map->last == old) map->last = rec;
+            if(map->cur == old) map->cur = rec;
+            if(*entp == old) *entp = rec;
+            if(rec->prev) rec->prev->next = rec;
+            if(rec->next) rec->next->prev = rec;
+            dbuf = (char *)rec + sizeof(*rec);
+          }
+        }
+        memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+        dbuf[ksiz+psiz+vsiz] = '\0';
+        rec->vsiz = vsiz;
+        return;
+      }
+    }
+  }
+  int psiz = TCALIGNPAD(ksiz);
+  map->msiz += ksiz + vsiz;
+  TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
+  char *dbuf = (char *)rec + sizeof(*rec);
+  memcpy(dbuf, kbuf, ksiz);
+  dbuf[ksiz] = '\0';
+  rec->ksiz = ksiz;
+  memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+  dbuf[ksiz+psiz+vsiz] = '\0';
+  rec->vsiz = vsiz;
+  rec->hash = hash;
+  rec->left = NULL;
+  rec->right = NULL;
+  rec->prev = map->last;
+  rec->next = NULL;
+  *entp = rec;
+  if(!map->first) map->first = rec;
+  if(map->last) map->last->next = rec;
+  map->last = rec;
+  map->rnum++;
+}
+
+
+/* Store a string record into a map object. */
+void tcmapput2(TCMAP *map, const char *kstr, const char *vstr){
+  assert(map && kstr && vstr);
+  tcmapput(map, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a record of the value of two regions into a map object. */
+void tcmapput3(TCMAP *map, const char *kbuf, int ksiz,
+               const void *fvbuf, int fvsiz, const char *lvbuf, int lvsiz){
+  assert(map && kbuf && ksiz >= 0 && fvbuf && fvsiz >= 0 && lvbuf && lvsiz >= 0);
+  unsigned int hash;
+  TCMAPHASH1(hash, kbuf, ksiz);
+  int bidx = hash % map->bnum;
+  TCMAPREC *rec = map->buckets[bidx];
+  TCMAPREC **entp = map->buckets + bidx;
+  TCMAPHASH2(hash, kbuf, ksiz);
+  while(rec){
+    if(hash > rec->hash){
+      entp = &(rec->left);
+      rec = rec->left;
+    } else if(hash < rec->hash){
+      entp = &(rec->right);
+      rec = rec->right;
+    } else {
+      char *dbuf = (char *)rec + sizeof(*rec);
+      int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+      if(kcmp < 0){
+        entp = &(rec->left);
+        rec = rec->left;
+      } else if(kcmp > 0){
+        entp = &(rec->right);
+        rec = rec->right;
+      } else {
+        int vsiz = fvsiz + lvsiz;
+        map->msiz += vsiz - rec->vsiz;
+        int psiz = TCALIGNPAD(ksiz);
+        ksiz += psiz;
+        if(vsiz > rec->vsiz){
+          TCMAPREC *old = rec;
+          TCREALLOC(rec, rec, sizeof(*rec) + ksiz + vsiz + 1);
+          if(rec != old){
+            if(map->first == old) map->first = rec;
+            if(map->last == old) map->last = rec;
+            if(map->cur == old) map->cur = rec;
+            if(*entp == old) *entp = rec;
+            if(rec->prev) rec->prev->next = rec;
+            if(rec->next) rec->next->prev = rec;
+            dbuf = (char *)rec + sizeof(*rec);
+          }
+        }
+        memcpy(dbuf + ksiz, fvbuf, fvsiz);
+        memcpy(dbuf + ksiz + fvsiz, lvbuf, lvsiz);
+        dbuf[ksiz+vsiz] = '\0';
+        rec->vsiz = vsiz;
+        return;
+      }
+    }
+  }
+  int vsiz = fvsiz + lvsiz;
+  int psiz = TCALIGNPAD(ksiz);
+  map->msiz += ksiz + vsiz;
+  TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
+  char *dbuf = (char *)rec + sizeof(*rec);
+  memcpy(dbuf, kbuf, ksiz);
+  dbuf[ksiz] = '\0';
+  rec->ksiz = ksiz;
+  ksiz += psiz;
+  memcpy(dbuf + ksiz, fvbuf, fvsiz);
+  memcpy(dbuf + ksiz + fvsiz, lvbuf, lvsiz);
+  dbuf[ksiz+vsiz] = '\0';
+  rec->vsiz = vsiz;
+  rec->hash = hash;
+  rec->left = NULL;
+  rec->right = NULL;
+  rec->prev = map->last;
+  rec->next = NULL;
+  *entp = rec;
+  if(!map->first) map->first = rec;
+  if(map->last) map->last->next = rec;
+  map->last = rec;
+  map->rnum++;
+}
+
+
+/* Store a new record into a map object. */
+bool tcmapputkeep(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  unsigned int hash;
+  TCMAPHASH1(hash, kbuf, ksiz);
+  int bidx = hash % map->bnum;
+  TCMAPREC *rec = map->buckets[bidx];
+  TCMAPREC **entp = map->buckets + bidx;
+  TCMAPHASH2(hash, kbuf, ksiz);
+  while(rec){
+    if(hash > rec->hash){
+      entp = &(rec->left);
+      rec = rec->left;
+    } else if(hash < rec->hash){
+      entp = &(rec->right);
+      rec = rec->right;
+    } else {
+      char *dbuf = (char *)rec + sizeof(*rec);
+      int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+      if(kcmp < 0){
+        entp = &(rec->left);
+        rec = rec->left;
+      } else if(kcmp > 0){
+        entp = &(rec->right);
+        rec = rec->right;
+      } else {
+        return false;
+      }
+    }
+  }
+  int psiz = TCALIGNPAD(ksiz);
+  map->msiz += ksiz + vsiz;
+  TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
+  char *dbuf = (char *)rec + sizeof(*rec);
+  memcpy(dbuf, kbuf, ksiz);
+  dbuf[ksiz] = '\0';
+  rec->ksiz = ksiz;
+  memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+  dbuf[ksiz+psiz+vsiz] = '\0';
+  rec->vsiz = vsiz;
+  rec->hash = hash;
+  rec->left = NULL;
+  rec->right = NULL;
+  rec->prev = map->last;
+  rec->next = NULL;
+  *entp = rec;
+  if(!map->first) map->first = rec;
+  if(map->last) map->last->next = rec;
+  map->last = rec;
+  map->rnum++;
+  return true;
+}
+
+
+/* Store a new string record into a map object.
+   `map' specifies the map object.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, this function has no effect. */
+bool tcmapputkeep2(TCMAP *map, const char *kstr, const char *vstr){
+  assert(map && kstr && vstr);
+  return tcmapputkeep(map, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Concatenate a value at the end of the value of the existing record in a map object. */
+void tcmapputcat(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  unsigned int hash;
+  TCMAPHASH1(hash, kbuf, ksiz);
+  int bidx = hash % map->bnum;
+  TCMAPREC *rec = map->buckets[bidx];
+  TCMAPREC **entp = map->buckets + bidx;
+  TCMAPHASH2(hash, kbuf, ksiz);
+  while(rec){
+    if(hash > rec->hash){
+      entp = &(rec->left);
+      rec = rec->left;
+    } else if(hash < rec->hash){
+      entp = &(rec->right);
+      rec = rec->right;
+    } else {
+      char *dbuf = (char *)rec + sizeof(*rec);
+      int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+      if(kcmp < 0){
+        entp = &(rec->left);
+        rec = rec->left;
+      } else if(kcmp > 0){
+        entp = &(rec->right);
+        rec = rec->right;
+      } else {
+        map->msiz += vsiz;
+        int psiz = TCALIGNPAD(ksiz);
+        int asiz = sizeof(*rec) + ksiz + psiz + rec->vsiz + vsiz + 1;
+        int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
+        asiz = (asiz - 1) + unit - (asiz - 1) % unit;
+        TCMAPREC *old = rec;
+        TCREALLOC(rec, rec, asiz);
+        if(rec != old){
+          if(map->first == old) map->first = rec;
+          if(map->last == old) map->last = rec;
+          if(map->cur == old) map->cur = rec;
+          if(*entp == old) *entp = rec;
+          if(rec->prev) rec->prev->next = rec;
+          if(rec->next) rec->next->prev = rec;
+          dbuf = (char *)rec + sizeof(*rec);
+        }
+        memcpy(dbuf + ksiz + psiz + rec->vsiz, vbuf, vsiz);
+        dbuf[ksiz+psiz+rec->vsiz+vsiz] = '\0';
+        rec->vsiz += vsiz;
+        return;
+      }
+    }
+  }
+  int psiz = TCALIGNPAD(ksiz);
+  int asiz = sizeof(*rec) + ksiz + psiz + vsiz + 1;
+  int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
+  asiz = (asiz - 1) + unit - (asiz - 1) % unit;
+  map->msiz += ksiz + vsiz;
+  TCMALLOC(rec, asiz);
+  char *dbuf = (char *)rec + sizeof(*rec);
+  memcpy(dbuf, kbuf, ksiz);
+  dbuf[ksiz] = '\0';
+  rec->ksiz = ksiz;
+  memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+  dbuf[ksiz+psiz+vsiz] = '\0';
+  rec->vsiz = vsiz;
+  rec->hash = hash;
+  rec->left = NULL;
+  rec->right = NULL;
+  rec->prev = map->last;
+  rec->next = NULL;
+  *entp = rec;
+  if(!map->first) map->first = rec;
+  if(map->last) map->last->next = rec;
+  map->last = rec;
+  map->rnum++;
+}
+
+
+/* Concatenate a string value at the end of the value of the existing record in a map object. */
+void tcmapputcat2(TCMAP *map, const char *kstr, const char *vstr){
+  assert(map && kstr && vstr);
+  tcmapputcat(map, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Remove a record of a map object. */
+bool tcmapout(TCMAP *map, const void *kbuf, int ksiz){
+  assert(map && kbuf && ksiz >= 0);
+  unsigned int hash;
+  TCMAPHASH1(hash, kbuf, ksiz);
+  int bidx = hash % map->bnum;
+  TCMAPREC *rec = map->buckets[bidx];
+  TCMAPREC **entp = map->buckets + bidx;
+  TCMAPHASH2(hash, kbuf, ksiz);
+  while(rec){
+    if(hash > rec->hash){
+      entp = &(rec->left);
+      rec = rec->left;
+    } else if(hash < rec->hash){
+      entp = &(rec->right);
+      rec = rec->right;
+    } else {
+      char *dbuf = (char *)rec + sizeof(*rec);
+      int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+      if(kcmp < 0){
+        entp = &(rec->left);
+        rec = rec->left;
+      } else if(kcmp > 0){
+        entp = &(rec->right);
+        rec = rec->right;
+      } else {
+        map->rnum--;
+        map->msiz -= rec->ksiz + rec->vsiz;
+        if(rec->prev) rec->prev->next = rec->next;
+        if(rec->next) rec->next->prev = rec->prev;
+        if(rec == map->first) map->first = rec->next;
+        if(rec == map->last) map->last = rec->prev;
+        if(rec == map->cur) map->cur = rec->next;
+        if(rec->left && !rec->right){
+          *entp = rec->left;
+        } else if(!rec->left && rec->right){
+          *entp = rec->right;
+        } else if(!rec->left && !rec->left){
+          *entp = NULL;
+        } else {
+          *entp = rec->left;
+          TCMAPREC *tmp = *entp;
+          while(tmp->right){
+            tmp = tmp->right;
+          }
+          tmp->right = rec->right;
+        }
+        free(rec);
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+
+/* Remove a string record of a map object. */
+bool tcmapout2(TCMAP *map, const char *kstr){
+  assert(map && kstr);
+  return tcmapout(map, kstr, strlen(kstr));
+}
+
+
+/* Retrieve a record in a map object. */
+const void *tcmapget(const TCMAP *map, const void *kbuf, int ksiz, int *sp){
+  assert(map && kbuf && ksiz >= 0 && sp);
+  unsigned int hash;
+  TCMAPHASH1(hash, kbuf, ksiz);
+  TCMAPREC *rec = map->buckets[hash%map->bnum];
+  TCMAPHASH2(hash, kbuf, ksiz);
+  while(rec){
+    if(hash > rec->hash){
+      rec = rec->left;
+    } else if(hash < rec->hash){
+      rec = rec->right;
+    } else {
+      char *dbuf = (char *)rec + sizeof(*rec);
+      int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+      if(kcmp < 0){
+        rec = rec->left;
+      } else if(kcmp > 0){
+        rec = rec->right;
+      } else {
+        *sp = rec->vsiz;
+        return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
+      }
+    }
+  }
+  return NULL;
+}
+
+
+/* Retrieve a string record in a map object. */
+const char *tcmapget2(const TCMAP *map, const char *kstr){
+  assert(map && kstr);
+  int ksiz = strlen(kstr);
+  unsigned int hash;
+  TCMAPHASH1(hash, kstr, ksiz);
+  TCMAPREC *rec = map->buckets[hash%map->bnum];
+  TCMAPHASH2(hash, kstr, ksiz);
+  while(rec){
+    if(hash > rec->hash){
+      rec = rec->left;
+    } else if(hash < rec->hash){
+      rec = rec->right;
+    } else {
+      char *dbuf = (char *)rec + sizeof(*rec);
+      int kcmp = TCKEYCMP(kstr, ksiz, dbuf, rec->ksiz);
+      if(kcmp < 0){
+        rec = rec->left;
+      } else if(kcmp > 0){
+        rec = rec->right;
+      } else {
+        return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
+      }
+    }
+  }
+  return NULL;
+}
+
+
+/* Retrieve a semivolatile record in a map object. */
+const void *tcmapget3(TCMAP *map, const void *kbuf, int ksiz, int *sp){
+  assert(map && kbuf && ksiz >= 0 && sp);
+  unsigned int hash;
+  TCMAPHASH1(hash, kbuf, ksiz);
+  TCMAPREC *rec = map->buckets[hash%map->bnum];
+  TCMAPHASH2(hash, kbuf, ksiz);
+  while(rec){
+    if(hash > rec->hash){
+      rec = rec->left;
+    } else if(hash < rec->hash){
+      rec = rec->right;
+    } else {
+      char *dbuf = (char *)rec + sizeof(*rec);
+      int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+      if(kcmp < 0){
+        rec = rec->left;
+      } else if(kcmp > 0){
+        rec = rec->right;
+      } else {
+        if(map->last != rec){
+          if(map->first == rec) map->first = rec->next;
+          if(rec->prev) rec->prev->next = rec->next;
+          if(rec->next) rec->next->prev = rec->prev;
+          rec->prev = map->last;
+          rec->next = NULL;
+          map->last->next = rec;
+          map->last = rec;
+        }
+        *sp = rec->vsiz;
+        return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
+      }
+    }
+  }
+  return NULL;
+}
+
+
+/* Move a record to the edge of a map object. */
+bool tcmapmove(TCMAP *map, const void *kbuf, int ksiz, bool head){
+  assert(map && kbuf && ksiz >= 0);
+  unsigned int hash;
+  TCMAPHASH1(hash, kbuf, ksiz);
+  TCMAPREC *rec = map->buckets[hash%map->bnum];
+  TCMAPHASH2(hash, kbuf, ksiz);
+  while(rec){
+    if(hash > rec->hash){
+      rec = rec->left;
+    } else if(hash < rec->hash){
+      rec = rec->right;
+    } else {
+      char *dbuf = (char *)rec + sizeof(*rec);
+      int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+      if(kcmp < 0){
+        rec = rec->left;
+      } else if(kcmp > 0){
+        rec = rec->right;
+      } else {
+        if(head){
+          if(map->first == rec) return true;
+          if(map->last == rec) map->last = rec->prev;
+          if(rec->prev) rec->prev->next = rec->next;
+          if(rec->next) rec->next->prev = rec->prev;
+          rec->prev = NULL;
+          rec->next = map->first;
+          map->first->prev = rec;
+          map->first = rec;
+        } else {
+          if(map->last == rec) return true;
+          if(map->first == rec) map->first = rec->next;
+          if(rec->prev) rec->prev->next = rec->next;
+          if(rec->next) rec->next->prev = rec->prev;
+          rec->prev = map->last;
+          rec->next = NULL;
+          map->last->next = rec;
+          map->last = rec;
+        }
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+
+/* Move a string record to the edge of a map object. */
+bool tcmapmove2(TCMAP *map, const char *kstr, bool head){
+  assert(map && kstr);
+  return tcmapmove(map, kstr, strlen(kstr), head);
+}
+
+
+/* Initialize the iterator of a map object. */
+void tcmapiterinit(TCMAP *map){
+  assert(map);
+  map->cur = map->first;
+}
+
+
+/* Get the next key of the iterator of a map object. */
+const void *tcmapiternext(TCMAP *map, int *sp){
+  assert(map && sp);
+  TCMAPREC *rec;
+  if(!map->cur) return NULL;
+  rec = map->cur;
+  map->cur = rec->next;
+  *sp = rec->ksiz;
+  return (char *)rec + sizeof(*rec);
+}
+
+
+/* Get the next key string of the iterator of a map object. */
+const char *tcmapiternext2(TCMAP *map){
+  assert(map);
+  TCMAPREC *rec;
+  if(!map->cur) return NULL;
+  rec = map->cur;
+  map->cur = rec->next;
+  return (char *)rec + sizeof(*rec);
+}
+
+
+/* Get the value bound to the key fetched from the iterator of a map object. */
+const void *tcmapiterval(const void *kbuf, int *sp){
+  assert(kbuf && sp);
+  TCMAPREC *rec = (TCMAPREC *)((char *)kbuf - sizeof(*rec));
+  *sp = rec->vsiz;
+  return (char *)kbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
+}
+
+
+/* Get the value string bound to the key fetched from the iterator of a map object. */
+const char *tcmapiterval2(const char *kstr){
+  assert(kstr);
+  TCMAPREC *rec = (TCMAPREC *)(kstr - sizeof(*rec));
+  return kstr + rec->ksiz + TCALIGNPAD(rec->ksiz);
+}
+
+
+/* Get the number of records stored in a map object. */
+uint64_t tcmaprnum(const TCMAP *map){
+  assert(map);
+  return map->rnum;
+}
+
+
+/* Get the total size of memory used in a map object. */
+uint64_t tcmapmsiz(const TCMAP *map){
+  assert(map);
+  return map->msiz + map->rnum * (sizeof(*map->first) + sizeof(void *)) +
+    map->bnum * sizeof(void *);
+}
+
+
+/* Create a list object containing all keys in a map object. */
+TCLIST *tcmapkeys(const TCMAP *map){
+  assert(map);
+  TCLIST *list = tclistnew2(map->rnum);
+  TCMAPREC *cur = map->cur;
+  const char *kbuf;
+  int ksiz;
+  ((TCMAP *)map)->cur = map->first;
+  while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
+    TCLISTPUSH(list, kbuf, ksiz);
+  }
+  ((TCMAP *)map)->cur = cur;
+  return list;
+}
+
+
+/* Create a list object containing all values in a map object. */
+TCLIST *tcmapvals(const TCMAP *map){
+  assert(map);
+  TCLIST *list = tclistnew2(map->rnum);
+  TCMAPREC *cur = map->cur;
+  const char *kbuf;
+  int ksiz;
+  ((TCMAP *)map)->cur = map->first;
+  while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
+    int vsiz;
+    const char *vbuf = tcmapiterval(kbuf, &vsiz);
+    TCLISTPUSH(list, vbuf, vsiz);
+  }
+  ((TCMAP *)map)->cur = cur;
+  return list;
+}
+
+
+/* Add an integer to a record in a map object. */
+void tcmapaddint(TCMAP *map, const char *kbuf, int ksiz, int num){
+  assert(map && kbuf && ksiz >= 0);
+  unsigned int hash;
+  TCMAPHASH1(hash, kbuf, ksiz);
+  int bidx = hash % map->bnum;
+  TCMAPREC *rec = map->buckets[bidx];
+  TCMAPREC **entp = map->buckets + bidx;
+  TCMAPHASH2(hash, kbuf, ksiz);
+  while(rec){
+    if(hash > rec->hash){
+      entp = &(rec->left);
+      rec = rec->left;
+    } else if(hash < rec->hash){
+      entp = &(rec->right);
+      rec = rec->right;
+    } else {
+      char *dbuf = (char *)rec + sizeof(*rec);
+      int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+      if(kcmp < 0){
+        entp = &(rec->left);
+        rec = rec->left;
+      } else if(kcmp > 0){
+        entp = &(rec->right);
+        rec = rec->right;
+      } else {
+        int psiz = TCALIGNPAD(ksiz);
+        *(int *)(dbuf + ksiz + psiz) += num;
+        return;
+      }
+    }
+  }
+  int psiz = TCALIGNPAD(ksiz);
+  TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
+  char *dbuf = (char *)rec + sizeof(*rec);
+  memcpy(dbuf, kbuf, ksiz);
+  dbuf[ksiz] = '\0';
+  rec->ksiz = ksiz;
+  memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
+  dbuf[ksiz+psiz+sizeof(num)] = '\0';
+  rec->vsiz = sizeof(num);
+  rec->hash = hash;
+  rec->left = NULL;
+  rec->right = NULL;
+  rec->prev = map->last;
+  rec->next = NULL;
+  *entp = rec;
+  if(!map->first) map->first = rec;
+  if(map->last) map->last->next = rec;
+  map->last = rec;
+  map->rnum++;
+}
+
+
+/* Clear a map object. */
+void tcmapclear(TCMAP *map){
+  assert(map);
+  TCMAPREC *rec = map->first;
+  while(rec){
+    TCMAPREC *next = rec->next;
+    free(rec);
+    rec = next;
+  }
+  TCMAPREC **buckets = map->buckets;
+  int bnum = map->bnum;
+  for(int i = 0; i < bnum; i++){
+    buckets[i] = NULL;
+  }
+  map->first = NULL;
+  map->last = NULL;
+  map->cur = NULL;
+  map->rnum = 0;
+  map->msiz = 0;
+}
+
+
+/* Remove front records of a map object. */
+void tcmapcutfront(TCMAP *map, int num){
+  assert(map && num >= 0);
+  tcmapiterinit(map);
+  while(num-- > 0){
+    int ksiz;
+    const char *kbuf = tcmapiternext(map, &ksiz);
+    if(!kbuf) break;
+    tcmapout(map, kbuf, ksiz);
+  }
+}
+
+
+/* Serialize a map object into a byte array. */
+void *tcmapdump(const TCMAP *map, int *sp){
+  assert(map && sp);
+  TCMAPREC *cur = map->cur;
+  int tsiz = 0;
+  const char *kbuf, *vbuf;
+  int ksiz, vsiz;
+  ((TCMAP *)map)->cur = map->first;
+  while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
+    vbuf = tcmapiterval(kbuf, &vsiz);
+    tsiz += ksiz + vsiz + sizeof(int) * 2;
+  }
+  char *buf;
+  TCMALLOC(buf, tsiz + 1);
+  char *wp = buf;
+  ((TCMAP *)map)->cur = map->first;
+  while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
+    vbuf = tcmapiterval(kbuf, &vsiz);
+    int step;
+    TCSETVNUMBUF(step, wp, ksiz);
+    wp += step;
+    memcpy(wp, kbuf, ksiz);
+    wp += ksiz;
+    TCSETVNUMBUF(step, wp, vsiz);
+    wp += step;
+    memcpy(wp, vbuf, vsiz);
+    wp += vsiz;
+  }
+  ((TCMAP *)map)->cur = cur;
+  *sp = wp - buf;
+  return buf;
+}
+
+
+/* Create a map object from a serialized byte array. */
+TCMAP *tcmapload(const void *ptr, int size){
+  assert(ptr && size >= 0);
+  TCMAP *map = tcmapnew();
+  const char *rp = ptr;
+  const char *ep = (char *)ptr + size;
+  while(rp < ep){
+    int step, ksiz, vsiz;
+    TCREADVNUMBUF(rp, ksiz, step);
+    rp += step;
+    const char *kbuf = rp;
+    rp += ksiz;
+    TCREADVNUMBUF(rp, vsiz, step);
+    rp += step;
+    tcmapputkeep(map, kbuf, ksiz, rp, vsiz);
+    rp += vsiz;
+  }
+  return map;
+}
+
+
+/* Extract a map record from a serialized byte array. */
+void *tcmaploadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp){
+  assert(ptr && size >= 0 && kbuf && ksiz >= 0 && sp);
+  const char *rp = ptr;
+  const char *ep = (char *)ptr + size;
+  while(rp < ep){
+    int step, rsiz;
+    TCREADVNUMBUF(rp, rsiz, step);
+    rp += step;
+    if(rsiz == ksiz && !memcmp(kbuf, rp, rsiz)){
+      rp += rsiz;
+      TCREADVNUMBUF(rp, rsiz, step);
+      rp += step;
+      *sp = rsiz;
+      char *rv;
+      TCMEMDUP(rv, rp, rsiz);
+      return rv;
+    }
+    rp += rsiz;
+    TCREADVNUMBUF(rp, rsiz, step);
+    rp += step;
+    rp += rsiz;
+  }
+  return NULL;
+}
+
+
+
+/*************************************************************************************************
+ * on-memory database
+ *************************************************************************************************/
+
+
+#define TCMDBMNUM      8                 // number of internal maps
+#define TCMDBBNUM      65536             // allocation unit number of a map
+
+/* get the first hash value */
+#define TCMDBHASH(TC_res, TC_kbuf, TC_ksiz) \
+  do { \
+    const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf) + TC_ksiz - 1; \
+    int _TC_ksiz = TC_ksiz; \
+    for((TC_res) = 0x20071123; _TC_ksiz--;){ \
+      (TC_res) = ((TC_res) << 5) + (TC_res) + *(_TC_p)--; \
+    } \
+    (TC_res) &= TCMDBMNUM - 1; \
+  } while(false)
+
+
+/* Create an on-memory database object. */
+TCMDB *tcmdbnew(void){
+  return tcmdbnew2(TCMDBBNUM);
+}
+
+
+/* Create an on-memory database with specifying the number of the buckets. */
+TCMDB *tcmdbnew2(uint32_t bnum){
+  TCMDB *mdb;
+  if(bnum < 1) bnum = TCMDBBNUM;
+  bnum = bnum / TCMDBMNUM + 17;
+  TCMALLOC(mdb, sizeof(*mdb));
+  TCMALLOC(mdb->mmtxs, sizeof(pthread_rwlock_t) * TCMDBMNUM);
+  TCMALLOC(mdb->imtx, sizeof(pthread_mutex_t));
+  TCMALLOC(mdb->maps, sizeof(TCMAP *) * TCMDBMNUM);
+  if(pthread_mutex_init(mdb->imtx, NULL) != 0) tcmyfatal("mutex error");
+  for(int i = 0; i < TCMDBMNUM; i++){
+    if(pthread_rwlock_init((pthread_rwlock_t *)mdb->mmtxs + i, NULL) != 0)
+      tcmyfatal("rwlock error");
+    mdb->maps[i] = tcmapnew2(bnum);
+  }
+  mdb->iter = -1;
+  return mdb;
+}
+
+
+/* Delete an on-memory database object. */
+void tcmdbdel(TCMDB *mdb){
+  assert(mdb);
+  for(int i = TCMDBMNUM - 1; i >= 0; i--){
+    tcmapdel(mdb->maps[i]);
+    pthread_rwlock_destroy((pthread_rwlock_t *)mdb->mmtxs + i);
+  }
+  pthread_mutex_destroy(mdb->imtx);
+  free(mdb->maps);
+  free(mdb->imtx);
+  free(mdb->mmtxs);
+  free(mdb);
+}
+
+
+/* Store a record into an on-memory database. */
+void tcmdbput(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  unsigned int mi;
+  TCMDBHASH(mi, kbuf, ksiz);
+  if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
+  tcmapput(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
+  pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+}
+
+
+/* Store a string record into an on-memory database. */
+void tcmdbput2(TCMDB *mdb, const char *kstr, const char *vstr){
+  assert(mdb && kstr && vstr);
+  tcmdbput(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a record of the value of two regions into an on-memory database object. */
+void tcmdbput3(TCMDB *mdb, const char *kbuf, int ksiz,
+               const void *fvbuf, int fvsiz, const char *lvbuf, int lvsiz){
+  assert(mdb && kbuf && ksiz >= 0 && fvbuf && fvsiz >= 0 && lvbuf && lvsiz >= 0);
+  unsigned int mi;
+  TCMDBHASH(mi, kbuf, ksiz);
+  if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
+  tcmapput3(mdb->maps[mi], kbuf, ksiz, fvbuf, fvsiz, lvbuf, lvsiz);
+  pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+}
+
+
+/* Store a new record into an on-memory database. */
+bool tcmdbputkeep(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  unsigned int mi;
+  TCMDBHASH(mi, kbuf, ksiz);
+  if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false;
+  bool rv = tcmapputkeep(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
+  pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+  return rv;
+}
+
+
+/* Store a new string record into an on-memory database. */
+bool tcmdbputkeep2(TCMDB *mdb, const char *kstr, const char *vstr){
+  assert(mdb && kstr && vstr);
+  return tcmdbputkeep(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Concatenate a value at the end of the value of the existing record in an on-memory database. */
+void tcmdbputcat(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+  assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  unsigned int mi;
+  TCMDBHASH(mi, kbuf, ksiz);
+  if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
+  tcmapputcat(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
+  pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+}
+
+
+/* Concatenate a string at the end of the value of the existing record in an on-memory database. */
+void tcmdbputcat2(TCMDB *mdb, const char *kstr, const char *vstr){
+  assert(mdb && kstr && vstr);
+  tcmdbputcat(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Remove a record of an on-memory database. */
+bool tcmdbout(TCMDB *mdb, const void *kbuf, int ksiz){
+  assert(mdb && kbuf && ksiz >= 0);
+  unsigned int mi;
+  TCMDBHASH(mi, kbuf, ksiz);
+  if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false;
+  bool rv = tcmapout(mdb->maps[mi], kbuf, ksiz);
+  pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+  return rv;
+}
+
+
+/* Remove a string record of an on-memory database. */
+bool tcmdbout2(TCMDB *mdb, const char *kstr){
+  assert(mdb && kstr);
+  return tcmdbout(mdb, kstr, strlen(kstr));
+}
+
+
+/* Retrieve a record in an on-memory database. */
+void *tcmdbget(TCMDB *mdb, const void *kbuf, int ksiz, int *sp){
+  assert(mdb && kbuf && ksiz >= 0 && sp);
+  unsigned int mi;
+  TCMDBHASH(mi, kbuf, ksiz);
+  if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return NULL;
+  int vsiz;
+  const char *vbuf = tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz);
+  char *rv;
+  if(vbuf){
+    TCMEMDUP(rv, vbuf, vsiz);
+    *sp = vsiz;
+  } else {
+    rv = NULL;
+  }
+  pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+  return rv;
+}
+
+
+/* Retrieve a string record in an on-memory database. */
+char *tcmdbget2(TCMDB *mdb, const char *kstr){
+  assert(mdb && kstr);
+  int vsiz;
+  return tcmdbget(mdb, kstr, strlen(kstr), &vsiz);
+}
+
+
+/* Retrieve a record and move it astern in an on-memory database. */
+void *tcmdbget3(TCMDB *mdb, const void *kbuf, int ksiz, int *sp){
+  assert(mdb && kbuf && ksiz >= 0 && sp);
+  unsigned int mi;
+  TCMDBHASH(mi, kbuf, ksiz);
+  if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return NULL;
+  int vsiz;
+  const char *vbuf = tcmapget3(mdb->maps[mi], kbuf, ksiz, &vsiz);
+  char *rv;
+  if(vbuf){
+    TCMEMDUP(rv, vbuf, vsiz);
+    *sp = vsiz;
+  } else {
+    rv = NULL;
+  }
+  pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+  return rv;
+}
+
+
+/* Get the size of the value of a record in an on-memory database object. */
+int tcmdbvsiz(TCMDB *mdb, const void *kbuf, int ksiz){
+  assert(mdb && kbuf && ksiz >= 0);
+  unsigned int mi;
+  TCMDBHASH(mi, kbuf, ksiz);
+  if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return -1;
+  int vsiz;
+  const char *vbuf = tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz);
+  if(!vbuf) vsiz = -1;
+  pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+  return vsiz;
+}
+
+
+/* Get the size of the value of a string record in an on-memory database object. */
+int tcmdbvsiz2(TCMDB *mdb, const char *kstr){
+  assert(mdb && kstr);
+  return tcmdbvsiz(mdb, kstr, strlen(kstr));
+}
+
+
+/* Initialize the iterator of an on-memory database. */
+void tcmdbiterinit(TCMDB *mdb){
+  assert(mdb);
+  if(pthread_mutex_lock(mdb->imtx) != 0) return;
+  for(int i = 0; i < TCMDBMNUM; i++){
+    tcmapiterinit(mdb->maps[i]);
+  }
+  mdb->iter = 0;
+  pthread_mutex_unlock(mdb->imtx);
+}
+
+
+/* Get the next key of the iterator of an on-memory database. */
+void *tcmdbiternext(TCMDB *mdb, int *sp){
+  assert(mdb && sp);
+  if(pthread_mutex_lock(mdb->imtx) != 0) return NULL;
+  if(mdb->iter < 0 || mdb->iter >= TCMDBMNUM){
+    pthread_mutex_unlock(mdb->imtx);
+    return NULL;
+  }
+  int mi = mdb->iter;
+  if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){
+    pthread_mutex_unlock(mdb->imtx);
+    return NULL;
+  }
+  int ksiz;
+  const char *kbuf;
+  while(!(kbuf = tcmapiternext(mdb->maps[mi], &ksiz)) && mi < TCMDBMNUM - 1){
+    pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+    mi = ++mdb->iter;
+    if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){
+      pthread_mutex_unlock(mdb->imtx);
+      return NULL;
+    }
+  }
+  char *rv;
+  if(kbuf){
+    TCMEMDUP(rv, kbuf, ksiz);
+    *sp = ksiz;
+  } else {
+    rv = NULL;
+  }
+  pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+  pthread_mutex_unlock(mdb->imtx);
+  return rv;
+}
+
+
+/* Get the next key string of the iterator of an on-memory database. */
+char *tcmdbiternext2(TCMDB *mdb){
+  assert(mdb);
+  int ksiz;
+  return tcmdbiternext(mdb, &ksiz);
+}
+
+
+/* Get forward matching keys in an on-memory database object. */
+TCLIST *tcmdbfwmkeys(TCMDB *mdb, const void *pbuf, int psiz, int max){
+  assert(mdb && pbuf && psiz >= 0);
+  TCLIST* keys = tclistnew();
+  if(pthread_mutex_lock(mdb->imtx) != 0) return keys;
+  if(max < 0) max = INT_MAX;
+  for(int i = 0; i < TCMDBMNUM && TCLISTNUM(keys) < max; i++){
+    if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
+      TCMAP *map = mdb->maps[i];
+      TCMAPREC *cur = map->cur;
+      tcmapiterinit(map);
+      int ksiz;
+      const char *kbuf;
+      while(TCLISTNUM(keys) < max && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
+        if(ksiz >= psiz && !memcmp(kbuf, pbuf, psiz)) tclistpush(keys, kbuf, ksiz);
+      }
+      map->cur = cur;
+      pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
+    }
+  }
+  pthread_mutex_unlock(mdb->imtx);
+  return keys;
+}
+
+
+/* Get forward matching string keys in an on-memory database object. */
+TCLIST *tcmdbfwmkeys2(TCMDB *mdb, const char *pstr, int max){
+  assert(mdb && pstr);
+  return tcmdbfwmkeys(mdb, pstr, strlen(pstr), max);
+}
+
+
+/* Get the number of records stored in an on-memory database. */
+uint64_t tcmdbrnum(TCMDB *mdb){
+  assert(mdb);
+  uint64_t rnum = 0;
+  for(int i = 0; i < TCMDBMNUM; i++){
+    rnum += tcmaprnum(mdb->maps[i]);
+  }
+  return rnum;
+}
+
+
+/* Get the total size of memory used in an on-memory database object. */
+uint64_t tcmdbmsiz(TCMDB *mdb){
+  assert(mdb);
+  uint64_t msiz = 0;
+  for(int i = 0; i < TCMDBMNUM; i++){
+    msiz += tcmapmsiz(mdb->maps[i]);
+  }
+  return msiz;
+}
+
+
+/* Clear an on-memory database object. */
+void tcmdbvanish(TCMDB *mdb){
+  assert(mdb);
+  for(int i = 0; i < TCMDBMNUM; i++){
+    if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
+      tcmapclear(mdb->maps[i]);
+      pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
+    }
+  }
+}
+
+
+/* Remove front records of a map object. */
+void tcmdbcutfront(TCMDB *mdb, int num){
+  assert(mdb && num >= 0);
+  num = num / TCMDBMNUM + 1;
+  for(int i = 0; i < TCMDBMNUM; i++){
+    if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
+      tcmapcutfront(mdb->maps[i], num);
+      pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
+    }
+  }
+}
+
+
+
+/*************************************************************************************************
+ * memory pool
+ *************************************************************************************************/
+
+
+#define TCMPOOLUNIT    128               // allocation unit size of memory pool elements
+
+
+/* Global memory pool object. */
+TCMPOOL *tcglobalmemorypool = NULL;
+
+
+/* private function prototypes */
+static void tcmpooldelglobal(void);
+
+
+/* Create a memory pool object. */
+TCMPOOL *tcmpoolnew(void){
+  TCMPOOL *mpool;
+  TCMALLOC(mpool, sizeof(*mpool));
+  TCMALLOC(mpool->mutex, sizeof(pthread_mutex_t));
+  if(pthread_mutex_init(mpool->mutex, NULL) != 0) tcmyfatal("locking failed");
+  mpool->anum = TCMPOOLUNIT;
+  TCMALLOC(mpool->elems, sizeof(mpool->elems[0]) * mpool->anum);
+  mpool->num = 0;
+  return mpool;
+}
+
+
+/* Delete a memory pool object. */
+void tcmpooldel(TCMPOOL *mpool){
+  assert(mpool);
+  TCMPELEM *elems = mpool->elems;
+  for(int i = mpool->num - 1; i >= 0; i--){
+    elems[i].del(elems[i].ptr);
+  }
+  free(elems);
+  pthread_mutex_destroy(mpool->mutex);
+  free(mpool->mutex);
+  free(mpool);
+}
+
+
+/* Relegate an arbitrary object to a memory pool object. */
+void tcmpoolput(TCMPOOL *mpool, void *ptr, void (*del)(void *)){
+  assert(mpool && ptr && del);
+  if(pthread_mutex_lock(mpool->mutex) != 0) tcmyfatal("locking failed");
+  int num = mpool->num;
+  if(num >= mpool->anum){
+    mpool->anum *= 2;
+    TCREALLOC(mpool->elems, mpool->elems, mpool->anum * sizeof(mpool->elems[0]));
+  }
+  mpool->elems[num].ptr = ptr;
+  mpool->elems[num].del = del;
+  mpool->num++;
+  pthread_mutex_unlock(mpool->mutex);
+}
+
+
+/* Relegate an allocated region to a memory pool object. */
+void tcmpoolputptr(TCMPOOL *mpool, void *ptr){
+  assert(mpool && ptr);
+  tcmpoolput(mpool, ptr, (void (*)(void *))free);
+}
+
+
+/* Relegate an extensible string object to a memory pool object. */
+void tcmpoolputxstr(TCMPOOL *mpool, TCXSTR *xstr){
+  assert(mpool && xstr);
+  tcmpoolput(mpool, xstr, (void (*)(void *))tcxstrdel);
+}
+
+
+/* Relegate a list object to a memory pool object. */
+void tcmpoolputlist(TCMPOOL *mpool, TCLIST *list){
+  assert(mpool && list);
+  tcmpoolput(mpool, list, (void (*)(void *))tclistdel);
+}
+
+
+/* Relegate a map object to a memory pool object. */
+void tcmpoolputmap(TCMPOOL *mpool, TCMAP *map){
+  assert(mpool && map);
+  tcmpoolput(mpool, map, (void (*)(void *))tcmapdel);
+}
+
+
+/* Allocate a region relegated to a memory pool object. */
+void *tcmpoolmalloc(TCMPOOL *mpool, size_t size){
+  assert(mpool && size > 0);
+  void *ptr;
+  TCMALLOC(ptr, size);
+  tcmpoolput(mpool, ptr, (void (*)(void *))free);
+  return ptr;
+}
+
+
+/* Create an extensible string object relegated to a memory pool object. */
+TCXSTR *tcmpoolxstrnew(TCMPOOL *mpool){
+  assert(mpool);
+  TCXSTR *xstr = tcxstrnew();
+  tcmpoolput(mpool, xstr, (void (*)(void *))tcxstrdel);
+  return xstr;
+}
+
+
+/* Create a list object relegated to a memory pool object. */
+TCLIST *tcmpoollistnew(TCMPOOL *mpool){
+  assert(mpool);
+  TCLIST *list = tclistnew();
+  tcmpoolput(mpool, list, (void (*)(void *))tclistdel);
+  return list;
+}
+
+
+/* Create a map object relegated to a memory pool object. */
+TCMAP *tcmpoolmapnew(TCMPOOL *mpool){
+  assert(mpool);
+  TCMAP *map = tcmapnew();
+  tcmpoolput(mpool, map, (void (*)(void *))tcmapdel);
+  return map;
+}
+
+
+/* Get the global memory pool object. */
+TCMPOOL *tcmpoolglobal(void){
+  if(tcglobalmemorypool) return tcglobalmemorypool;
+  tcglobalmemorypool = tcmpoolnew();
+  atexit(tcmpooldelglobal);
+  return tcglobalmemorypool;
+}
+
+
+/* Detete global memory pool object. */
+static void tcmpooldelglobal(void){
+  if(tcglobalmemorypool) tcmpooldel(tcglobalmemorypool);
+}
+
+
+
+/*************************************************************************************************
+ * miscellaneous utilities
+ *************************************************************************************************/
+
+
+#define TCRANDDEV      "/dev/urandom"    // path of the random device file
+#define TCDISTBUFSIZ   16384             // size of an distance buffer
+
+
+/* File descriptor of random number generator. */
+int tcrandomdevfd = -1;
+
+
+/* private function prototypes */
+static void tcrandomfdclose(void);
+static time_t tcmkgmtime(struct tm *tm);
+
+
+/* Get the larger value of two integers. */
+long tclmax(long a, long b){
+  return (a > b) ? a : b;
+}
+
+
+/* Get the lesser value of two integers. */
+long tclmin(long a, long b){
+  return (a < b) ? a : b;
+}
+
+
+/* Get a random number as long integer based on uniform distribution. */
+unsigned long tclrand(void){
+  static unsigned int cnt = 0;
+  static unsigned int seed = 0;
+  static unsigned long mask = 0;
+  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+  if((cnt & 0xff) == 0 && pthread_mutex_lock(&mutex) == 0){
+    if(cnt++ == 0) seed = time(NULL);
+    if(tcrandomdevfd == -1 && (tcrandomdevfd = open(TCRANDDEV, O_RDONLY, 00644)) != -1)
+      atexit(tcrandomfdclose);
+    if(tcrandomdevfd != -1) read(tcrandomdevfd, &mask, sizeof(mask));
+    pthread_mutex_unlock(&mutex);
+  }
+  return (mask ^ cnt++) ^ (unsigned long)rand_r(&seed);
+}
+
+
+/* Get a random number as double decimal based on uniform distribution. */
+double tcdrand(void){
+  return tclrand() / (ULONG_MAX + 0.01);
+}
+
+
+/* Get a random number as double decimal based on normal distribution. */
+double tcdrandnd(double avg, double sd){
+  assert(sd >= 0.0);
+  return sqrt(-2.0 * log(tcdrand())) * cos(2 * 3.141592653589793 * tcdrand()) * sd + avg;
+}
+
+
+/* Compare two strings with case insensitive evaluation. */
+int tcstricmp(const char *astr, const char *bstr){
+  assert(astr && bstr);
+  while(*astr != '\0'){
+    if(*bstr == '\0') return 1;
+    int ac = (*astr >= 'A' && *astr <= 'Z') ? *astr + ('a' - 'A') : *(unsigned char *)astr;
+    int bc = (*bstr >= 'A' && *bstr <= 'Z') ? *bstr + ('a' - 'A') : *(unsigned char *)bstr;
+    if(ac != bc) return ac - bc;
+    astr++;
+    bstr++;
+  }
+  return (*bstr == '\0') ? 0 : -1;
+}
+
+
+/* Check whether a string begins with a key. */
+bool tcstrfwm(const char *str, const char *key){
+  assert(str && key);
+  while(*key != '\0'){
+    if(*str != *key || *str == '\0') return false;
+    key++;
+    str++;
+  }
+  return true;
+}
+
+
+/* Check whether a string begins with a key with case insensitive evaluation. */
+bool tcstrifwm(const char *str, const char *key){
+  assert(str && key);
+  while(*key != '\0'){
+    if(*str == '\0') return false;
+    int sc = *str;
+    if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
+    int kc = *key;
+    if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
+    if(sc != kc) return false;
+    key++;
+    str++;
+  }
+  return true;
+}
+
+
+/* Check whether a string ends with a key. */
+bool tcstrbwm(const char *str, const char *key){
+  assert(str && key);
+  int slen = strlen(str);
+  int klen = strlen(key);
+  for(int i = 1; i <= klen; i++){
+    if(i > slen || str[slen-i] != key[klen-i]) return false;
+  }
+  return true;
+}
+
+
+/* Check whether a string ends with a key with case insensitive evaluation. */
+bool tcstribwm(const char *str, const char *key){
+  assert(str && key);
+  int slen = strlen(str);
+  int klen = strlen(key);
+  for(int i = 1; i <= klen; i++){
+    if(i > slen) return false;
+    int sc = str[slen-i];
+    if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
+    int kc = key[klen-i];
+    if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
+    if(sc != kc) return false;
+  }
+  return true;
+}
+
+
+/* Calculate the edit distance of two strings. */
+int tcstrdist(const char *astr, const char *bstr){
+  assert(astr && bstr);
+  int alen = strlen(astr);
+  int blen = strlen(bstr);
+  int dsiz = blen + 1;
+  int tbuf[TCDISTBUFSIZ];
+  int *tbl;
+  if((alen + 1) * dsiz < TCDISTBUFSIZ){
+    tbl = tbuf;
+  } else {
+    TCMALLOC(tbl, (alen + 1) * dsiz * sizeof(*tbl));
+  }
+  for(int i = 0; i <= alen; i++){
+    tbl[i*dsiz] = i;
+  }
+  for(int i = 1; i <= blen; i++){
+    tbl[i] = i;
+  }
+  astr--;
+  bstr--;
+  for(int i = 1; i <= alen; i++){
+    for(int j = 1; j <= blen; j++){
+      int ac = tbl[(i-1)*dsiz+j] + 1;
+      int bc = tbl[i*dsiz+j-1] + 1;
+      int cc = tbl[(i-1)*dsiz+j-1] + (astr[i] != bstr[j]);
+      ac = ac < bc ? ac : bc;
+      tbl[i*dsiz+j] = ac < cc ? ac : cc;
+    }
+  }
+  int rv = tbl[alen*dsiz+blen];
+  if(tbl != tbuf) free(tbl);
+  return rv;
+}
+
+
+/* Calculate the edit distance of two UTF-8 strings. */
+int tcstrdistutf(const char *astr, const char *bstr){
+  assert(astr && bstr);
+  int alen = strlen(astr);
+  uint16_t abuf[TCDISTBUFSIZ];
+  uint16_t *aary;
+  if(alen < TCDISTBUFSIZ){
+    aary = abuf;
+  } else {
+    TCMALLOC(aary, alen * sizeof(*aary));
+  }
+  tcstrutftoucs(astr, aary, &alen);
+  int blen = strlen(bstr);
+  uint16_t bbuf[TCDISTBUFSIZ];
+  uint16_t *bary;
+  if(blen < TCDISTBUFSIZ){
+    bary = bbuf;
+  } else {
+    TCMALLOC(bary, blen * sizeof(*bary));
+  }
+  tcstrutftoucs(bstr, bary, &blen);
+  int dsiz = blen + 1;
+  int tbuf[TCDISTBUFSIZ];
+  int *tbl;
+  if((alen + 1) * dsiz < TCDISTBUFSIZ){
+    tbl = tbuf;
+  } else {
+    TCMALLOC(tbl, (alen + 1) * dsiz * sizeof(*tbl));
+  }
+  for(int i = 0; i <= alen; i++){
+    tbl[i*dsiz] = i;
+  }
+  for(int i = 1; i <= blen; i++){
+    tbl[i] = i;
+  }
+  aary--;
+  bary--;
+  for(int i = 1; i <= alen; i++){
+    for(int j = 1; j <= blen; j++){
+      int ac = tbl[(i-1)*dsiz+j] + 1;
+      int bc = tbl[i*dsiz+j-1] + 1;
+      int cc = tbl[(i-1)*dsiz+j-1] + (aary[i] != bary[j]);
+      ac = ac < bc ? ac : bc;
+      tbl[i*dsiz+j] = ac < cc ? ac : cc;
+    }
+  }
+  aary++;
+  bary++;
+  int rv = tbl[alen*dsiz+blen];
+  if(tbl != tbuf) free(tbl);
+  if(bary != bbuf) free(bary);
+  if(aary != abuf) free(aary);
+  return rv;
+}
+
+
+/* Convert the letters of a string into upper case. */
+char *tcstrtoupper(char *str){
+  assert(str);
+  char *wp = str;
+  while(*wp != '\0'){
+    if(*wp >= 'a' && *wp <= 'z') *wp -= 'a' - 'A';
+    wp++;
+  }
+  return str;
+}
+
+
+/* Convert the letters of a string into lower case. */
+char *tcstrtolower(char *str){
+  assert(str);
+  char *wp = str;
+  while(*wp != '\0'){
+    if(*wp >= 'A' && *wp <= 'Z') *wp += 'a' - 'A';
+    wp++;
+  }
+  return str;
+}
+
+
+/* Cut space characters at head or tail of a string. */
+char *tcstrtrim(char *str){
+  assert(str);
+  const char *rp = str;
+  char *wp = str;
+  bool head = true;
+  while(*rp != '\0'){
+    if(*rp > '\0' && *rp <= ' '){
+      if(!head) *(wp++) = *rp;
+    } else {
+      *(wp++) = *rp;
+      head = false;
+    }
+    rp++;
+  }
+  *wp = '\0';
+  while(wp > str && wp[-1] > '\0' && wp[-1] <= ' '){
+    *(--wp) = '\0';
+  }
+  return str;
+}
+
+
+/* Squeeze space characters in a string and trim it. */
+char *tcstrsqzspc(char *str){
+  assert(str);
+  char *rp = str;
+  char *wp = str;
+  bool spc = true;
+  while(*rp != '\0'){
+    if(*rp > 0 && *rp <= ' '){
+      if(!spc) *(wp++) = *rp;
+      spc = true;
+    } else {
+      *(wp++) = *rp;
+      spc = false;
+    }
+    rp++;
+  }
+  *wp = '\0';
+  for(wp--; wp >= str; wp--){
+    if(*wp > 0 && *wp <= ' '){
+      *wp = '\0';
+    } else {
+      break;
+    }
+  }
+  return str;
+}
+
+
+/* Substitute characters in a string. */
+char *tcstrsubchr(char *str, const char *rstr, const char *sstr){
+  assert(str && rstr && sstr);
+  int slen = strlen(sstr);
+  char *wp = str;
+  for(int i = 0; str[i] != '\0'; i++){
+    const char *p = strchr(rstr, str[i]);
+    if(p){
+      int idx = p - rstr;
+      if(idx < slen) *(wp++) = sstr[idx];
+    } else {
+      *(wp++) = str[i];
+    }
+  }
+  *wp = '\0';
+  return str;
+}
+
+
+/* Count the number of characters in a string of UTF-8. */
+int tcstrcntutf(const char *str){
+  assert(str);
+  const unsigned char *rp = (unsigned char *)str;
+  int cnt = 0;
+  while(*rp != '\0'){
+    if((*rp & 0x80) == 0x00 || (*rp & 0xe0) == 0xc0 ||
+       (*rp & 0xf0) == 0xe0 || (*rp & 0xf8) == 0xf0) cnt++;
+    rp++;
+  }
+  return cnt;
+}
+
+
+/* Cut a string of UTF-8 at the specified number of characters. */
+char *tcstrcututf(char *str, int num){
+  assert(str && num >= 0);
+  unsigned char *wp = (unsigned char *)str;
+  int cnt = 0;
+  while(*wp != '\0'){
+    if((*wp & 0x80) == 0x00 || (*wp & 0xe0) == 0xc0 ||
+       (*wp & 0xf0) == 0xe0 || (*wp & 0xf8) == 0xf0){
+      cnt++;
+      if(cnt > num){
+        *wp = '\0';
+        break;
+      }
+    }
+    wp++;
+  }
+  return str;
+}
+
+
+/* Convert a UTF-8 string into a UCS-2 array. */
+void tcstrutftoucs(const char *str, uint16_t *ary, int *np){
+  assert(str && ary && np);
+  const unsigned char *rp = (unsigned char *)str;
+  unsigned int wi = 0;
+  while(*rp != '\0'){
+    int c = *(unsigned char *)rp;
+    if(c < 0x80){
+      ary[wi++] = c;
+    } else if(c < 0xe0){
+      if(rp[1] >= 0x80){
+        ary[wi++] = ((rp[0] & 0x1f) << 6) | (rp[1] & 0x3f);
+        rp++;
+      }
+    } else if(c < 0xf0){
+      if(rp[1] >= 0x80 && rp[2] >= 0x80){
+        ary[wi++] = ((rp[0] & 0xf) << 12) | ((rp[1] & 0x3f) << 6) | (rp[2] & 0x3f);
+        rp += 2;
+      }
+    }
+    rp++;
+  }
+  *np = wi;
+}
+
+
+/* Convert a UCS-2 array into a UTF-8 string. */
+void tcstrucstoutf(const uint16_t *ary, int num, char *str){
+  assert(ary && num >= 0 && str);
+  unsigned char *wp = (unsigned char *)str;
+  for(int i = 0; i < num; i++){
+    unsigned int c = ary[i];
+    if(c < 0x80){
+      *(wp++) = c;
+    } else if(c < 0x800){
+      *(wp++) = 0xc0 | (c >> 6);
+      *(wp++) = 0x80 | (c & 0x3f);
+    } else {
+      *(wp++) = 0xe0 | (c >> 12);
+      *(wp++) = 0x80 | ((c & 0xfff) >> 6);
+      *(wp++) = 0x80 | (c & 0x3f);
+    }
+  }
+  *wp = '\0';
+}
+
+
+/* Create a list object by splitting a string. */
+TCLIST *tcstrsplit(const char *str, const char *delims){
+  assert(str && delims);
+  TCLIST *list = tclistnew();
+  while(true){
+    const char *sp = str;
+    while(*str != '\0' && !strchr(delims, *str)){
+      str++;
+    }
+    TCLISTPUSH(list, sp, str - sp);
+    if(*str == '\0') break;
+    str++;
+  }
+  return list;
+}
+
+
+/* Create a string by joining all elements of a list object. */
+char *tcstrjoin(TCLIST *list, char delim){
+  assert(list);
+  int num = TCLISTNUM(list);
+  int size = num + 1;
+  for(int i = 0; i < num; i++){
+    size += TCLISTVALNUM(list, i);
+  }
+  char *buf;
+  TCMALLOC(buf, size);
+  char *wp = buf;
+  for(int i = 0; i < num; i++){
+    if(i > 0) *(wp++) = delim;
+    int vsiz;
+    const char *vbuf = tclistval(list, i, &vsiz);
+    memcpy(wp, vbuf, vsiz);
+    wp += vsiz;
+  }
+  *wp = '\0';
+  return buf;
+}
+
+
+/* Check whether a string matches a regular expression. */
+bool tcregexmatch(const char *str, const char *regex){
+  assert(str && regex);
+  int options = REG_EXTENDED | REG_NOSUB;
+  if(*regex == '*'){
+    options |= REG_ICASE;
+    regex++;
+  }
+  regex_t rbuf;
+  if(regcomp(&rbuf, regex, options) != 0) return false;
+  bool rv = regexec(&rbuf, str, 0, NULL, 0) == 0;
+  regfree(&rbuf);
+  return rv;
+}
+
+
+/* Replace each substring matching a regular expression string. */
+char *tcregexreplace(const char *str, const char *regex, const char *alt){
+  assert(str && regex && alt);
+  int options = REG_EXTENDED;
+  if(*regex == '*'){
+    options |= REG_ICASE;
+    regex++;
+  }
+  regex_t rbuf;
+  if(regex[0] == '\0' || regcomp(&rbuf, regex, options) != 0) return tcstrdup(str);
+  regmatch_t subs[256];
+  if(regexec(&rbuf, str, 32, subs, 0) != 0){
+    regfree(&rbuf);
+    return tcstrdup(str);
+  }
+  const char *sp = str;
+  TCXSTR *xstr = tcxstrnew();
+  bool first = true;
+  while(sp[0] != '\0' && regexec(&rbuf, sp, 10, subs, first ? 0 : REG_NOTBOL) == 0){
+    first = false;
+    if(subs[0].rm_so == -1) break;
+    tcxstrcat(xstr, sp, subs[0].rm_so);
+    for(const char *rp = alt; *rp != '\0'; rp++){
+      if(*rp == '\\'){
+        if(rp[1] >= '0' && rp[1] <= '9'){
+          int num = rp[1] - '0';
+          if(subs[num].rm_so != -1 && subs[num].rm_eo != -1)
+            tcxstrcat(xstr, sp + subs[num].rm_so, subs[num].rm_eo - subs[num].rm_so);
+          ++rp;
+        } else if(rp[1] != '\0'){
+          tcxstrcat(xstr, ++rp, 1);
+        }
+      } else if(*rp == '&'){
+        tcxstrcat(xstr, sp + subs[0].rm_so, subs[0].rm_eo - subs[0].rm_so);
+      } else {
+        tcxstrcat(xstr, rp, 1);
+      }
+    }
+    sp += subs[0].rm_eo;
+    if(subs[0].rm_eo < 1) break;
+  }
+  tcxstrcat2(xstr, sp);
+  regfree(&rbuf);
+  return tcxstrtomalloc(xstr);
+}
+
+
+/* Get the time of day in seconds. */
+double tctime(void){
+  struct timeval tv;
+  if(gettimeofday(&tv, NULL) == -1) return 0.0;
+  return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
+}
+
+
+/* Get the Gregorian calendar of a time. */
+void tccalendar(int64_t t, int jl, int *yearp, int *monp, int *dayp,
+                int *hourp, int *minp, int *secp){
+  if(t == INT64_MAX || jl == INT_MAX){
+    struct timeval tv;
+    struct timezone tz;
+    if(gettimeofday(&tv, &tz) == 0){
+      if(t == INT64_MAX) t = tv.tv_sec;
+      if(jl == INT_MAX) jl = tz.tz_minuteswest * -60;
+    } else {
+      if(yearp) *yearp = 0;
+      if(monp) *monp = 0;
+      if(dayp) *dayp = 0;
+      if(hourp) *hourp = 0;
+      if(minp) *minp = 0;
+      if(secp) *secp = 0;
+    }
+  }
+  time_t tt = (time_t)t + jl;
+  struct tm ts;
+  if(!gmtime_r(&tt, &ts)){
+    if(yearp) *yearp = 0;
+    if(monp) *monp = 0;
+    if(dayp) *dayp = 0;
+    if(hourp) *hourp = 0;
+    if(minp) *minp = 0;
+    if(secp) *secp = 0;
+  }
+  if(yearp) *yearp = ts.tm_year + 1900;
+  if(monp) *monp = ts.tm_mon + 1;
+  if(dayp) *dayp = ts.tm_mday;
+  if(hourp) *hourp = ts.tm_hour;
+  if(minp) *minp = ts.tm_min;
+  if(secp) *secp = ts.tm_sec;
+}
+
+
+/* Format a date as a string in W3CDTF. */
+void tcdatestrwww(int64_t t, int jl, char *buf){
+  assert(buf);
+  if(t == INT64_MAX || jl == INT_MAX){
+    struct timeval tv;
+    struct timezone tz;
+    if(gettimeofday(&tv, &tz) == 0){
+      if(t == INT64_MAX) t = tv.tv_sec;
+      if(jl == INT_MAX) jl = tz.tz_minuteswest * -60;
+    } else {
+      if(t == INT64_MAX) t = time(NULL);
+      if(jl == INT_MAX) jl = 0;
+    }
+  }
+  time_t tt = (time_t)t + jl;
+  struct tm ts;
+  if(!gmtime_r(&tt, &ts)) memset(&ts, 0, sizeof(ts));
+  ts.tm_year += 1900;
+  ts.tm_mon += 1;
+  jl /= 60;
+  char tzone[16];
+  if(jl == 0){
+    sprintf(tzone, "Z");
+  } else if(jl < 0){
+    jl *= -1;
+    sprintf(tzone, "-%02d:%02d", jl / 60, jl % 60);
+  } else {
+    sprintf(tzone, "+%02d:%02d", jl / 60, jl % 60);
+  }
+  sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%s",
+          ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, tzone);
+}
+
+
+/* Format a date as a string in RFC 1123 format. */
+void tcdatestrhttp(int64_t t, int jl, char *buf){
+  assert(buf);
+  if(t == INT64_MAX || jl == INT_MAX){
+    struct timeval tv;
+    struct timezone tz;
+    if(gettimeofday(&tv, &tz) == 0){
+      if(t == INT64_MAX) t = tv.tv_sec;
+      if(jl == INT_MAX) jl = tz.tz_minuteswest * -60;
+    } else {
+      if(t == INT64_MAX) t = time(NULL);
+      if(jl == INT_MAX) jl = 0;
+    }
+  }
+  time_t tt = (time_t)t + jl;
+  struct tm ts;
+  if(!gmtime_r(&tt, &ts)) memset(&ts, 0, sizeof(ts));
+  ts.tm_year += 1900;
+  ts.tm_mon += 1;
+  jl /= 60;
+  char *wp = buf;
+  switch(tcdayofweek(ts.tm_year, ts.tm_mon, ts.tm_mday)){
+  case 0: wp += sprintf(wp, "Sun, "); break;
+  case 1: wp += sprintf(wp, "Mon, "); break;
+  case 2: wp += sprintf(wp, "Tue, "); break;
+  case 3: wp += sprintf(wp, "Wed, "); break;
+  case 4: wp += sprintf(wp, "Thu, "); break;
+  case 5: wp += sprintf(wp, "Fri, "); break;
+  case 6: wp += sprintf(wp, "Sat, "); break;
+  }
+  wp += sprintf(wp, "%02d ", ts.tm_mday);
+  switch(ts.tm_mon){
+  case 1: wp += sprintf(wp, "Jan "); break;
+  case 2: wp += sprintf(wp, "Feb "); break;
+  case 3: wp += sprintf(wp, "Mar "); break;
+  case 4: wp += sprintf(wp, "Apr "); break;
+  case 5: wp += sprintf(wp, "May "); break;
+  case 6: wp += sprintf(wp, "Jun "); break;
+  case 7: wp += sprintf(wp, "Jul "); break;
+  case 8: wp += sprintf(wp, "Aug "); break;
+  case 9: wp += sprintf(wp, "Sep "); break;
+  case 10: wp += sprintf(wp, "Oct "); break;
+  case 11: wp += sprintf(wp, "Nov "); break;
+  case 12: wp += sprintf(wp, "Dec "); break;
+  }
+  wp += sprintf(wp, "%04d %02d:%02d:%02d ", ts.tm_year, ts.tm_hour, ts.tm_min, ts.tm_sec);
+  if(jl == 0){
+    sprintf(wp, "GMT");
+  } else if(jl < 0){
+    jl *= -1;
+    sprintf(wp, "-%02d%02d", jl / 60, jl % 60);
+  } else {
+    sprintf(wp, "+%02d%02d", jl / 60, jl % 60);
+  }
+}
+
+
+/* Get the time value of a date string. */
+int64_t tcstrmktime(const char *str){
+  assert(str);
+  while(*str > '\0' && *str <= ' '){
+    str++;
+  }
+  if(*str == '\0') return 0;
+  if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+    return (int64_t)strtoll(str + 2, NULL, 16);
+  struct tm ts;
+  memset(&ts, 0, sizeof(ts));
+  ts.tm_year = 70;
+  ts.tm_mon = 0;
+  ts.tm_mday = 1;
+  ts.tm_hour = 0;
+  ts.tm_min = 0;
+  ts.tm_sec = 0;
+  ts.tm_isdst = 0;
+  int len = strlen(str);
+  char *pv;
+  time_t t = (time_t)strtoll(str, &pv, 10);
+  if(*(signed char *)pv >= '\0' && *pv <= ' '){
+    while(*pv > '\0' && *pv <= ' '){
+      pv++;
+    }
+    if(*pv == '\0') return (int64_t)t;
+  }
+  if((pv[0] == 's' || pv[0] == 'S') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+    return (int64_t)t;
+  if((pv[0] == 'm' || pv[0] == 'M') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+    return (int64_t)t * 60;
+  if((pv[0] == 'h' || pv[0] == 'H') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+    return (int64_t)t * 60 * 60;
+  if((pv[0] == 'd' || pv[0] == 'D') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+    return (int64_t)t * 60 * 60 * 24;
+  if(len > 4 && str[4] == '-'){
+    ts.tm_year = atoi(str) - 1900;
+    if((pv = strchr(str, '-')) != NULL && pv - str == 4){
+      char *rp = pv + 1;
+      ts.tm_mon = atoi(rp) - 1;
+      if((pv = strchr(rp, '-')) != NULL && pv - str == 7){
+        rp = pv + 1;
+        ts.tm_mday = atoi(rp);
+        if((pv = strchr(rp, 'T')) != NULL && pv - str == 10){
+          rp = pv + 1;
+          ts.tm_hour = atoi(rp);
+          if((pv = strchr(rp, ':')) != NULL && pv - str == 13){
+            rp = pv + 1;
+            ts.tm_min = atoi(rp);
+          }
+          if((pv = strchr(rp, ':')) != NULL && pv - str == 16){
+            rp = pv + 1;
+            ts.tm_sec = atoi(rp);
+          }
+          if((pv = strchr(rp, '.')) != NULL && pv - str >= 19) rp = pv + 1;
+          strtol(rp, &pv, 10);
+          if((*pv == '+' || *pv == '-') && strlen(pv) >= 6 && pv[3] == ':')
+            ts.tm_sec -= (atoi(pv + 1) * 3600 + atoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1);
+        }
+      }
+    }
+    return (int64_t)tcmkgmtime(&ts);
+  }
+  if(len > 4 && str[4] == '/'){
+    ts.tm_year = atoi(str) - 1900;
+    if((pv = strchr(str, '/')) != NULL && pv - str == 4){
+      char *rp = pv + 1;
+      ts.tm_mon = atoi(rp) - 1;
+      if((pv = strchr(rp, '/')) != NULL && pv - str == 7){
+        rp = pv + 1;
+        ts.tm_mday = atoi(rp);
+        if((pv = strchr(rp, ' ')) != NULL && pv - str == 10){
+          rp = pv + 1;
+          ts.tm_hour = atoi(rp);
+          if((pv = strchr(rp, ':')) != NULL && pv - str == 13){
+            rp = pv + 1;
+            ts.tm_min = atoi(rp);
+          }
+          if((pv = strchr(rp, ':')) != NULL && pv - str == 16){
+            rp = pv + 1;
+            ts.tm_sec = atoi(rp);
+          }
+          if((pv = strchr(rp, '.')) != NULL && pv - str >= 19) rp = pv + 1;
+          strtol(rp, &pv, 10);
+          if((*pv == '+' || *pv == '-') && strlen(pv) >= 6 && pv[3] == ':')
+            ts.tm_sec -= (atoi(pv + 1) * 3600 + atoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1);
+        }
+      }
+    }
+    return (int64_t)tcmkgmtime(&ts);
+  }
+  const char *crp = str;
+  if(len >= 4 && str[3] == ',') crp = str + 4;
+  while(*crp == ' '){
+    crp++;
+  }
+  ts.tm_mday = atoi(crp);
+  while((*crp >= '0' && *crp <= '9') || *crp == ' '){
+    crp++;
+  }
+  if(tcstrifwm(crp, "Jan")){
+    ts.tm_mon = 0;
+  } else if(tcstrifwm(crp, "Feb")){
+    ts.tm_mon = 1;
+  } else if(tcstrifwm(crp, "Mar")){
+    ts.tm_mon = 2;
+  } else if(tcstrifwm(crp, "Apr")){
+    ts.tm_mon = 3;
+  } else if(tcstrifwm(crp, "May")){
+    ts.tm_mon = 4;
+  } else if(tcstrifwm(crp, "Jun")){
+    ts.tm_mon = 5;
+  } else if(tcstrifwm(crp, "Jul")){
+    ts.tm_mon = 6;
+  } else if(tcstrifwm(crp, "Aug")){
+    ts.tm_mon = 7;
+  } else if(tcstrifwm(crp, "Sep")){
+    ts.tm_mon = 8;
+  } else if(tcstrifwm(crp, "Oct")){
+    ts.tm_mon = 9;
+  } else if(tcstrifwm(crp, "Nov")){
+    ts.tm_mon = 10;
+  } else if(tcstrifwm(crp, "Dec")){
+    ts.tm_mon = 11;
+  } else {
+    ts.tm_mon = -1;
+  }
+  if(ts.tm_mon >= 0) crp += 3;
+  while(*crp == ' '){
+    crp++;
+  }
+  ts.tm_year = atoi(crp);
+  if(ts.tm_year >= 1969) ts.tm_year -= 1900;
+  while(*crp >= '0' && *crp <= '9'){
+    crp++;
+  }
+  while(*crp == ' '){
+    crp++;
+  }
+  if(ts.tm_mday > 0 && ts.tm_mon >= 0 && ts.tm_year >= 0){
+    int clen = strlen(crp);
+    if(clen >= 8 && crp[2] == ':' && crp[5] == ':'){
+      ts.tm_hour = atoi(crp + 0);
+      ts.tm_min = atoi(crp + 3);
+      ts.tm_sec = atoi(crp + 6);
+      if(clen >= 14 && crp[8] == ' ' && (crp[9] == '+' || crp[9] == '-')){
+        ts.tm_sec -= ((crp[10] - '0') * 36000 + (crp[11] - '0') * 3600 +
+                      (crp[12] - '0') * 600 + (crp[13] - '0') * 60) * (crp[9] == '+' ? 1 : -1);
+      } else if(clen > 9){
+        if(!strcmp(crp + 9, "JST")){
+          ts.tm_sec -= 9 * 3600;
+        } else if(!strcmp(crp + 9, "CCT")){
+          ts.tm_sec -= 8 * 3600;
+        } else if(!strcmp(crp + 9, "KST")){
+          ts.tm_sec -= 9 * 3600;
+        } else if(!strcmp(crp + 9, "EDT")){
+          ts.tm_sec -= -4 * 3600;
+        } else if(!strcmp(crp + 9, "EST")){
+          ts.tm_sec -= -5 * 3600;
+        } else if(!strcmp(crp + 9, "CDT")){
+          ts.tm_sec -= -5 * 3600;
+        } else if(!strcmp(crp + 9, "CST")){
+          ts.tm_sec -= -6 * 3600;
+        } else if(!strcmp(crp + 9, "MDT")){
+          ts.tm_sec -= -6 * 3600;
+        } else if(!strcmp(crp + 9, "MST")){
+          ts.tm_sec -= -7 * 3600;
+        } else if(!strcmp(crp + 9, "PDT")){
+          ts.tm_sec -= -7 * 3600;
+        } else if(!strcmp(crp + 9, "PST")){
+          ts.tm_sec -= -8 * 3600;
+        } else if(!strcmp(crp + 9, "HDT")){
+          ts.tm_sec -= -9 * 3600;
+        } else if(!strcmp(crp + 9, "HST")){
+          ts.tm_sec -= -10 * 3600;
+        }
+      }
+    }
+    return (int64_t)tcmkgmtime(&ts);
+  }
+  return 0;
+}
+
+
+/* Get the day of week of a date. */
+int tcdayofweek(int year, int mon, int day){
+  if(mon < 3){
+    year--;
+    mon += 12;
+  }
+  return (day + ((8 + (13 * mon)) / 5) + (year + (year / 4) - (year / 100) + (year / 400))) % 7;
+}
+
+
+/* Close the random number generator. */
+static void tcrandomfdclose(void){
+  close(tcrandomdevfd);
+}
+
+
+/* Make the GMT from a time structure.
+   `tm' specifies the pointer to the time structure.
+   The return value is the GMT. */
+static time_t tcmkgmtime(struct tm *tm){
+#if defined(_SYS_LINUX_)
+  assert(tm);
+  return timegm(tm);
+#else
+  assert(tm);
+  static int jl = INT_MAX;
+  if(jl == INT_MAX){
+    struct timeval tv;
+    struct timezone tz;
+    if(gettimeofday(&tv, &tz) == 0){
+      jl = tz.tz_minuteswest * -60;
+    } else {
+      jl = 0;
+    }
+  }
+  tm->tm_sec += jl;
+  return mktime(tm);
+#endif
+}
+
+
+
+/*************************************************************************************************
+ * filesystem utilities
+ *************************************************************************************************/
+
+
+#define TCFILEMODE     00644             // permission of a creating file
+#define TCIOBUFSIZ     16384             // size of an I/O buffer
+
+
+/* Get the canonicalized absolute path of a file. */
+char *tcrealpath(const char *path){
+  assert(path);
+  char buf[PATH_MAX];
+  if(!realpath(path, buf)) return NULL;
+  return tcstrdup(buf);
+}
+
+
+/* Read whole data of a file. */
+void *tcreadfile(const char *path, int limit, int *sp){
+  int fd = path ? open(path, O_RDONLY, TCFILEMODE) : 0;
+  if(fd == -1) return NULL;
+  if(fd == 0){
+    TCXSTR *xstr = tcxstrnew();
+    char buf[TCIOBUFSIZ];
+    limit = limit > 0 ? limit : INT_MAX;
+    int rsiz;
+    while((rsiz = read(fd, buf, tclmin(TCIOBUFSIZ, limit))) > 0){
+      TCXSTRCAT(xstr, buf, rsiz);
+      limit -= rsiz;
+    }
+    if(sp) *sp = TCXSTRSIZE(xstr);
+    return tcxstrtomalloc(xstr);
+  }
+  struct stat sbuf;
+  if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
+    close(fd);
+    return NULL;
+  }
+  limit = limit > 0 ? tclmin((int)sbuf.st_size, limit) : sbuf.st_size;
+  char *buf;
+  TCMALLOC(buf, sbuf.st_size + 1);
+  char *wp = buf;
+  int rsiz;
+  while((rsiz = read(fd, wp, limit - (wp - buf))) > 0){
+    wp += rsiz;
+  }
+  *wp = '\0';
+  close(fd);
+  if(sp) *sp = wp - buf;
+  return buf;
+}
+
+
+/* Read every line of a file. */
+TCLIST *tcreadfilelines(const char *path){
+  int fd = path ? open(path, O_RDONLY, TCFILEMODE) : 0;
+  if(fd == -1) return NULL;
+  TCLIST *list = tclistnew();
+  TCXSTR *xstr = tcxstrnew();
+  char buf[TCIOBUFSIZ];
+  int rsiz;
+  while((rsiz = read(fd, buf, TCIOBUFSIZ)) > 0){
+    for(int i = 0; i < rsiz; i++){
+      switch(buf[i]){
+      case '\r':
+        break;
+      case '\n':
+        TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
+        tcxstrclear(xstr);
+        break;
+      default:
+        TCXSTRCAT(xstr, buf + i, 1);
+        break;
+      }
+    }
+  }
+  TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
+  tcxstrdel(xstr);
+  if(path) close(fd);
+  return list;
+}
+
+
+/* Write data into a file. */
+bool tcwritefile(const char *path, const void *ptr, int size){
+  assert(ptr && size >= 0);
+  int fd = 1;
+  if(path && (fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, TCFILEMODE)) == -1) return false;
+  bool err = false;
+  if(!tcwrite(fd, ptr, size)) err = true;
+  if(close(fd) == -1) err = true;
+  return !err;
+}
+
+
+/* Copy a file. */
+bool tccopyfile(const char *src, const char *dest){
+  int ifd = open(src, O_RDONLY, TCFILEMODE);
+  if(ifd == -1) return false;
+  int ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, TCFILEMODE);
+  if(ofd == -1){
+    close(ifd);
+    return false;
+  }
+  bool err = false;
+  while(true){
+    char buf[TCIOBUFSIZ];
+    int size = read(ifd, buf, TCIOBUFSIZ);
+    if(size > 0){
+      if(!tcwrite(ofd, buf, size)){
+        err = true;
+        break;
+      }
+    } else if(size == -1){
+      if(errno != EINTR){
+        err = true;
+        break;
+      }
+    } else {
+      break;
+    }
+  }
+  if(close(ofd) == -1) err = true;
+  if(close(ifd) == -1) err = true;
+  return !err;
+}
+
+
+/* Read names of files in a directory. */
+TCLIST *tcreaddir(const char *path){
+  assert(path);
+  DIR *DD;
+  struct dirent *dp;
+  if(!(DD = opendir(path))) return NULL;
+  TCLIST *list = tclistnew();
+  while((dp = readdir(DD)) != NULL){
+    if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue;
+    TCLISTPUSH(list, dp->d_name, strlen(dp->d_name));
+  }
+  closedir(DD);
+  return list;
+}
+
+
+/* Expand a pattern into a list of matched paths. */
+TCLIST *tcglobpat(const char *pattern){
+  assert(pattern);
+  TCLIST *list = tclistnew();
+  glob_t gbuf;
+  memset(&gbuf, 0, sizeof(gbuf));
+  if(glob(pattern, GLOB_ERR | GLOB_NOSORT, NULL, &gbuf) == 0){
+    for(int i = 0; i < gbuf.gl_pathc; i++){
+      tclistpush2(list, gbuf.gl_pathv[i]);
+    }
+    globfree(&gbuf);
+  }
+  return list;
+}
+
+
+/* Remove a file or a directory and its sub ones recursively. */
+bool tcremovelink(const char *path){
+  assert(path);
+  struct stat sbuf;
+  if(lstat(path, &sbuf) == -1) return false;
+  if(unlink(path) == 0) return true;
+  TCLIST *list;
+  if(!S_ISDIR(sbuf.st_mode) || !(list = tcreaddir(path))) return false;
+  bool tail = path[0] != '\0' && path[strlen(path)-1] == MYPATHCHR;
+  for(int i = 0; i < TCLISTNUM(list); i++){
+    const char *elem = TCLISTVALPTR(list, i);
+    if(!strcmp(MYCDIRSTR, elem) || !strcmp(MYPDIRSTR, elem)) continue;
+    char *cpath;
+    if(tail){
+      cpath = tcsprintf("%s%s", path, elem);
+    } else {
+      cpath = tcsprintf("%s%c%s", path, MYPATHCHR, elem);
+    }
+    tcremovelink(cpath);
+    free(cpath);
+  }
+  tclistdel(list);
+  return rmdir(path) == 0 ? true : false;
+}
+
+
+/* Write data into a file. */
+bool tcwrite(int fd, const void *buf, size_t size){
+  assert(fd >= 0 && buf && size >= 0);
+  const char *rp = buf;
+  do {
+    int wb = write(fd, rp, size);
+    switch(wb){
+    case -1: if(errno != EINTR) return false;
+    case 0: break;
+    default:
+      rp += wb;
+      size -= wb;
+      break;
+    }
+  } while(size > 0);
+  return true;
+}
+
+
+/* Read data from a file. */
+bool tcread(int fd, void *buf, size_t size){
+  assert(fd >= 0 && buf && size >= 0);
+  char *wp = buf;
+  do {
+    int rb = read(fd, wp, size);
+    switch(rb){
+    case -1: if(errno != EINTR) return false;
+    case 0: return size < 1;
+    default:
+      wp += rb;
+      size -= rb;
+    }
+  } while(size > 0);
+  return true;
+}
+
+
+/* Lock a file. */
+bool tclock(int fd, bool ex, bool nb){
+  assert(fd >= 0);
+  struct flock lock;
+  memset(&lock, 0, sizeof(struct flock));
+  lock.l_type = ex ? F_WRLCK : F_RDLCK;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = 0;
+  lock.l_len = 0;
+  lock.l_pid = 0;
+  while(fcntl(fd, nb ? F_SETLK : F_SETLKW, &lock) == -1){
+    if(errno != EINTR) return false;
+  }
+  return true;
+}
+
+
+
+/*************************************************************************************************
+ * encoding utilities
+ *************************************************************************************************/
+
+
+#define TCURLELBNUM    31                // bucket number of URL elements
+#define TCENCBUFSIZ    32                // size of a buffer for encoding name
+#define TCXMLATBNUM    31                // bucket number of XML attributes
+
+
+/* Encode a serial object with URL encoding. */
+char *tcurlencode(const char *ptr, int size){
+  assert(ptr && size >= 0);
+  char *buf;
+  TCMALLOC(buf, size * 3 + 1);
+  char *wp = buf;
+  for(int i = 0; i < size; i++){
+    int c = ((unsigned char *)ptr)[i];
+    if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+       (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.!~*'()", c))){
+      *(wp++) = c;
+    } else {
+      wp += sprintf(wp, "%%%02X", c);
+    }
+  }
+  *wp = '\0';
+  return buf;
+}
+
+
+/* Decode a string encoded with URL encoding. */
+char *tcurldecode(const char *str, int *sp){
+  assert(str && sp);
+  char *buf = tcstrdup(str);
+  char *wp = buf;
+  while(*str != '\0'){
+    if(*str == '%'){
+      str++;
+      if(((str[0] >= '0' && str[0] <= '9') || (str[0] >= 'A' && str[0] <= 'F') ||
+          (str[0] >= 'a' && str[0] <= 'f')) &&
+         ((str[1] >= '0' && str[1] <= '9') || (str[1] >= 'A' && str[1] <= 'F') ||
+          (str[1] >= 'a' && str[1] <= 'f'))){
+        unsigned char c = *str;
+        if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
+        if(c >= 'a' && c <= 'z'){
+          *wp = c - 'a' + 10;
+        } else {
+          *wp = c - '0';
+        }
+        *wp *= 0x10;
+        str++;
+        c = *str;
+        if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
+        if(c >= 'a' && c <= 'z'){
+          *wp += c - 'a' + 10;
+        } else {
+          *wp += c - '0';
+        }
+        str++;
+        wp++;
+      } else {
+        break;
+      }
+    } else if(*str == '+'){
+      *wp = ' ';
+      str++;
+      wp++;
+    } else {
+      *wp = *str;
+      str++;
+      wp++;
+    }
+  }
+  *wp = '\0';
+  *sp = wp - buf;
+  return buf;
+}
+
+
+/* Break up a URL into elements. */
+TCMAP *tcurlbreak(const char *str){
+  assert(str);
+  TCMAP *map = tcmapnew2(TCURLELBNUM);
+  char *tmp = tcstrdup(str);
+  const char *rp = tcstrtrim(tmp);
+  tcmapput2(map, "self", rp);
+  bool serv = false;
+  if(tcstrifwm(rp, "http://")){
+    tcmapput2(map, "scheme", "http");
+    rp += 7;
+    serv = true;
+  } else if(tcstrifwm(rp, "https://")){
+    tcmapput2(map, "scheme", "https");
+    rp += 8;
+    serv = true;
+  } else if(tcstrifwm(rp, "ftp://")){
+    tcmapput2(map, "scheme", "ftp");
+    rp += 6;
+    serv = true;
+  } else if(tcstrifwm(rp, "sftp://")){
+    tcmapput2(map, "scheme", "sftp");
+    rp += 7;
+    serv = true;
+  } else if(tcstrifwm(rp, "ftps://")){
+    tcmapput2(map, "scheme", "ftps");
+    rp += 7;
+    serv = true;
+  } else if(tcstrifwm(rp, "tftp://")){
+    tcmapput2(map, "scheme", "tftp");
+    rp += 7;
+    serv = true;
+  } else if(tcstrifwm(rp, "ldap://")){
+    tcmapput2(map, "scheme", "ldap");
+    rp += 7;
+    serv = true;
+  } else if(tcstrifwm(rp, "ldaps://")){
+    tcmapput2(map, "scheme", "ldaps");
+    rp += 8;
+    serv = true;
+  } else if(tcstrifwm(rp, "file://")){
+    tcmapput2(map, "scheme", "file");
+    rp += 7;
+    serv = true;
+  }
+  char *ep;
+  if((ep = strchr(rp, '#')) != NULL){
+    tcmapput2(map, "fragment", ep + 1);
+    *ep = '\0';
+  }
+  if((ep = strchr(rp, '?')) != NULL){
+    tcmapput2(map, "query", ep + 1);
+    *ep = '\0';
+  }
+  if(serv){
+    if((ep = strchr(rp, '/')) != NULL){
+      tcmapput2(map, "path", ep);
+      *ep = '\0';
+    } else {
+      tcmapput2(map, "path", "/");
+    }
+    if((ep = strchr(rp, '@')) != NULL){
+      *ep = '\0';
+      if(rp[0] != '\0') tcmapput2(map, "authority", rp);
+      rp = ep + 1;
+    }
+    if((ep = strchr(rp, ':')) != NULL){
+      if(ep[1] != '\0') tcmapput2(map, "port", ep + 1);
+      *ep = '\0';
+    }
+    if(rp[0] != '\0') tcmapput2(map, "host", rp);
+  } else {
+    tcmapput2(map, "path", rp);
+  }
+  free(tmp);
+  if((rp = tcmapget2(map, "path")) != NULL){
+    if((ep = strrchr(rp, '/')) != NULL){
+      if(ep[1] != '\0') tcmapput2(map, "file", ep + 1);
+    } else {
+      tcmapput2(map, "file", rp);
+    }
+  }
+  if((rp = tcmapget2(map, "file")) != NULL && (!strcmp(rp, ".") || !strcmp(rp, "..")))
+    tcmapout2(map, "file");
+  return map;
+}
+
+
+/* Resolve a relative URL with an absolute URL. */
+char *tcurlresolve(const char *base, const char *target){
+  assert(base && target);
+  const char *vbuf, *path;
+  char *tmp, *wp, *enc;
+  while(*base > '\0' && *base <= ' '){
+    base++;
+  }
+  while(*target > '\0' && *target <= ' '){
+    target++;
+  }
+  if(*target == '\0') target = base;
+  TCXSTR *rbuf = tcxstrnew();
+  TCMAP *telems = tcurlbreak(target);
+  int port = 80;
+  TCMAP *belems = tcurlbreak(tcmapget2(telems, "scheme") ? target : base);
+  if((vbuf = tcmapget2(belems, "scheme")) != NULL){
+    tcxstrcat2(rbuf, vbuf);
+    TCXSTRCAT(rbuf, "://", 3);
+    if(!tcstricmp(vbuf, "https")){
+      port = 443;
+    } else if(!tcstricmp(vbuf, "ftp")){
+      port = 21;
+    } else if(!tcstricmp(vbuf, "sftp")){
+      port = 115;
+    } else if(!tcstricmp(vbuf, "ftps")){
+      port = 22;
+    } else if(!tcstricmp(vbuf, "tftp")){
+      port = 69;
+    } else if(!tcstricmp(vbuf, "ldap")){
+      port = 389;
+    } else if(!tcstricmp(vbuf, "ldaps")){
+      port = 636;
+    }
+  } else {
+    tcxstrcat2(rbuf, "http://");
+  }
+  int vsiz;
+  if((vbuf = tcmapget2(belems, "authority")) != NULL){
+    if((wp = strchr(vbuf, ':')) != NULL){
+      *wp = '\0';
+      tmp = tcurldecode(vbuf, &vsiz);
+      enc = tcurlencode(tmp, vsiz);
+      tcxstrcat2(rbuf, enc);
+      free(enc);
+      free(tmp);
+      TCXSTRCAT(rbuf, ":", 1);
+      wp++;
+      tmp = tcurldecode(wp, &vsiz);
+      enc = tcurlencode(tmp, vsiz);
+      tcxstrcat2(rbuf, enc);
+      free(enc);
+      free(tmp);
+    } else {
+      tmp = tcurldecode(vbuf, &vsiz);
+      enc = tcurlencode(tmp, vsiz);
+      tcxstrcat2(rbuf, enc);
+      free(enc);
+      free(tmp);
+    }
+    TCXSTRCAT(rbuf, "@", 1);
+  }
+  if((vbuf = tcmapget2(belems, "host")) != NULL){
+    tmp = tcurldecode(vbuf, &vsiz);
+    tcstrtolower(tmp);
+    enc = tcurlencode(tmp, vsiz);
+    tcxstrcat2(rbuf, enc);
+    free(enc);
+    free(tmp);
+  } else {
+    TCXSTRCAT(rbuf, "localhost", 9);
+  }
+  int num;
+  char numbuf[TCNUMBUFSIZ];
+  if((vbuf = tcmapget2(belems, "port")) != NULL && (num = atoi(vbuf)) != port && num > 0){
+    sprintf(numbuf, ":%d", num);
+    tcxstrcat2(rbuf, numbuf);
+  }
+  if(!(path = tcmapget2(telems, "path"))) path = "/";
+  if(path[0] == '\0' && (vbuf = tcmapget2(belems, "path")) != NULL) path = vbuf;
+  if(path[0] == '\0') path = "/";
+  TCLIST *bpaths = tclistnew();
+  TCLIST *opaths;
+  if(path[0] != '/' && (vbuf = tcmapget2(belems, "path")) != NULL){
+    opaths = tcstrsplit(vbuf, "/");
+  } else {
+    opaths = tcstrsplit("/", "/");
+  }
+  free(tclistpop2(opaths));
+  for(int i = 0; i < TCLISTNUM(opaths); i++){
+    vbuf = tclistval(opaths, i, &vsiz);
+    if(vsiz < 1 || !strcmp(vbuf, ".")) continue;
+    if(!strcmp(vbuf, "..")){
+      free(tclistpop2(bpaths));
+    } else {
+      TCLISTPUSH(bpaths, vbuf, vsiz);
+    }
+  }
+  tclistdel(opaths);
+  opaths = tcstrsplit(path, "/");
+  for(int i = 0; i < TCLISTNUM(opaths); i++){
+    vbuf = tclistval(opaths, i, &vsiz);
+    if(vsiz < 1 || !strcmp(vbuf, ".")) continue;
+    if(!strcmp(vbuf, "..")){
+      free(tclistpop2(bpaths));
+    } else {
+      TCLISTPUSH(bpaths, vbuf, vsiz);
+    }
+  }
+  tclistdel(opaths);
+  for(int i = 0; i < TCLISTNUM(bpaths); i++){
+    vbuf = TCLISTVALPTR(bpaths, i);
+    if(strchr(vbuf, '%')){
+      tmp = tcurldecode(vbuf, &vsiz);
+    } else {
+      tmp = tcstrdup(vbuf);
+    }
+    enc = tcurlencode(tmp, strlen(tmp));
+    TCXSTRCAT(rbuf, "/", 1);
+    tcxstrcat2(rbuf, enc);
+    free(enc);
+    free(tmp);
+  }
+  if(tcstrbwm(path, "/")) TCXSTRCAT(rbuf, "/", 1);
+  tclistdel(bpaths);
+  if((vbuf = tcmapget2(telems, "query")) != NULL ||
+     (*target == '#' && (vbuf = tcmapget2(belems, "query")) != NULL)){
+    TCXSTRCAT(rbuf, "?", 1);
+    TCLIST *qelems = tcstrsplit(vbuf, "&;");
+    for(int i = 0; i < TCLISTNUM(qelems); i++){
+      vbuf = TCLISTVALPTR(qelems, i);
+      if(i > 0) TCXSTRCAT(rbuf, "&", 1);
+      if((wp = strchr(vbuf, '=')) != NULL){
+        *wp = '\0';
+        tmp = tcurldecode(vbuf, &vsiz);
+        enc = tcurlencode(tmp, vsiz);
+        tcxstrcat2(rbuf, enc);
+        free(enc);
+        free(tmp);
+        TCXSTRCAT(rbuf, "=", 1);
+        wp++;
+        tmp = tcurldecode(wp, &vsiz);
+        enc = tcurlencode(tmp, strlen(tmp));
+        tcxstrcat2(rbuf, enc);
+        free(enc);
+        free(tmp);
+      } else {
+        tmp = tcurldecode(vbuf, &vsiz);
+        enc = tcurlencode(tmp, vsiz);
+        tcxstrcat2(rbuf, enc);
+        free(enc);
+        free(tmp);
+      }
+    }
+    tclistdel(qelems);
+  }
+  if((vbuf = tcmapget2(telems, "fragment")) != NULL){
+    tmp = tcurldecode(vbuf, &vsiz);
+    enc = tcurlencode(tmp, vsiz);
+    TCXSTRCAT(rbuf, "#", 1);
+    tcxstrcat2(rbuf, enc);
+    free(enc);
+    free(tmp);
+  }
+  tcmapdel(belems);
+  tcmapdel(telems);
+  return tcxstrtomalloc(rbuf);
+}
+
+
+/* Encode a serial object with Base64 encoding. */
+char *tcbaseencode(const char *ptr, int size){
+  assert(ptr && size >= 0);
+  char *tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  const unsigned char *obj = (const unsigned char *)ptr;
+  char *buf;
+  TCMALLOC(buf, 4 * (size + 2) / 3 + 1);
+  char *wp = buf;
+  for(int i = 0; i < size; i += 3){
+    switch(size - i){
+    case 1:
+      *wp++ = tbl[obj[0] >> 2];
+      *wp++ = tbl[(obj[0] & 3) << 4];
+      *wp++ = '=';
+      *wp++ = '=';
+      break;
+    case 2:
+      *wp++ = tbl[obj[0] >> 2];
+      *wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
+      *wp++ = tbl[(obj[1] & 0xf) << 2];
+      *wp++ = '=';
+      break;
+    default:
+      *wp++ = tbl[obj[0] >> 2];
+      *wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
+      *wp++ = tbl[((obj[1] & 0xf) << 2) + (obj[2] >> 6)];
+      *wp++ = tbl[obj[2] & 0x3f];
+      break;
+    }
+    obj += 3;
+  }
+  *wp = '\0';
+  return buf;
+}
+
+
+/* Decode a string encoded with Base64 encoding. */
+char *tcbasedecode(const char *str, int *sp){
+  assert(str && sp);
+  int cnt = 0;
+  int bpos = 0;
+  int eqcnt = 0;
+  int len = strlen(str);
+  unsigned char *obj;
+  TCMALLOC(obj, len + 4);
+  unsigned char *wp = obj;
+  while(bpos < len && eqcnt == 0){
+    int bits = 0;
+    int i;
+    for(i = 0; bpos < len && i < 4; bpos++){
+      if(str[bpos] >= 'A' && str[bpos] <= 'Z'){
+        bits = (bits << 6) | (str[bpos] - 'A');
+        i++;
+      } else if(str[bpos] >= 'a' && str[bpos] <= 'z'){
+        bits = (bits << 6) | (str[bpos] - 'a' + 26);
+        i++;
+      } else if(str[bpos] >= '0' && str[bpos] <= '9'){
+        bits = (bits << 6) | (str[bpos] - '0' + 52);
+        i++;
+      } else if(str[bpos] == '+'){
+        bits = (bits << 6) | 62;
+        i++;
+      } else if(str[bpos] == '/'){
+        bits = (bits << 6) | 63;
+        i++;
+      } else if(str[bpos] == '='){
+        bits <<= 6;
+        i++;
+        eqcnt++;
+      }
+    }
+    if(i == 0 && bpos >= len) continue;
+    switch(eqcnt){
+    case 0:
+      *wp++ = (bits >> 16) & 0xff;
+      *wp++ = (bits >> 8) & 0xff;
+      *wp++ = bits & 0xff;
+      cnt += 3;
+      break;
+    case 1:
+      *wp++ = (bits >> 16) & 0xff;
+      *wp++ = (bits >> 8) & 0xff;
+      cnt += 2;
+      break;
+    case 2:
+      *wp++ = (bits >> 16) & 0xff;
+      cnt += 1;
+      break;
+    }
+  }
+  obj[cnt] = '\0';
+  *sp = cnt;
+  return (char *)obj;
+}
+
+
+/* Encode a serial object with Quoted-printable encoding. */
+char *tcquoteencode(const char *ptr, int size){
+  assert(ptr && size >= 0);
+  const unsigned char *rp = (const unsigned char *)ptr;
+  char *buf;
+  TCMALLOC(buf, size * 3 + 1);
+  char *wp = buf;
+  int cols = 0;
+  for(int i = 0; i < size; i++){
+    if(rp[i] == '=' || (rp[i] < 0x20 && rp[i] != '\r' && rp[i] != '\n' && rp[i] != '\t') ||
+       rp[i] > 0x7e){
+      wp += sprintf(wp, "=%02X", rp[i]);
+      cols += 3;
+    } else {
+      *(wp++) = rp[i];
+      cols++;
+    }
+  }
+  *wp = '\0';
+  return buf;
+}
+
+
+/* Decode a string encoded with Quoted-printable encoding. */
+char *tcquotedecode(const char *str, int *sp){
+  assert(str && sp);
+  char *buf;
+  TCMALLOC(buf, strlen(str) + 1);
+  char *wp = buf;
+  for(; *str != '\0'; str++){
+    if(*str == '='){
+      str++;
+      if(*str == '\0'){
+        break;
+      } else if(str[0] == '\r' && str[1] == '\n'){
+        str++;
+      } else if(str[0] != '\n' && str[0] != '\r'){
+        if(*str >= 'A' && *str <= 'Z'){
+          *wp = (*str - 'A' + 10) * 16;
+        } else if(*str >= 'a' && *str <= 'z'){
+          *wp = (*str - 'a' + 10) * 16;
+        } else {
+          *wp = (*str - '0') * 16;
+        }
+        str++;
+        if(*str == '\0') break;
+        if(*str >= 'A' && *str <= 'Z'){
+          *wp += *str - 'A' + 10;
+        } else if(*str >= 'a' && *str <= 'z'){
+          *wp += *str - 'a' + 10;
+        } else {
+          *wp += *str - '0';
+        }
+        wp++;
+      }
+    } else {
+      *wp = *str;
+      wp++;
+    }
+  }
+  *wp = '\0';
+  *sp = wp - buf;
+  return buf;
+}
+
+
+/* Encode a string with MIME encoding. */
+char *tcmimeencode(const char *str, const char *encname, bool base){
+  assert(str && encname);
+  int len = strlen(str);
+  char *buf;
+  TCMALLOC(buf, len * 3 + strlen(encname) + 16);
+  char *wp = buf;
+  wp += sprintf(wp, "=?%s?%c?", encname, base ? 'B' : 'Q');
+  char *enc = base ? tcbaseencode(str, len) : tcquoteencode(str, len);
+  wp += sprintf(wp, "%s?=", enc);
+  free(enc);
+  return buf;
+}
+
+
+/* Decode a string encoded with MIME encoding. */
+char *tcmimedecode(const char *str, char *enp){
+  assert(str);
+  if(enp) sprintf(enp, "US-ASCII");
+  char *buf;
+  TCMALLOC(buf, strlen(str) + 1);
+  char *wp = buf;
+  while(*str != '\0'){
+    if(tcstrfwm(str, "=?")){
+      str += 2;
+      const char *pv = str;
+      const char *ep = strchr(str, '?');
+      if(!ep) continue;
+      if(enp && ep - pv < TCENCBUFSIZ){
+        memcpy(enp, pv, ep - pv);
+        enp[ep-pv] = '\0';
+      }
+      pv = ep + 1;
+      bool quoted = (*pv == 'Q' || *pv == 'q');
+      if(*pv != '\0') pv++;
+      if(*pv != '\0') pv++;
+      if(!(ep = strchr(pv, '?'))) continue;
+      char *tmp;
+      TCMEMDUP(tmp, pv, ep - pv);
+      int len;
+      char *dec = quoted ? tcquotedecode(tmp, &len) : tcbasedecode(tmp, &len);
+      wp += sprintf(wp, "%s", dec);
+      free(dec);
+      free(tmp);
+      str = ep + 1;
+      if(*str != '\0') str++;
+    } else {
+      *(wp++) = *str;
+      str++;
+    }
+  }
+  *wp = '\0';
+  return buf;
+}
+
+
+/* Compress a serial object with Packbits encoding. */
+char *tcpackencode(const char *ptr, int size, int *sp){
+  assert(ptr && size >= 0 && sp);
+  char *buf;
+  TCMALLOC(buf, size * 2 + 1);
+  char *wp = buf;
+  const char *end = ptr + size;
+  while(ptr < end){
+    char *sp = wp;
+    const char *rp = ptr + 1;
+    int step = 1;
+    while(rp < end && step < 0x7f && *rp == *ptr){
+      step++;
+      rp++;
+    }
+    if(step <= 1 && rp < end){
+      wp = sp + 1;
+      *(wp++) = *ptr;
+      while(rp < end && step < 0x7f && *rp != *(rp - 1)){
+        *(wp++) = *rp;
+        step++;
+        rp++;
+      }
+      if(rp < end && *(rp - 1) == *rp){
+        wp--;
+        rp--;
+        step--;
+      }
+      *sp = step == 1 ? 1 : -step;
+    } else {
+      *(wp++) = step;
+      *(wp++) = *ptr;
+    }
+    ptr += step;
+  }
+  *sp = wp - buf;
+  return buf;
+}
+
+
+/* Decompress a serial object compressed with Packbits encoding. */
+char *tcpackdecode(const char *ptr, int size, int *sp){
+  assert(ptr && size >= 0 && sp);
+  int asiz = size * 3;
+  char *buf;
+  TCMALLOC(buf, asiz + 1);
+  int wi = 0;
+  const char *end = ptr + size;
+  while(ptr < end){
+    int step = abs(*ptr);
+    if(wi + step >= asiz){
+      asiz = asiz * 2 + step;
+      TCREALLOC(buf, buf, asiz + 1);
+    }
+    if(*(ptr++) >= 0){
+      memset(buf + wi, *ptr, step);
+      ptr++;
+    } else {
+      step = tclmin(step, end - ptr);
+      memcpy(buf + wi, ptr, step);
+      ptr += step;
+    }
+    wi += step;
+  }
+  buf[wi] = '\0';
+  *sp = wi;
+  return buf;
+}
+
+
+/* Compress a serial object with Deflate encoding. */
+char *tcdeflate(const char *ptr, int size, int *sp){
+  assert(ptr && sp);
+  if(!_tc_deflate) return NULL;
+  return _tc_deflate(ptr, size, sp, _TCZMZLIB);
+}
+
+
+/* Decompress a serial object compressed with Deflate encoding. */
+char *tcinflate(const char *ptr, int size, int *sp){
+  assert(ptr && size >= 0);
+  if(!_tc_inflate) return NULL;
+  return _tc_inflate(ptr, size, sp, _TCZMZLIB);
+}
+
+
+/* Compress a serial object with GZIP encoding. */
+char *tcgzipencode(const char *ptr, int size, int *sp){
+  assert(ptr && sp);
+  if(!_tc_deflate) return NULL;
+  return _tc_deflate(ptr, size, sp, _TCZMGZIP);
+}
+
+
+/* Decompress a serial object compressed with GZIP encoding. */
+char *tcgzipdecode(const char *ptr, int size, int *sp){
+  assert(ptr && size >= 0);
+  if(!_tc_inflate) return NULL;
+  return _tc_inflate(ptr, size, sp, _TCZMGZIP);
+}
+
+
+/* Get the CRC32 checksum of a serial object. */
+unsigned int tcgetcrc(const char *ptr, int size){
+  assert(ptr && size >= 0);
+  if(!_tc_getcrc) return 0;
+  return _tc_getcrc(ptr, size);
+}
+
+
+/* Encode an array of nonnegative integers with BER encoding. */
+char *tcberencode(const unsigned int *ary, int anum, int *sp){
+  assert(ary && anum >= 0 && sp);
+  char *buf;
+  TCMALLOC(buf, anum * (sizeof(int) + 1) + 1);
+  char *wp = buf;
+  for(int i = 0; i < anum; i++){
+    unsigned int num = ary[i];
+    if(num < (1 << 7)){
+      *(wp++) = num;
+    } else if(num < (1 << 14)){
+      *(wp++) = (num >> 7) | 0x80;
+      *(wp++) = num & 0x7f;
+    } else if(num < (1 << 21)){
+      *(wp++) = (num >> 14) | 0x80;
+      *(wp++) = ((num >> 7) & 0x7f) | 0x80;
+      *(wp++) = num & 0x7f;
+    } else if(num < (1 << 28)){
+      *(wp++) = (num >> 21) | 0x80;
+      *(wp++) = ((num >> 14) & 0x7f) | 0x80;
+      *(wp++) = ((num >> 7) & 0x7f) | 0x80;
+      *(wp++) = num & 0x7f;
+    } else {
+      *(wp++) = (num >> 28) | 0x80;
+      *(wp++) = ((num >> 21) & 0x7f) | 0x80;
+      *(wp++) = ((num >> 14) & 0x7f) | 0x80;
+      *(wp++) = ((num >> 7) & 0x7f) | 0x80;
+      *(wp++) = num & 0x7f;
+    }
+  }
+  *sp = wp - buf;
+  return buf;
+}
+
+
+/* Decode a serial object encoded with BER encoding. */
+unsigned int *tcberdecode(const char *ptr, int size, int *np){
+  assert(ptr && size >= 0 && np);
+  unsigned int *buf;
+  TCMALLOC(buf, size * sizeof(*buf) + 1);
+  unsigned int *wp = buf;
+  while(size > 0){
+    unsigned int num = 0;
+    int c;
+    do {
+      c = *(unsigned char *)ptr;
+      num = num * 0x80 + (c & 0x7f);
+      ptr++;
+      size--;
+    } while(c >= 0x80 && size > 0);
+    *(wp++) = num;
+  }
+  *np = wp - buf;
+  return buf;
+}
+
+
+/* Escape meta characters in a string with the entity references of XML. */
+char *tcxmlescape(const char *str){
+  assert(str);
+  const char *rp = str;
+  int bsiz = 0;
+  while(*rp != '\0'){
+    switch(*rp){
+    case '&':
+      bsiz += 5;
+      break;
+    case '<':
+      bsiz += 4;
+      break;
+    case '>':
+      bsiz += 4;
+      break;
+    case '"':
+      bsiz += 6;
+      break;
+    default:
+      bsiz++;
+      break;
+    }
+    rp++;
+  }
+  char *buf;
+  TCMALLOC(buf, bsiz + 1);
+  char *wp = buf;
+  while(*str != '\0'){
+    switch(*str){
+    case '&':
+      memcpy(wp, "&amp;", 5);
+      wp += 5;
+      break;
+    case '<':
+      memcpy(wp, "&lt;", 4);
+      wp += 4;
+      break;
+    case '>':
+      memcpy(wp, "&gt;", 4);
+      wp += 4;
+      break;
+    case '"':
+      memcpy(wp, "&quot;", 6);
+      wp += 6;
+      break;
+    default:
+      *(wp++) = *str;
+      break;
+    }
+    str++;
+  }
+  *wp = '\0';
+  return buf;
+}
+
+
+/* Unescape entity references in a string of XML. */
+char *tcxmlunescape(const char *str){
+  assert(str);
+  char *buf;
+  TCMALLOC(buf, strlen(str) + 1);
+  char *wp = buf;
+  while(*str != '\0'){
+    if(*str == '&'){
+      if(tcstrfwm(str, "&amp;")){
+        *(wp++) = '&';
+        str += 5;
+      } else if(tcstrfwm(str, "&lt;")){
+        *(wp++) = '<';
+        str += 4;
+      } else if(tcstrfwm(str, "&gt;")){
+        *(wp++) = '>';
+        str += 4;
+      } else if(tcstrfwm(str, "&quot;")){
+        *(wp++) = '"';
+        str += 6;
+      } else {
+        *(wp++) = *(str++);
+      }
+    } else {
+      *(wp++) = *(str++);
+    }
+  }
+  *wp = '\0';
+  return buf;
+}
+
+
+/* Split an XML string into tags and text sections. */
+TCLIST *tcxmlbreak(const char *str){
+  assert(str);
+  TCLIST *list = tclistnew();
+  int i = 0;
+  int pv = 0;
+  bool tag = false;
+  char *ep;
+  while(true){
+    if(str[i] == '\0'){
+      if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
+      break;
+    } else if(!tag && str[i] == '<'){
+      if(str[i+1] == '!' && str[i+2] == '-' && str[i+3] == '-'){
+        if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
+        if((ep = strstr(str + i, "-->")) != NULL){
+          TCLISTPUSH(list, str + i, ep - str - i + 3);
+          i = ep - str + 2;
+          pv = i + 1;
+        }
+      } else if(str[i+1] == '!' && str[i+2] == '[' && tcstrifwm(str + i, "<![CDATA[")){
+        if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
+        if((ep = strstr(str + i, "]]>")) != NULL){
+          i += 9;
+          TCXSTR *xstr = tcxstrnew();
+          while(str + i < ep){
+            if(str[i] == '&'){
+              TCXSTRCAT(xstr, "&amp;", 5);
+            } else if(str[i] == '<'){
+              TCXSTRCAT(xstr, "&lt;", 4);
+            } else if(str[i] == '>'){
+              TCXSTRCAT(xstr, "&gt;", 4);
+            } else {
+              TCXSTRCAT(xstr, str + i, 1);
+            }
+            i++;
+          }
+          if(TCXSTRSIZE(xstr) > 0) TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
+          tcxstrdel(xstr);
+          i = ep - str + 2;
+          pv = i + 1;
+        }
+      } else {
+        if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
+        tag = true;
+        pv = i;
+      }
+    } else if(tag && str[i] == '>'){
+      if(i > pv) TCLISTPUSH(list, str + pv, i - pv + 1);
+      tag = false;
+      pv = i + 1;
+    }
+    i++;
+  }
+  return list;
+}
+
+
+/* Get the map of attributes of an XML tag. */
+TCMAP *tcxmlattrs(const char *str){
+  assert(str);
+  TCMAP *map = tcmapnew2(TCXMLATBNUM);
+  const unsigned char *rp = (unsigned char *)str;
+  while(*rp == '<' || *rp == '/' || *rp == '?' || *rp == '!' || *rp == ' '){
+    rp++;
+  }
+  const unsigned char *key = rp;
+  while(*rp > 0x20 && *rp != '/' && *rp != '>'){
+    rp++;
+  }
+  tcmapputkeep(map, "", 0, (char *)key, rp - key);
+  while(*rp != '\0'){
+    while(*rp != '\0' && (*rp <= 0x20 || *rp == '/' || *rp == '?' || *rp == '>')){
+      rp++;
+    }
+    key = rp;
+    while(*rp > 0x20 && *rp != '/' && *rp != '>' && *rp != '='){
+      rp++;
+    }
+    int ksiz = rp - key;
+    while(*rp != '\0' && (*rp == '=' || *rp <= 0x20)){
+      rp++;
+    }
+    const unsigned char *val;
+    int vsiz;
+    if(*rp == '"'){
+      rp++;
+      val = rp;
+      while(*rp != '\0' && *rp != '"'){
+        rp++;
+      }
+      vsiz = rp - val;
+    } else if(*rp == '\''){
+      rp++;
+      val = rp;
+      while(*rp != '\0' && *rp != '\''){
+        rp++;
+      }
+      vsiz = rp - val;
+    } else {
+      val = rp;
+      while(*rp > 0x20 && *rp != '"' && *rp != '\'' && *rp != '>'){
+        rp++;
+      }
+      vsiz = rp - val;
+    }
+    if(*rp != '\0') rp++;
+    if(ksiz > 0){
+      char *copy;
+      TCMEMDUP(copy, val, vsiz);
+      char *raw = tcxmlunescape(copy);
+      tcmapputkeep(map, (char *)key, ksiz, raw, strlen(raw));
+      free(raw);
+      free(copy);
+    }
+  }
+  return map;
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+#define TCBSENCUNIT    8192             // unit size of TCBS encoding
+#define TCBWTCNTMIN    64               // minimum element number of counting sort
+#define TCBWTCNTLV     4                // maximum recursion level of counting sort
+#define TCBWTBUFNUM    16384            // number of elements of BWT buffer
+
+typedef struct {                         // type of structure for a BWT character
+  int fchr;                              // character code of the first character
+  int tchr;                              // character code of the last character
+} TCBWTREC;
+
+
+/* private function prototypes */
+static void tcglobalmutexinit(void);
+static void tcglobalmutexdestroy(void);
+static void tcbwtsortstrcount(const char **arrays, int anum, int len, int level);
+static void tcbwtsortstrinsert(const char **arrays, int anum, int len, int skip);
+static void tcbwtsortstrheap(const char **arrays, int anum, int len, int skip);
+static void tcbwtsortchrcount(unsigned char *str, int len);
+static void tcbwtsortchrinsert(unsigned char *str, int len);
+static void tcbwtsortreccount(TCBWTREC *arrays, int anum);
+static void tcbwtsortrecinsert(TCBWTREC *array, int anum);
+static int tcbwtsearchrec(TCBWTREC *array, int anum, int tchr);
+static void tcmtfencode(char *ptr, int size);
+static void tcmtfdecode(char *ptr, int size);
+static int tcgammaencode(const char *ptr, int size, char *obuf);
+static int tcgammadecode(const char *ptr, int size, char *obuf);
+
+
+/* Show error message on the standard error output and exit. */
+void *tcmyfatal(const char *message){
+  assert(message);
+  if(tcfatalfunc){
+    tcfatalfunc(message);
+  } else {
+    fprintf(stderr, "fatal error: %s\n", message);
+  }
+  exit(1);
+  return NULL;
+}
+
+
+/* Global mutex object. */
+static pthread_rwlock_t tcglobalmutex;
+static pthread_once_t tcglobalmutexonce = PTHREAD_ONCE_INIT;
+
+
+/* Lock the global mutex object. */
+bool tcglobalmutexlock(void){
+  pthread_once(&tcglobalmutexonce, tcglobalmutexinit);
+  return pthread_rwlock_wrlock(&tcglobalmutex) == 0;
+}
+
+
+/* Lock the global mutex object by shared locking. */
+bool tcglobalmutexlockshared(void){
+  pthread_once(&tcglobalmutexonce, tcglobalmutexinit);
+  return pthread_rwlock_rdlock(&tcglobalmutex) == 0;
+}
+
+
+/* Unlock the global mutex object. */
+bool tcglobalmutexunlock(void){
+  return pthread_rwlock_unlock(&tcglobalmutex) == 0;
+}
+
+
+/* Compress a serial object with TCBS encoding. */
+char *tcbsencode(const char *ptr, int size, int *sp){
+  assert(ptr && size >= 0 && sp);
+  char *result;
+  TCMALLOC(result, (size * 7) / 3 + (size / TCBSENCUNIT + 1) * sizeof(uint16_t) +
+           TCBSENCUNIT * 2 + 0x200);
+  char *pv = result + size + 0x100;
+  char *wp = pv;
+  char *tp = pv + size + 0x100;
+  const char *end = ptr + size;
+  while(ptr < end){
+    int usiz = tclmin(TCBSENCUNIT, end - ptr);
+    memcpy(tp, ptr, usiz);
+    memcpy(tp + usiz, ptr, usiz);
+    char *sp = wp;
+    uint16_t idx = 0;
+    wp += sizeof(idx);
+    const char *arrays[usiz+1];
+    for(int i = 0; i < usiz; i++){
+      arrays[i] = tp + i;
+    }
+    const char *fp = arrays[0];
+    if(usiz >= TCBWTCNTMIN){
+      tcbwtsortstrcount(arrays, usiz, usiz, 0);
+    } else if(usiz > 1){
+      tcbwtsortstrinsert(arrays, usiz, usiz, 0);
+    }
+    for(int i = 0; i < usiz; i++){
+      int tidx = arrays[i] - fp;
+      if(tidx == 0){
+        idx = i;
+        *(wp++) = ptr[usiz-1];
+      } else {
+        *(wp++) = ptr[tidx-1];
+      }
+    }
+    idx = TCHTOIS(idx);
+    memcpy(sp, &idx, sizeof(idx));
+    ptr += TCBSENCUNIT;
+  }
+  size = wp - pv;
+  tcmtfencode(pv, size);
+  int nsiz = tcgammaencode(pv, size, result);
+  *sp = nsiz;
+  return result;
+}
+
+
+/* Decompress a serial object compressed with TCBS encoding. */
+char *tcbsdecode(const char *ptr, int size, int *sp){
+  char *result;
+  TCMALLOC(result, size * 9 + 0x200);
+  char *wp = result + size + 0x100;
+  int nsiz = tcgammadecode(ptr, size, wp);
+  tcmtfdecode(wp, nsiz);
+  ptr = wp;
+  wp = result;
+  const char *end = ptr + nsiz;
+  while(ptr < end){
+    uint16_t idx;
+    memcpy(&idx, ptr, sizeof(idx));
+    idx = TCITOHS(idx);
+    ptr += sizeof(idx);
+    int usiz = tclmin(TCBSENCUNIT, end - ptr);
+    if(idx >= usiz) idx = 0;
+    char rbuf[usiz+1];
+    memcpy(rbuf, ptr, usiz);
+    if(usiz >= TCBWTCNTMIN){
+      tcbwtsortchrcount((unsigned char *)rbuf, usiz);
+    } else if(usiz > 0){
+      tcbwtsortchrinsert((unsigned char *)rbuf, usiz);
+    }
+    int fnums[0x100], tnums[0x100];
+    memset(fnums, 0, sizeof(fnums));
+    memset(tnums, 0, sizeof(tnums));
+    TCBWTREC array[usiz+1];
+    TCBWTREC *rp = array;
+    for(int i = 0; i < usiz; i++){
+      int fc = *(unsigned char *)(rbuf + i);
+      rp->fchr = (fc << 23) + fnums[fc]++;
+      int tc = *(unsigned char *)(ptr + i);
+      rp->tchr = (tc << 23) + tnums[tc]++;
+      rp++;
+    }
+    unsigned int fchr = array[idx].fchr;
+    if(usiz >= TCBWTCNTMIN){
+      tcbwtsortreccount(array, usiz);
+    } else if(usiz > 1){
+      tcbwtsortrecinsert(array, usiz);
+    }
+    for(int i = 0; i < usiz; i++){
+      if(array[i].fchr == fchr){
+        idx = i;
+        break;
+      }
+    }
+    for(int i = 0; i < usiz; i++){
+      *(wp++) = array[idx].fchr >> 23;
+      idx = tcbwtsearchrec(array, usiz, array[idx].fchr);
+    }
+    ptr += usiz;
+  }
+  *wp = '\0';
+  *sp = wp - result;
+  return result;
+}
+
+
+/* Encode a serial object with BWT encoding. */
+char *tcbwtencode(const char *ptr, int size, int *idxp){
+  assert(ptr && size >= 0 && idxp);
+  if(size < 1){
+    *idxp = 0;
+    char *rv;
+    TCMEMDUP(rv, "", 0);
+    return rv;
+  }
+  char *result;
+  TCMALLOC(result, size * 3 + 1);
+  char *tp = result + size + 1;
+  memcpy(tp, ptr, size);
+  memcpy(tp + size, ptr, size);
+  const char *abuf[TCBWTBUFNUM];
+  const char **arrays = abuf;
+  if(size > TCBWTBUFNUM) TCMALLOC(arrays, sizeof(*arrays) * size);
+  for(int i = 0; i < size; i++){
+    arrays[i] = tp + i;
+  }
+  const char *fp = arrays[0];
+  if(size >= TCBWTCNTMIN){
+    tcbwtsortstrcount(arrays, size, size, -1);
+  } else if(size > 1){
+    tcbwtsortstrinsert(arrays, size, size, 0);
+  }
+  for(int i = 0; i < size; i++){
+    int idx = arrays[i] - fp;
+    if(idx == 0){
+      *idxp = i;
+      result[i] = ptr[size-1];
+    } else {
+      result[i] = ptr[idx-1];
+    }
+  }
+  if(arrays != abuf) free(arrays);
+  result[size] = '\0';
+  return result;
+}
+
+
+/* Decode a serial object encoded with BWT encoding. */
+char *tcbwtdecode(const char *ptr, int size, int idx){
+  assert(ptr && size >= 0);
+  if(size < 1 || idx < 0){
+    char *rv;
+    TCMEMDUP(rv, "", 0);
+    return rv;
+  }
+  if(idx >= size) idx = 0;
+  char *result;
+  TCMALLOC(result, size + 1);
+  memcpy(result, ptr, size);
+  if(size >= TCBWTCNTMIN){
+    tcbwtsortchrcount((unsigned char *)result, size);
+  } else {
+    tcbwtsortchrinsert((unsigned char *)result, size);
+  }
+  int fnums[0x100], tnums[0x100];
+  memset(fnums, 0, sizeof(fnums));
+  memset(tnums, 0, sizeof(tnums));
+  TCBWTREC abuf[TCBWTBUFNUM];
+  TCBWTREC *array = abuf;
+  if(size > TCBWTBUFNUM) TCMALLOC(array, sizeof(*array) * size);
+  TCBWTREC *rp = array;
+  for(int i = 0; i < size; i++){
+    int fc = *(unsigned char *)(result + i);
+    rp->fchr = (fc << 23) + fnums[fc]++;
+    int tc = *(unsigned char *)(ptr + i);
+    rp->tchr = (tc << 23) + tnums[tc]++;
+    rp++;
+  }
+  unsigned int fchr = array[idx].fchr;
+  if(size >= TCBWTCNTMIN){
+    tcbwtsortreccount(array, size);
+  } else if(size > 1){
+    tcbwtsortrecinsert(array, size);
+  }
+  for(int i = 0; i < size; i++){
+    if(array[i].fchr == fchr){
+      idx = i;
+      break;
+    }
+  }
+  char *wp = result;
+  for(int i = 0; i < size; i++){
+    *(wp++) = array[idx].fchr >> 23;
+    idx = tcbwtsearchrec(array, size, array[idx].fchr);
+  }
+  *wp = '\0';
+  if(array != abuf) free(array);
+  return result;
+}
+
+
+/* Initialize the global mutex object */
+static void tcglobalmutexinit(void){
+  if(!TCUSEPTHREAD) memset(&tcglobalmutex, 0, sizeof(tcglobalmutex));
+  if(pthread_rwlock_init(&tcglobalmutex, NULL) != 0) tcmyfatal("rwlock error");
+  atexit(tcglobalmutexdestroy);
+}
+
+
+/* Destroy the global mutex object */
+static void tcglobalmutexdestroy(void){
+  pthread_rwlock_destroy(&tcglobalmutex);
+}
+
+
+/* Sort BWT string arrays by dicrionary order by counting sort.
+   `array' specifies an array of string arrays.
+   `anum' specifies the number of the array.
+   `len' specifies the size of each string.
+   `level' specifies the level of recursion. */
+static void tcbwtsortstrcount(const char **arrays, int anum, int len, int level){
+  assert(arrays && anum >= 0 && len >= 0);
+  const char *nbuf[TCBWTBUFNUM];
+  const char **narrays = nbuf;
+  if(anum > TCBWTBUFNUM) TCMALLOC(narrays, sizeof(*narrays) * anum);
+  int count[0x100], accum[0x100];
+  memset(count, 0, sizeof(count));
+  int skip = level < 0 ? 0 : level;
+  for(int i = 0; i < anum; i++){
+    count[((unsigned char *)arrays[i])[skip]]++;
+  }
+  memcpy(accum, count, sizeof(count));
+  for(int i = 1; i < 0x100; i++){
+    accum[i] = accum[i-1] + accum[i];
+  }
+  for(int i = 0; i < anum; i++){
+    narrays[--accum[((unsigned char *)arrays[i])[skip]]] = arrays[i];
+  }
+  int off = 0;
+  if(level >= 0 && level < TCBWTCNTLV){
+    for(int i = 0; i < 0x100; i++){
+      int c = count[i];
+      if(c > 1){
+        if(c >= TCBWTCNTMIN){
+          tcbwtsortstrcount(narrays + off, c, len, level + 1);
+        } else {
+          tcbwtsortstrinsert(narrays + off, c, len, skip + 1);
+        }
+      }
+      off += c;
+    }
+  } else {
+    for(int i = 0; i < 0x100; i++){
+      int c = count[i];
+      if(c > 1){
+        if(c >= TCBWTCNTMIN){
+          tcbwtsortstrheap(narrays + off, c, len, skip + 1);
+        } else {
+          tcbwtsortstrinsert(narrays + off, c, len, skip + 1);
+        }
+      }
+      off += c;
+    }
+  }
+  memcpy(arrays, narrays, anum * sizeof(*narrays));
+  if(narrays != nbuf) free(narrays);
+}
+
+
+/* Sort BWT string arrays by dicrionary order by insertion sort.
+   `array' specifies an array of string arrays.
+   `anum' specifies the number of the array.
+   `len' specifies the size of each string.
+   `skip' specifies the number of skipped bytes. */
+static void tcbwtsortstrinsert(const char **arrays, int anum, int len, int skip){
+  assert(arrays && anum >= 0 && len >= 0);
+  for(int i = 1; i < anum; i++){
+    int cmp = 0;
+    const unsigned char *ap = (unsigned char *)arrays[i-1];
+    const unsigned char *bp = (unsigned char *)arrays[i];
+    for(int j = skip; j < len; j++){
+      if(ap[j] != bp[j]){
+        cmp = ap[j] - bp[j];
+        break;
+      }
+    }
+    if(cmp > 0){
+      const char *swap = arrays[i];
+      int j;
+      for(j = i; j > 0; j--){
+        int cmp = 0;
+        const unsigned char *ap = (unsigned char *)arrays[j-1];
+        const unsigned char *bp = (unsigned char *)swap;
+        for(int k = skip; k < len; k++){
+          if(ap[k] != bp[k]){
+            cmp = ap[k] - bp[k];
+            break;
+          }
+        }
+        if(cmp < 0) break;
+        arrays[j] = arrays[j-1];
+      }
+      arrays[j] = swap;
+    }
+  }
+}
+
+
+/* Sort BWT string arrays by dicrionary order by heap sort.
+   `array' specifies an array of string arrays.
+   `anum' specifies the number of the array.
+   `len' specifies the size of each string.
+   `skip' specifies the number of skipped bytes. */
+static void tcbwtsortstrheap(const char **arrays, int anum, int len, int skip){
+  assert(arrays && anum >= 0 && len >= 0);
+  anum--;
+  int bottom = (anum >> 1) + 1;
+  int top = anum;
+  while(bottom > 0){
+    bottom--;
+    int mybot = bottom;
+    int i = mybot << 1;
+    while(i <= top){
+      if(i < top){
+        int cmp = 0;
+        const unsigned char *ap = (unsigned char *)arrays[i+1];
+        const unsigned char *bp = (unsigned char *)arrays[i];
+        for(int j = skip; j < len; j++){
+          if(ap[j] != bp[j]){
+            cmp = ap[j] - bp[j];
+            break;
+          }
+        }
+        if(cmp > 0) i++;
+      }
+      int cmp = 0;
+      const unsigned char *ap = (unsigned char *)arrays[mybot];
+      const unsigned char *bp = (unsigned char *)arrays[i];
+      for(int j = skip; j < len; j++){
+        if(ap[j] != bp[j]){
+          cmp = ap[j] - bp[j];
+          break;
+        }
+      }
+      if(cmp >= 0) break;
+      const char *swap = arrays[mybot];
+      arrays[mybot] = arrays[i];
+      arrays[i] = swap;
+      mybot = i;
+      i = mybot << 1;
+    }
+  }
+  while(top > 0){
+    const char *swap = arrays[0];
+    arrays[0] = arrays[top];
+    arrays[top] = swap;
+    top--;
+    int mybot = bottom;
+    int i = mybot << 1;
+    while(i <= top){
+      if(i < top){
+        int cmp = 0;
+        const unsigned char *ap = (unsigned char *)arrays[i+1];
+        const unsigned char *bp = (unsigned char *)arrays[i];
+        for(int j = 0; j < len; j++){
+          if(ap[j] != bp[j]){
+            cmp = ap[j] - bp[j];
+            break;
+          }
+        }
+        if(cmp > 0) i++;
+      }
+      int cmp = 0;
+      const unsigned char *ap = (unsigned char *)arrays[mybot];
+      const unsigned char *bp = (unsigned char *)arrays[i];
+      for(int j = 0; j < len; j++){
+        if(ap[j] != bp[j]){
+          cmp = ap[j] - bp[j];
+          break;
+        }
+      }
+      if(cmp >= 0) break;
+      swap = arrays[mybot];
+      arrays[mybot] = arrays[i];
+      arrays[i] = swap;
+      mybot = i;
+      i = mybot << 1;
+    }
+  }
+}
+
+
+/* Sort BWT characters by code number by counting sort.
+   `str' specifies a string.
+   `len' specifies the length of the string. */
+static void tcbwtsortchrcount(unsigned char *str, int len){
+  assert(str && len >= 0);
+  int cnt[0x100];
+  memset(cnt, 0, sizeof(cnt));
+  for(int i = 0; i < len; i++){
+    cnt[str[i]]++;
+  }
+  int pos = 0;
+  for(int i = 0; i < 0x100; i++){
+    memset(str + pos, i, cnt[i]);
+    pos += cnt[i];
+  }
+}
+
+
+/* Sort BWT characters by code number by insertion sort.
+   `str' specifies a string.
+   `len' specifies the length of the string. */
+static void tcbwtsortchrinsert(unsigned char *str, int len){
+  assert(str && len >= 0);
+  for(int i = 1; i < len; i++){
+    if(str[i-1] - str[i] > 0){
+      unsigned char swap = str[i];
+      int j;
+      for(j = i; j > 0; j--){
+        if(str[j-1] - swap < 0) break;
+        str[j] = str[j-1];
+      }
+      str[j] = swap;
+    }
+  }
+}
+
+
+/* Sort BWT records by code number by counting sort.
+   `array' specifies an array of records.
+   `anum' specifies the number of the array. */
+static void tcbwtsortreccount(TCBWTREC *array, int anum){
+  assert(array && anum >= 0);
+  TCBWTREC nbuf[TCBWTBUFNUM];
+  TCBWTREC *narray = nbuf;
+  if(anum > TCBWTBUFNUM) TCMALLOC(narray, sizeof(*narray) * anum);
+  int count[0x100], accum[0x100];
+  memset(count, 0, sizeof(count));
+  for(int i = 0; i < anum; i++){
+    count[array[i].tchr>>23]++;
+  }
+  memcpy(accum, count, sizeof(count));
+  for(int i = 1; i < 0x100; i++){
+    accum[i] = accum[i-1] + accum[i];
+  }
+  for(int i = 0; i < 0x100; i++){
+    accum[i] -= count[i];
+  }
+  for(int i = 0; i < anum; i++){
+    narray[accum[array[i].tchr>>23]++] = array[i];
+  }
+  memcpy(array, narray, anum * sizeof(*narray));
+  if(narray != nbuf) free(narray);
+}
+
+
+/* Sort BWT records by code number by insertion sort.
+   `array' specifies an array of records..
+   `anum' specifies the number of the array. */
+static void tcbwtsortrecinsert(TCBWTREC *array, int anum){
+  assert(array && anum >= 0);
+  for(int i = 1; i < anum; i++){
+    if(array[i-1].tchr - array[i].tchr > 0){
+      TCBWTREC swap = array[i];
+      int j;
+      for(j = i; j > 0; j--){
+        if(array[j-1].tchr - swap.tchr < 0) break;
+        array[j] = array[j-1];
+      }
+      array[j] = swap;
+    }
+  }
+}
+
+
+/* Search the element of BWT records.
+   `array' specifies an array of records.
+   `anum' specifies the number of the array.
+   `tchr' specifies the last code number. */
+static int tcbwtsearchrec(TCBWTREC *array, int anum, int tchr){
+  assert(array && anum >= 0);
+  int bottom = 0;
+  int top = anum;
+  int mid;
+  do {
+    mid = (bottom + top) >> 1;
+    if(array[mid].tchr == tchr){
+      return mid;
+    } else if(array[mid].tchr < tchr){
+      bottom = mid + 1;
+      if(bottom >= anum) break;
+    } else {
+      top = mid - 1;
+    }
+  } while(bottom <= top);
+  return -1;
+}
+
+
+/* Initialization table for MTF encoder. */
+const unsigned char tcmtftable[] = {
+  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+  0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
+  0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
+  0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
+  0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
+  0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
+  0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
+  0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
+  0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
+  0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
+  0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
+  0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
+  0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
+  0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
+  0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
+  0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
+};
+
+
+/* Encode a region with MTF encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region. */
+static void tcmtfencode(char *ptr, int size){
+  unsigned char table1[0x100], table2[0x100], *table, *another;
+  assert(ptr && size >= 0);
+  memcpy(table1, tcmtftable, sizeof(tcmtftable));
+  table = table1;
+  another = table2;
+  const char *end = ptr + size;
+  char *wp = ptr;
+  while(ptr < end){
+    unsigned char c = *ptr;
+    unsigned char *tp = table;
+    unsigned char *tend = table + 0x100;
+    while(tp < tend && *tp != c){
+      tp++;
+    }
+    int idx = tp - table;
+    *(wp++) = idx;
+    if(idx > 0){
+      memcpy(another, &c, 1);
+      memcpy(another + 1, table, idx);
+      memcpy(another + 1 + idx, table + idx + 1, 255 - idx);
+      unsigned char *swap = table;
+      table = another;
+      another = swap;
+    }
+    ptr++;
+  }
+}
+
+
+/* Decode a region compressed with MTF encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region. */
+static void tcmtfdecode(char *ptr, int size){
+  assert(ptr && size >= 0);
+  unsigned char table1[0x100], table2[0x100], *table, *another;
+  assert(ptr && size >= 0);
+  memcpy(table1, tcmtftable, sizeof(tcmtftable));
+  table = table1;
+  another = table2;
+  const char *end = ptr + size;
+  char *wp = ptr;
+  while(ptr < end){
+    int idx = *(unsigned char *)ptr;
+    unsigned char c = table[idx];
+    *(wp++) = c;
+    if(idx > 0){
+      memcpy(another, &c, 1);
+      memcpy(another + 1, table, idx);
+      memcpy(another + 1 + idx, table + idx + 1, 255 - idx);
+      unsigned char *swap = table;
+      table = another;
+      another = swap;
+    }
+    ptr++;
+  }
+}
+
+
+/* Encode a region with Elias gamma encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `ptr' specifies the pointer to the output buffer. */
+static int tcgammaencode(const char *ptr, int size, char *obuf){
+  assert(ptr && size >= 0 && obuf);
+  TCBITSTRM strm;
+  TCBITSTRMINITW(strm, obuf);
+  const char *end = ptr + size;
+  while(ptr < end){
+    unsigned int c = *(unsigned char *)ptr;
+    if(!c){
+      TCBITSTRMCAT(strm, 1);
+    } else {
+      c++;
+      int plen = 8;
+      while(plen > 0 && !(c & (1 << plen))){
+        plen--;
+      }
+      int jlen = plen;
+      while(jlen-- > 0){
+        TCBITSTRMCAT(strm, 0);
+      }
+      while(plen >= 0){
+        int sign = (c & (1 << plen)) > 0;
+        TCBITSTRMCAT(strm, sign);
+        plen--;
+      }
+    }
+    ptr++;
+  }
+  TCBITSTRMSETEND(strm);
+  return TCBITSTRMSIZE(strm);
+}
+
+
+/* Decode a region compressed with Elias gamma encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `ptr' specifies the pointer to the output buffer. */
+static int tcgammadecode(const char *ptr, int size, char *obuf){
+  assert(ptr && size >= 0 && obuf);
+  char *wp = obuf;
+  TCBITSTRM strm;
+  TCBITSTRMINITR(strm, ptr, size);
+  int bnum = TCBITSTRMNUM(strm);
+  while(bnum > 0){
+    int sign;
+    TCBITSTRMREAD(strm, sign);
+    bnum--;
+    if(sign){
+      *(wp++) = 0;
+    } else {
+      int plen = 1;
+      while(bnum > 0){
+        TCBITSTRMREAD(strm, sign);
+        bnum--;
+        if(sign) break;
+        plen++;
+      }
+      unsigned int c = 1;
+      while(bnum > 0 && plen-- > 0){
+        TCBITSTRMREAD(strm, sign);
+        bnum--;
+        c = (c << 1) + (sign > 0);
+      }
+      *(wp++) = c - 1;
+    }
+  }
+  return wp - obuf;;
+}
+
+
+
+// END OF FILE
diff --git a/bacula/src/lib/tokyocabinet/tcutil.h b/bacula/src/lib/tokyocabinet/tcutil.h
new file mode 100644 (file)
index 0000000..05a1e1d
--- /dev/null
@@ -0,0 +1,2169 @@
+/*************************************************************************************************
+ * The utility API of Tokyo Cabinet
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _TCUTIL_H                        /* duplication check */
+#define _TCUTIL_H
+
+#if defined(__cplusplus)
+#define __TCUTIL_CLINKAGEBEGIN extern "C" {
+#define __TCUTIL_CLINKAGEEND }
+#else
+#define __TCUTIL_CLINKAGEBEGIN
+#define __TCUTIL_CLINKAGEEND
+#endif
+__TCUTIL_CLINKAGEBEGIN
+
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+
+
+
+/*************************************************************************************************
+ * basic utilities
+ *************************************************************************************************/
+
+
+/* String containing the version information. */
+extern const char *tcversion;
+
+
+/* Pointer to the call back function for handling a fatal error.
+   The argument specifies the error message.
+   The initial value of this variable is `NULL'.  If the value is `NULL', the default function is
+   called when a fatal error occurs.  A fatal error occurs when memory allocation is failed. */
+extern void (*tcfatalfunc)(const char *);
+
+
+/* Allocate a region on memory.
+   `size' specifies the size of the region.
+   The return value is the pointer to the allocated region.
+   This function handles failure of memory allocation implicitly.  Because the region of the
+   return value is allocated with the `malloc' call, it should be released with the `free' call
+   when it is no longer in use. */
+void *tcmalloc(size_t size);
+
+
+/* Allocate a nullified region on memory.
+   `nmemb' specifies the number of elements.
+   `size' specifies the size of each element.
+   The return value is the pointer to the allocated nullified region.
+   This function handles failure of memory allocation implicitly.  Because the region of the
+   return value is allocated with the `calloc' call, it should be released with the `free' call
+   when it is no longer in use. */
+void *tccalloc(size_t nmemb, size_t size);
+
+
+/* Re-allocate a region on memory.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   The return value is the pointer to the re-allocated region.
+   This function handles failure of memory allocation implicitly.  Because the region of the
+   return value is allocated with the `realloc' call, it should be released with the `free' call
+   when it is no longer in use. */
+void *tcrealloc(void *ptr, size_t size);
+
+
+/* Duplicate a region on memory.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   The return value is the pointer to the allocated region of the duplicate.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when
+   it is no longer in use. */
+void *tcmemdup(const void *ptr, size_t size);
+
+
+/* Duplicate a string on memory.
+   `str' specifies the string.
+   The return value is the allocated string equivalent to the specified string.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcstrdup(const void *str);
+
+
+/* Free a region on memory.
+   `ptr' specifies the pointer to the region.  If it is `NULL', this function has no effect.
+   Although this function is just a wrapper of `free' call, this is useful in applications using
+   another package of the `malloc' series. */
+void tcfree(void *ptr);
+
+
+
+/*************************************************************************************************
+ * extensible string
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of structure for an extensible string object */
+  char *ptr;                             /* pointer to the region */
+  int size;                              /* size of the region */
+  int asize;                             /* size of the allocated region */
+} TCXSTR;
+
+
+/* Create an extensible string object.
+   The return value is the new extensible string object. */
+TCXSTR *tcxstrnew(void);
+
+
+/* Create an extensible string object from a character string.
+   `str' specifies the string of the initial content.
+   The return value is the new extensible string object containing the specified string. */
+TCXSTR *tcxstrnew2(const char *str);
+
+
+/* Create an extensible string object with the initial allocation size.
+   `asiz' specifies the initial allocation size.
+   The return value is the new extensible string object. */
+TCXSTR *tcxstrnew3(int asiz);
+
+
+/* Copy an extensible string object.
+   `xstr' specifies the extensible string object.
+   The return value is the new extensible string object equivalent to the specified object. */
+TCXSTR *tcxstrdup(const TCXSTR *xstr);
+
+
+/* Delete an extensible string object.
+   `xstr' specifies the extensible string object.
+   Note that the deleted object and its derivatives can not be used anymore. */
+void tcxstrdel(TCXSTR *xstr);
+
+
+/* Concatenate a region to the end of an extensible string object.
+   `xstr' specifies the extensible string object.
+   `ptr' specifies the pointer to the region to be appended.
+   `size' specifies the size of the region. */
+void tcxstrcat(TCXSTR *xstr, const void *ptr, int size);
+
+
+/* Concatenate a character string to the end of an extensible string object.
+   `xstr' specifies the extensible string object.
+   `str' specifies the string to be appended. */
+void tcxstrcat2(TCXSTR *xstr, const char *str);
+
+
+/* Get the pointer of the region of an extensible string object.
+   `xstr' specifies the extensible string object.
+   The return value is the pointer of the region of the object.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string. */
+const void *tcxstrptr(const TCXSTR *xstr);
+
+
+/* Get the size of the region of an extensible string object.
+   `xstr' specifies the extensible string object.
+   The return value is the size of the region of the object. */
+int tcxstrsize(const TCXSTR *xstr);
+
+
+/* Clear an extensible string object.
+   `xstr' specifies the extensible string object.
+   The internal buffer of the object is cleared and the size is set zero. */
+void tcxstrclear(TCXSTR *xstr);
+
+
+/* Convert an extensible string object into a usual allocated region.
+   `xstr' specifies the extensible string object.
+   The return value is the pointer to the allocated region of the object.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when it
+   is no longer in use.  Because the region of the original object is deleted, it should not be
+   deleted again. */
+void *tcxstrtomalloc(TCXSTR *xstr);
+
+
+/* Create an extensible string object from an allocated region.
+   `ptr' specifies the pointer to the region allocated with `malloc' call.
+   `size' specifies the size of the region.
+   The return value is the new extensible string object wrapping the specified region.
+   Note that the specified region is released when the object is deleted. */
+TCXSTR *tcxstrfrommalloc(void *ptr, int size);
+
+
+/* Perform formatted output into an extensible string object.
+   `xstr' specifies the extensible string object.
+   `format' specifies the printf-like format string.
+   The conversion character `%' can be used with such flag characters as `s', `d', `o', `u',
+   `x', `X', `c', `e', `E', `f', `g', `G', `@', `?', `%'.  `@' works as with `s' but escapes meta
+   characters of XML.  `?' works as with `s' but escapes meta characters of URL.  The other
+   conversion character work as with each original.
+   The other arguments are used according to the format string. */
+void tcxstrprintf(TCXSTR *xstr, const char *format, ...);
+
+
+/* Allocate a formatted string on memory.
+   `format' specifies the printf-like format string.
+   The conversion character `%' can be used with such flag characters as `s', `d', `o', `u',
+   `x', `X', `c', `e', `E', `f', `g', `G', `@', `?', `%'.  `@' works as with `s' but escapes meta
+   characters of XML.  `?' works as with `s' but escapes meta characters of URL.  The other
+   conversion character work as with each original.
+   The other arguments are used according to the format string.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcsprintf(const char *format, ...);
+
+
+
+/*************************************************************************************************
+ * array list
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of structure for an element of a list */
+  char *ptr;                             /* pointer to the region */
+  int size;                              /* size of the effective region */
+} TCLISTDATUM;
+
+typedef struct {                         /* type of structure for an array list */
+  TCLISTDATUM *array;                    /* array of data */
+  int anum;                              /* number of the elements of the array */
+  int start;                             /* start index of used elements */
+  int num;                               /* number of used elements */
+} TCLIST;
+
+
+/* Create a list object.
+   The return value is the new list object. */
+TCLIST *tclistnew(void);
+
+
+/* Create a list object with expecting the number of elements.
+   `anum' specifies the number of elements expected to be stored in the list.
+   The return value is the new list object. */
+TCLIST *tclistnew2(int anum);
+
+
+/* Copy a list object.
+   `list' specifies the list object.
+   The return value is the new list object equivalent to the specified object. */
+TCLIST *tclistdup(const TCLIST *list);
+
+
+/* Delete a list object.
+   `list' specifies the list object.
+   Note that the deleted object and its derivatives can not be used anymore. */
+void tclistdel(TCLIST *list);
+
+
+/* Get the number of elements of a list object.
+   `list' specifies the list object.
+   The return value is the number of elements of the list. */
+int tclistnum(const TCLIST *list);
+
+
+/* Get the pointer to the region of an element of a list object.
+   `list' specifies the list object.
+   `index' specifies the index of the element.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the value.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  If `index' is equal to or more than
+   the number of elements, the return value is `NULL'. */
+const void *tclistval(const TCLIST *list, int index, int *sp);
+
+
+/* Get the string of an element of a list object.
+   `list' specifies the list object.
+   `index' specifies the index of the element.
+   The return value is the string of the value.
+   If `index' is equal to or more than the number of elements, the return value is `NULL'. */
+const char *tclistval2(const TCLIST *list, int index);
+
+
+/* Add an element at the end of a list object.
+   `list' specifies the list object.
+   `ptr' specifies the pointer to the region of the new element.
+   `size' specifies the size of the region. */
+void tclistpush(TCLIST *list, const void *ptr, int size);
+
+
+/* Add a string element at the end of a list object.
+   `list' specifies the list object.
+   `str' specifies the string of the new element. */
+void tclistpush2(TCLIST *list, const char *str);
+
+
+/* Add an allocated element at the end of a list object.
+   `list' specifies the list object.
+   `ptr' specifies the pointer to the region allocated with `malloc' call.
+   `size' specifies the size of the region.
+   Note that the specified region is released when the object is deleted. */
+void tclistpushmalloc(TCLIST *list, void *ptr, int size);
+
+
+/* Remove an element of the end of a list object.
+   `list' specifies the list object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the removed element.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when it
+   is no longer in use.  If the list is empty, the return value is `NULL'. */
+void *tclistpop(TCLIST *list, int *sp);
+
+
+/* Remove a string element of the end of a list object.
+   `list' specifies the list object.
+   The return value is the string of the removed element.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use.  If the list is empty, the return
+   value is `NULL'. */
+char *tclistpop2(TCLIST *list);
+
+
+/* Add an element at the top of a list object.
+   `list' specifies the list object.
+   `ptr' specifies the pointer to the region of the new element.
+   `size' specifies the size of the region. */
+void tclistunshift(TCLIST *list, const void *ptr, int size);
+
+
+/* Add a string element at the top of a list object.
+   `list' specifies the list object.
+   `str' specifies the string of the new element. */
+void tclistunshift2(TCLIST *list, const char *str);
+
+
+/* Remove an element of the top of a list object.
+   `list' specifies the list object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the removed element.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when it
+   is no longer in use.  If the list is empty, the return value is `NULL'. */
+void *tclistshift(TCLIST *list, int *sp);
+
+
+/* Remove a string element of the top of a list object.
+   `list' specifies the list object.
+   The return value is the string of the removed element.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use.  If the list is empty, the return
+   value is `NULL'. */
+char *tclistshift2(TCLIST *list);
+
+
+/* Add an element at the specified location of a list object.
+   `list' specifies the list object.
+   `index' specifies the index of the new element.
+   `ptr' specifies the pointer to the region of the new element.
+   `size' specifies the size of the region.
+   If `index' is equal to or more than the number of elements, this function has no effect. */
+void tclistinsert(TCLIST *list, int index, const void *ptr, int size);
+
+
+/* Add a string element at the specified location of a list object.
+   `list' specifies the list object.
+   `index' specifies the index of the new element.
+   `str' specifies the string of the new element.
+   If `index' is equal to or more than the number of elements, this function has no effect. */
+void tclistinsert2(TCLIST *list, int index, const char *str);
+
+
+/* Remove an element at the specified location of a list object.
+   `list' specifies the list object.
+   `index' specifies the index of the element to be removed.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the removed element.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when it
+   is no longer in use.  If `index' is equal to or more than the number of elements, no element
+   is removed and the return value is `NULL'. */
+void *tclistremove(TCLIST *list, int index, int *sp);
+
+
+/* Remove a string element at the specified location of a list object.
+   `list' specifies the list object.
+   `index' specifies the index of the element to be removed.
+   The return value is the string of the removed element.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use.  If `index' is equal to or more
+   than the number of elements, no element is removed and the return value is `NULL'. */
+char *tclistremove2(TCLIST *list, int index);
+
+
+/* Overwrite an element at the specified location of a list object.
+   `list' specifies the list object.
+   `index' specifies the index of the element to be overwritten.
+   `ptr' specifies the pointer to the region of the new content.
+   `size' specifies the size of the new content.
+   If `index' is equal to or more than the number of elements, this function has no effect. */
+void tclistover(TCLIST *list, int index, const void *ptr, int size);
+
+
+/* Overwrite a string element at the specified location of a list object.
+   `list' specifies the list object.
+   `index' specifies the index of the element to be overwritten.
+   `str' specifies the string of the new content.
+   If `index' is equal to or more than the number of elements, this function has no effect. */
+void tclistover2(TCLIST *list, int index, const char *str);
+
+
+/* Sort elements of a list object in lexical order.
+   `list' specifies the list object. */
+void tclistsort(TCLIST *list);
+
+
+/* Sort elements of a list object in case-insensitive lexical order.
+   `list' specifies the list object. */
+void tclistsortci(TCLIST *list);
+
+
+/* Sort elements of a list object by an arbitrary comparison function.
+   `list' specifies the list object.
+   `cmp' specifies the pointer to the comparison function.  The structure TCLISTDATUM has the
+   member "ptr" which is the pointer to the region of the element, and the member "size" which is
+   the size of the region. */
+void tclistsortex(TCLIST *list, int (*cmp)(const TCLISTDATUM *, const TCLISTDATUM *));
+
+
+/* Search a list object for an element using liner search.
+   `list' specifies the list object.
+   `ptr' specifies the pointer to the region of the key.
+   `size' specifies the size of the region.
+   The return value is the index of a corresponding element or -1 if there is no corresponding
+   element.
+   If two or more elements correspond, the former returns. */
+int tclistlsearch(const TCLIST *list, const void *ptr, int size);
+
+
+/* Search a list object for an element using binary search.
+   `list' specifies the list object.  It should be sorted in lexical order.
+   `ptr' specifies the pointer to the region of the key.
+   `size' specifies the size of the region.
+   The return value is the index of a corresponding element or -1 if there is no corresponding
+   element.
+   If two or more elements correspond, which returns is not defined. */
+int tclistbsearch(const TCLIST *list, const void *ptr, int size);
+
+
+/* Clear a list object.
+   `list' specifies the list object.
+   All elements are removed. */
+void tclistclear(TCLIST *list);
+
+
+/* Serialize a list object into a byte array.
+   `list' specifies the list object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the result serial region.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+void *tclistdump(const TCLIST *list, int *sp);
+
+
+/* Create a list object from a serialized byte array.
+   `ptr' specifies the pointer to the region of serialized byte array.
+   `size' specifies the size of the region.
+   The return value is a new list object.
+   Because the object of the return value is created with the function `tclistnew', it should
+   be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tclistload(const void *ptr, int size);
+
+
+
+/*************************************************************************************************
+ * hash map
+ *************************************************************************************************/
+
+
+typedef struct _TCMAPREC {               /* type of structure for an element of a map */
+  int ksiz;                              /* size of the region of the key */
+  int vsiz;                              /* size of the region of the value */
+  unsigned int hash;                     /* second hash value */
+  struct _TCMAPREC *left;                /* pointer to the left child */
+  struct _TCMAPREC *right;               /* pointer to the right child */
+  struct _TCMAPREC *prev;                /* pointer to the previous element */
+  struct _TCMAPREC *next;                /* pointer to the next element */
+} TCMAPREC;
+
+typedef struct {                         /* type of structure for a map */
+  TCMAPREC **buckets;                    /* bucket array */
+  TCMAPREC *first;                       /* pointer to the first element */
+  TCMAPREC *last;                        /* pointer to the last element */
+  TCMAPREC *cur;                         /* pointer to the current element */
+  uint32_t bnum;                         /* number of buckets */
+  uint64_t rnum;                         /* number of records */
+  uint64_t msiz;                         /* total size of records */
+} TCMAP;
+
+
+/* Create a map object.
+   The return value is the new map object. */
+TCMAP *tcmapnew(void);
+
+
+/* Create a map object with specifying the number of the buckets.
+   `bnum' specifies the number of the buckets.
+   The return value is the new map object. */
+TCMAP *tcmapnew2(uint32_t bnum);
+
+
+/* Copy a map object.
+   `map' specifies the map object.
+   The return value is the new map object equivalent to the specified object. */
+TCMAP *tcmapdup(const TCMAP *map);
+
+
+/* Delete a map object.
+   `map' specifies the map object.
+   Note that the deleted object and its derivatives can not be used anymore. */
+void tcmapdel(TCMAP *map);
+
+
+/* Store a record into a map object.
+   `map' specifies the map object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If a record with the same key exists in the map, it is overwritten. */
+void tcmapput(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into a map object.
+   `map' specifies the map object.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If a record with the same key exists in the map, it is overwritten. */
+void tcmapput2(TCMAP *map, const char *kstr, const char *vstr);
+
+
+/* Store a record of the value of two regions into a map object.
+   `map' specifies the map object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `fvbuf' specifies the pointer to the former region of the value.
+   `fvsiz' specifies the size of the former region of the value.
+   `lvbuf' specifies the pointer to the latter region of the value.
+   `lvsiz' specifies the size of the latter region of the value.
+   If a record with the same key exists in the map, it is overwritten. */
+void tcmapput3(TCMAP *map, const char *kbuf, int ksiz,
+               const void *fvbuf, int fvsiz, const char *lvbuf, int lvsiz);
+
+
+/* Store a new record into a map object.
+   `map' specifies the map object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the map, this function has no effect. */
+bool tcmapputkeep(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into a map object.
+   `map' specifies the map object.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the map, this function has no effect. */
+bool tcmapputkeep2(TCMAP *map, const char *kstr, const char *vstr);
+
+
+/* Concatenate a value at the end of the value of the existing record in a map object.
+   `map' specifies the map object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If there is no corresponding record, a new record is created. */
+void tcmapputcat(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Concatenate a string value at the end of the value of the existing record in a map object.
+   `map' specifies the map object.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If there is no corresponding record, a new record is created. */
+void tcmapputcat2(TCMAP *map, const char *kstr, const char *vstr);
+
+
+/* Remove a record of a map object.
+   `map' specifies the map object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true.  False is returned when no record corresponds to
+   the specified key. */
+bool tcmapout(TCMAP *map, const void *kbuf, int ksiz);
+
+
+/* Remove a string record of a map object.
+   `map' specifies the map object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is true.  False is returned when no record corresponds to
+   the specified key. */
+bool tcmapout2(TCMAP *map, const char *kstr);
+
+
+/* Retrieve a record in a map object.
+   `map' specifies the map object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record.  `NULL' is returned when no record corresponds.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string. */
+const void *tcmapget(const TCMAP *map, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve a string record in a map object.
+   `map' specifies the map object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is the string of the value of the corresponding record.
+   `NULL' is returned when no record corresponds. */
+const char *tcmapget2(const TCMAP *map, const char *kstr);
+
+
+/* Retrieve a semivolatile record in a map object.
+   `map' specifies the map object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record.  `NULL' is returned when no record corresponds.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  The internal region of the returned
+   record is moved to the tail so that the record will survive for a time under LRU cache
+   algorithm removing records from the head. */
+const void *tcmapget3(TCMAP *map, const void *kbuf, int ksiz, int *sp);
+
+
+/* Move a record to the edge of a map object.
+   `map' specifies the map object.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.
+   `head' specifies the destination which is head if it is true or tail if else.
+   If successful, the return value is true.  False is returned when no record corresponds to
+   the specified key. */
+bool tcmapmove(TCMAP *map, const void *kbuf, int ksiz, bool head);
+
+
+/* Move a string record to the edge of a map object.
+   `map' specifies the map object.
+   `kstr' specifies the string of a key.
+   `head' specifies the destination which is head if it is true or tail if else.
+   If successful, the return value is true.  False is returned when no record corresponds to
+   the specified key. */
+bool tcmapmove2(TCMAP *map, const char *kstr, bool head);
+
+
+/* Initialize the iterator of a map object.
+   `map' specifies the map object.
+   The iterator is used in order to access the key of every record stored in the map object. */
+void tcmapiterinit(TCMAP *map);
+
+
+/* Get the next key of the iterator of a map object.
+   `map' specifies the map object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the next key, else, it is
+   `NULL'.  `NULL' is returned when no record can be fetched from the iterator.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.
+   The order of iteration is assured to be the same as the stored order. */
+const void *tcmapiternext(TCMAP *map, int *sp);
+
+
+/* Get the next key string of the iterator of a map object.
+   `map' specifies the map object.
+   If successful, the return value is the pointer to the region of the next key, else, it is
+   `NULL'.  `NULL' is returned when no record can be fetched from the iterator.
+   The order of iteration is assured to be the same as the stored order. */
+const char *tcmapiternext2(TCMAP *map);
+
+
+/* Get the value bound to the key fetched from the iterator of a map object.
+   `kbuf' specifies the pointer to the region of the iteration key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the value of the corresponding record.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string. */
+const void *tcmapiterval(const void *kbuf, int *sp);
+
+
+/* Get the value string bound to the key fetched from the iterator of a map object.
+   `kstr' specifies the string of the iteration key.
+   The return value is the pointer to the region of the value of the corresponding record. */
+const char *tcmapiterval2(const char *kstr);
+
+
+/* Get the number of records stored in a map object.
+   `map' specifies the map object.
+   The return value is the number of the records stored in the map object. */
+uint64_t tcmaprnum(const TCMAP *map);
+
+
+/* Get the total size of memory used in a map object.
+   `map' specifies the map object.
+   The return value is the total size of memory used in a map object. */
+uint64_t tcmapmsiz(const TCMAP *map);
+
+
+/* Create a list object containing all keys in a map object.
+   `map' specifies the map object.
+   The return value is the new list object containing all keys in the map object.
+   Because the object of the return value is created with the function `tclistnew', it should
+   be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcmapkeys(const TCMAP *map);
+
+
+/* Create a list object containing all values in a map object.
+   `map' specifies the map object.
+   The return value is the new list object containing all values in the map object.
+   Because the object of the return value is created with the function `tclistnew', it should
+   be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcmapvals(const TCMAP *map);
+
+
+/* Add an integer to a record in a map object.
+   `map' specifies the map object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `num' specifies the additional value.
+   If the corresponding record exists, the value is treated as a integer and is added to.  If no
+   record corresponds, a new record of the additional value is stored. */
+void tcmapaddint(TCMAP *map, const char *kbuf, int ksiz, int num);
+
+
+/* Clear a map object.
+   `map' specifies the map object.
+   All records are removed. */
+void tcmapclear(TCMAP *map);
+
+
+/* Remove front records of a map object.
+   `map' specifies the map object.
+   `num' specifies the number of records to be removed.
+   If successful, the return value is true.  False is returned when no record corresponds to
+   the specified key. */
+void tcmapcutfront(TCMAP *map, int num);
+
+
+/* Serialize a map object into a byte array.
+   `map' specifies the map object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the result serial region.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+void *tcmapdump(const TCMAP *map, int *sp);
+
+
+/* Create a map object from a serialized byte array.
+   `ptr' specifies the pointer to the region of serialized byte array.
+   `size' specifies the size of the region.
+   The return value is a new map object.
+   Because the object of the return value is created with the function `tcmapnew', it should be
+   deleted with the function `tcmapdel' when it is no longer in use. */
+TCMAP *tcmapload(const void *ptr, int size);
+
+
+/* Extract a map record from a serialized byte array.
+   `ptr' specifies the pointer to the region of serialized byte array.
+   `size' specifies the size of the region.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record.  `NULL' is returned when no record corresponds.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string. */
+void *tcmaploadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp);
+
+
+
+/*************************************************************************************************
+ * on-memory database
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of structure for a on-memory database */
+  void **mmtxs;                          /* mutexes for method */
+  void *imtx;                            /* mutex for iterator */
+  TCMAP **maps;                          /* internal map object */
+  int iter;                              /* index of maps for the iterator */
+} TCMDB;
+
+
+/* Create an on-memory database object.
+   The return value is the new on-memory database object.
+   The object can be shared by plural threads because of the internal mutex. */
+TCMDB *tcmdbnew(void);
+
+
+/* Create an on-memory database object with specifying the number of the buckets.
+   `bnum' specifies the number of the buckets.
+   The return value is the new on-memory database object.
+   The object can be shared by plural threads because of the internal mutex. */
+TCMDB *tcmdbnew2(uint32_t bnum);
+
+
+/* Delete an on-memory database object.
+   `mdb' specifies the on-memory database object. */
+void tcmdbdel(TCMDB *mdb);
+
+
+/* Store a record into an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If a record with the same key exists in the database, it is overwritten. */
+void tcmdbput(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If a record with the same key exists in the database, it is overwritten. */
+void tcmdbput2(TCMDB *mdb, const char *kstr, const char *vstr);
+
+
+/* Store a record of the value of two regions into an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `fvbuf' specifies the pointer to the former region of the value.
+   `fvsiz' specifies the size of the former region of the value.
+   `lvbuf' specifies the pointer to the latter region of the value.
+   `lvsiz' specifies the size of the latter region of the value.
+   If a record with the same key exists in the database, it is overwritten. */
+void tcmdbput3(TCMDB *mdb, const char *kbuf, int ksiz,
+               const void *fvbuf, int fvsiz, const char *lvbuf, int lvsiz);
+
+
+/* Store a new record into an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, this function has no effect. */
+bool tcmdbputkeep(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If successful, the return value is true, else, it is false.
+   If a record with the same key exists in the database, this function has no effect. */
+bool tcmdbputkeep2(TCMDB *mdb, const char *kstr, const char *vstr);
+
+
+/* Concatenate a value at the end of the value of the existing record in an on-memory database.
+   `mdb' specifies the on-memory database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of the value.
+   `vsiz' specifies the size of the region of the value.
+   If there is no corresponding record, a new record is created. */
+void tcmdbputcat(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Concatenate a string at the end of the value of the existing record in an on-memory database.
+   `mdb' specifies the on-memory database object.
+   `kstr' specifies the string of the key.
+   `vstr' specifies the string of the value.
+   If there is no corresponding record, a new record is created. */
+void tcmdbputcat2(TCMDB *mdb, const char *kstr, const char *vstr);
+
+
+/* Remove a record of an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is true.  False is returned when no record corresponds to
+   the specified key. */
+bool tcmdbout(TCMDB *mdb, const void *kbuf, int ksiz);
+
+
+/* Remove a string record of an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is true.  False is returned when no record corresponds to
+   the specified key. */
+bool tcmdbout2(TCMDB *mdb, const char *kstr);
+
+
+/* Retrieve a record in an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record.  `NULL' is returned when no record corresponds.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when
+   it is no longer in use. */
+void *tcmdbget(TCMDB *mdb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve a string record in an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is the string of the value of the corresponding record.
+   `NULL' is returned when no record corresponds.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcmdbget2(TCMDB *mdb, const char *kstr);
+
+
+/* Retrieve a record and move it astern in an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record.  `NULL' is returned when no record corresponds.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return value
+   is allocated with the `malloc' call, it should be released with the `free' call when it is no
+   longer in use.  The internal region of the returned record is moved to the tail so that the
+   record will survive for a time under LRU cache algorithm removing records from the head. */
+void *tcmdbget3(TCMDB *mdb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Get the size of the value of a record in an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kbuf' specifies the pointer to the region of the key.
+   `ksiz' specifies the size of the region of the key.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1. */
+int tcmdbvsiz(TCMDB *mdb, const void *kbuf, int ksiz);
+
+
+/* Get the size of the value of a string record in an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `kstr' specifies the string of the key.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1. */
+int tcmdbvsiz2(TCMDB *mdb, const char *kstr);
+
+
+/* Initialize the iterator of an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   The iterator is used in order to access the key of every record stored in the on-memory
+   database. */
+void tcmdbiterinit(TCMDB *mdb);
+
+
+/* Get the next key of the iterator of an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the region of the next key, else, it is
+   `NULL'.  `NULL' is returned when no record can be fetched from the iterator.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when
+   it is no longer in use.  The order of iteration is assured to be the same as the stored
+   order. */
+void *tcmdbiternext(TCMDB *mdb, int *sp);
+
+
+/* Get the next key string of the iterator of an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   If successful, the return value is the pointer to the region of the next key, else, it is
+   `NULL'.  `NULL' is returned when no record can be fetched from the iterator.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use.  The order of iteration is assured
+   to be the same as the stored order. */
+char *tcmdbiternext2(TCMDB *mdb);
+
+
+/* Get forward matching keys in an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `pbuf' specifies the pointer to the region of the prefix.
+   `psiz' specifies the size of the region of the prefix.
+   `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
+   specified.
+   The return value is a list object of the corresponding keys.  This function does never fail
+   and return an empty list even if no key corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should be
+   deleted with the function `tclistdel' when it is no longer in use.  Note that this function
+   may be very slow because every key in the database is scanned. */
+TCLIST *tcmdbfwmkeys(TCMDB *mdb, const void *pbuf, int psiz, int max);
+
+
+/* Get forward matching string keys in an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `pstr' specifies the string of the prefix.
+   `max' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
+   specified.
+   The return value is a list object of the corresponding keys.  This function does never fail
+   and return an empty list even if no key corresponds.
+   Because the object of the return value is created with the function `tclistnew', it should be
+   deleted with the function `tclistdel' when it is no longer in use.  Note that this function
+   may be very slow because every key in the database is scanned. */
+TCLIST *tcmdbfwmkeys2(TCMDB *mdb, const char *pstr, int max);
+
+
+/* Get the number of records stored in an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   The return value is the number of the records stored in the database. */
+uint64_t tcmdbrnum(TCMDB *mdb);
+
+
+/* Get the total size of memory used in an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   The return value is the total size of memory used in the database. */
+uint64_t tcmdbmsiz(TCMDB *mdb);
+
+
+/* Clear an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   All records are removed. */
+void tcmdbvanish(TCMDB *mdb);
+
+
+/* Remove front records of an on-memory database object.
+   `mdb' specifies the on-memory database object.
+   `num' specifies the number of records to be removed.
+   If successful, the return value is true.  False is returned when no record corresponds to
+   the specified key. */
+void tcmdbcutfront(TCMDB *mdb, int num);
+
+
+
+/*************************************************************************************************
+ * memory pool
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of an element of memory pool */
+  void *ptr;                             /* pointer */
+  void (*del)(void *);                   /* deleting function */
+} TCMPELEM;
+
+typedef struct {                         /* type of structure for a memory pool object */
+  void *mutex;                           /* mutex for operations */
+  TCMPELEM *elems;                       /* array of elements */
+  int anum;                              /* number of the elements of the array */
+  int num;                               /* number of used elements */
+} TCMPOOL;
+
+
+/* Create a memory pool object.
+   The return value is the new memory pool object. */
+TCMPOOL *tcmpoolnew(void);
+
+
+/* Delete a memory pool object.
+   `mpool' specifies the memory pool object.
+   Note that the deleted object and its derivatives can not be used anymore. */
+void tcmpooldel(TCMPOOL *mpool);
+
+
+/* Relegate an arbitrary object to a memory pool object.
+   `mpool' specifies the memory pool object.
+   `ptr' specifies the pointer to the object to be relegated.
+   `del' specifies the pointer to the function to delete the object.
+   This function assures that the specified object is deleted when the memory pool object is
+   deleted. */
+void tcmpoolput(TCMPOOL *mpool, void *ptr, void (*del)(void *));
+
+
+/* Relegate an allocated region to a memory pool object.
+   `ptr' specifies the pointer to the region to be relegated.
+   This function assures that the specified region is released when the memory pool object is
+   deleted. */
+void tcmpoolputptr(TCMPOOL *mpool, void *ptr);
+
+
+/* Relegate an extensible string object to a memory pool object.
+   `mpool' specifies the memory pool object.
+   `xstr' specifies the extensible string object.
+   This function assures that the specified object is deleted when the memory pool object is
+   deleted. */
+void tcmpoolputxstr(TCMPOOL *mpool, TCXSTR *xstr);
+
+
+/* Relegate a list object to a memory pool object.
+   `mpool' specifies the memory pool object.
+   `list' specifies the list object.
+   This function assures that the specified object is deleted when the memory pool object is
+   deleted. */
+void tcmpoolputlist(TCMPOOL *mpool, TCLIST *list);
+
+
+/* Relegate a map object to a memory pool object.
+   `mpool' specifies the memory pool object.
+   `map' specifies the map object.
+   This function assures that the specified object is deleted when the memory pool object is
+   deleted. */
+void tcmpoolputmap(TCMPOOL *mpool, TCMAP *map);
+
+
+/* Allocate a region relegated to a memory pool object.
+   `mpool' specifies the memory pool object.
+   The return value is the pointer to the allocated region under the memory pool. */
+void *tcmpoolmalloc(TCMPOOL *mpool, size_t size);
+
+
+/* Create an extensible string object relegated to a memory pool object.
+   The return value is the new extensible string object under the memory pool. */
+TCXSTR *tcmpoolxstrnew(TCMPOOL *mpool);
+
+
+/* Create a list object relegated to a memory pool object.
+   The return value is the new list object under the memory pool. */
+TCLIST *tcmpoollistnew(TCMPOOL *mpool);
+
+
+/* Create a map object relegated to a memory pool object.
+   The return value is the new map object under the memory pool. */
+TCMAP *tcmpoolmapnew(TCMPOOL *mpool);
+
+
+/* Get the global memory pool object.
+   The return value is the global memory pool object.
+   The global memory pool object is a singleton and assured to be deleted when the porcess is
+   terminating normally. */
+TCMPOOL *tcmpoolglobal(void);
+
+
+
+/*************************************************************************************************
+ * miscellaneous utilities
+ *************************************************************************************************/
+
+
+/* Get the larger value of two integers.
+   `a' specifies an integer.
+   `b' specifies the other integer.
+   The return value is the larger value of the two. */
+long tclmax(long a, long b);
+
+
+/* Get the lesser value of two integers.
+   `a' specifies an integer.
+   `b' specifies the other integer.
+   The return value is the lesser value of the two. */
+long tclmin(long a, long b);
+
+
+/* Get a random number as long integer based on uniform distribution.
+   The return value is the random number between 0 and `ULONG_MAX'.
+   This function uses the random number source device and generates a real random number if
+   possible. */
+unsigned long tclrand(void);
+
+
+/* Get a random number as double decimal based on uniform distribution.
+   The return value is the random number equal to or greater than 0, and less than 1.0.
+   This function uses the random number source device and generates a real random number if
+   possible. */
+double tcdrand(void);
+
+
+/* Get a random number as double decimal based on normal distribution.
+   `avg' specifies the average.
+   `sd' specifies the standard deviation.
+   The return value is the random number.
+   This function uses the random number source device and generates a real random number if
+   possible. */
+double tcdrandnd(double avg, double sd);
+
+
+/* Compare two strings with case insensitive evaluation.
+   `astr' specifies a string.
+   `bstr' specifies of the other string.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+int tcstricmp(const char *astr, const char *bstr);
+
+
+/* Check whether a string begins with a key.
+   `str' specifies the target string.
+   `key' specifies the forward matching key string.
+   The return value is true if the target string begins with the key, else, it is false. */
+bool tcstrfwm(const char *str, const char *key);
+
+
+/* Check whether a string begins with a key with case insensitive evaluation.
+   `str' specifies the target string.
+   `key' specifies the forward matching key string.
+   The return value is true if the target string begins with the key, else, it is false. */
+bool tcstrifwm(const char *str, const char *key);
+
+
+/* Check whether a string ends with a key.
+   `str' specifies the target string.
+   `key' specifies the backward matching key string.
+   The return value is true if the target string ends with the key, else, it is false. */
+bool tcstrbwm(const char *str, const char *key);
+
+
+/* Check whether a string ends with a key with case insensitive evaluation.
+   `str' specifies the target string.
+   `key' specifies the backward matching key string.
+   The return value is true if the target string ends with the key, else, it is false. */
+bool tcstribwm(const char *str, const char *key);
+
+
+/* Calculate the edit distance of two strings.
+   `astr' specifies a string.
+   `bstr' specifies of the other string.
+   The return value is the edit distance which is known as the Levenshtein distance.  The cost is
+   calculated by byte. */
+int tcstrdist(const char *astr, const char *bstr);
+
+
+/* Calculate the edit distance of two UTF-8 strings.
+   `astr' specifies a string.
+   `bstr' specifies of the other string.
+   The return value is the edit distance which is known as the Levenshtein distance.  The cost is
+   calculated by Unicode character. */
+int tcstrdistutf(const char *astr, const char *bstr);
+
+
+/* Convert the letters of a string into upper case.
+   `str' specifies the string to be converted.
+   The return value is the string itself. */
+char *tcstrtoupper(char *str);
+
+
+/* Convert the letters of a string into lower case.
+   `str' specifies the string to be converted.
+   The return value is the string itself. */
+char *tcstrtolower(char *str);
+
+
+/* Cut space characters at head or tail of a string.
+   `str' specifies the string to be converted.
+   The return value is the string itself. */
+char *tcstrtrim(char *str);
+
+
+/* Squeeze space characters in a string and trim it.
+   `str' specifies the string to be converted.
+   The return value is the string itself. */
+char *tcstrsqzspc(char *str);
+
+
+/* Substitute characters in a string.
+   `str' specifies the string to be converted.
+   `rstr' specifies the string containing characters to be replaced.
+   `sstr' specifies the string containing characters to be substituted.
+   If the substitute string is shorter then the replacement string, corresponding characters are
+   removed. */
+char *tcstrsubchr(char *str, const char *rstr, const char *sstr);
+
+
+/* Count the number of characters in a string of UTF-8.
+   `str' specifies the string of UTF-8.
+   The return value is the number of characters in the string. */
+int tcstrcntutf(const char *str);
+
+
+/* Cut a string of UTF-8 at the specified number of characters.
+   `str' specifies the string of UTF-8.
+   `num' specifies the number of characters to be kept.
+   The return value is the string itself. */
+char *tcstrcututf(char *str, int num);
+
+
+/* Convert a UTF-8 string into a UCS-2 array.
+   `str' specifies the UTF-8 string.
+   `ary' specifies the pointer to the region into which the result UCS-2 codes are written.  The
+   size of the buffer should be sufficient.
+   `np' specifies the pointer to a variable into which the number of elements of the result array
+   is assigned. */
+void tcstrutftoucs(const char *str, uint16_t *ary, int *np);
+
+
+/* Convert a UCS-2 array into a UTF-8 string.
+   `ary' specifies the array of UCS-2 code codes.
+   `num' specifies the number of the array.
+   `str' specifies the pointer to the region into which the result UTF-8 string is written.  The
+   size of the buffer should be sufficient. */
+void tcstrucstoutf(const uint16_t *ary, int num, char *str);
+
+
+/* Create a list object by splitting a string.
+   `str' specifies the source string.
+   `delim' specifies a string containing delimiting characters.
+   The return value is a list object of the split elements.
+   If two delimiters are successive, it is assumed that an empty element is between the two.
+   Because the object of the return value is created with the function `tclistnew', it should
+   be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcstrsplit(const char *str, const char *delims);
+
+
+/* Create a string by joining all elements of a list object.
+   `list' specifies a list object.
+   `delim' specifies a delimiting character.
+   The return value is the result string.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcstrjoin(TCLIST *list, char delim);
+
+
+/* Check whether a string matches a regular expression.
+   `str' specifies the target string.
+   `regex' specifies the regular expression string.  If it begins with `*', the trailing
+   substring is used as a case-insensitive regular expression.
+   The return value is true if matching is success, else, it is false. */
+bool tcregexmatch(const char *str, const char *regex);
+
+
+/* Replace each substring matching a regular expression string.
+   `str' specifies the target string.
+   `regex' specifies the regular expression string for substrings.  If it begins with `*', the
+   trailing substring is used as a case-insensitive regular expression.
+   `alt' specifies the alternative string with which each substrings is replaced.  Each `&' in
+   the string is replaced with the matched substring.  Each `\' in the string escapes the
+   following character.  Special escapes "\1" through "\9" referring to the corresponding
+   matching sub-expressions in the regular expression string are supported.
+   The return value is a new converted string.  Even if the regular expression is invalid, a copy
+   of the original string is returned.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcregexreplace(const char *str, const char *regex, const char *alt);
+
+
+/* Get the time of day in seconds.
+   The return value is the time of day in seconds.  The accuracy is in microseconds. */
+double tctime(void);
+
+
+/* Get the Gregorian calendar of a time.
+   `t' specifies the source time in seconds from the epoch.  If it is `INT64_MAX', the current
+   time is specified.
+   `jl' specifies the jet lag of a location in seconds.  If it is `INT_MAX', the local jet lag is
+   specified.
+   `yearp' specifies the pointer to a variable to which the year is assigned.  If it is `NULL',
+   it is not used.
+   `monp' specifies the pointer to a variable to which the month is assigned.  If it is `NULL',
+   it is not used.  1 means January and 12 means December.
+   `dayp' specifies the pointer to a variable to which the day of the month is assigned.  If it
+   is `NULL', it is not used.
+   `hourp' specifies the pointer to a variable to which the hours is assigned.  If it is `NULL',
+   it is not used.
+   `minp' specifies the pointer to a variable to which the minutes is assigned.  If it is `NULL',
+   it is not used.
+   `secp' specifies the pointer to a variable to which the seconds is assigned.  If it is `NULL',
+   it is not used. */
+void tccalendar(int64_t t, int jl, int *yearp, int *monp, int *dayp,
+                int *hourp, int *minp, int *secp);
+
+
+/* Format a date as a string in W3CDTF.
+   `t' specifies the source time in seconds from the epoch.  If it is `INT64_MAX', the current
+   time is specified.
+   `jl' specifies the jet lag of a location in seconds.  If it is `INT_MAX', the local jet lag is
+   specified.
+   `buf' specifies the pointer to the region into which the result string is written.  The size
+   of the buffer should be equal to or more than 48 bytes.
+   W3CDTF represents a date as "YYYY-MM-DDThh:mm:ddTZD". */
+void tcdatestrwww(int64_t t, int jl, char *buf);
+
+
+/* Format a date as a string in RFC 1123 format.
+   `t' specifies the source time in seconds from the epoch.  If it is `INT64_MAX', the current
+   time is specified.
+   `jl' specifies the jet lag of a location in seconds.  If it is `INT_MAX', the local jet lag is
+   specified.
+   `buf' specifies the pointer to the region into which the result string is written.  The size
+   of the buffer should be equal to or more than 48 bytes.
+   RFC 1123 format represents a date as "Wdy, DD-Mon-YYYY hh:mm:dd TZD". */
+void tcdatestrhttp(int64_t t, int jl, char *buf);
+
+
+/* Get the time value of a date string.
+   `str' specifies the date string in decimal, hexadecimal, W3CDTF, or RFC 822 (1123).  Decimal
+   can be trailed by "s" for in seconds, "m" for in minutes, "h" for in hours, and "d" for in
+   days.
+   The return value is the time value of the date or `INT64_MAX' if the format is invalid. */
+int64_t tcstrmktime(const char *str);
+
+
+/* Get the day of week of a date.
+   `year' specifies the year of a date.
+   `mon' specifies the month of the date.
+   `day' specifies the day of the date.
+   The return value is the day of week of the date.  0 means Sunday and 6 means Saturday. */
+int tcdayofweek(int year, int mon, int day);
+
+
+
+/*************************************************************************************************
+ * filesystem utilities
+ *************************************************************************************************/
+
+
+/* Get the canonicalized absolute path of a file.
+   `path' specifies the path of the file.
+   The return value is the canonicalized absolute path of a file, or `NULL' if the path is
+   invalid.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcrealpath(const char *path);
+
+
+/* Read whole data of a file.
+   `path' specifies the path of the file.  If it is `NULL', the standard input is specified.
+   `limit' specifies the limiting size of reading data.  If it is not more than 0, the limitation
+   is not specified.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   The return value is the pointer to the allocated region of the read data, or `NULL' if the
+   file could not be opened.
+   Because an additional zero code is appended at the end of the region of the return value, the
+   return value can be treated as a character string.  Because the region of the return value is
+   allocated with the `malloc' call, it should be released with the `free' call when when is no
+   longer in use.  */
+void *tcreadfile(const char *path, int limit, int *sp);
+
+
+/* Read every line of a file.
+   `path' specifies the path of the file.  If it is `NULL', the standard input is specified.
+   The return value is a list object of every lines if successful, else it is `NULL'.
+   Line separators are cut out.  Because the object of the return value is created with the
+   function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer
+   in use. */
+TCLIST *tcreadfilelines(const char *path);
+
+
+/* Write data into a file.
+   `path' specifies the path of the file.  If it is `NULL', the standard output is specified.
+   `ptr' specifies the pointer to the data region.
+   `size' specifies the size of the region.
+   If successful, the return value is true, else, it is false. */
+bool tcwritefile(const char *path, const void *ptr, int size);
+
+
+/* Copy a file.
+   `src' specifies the path of the source file.
+   `dest' specifies the path of the destination file.
+   The return value is true if successful, else, it is false.
+   If the destination file exists, it is overwritten. */
+bool tccopyfile(const char *src, const char *dest);
+
+
+/* Read names of files in a directory.
+   `path' specifies the path of the directory.
+   The return value is a list object of names if successful, else it is `NULL'.
+   Links to the directory itself and to the parent directory are ignored.
+   Because the object of the return value is created with the function `tclistnew', it should
+   be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcreaddir(const char *path);
+
+
+/* Expand a pattern into a list of matched paths.
+   `pattern' specifies the matching pattern.
+   The return value is a list object of matched paths.  If no path is matched, an empty list is
+   returned.
+   Because the object of the return value is created with the function `tclistnew', it should
+   be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcglobpat(const char *pattern);
+
+
+/* Remove a file or a directory and its sub ones recursively.
+   `path' specifies the path of the link.
+   If successful, the return value is true, else, it is false.  False is returned when the link
+   does not exist or the permission is denied. */
+bool tcremovelink(const char *path);
+
+
+/* Write data into a file.
+   `fd' specifies the file descriptor.
+   `buf' specifies the buffer to be written.
+   `size' specifies the size of the buffer.
+   The return value is true if successful, else, it is false. */
+bool tcwrite(int fd, const void *buf, size_t size);
+
+
+/* Read data from a file.
+   `fd' specifies the file descriptor.
+   `buf' specifies the buffer to store into.
+   `size' specifies the size of the buffer.
+   The return value is true if successful, else, it is false. */
+bool tcread(int fd, void *buf, size_t size);
+
+
+/* Lock a file.
+   `fd' specifies the file descriptor.
+   `ex' specifies whether an exclusive lock or a shared lock is performed.
+   `nb' specifies whether to request with non-blocking.
+   The return value is true if successful, else, it is false. */
+bool tclock(int fd, bool ex, bool nb);
+
+
+
+/*************************************************************************************************
+ * encoding utilities
+ *************************************************************************************************/
+
+
+/* Encode a serial object with URL encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   The return value is the result string.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if when is no longer in use. */
+char *tcurlencode(const char *ptr, int size);
+
+
+/* Decode a string encoded with URL encoding.
+   `str' specifies the encoded string.
+   `sp' specifies the pointer to a variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the result.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when
+   it is no longer in use. */
+char *tcurldecode(const char *str, int *sp);
+
+
+/* Break up a URL into elements.
+   `str' specifies the URL string.
+   The return value is the map handle whose keys are the name of elements.  The key "self"
+   specifies the URL itself.  The key "scheme" specifies the scheme.  The key "host" specifies
+   the host of the server.  The key "port" specifies the port number of the server.  The key
+   "authority" specifies the authority information.  The key "path" specifies the path of the
+   resource.  The key "file" specifies the file name without the directory section.  The key
+   "query" specifies the query string.  The key "fragment" specifies the fragment string.
+   Supported schema are HTTP, HTTPS, FTP, and FILE.  Absolute URL and relative URL are supported.
+   Because the object of the return value is created with the function `tcmapnew', it should be
+   deleted with the function `tcmapdel' when it is no longer in use. */
+TCMAP *tcurlbreak(const char *str);
+
+
+/* Resolve a relative URL with an absolute URL.
+   `base' specifies the absolute URL of the base location.
+   `target' specifies the URL to be resolved.
+   The return value is the resolved URL.  If the target URL is relative, a new URL of relative
+   location from the base location is returned.  Else, a copy of the target URL is returned.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcurlresolve(const char *base, const char *target);
+
+
+/* Encode a serial object with Base64 encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   The return value is the result string.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if when is no longer in use. */
+char *tcbaseencode(const char *ptr, int size);
+
+
+/* Decode a string encoded with Base64 encoding.
+   `str' specifies the encoded string.
+   `sp' specifies the pointer to a variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the result.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when
+   it is no longer in use. */
+char *tcbasedecode(const char *str, int *sp);
+
+
+/* Encode a serial object with Quoted-printable encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   The return value is the result string.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if when is no longer in use. */
+char *tcquoteencode(const char *ptr, int size);
+
+
+/* Decode a string encoded with Quoted-printable encoding.
+   `str' specifies the encoded string.
+   `sp' specifies the pointer to a variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the result.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when
+   it is no longer in use. */
+char *tcquotedecode(const char *str, int *sp);
+
+
+/* Encode a string with MIME encoding.
+   `str' specifies the string.
+   `encname' specifies the string of the name of the character encoding.
+   `base' specifies whether to use Base64 encoding.  If it is false, Quoted-printable is used.
+   The return value is the result string.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcmimeencode(const char *str, const char *encname, bool base);
+
+
+/* Decode a string encoded with MIME encoding.
+   `str' specifies the encoded string.
+   `enp' specifies the pointer to the region into which the name of encoding is written.  If it
+   is `NULL', it is not used.  The size of the buffer should be equal to or more than 32 bytes.
+   The return value is the result string.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcmimedecode(const char *str, char *enp);
+
+
+/* Compress a serial object with Packbits encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the result object, else, it is `NULL'.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcpackencode(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with Packbits encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to a variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the result object, else, it is `NULL'.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when it
+   is no longer in use. */
+char *tcpackdecode(const char *ptr, int size, int *sp);
+
+
+/* Compress a serial object with TCBS encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the result object, else, it is `NULL'.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcbsencode(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with TCBS encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to a variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the result object, else, it is `NULL'.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when it
+   is no longer in use. */
+char *tcbsdecode(const char *ptr, int size, int *sp);
+
+
+/* Compress a serial object with Deflate encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the result object, else, it is `NULL'.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcdeflate(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with Deflate encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to a variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the result object, else, it is `NULL'.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when it
+   is no longer in use. */
+char *tcinflate(const char *ptr, int size, int *sp);
+
+
+/* Compress a serial object with GZIP encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to the variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the result object, else, it is `NULL'.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call when it is no longer in use. */
+char *tcgzipencode(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with GZIP encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to a variable into which the size of the region of the return
+   value is assigned.
+   If successful, the return value is the pointer to the result object, else, it is `NULL'.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when it
+   is no longer in use. */
+char *tcgzipdecode(const char *ptr, int size, int *sp);
+
+
+/* Get the CRC32 checksum of a serial object.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   The return value is the CRC32 checksum of the object. */
+unsigned int tcgetcrc(const char *ptr, int size);
+
+
+/* Encode an array of nonnegative integers with BER encoding.
+   `ary' specifies the pointer to the array of nonnegative integers.
+   `anum' specifies the size of the array.
+   `sp' specifies the pointer to a variable into which the size of the region of the return
+   value is assigned.
+   The return value is the pointer to the region of the result.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if when is no longer in use. */
+char *tcberencode(const unsigned int *ary, int anum, int *sp);
+
+
+/* Decode a serial object encoded with BER encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `np' specifies the pointer to a variable into which the number of elements of the return value
+   is assigned.
+   The return value is the pointer to the array of the result.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if when is no longer in use. */
+unsigned int *tcberdecode(const char *ptr, int size, int *np);
+
+
+/* Escape meta characters in a string with the entity references of XML.
+   `str' specifies the string.
+   The return value is the pointer to the escaped string.
+   This function escapes only `&', `<', `>', and `"'.  Because the region of the return value
+   is allocated with the `malloc' call, it should be released with the `free' call when it is no
+   longer in use. */
+char *tcxmlescape(const char *str);
+
+
+/* Unescape entity references in a string of XML.
+   `str' specifies the string.
+   The return value is the unescaped string.
+   This function restores only `&amp;', `&lt;', `&gt;', and `&quot;'.  Because the region of the
+   return value is allocated with the `malloc' call, it should be released with the `free' call
+   when it is no longer in use. */
+char *tcxmlunescape(const char *str);
+
+
+/* Split an XML string into tags and text sections.
+   `str' specifies the XML string.
+   The return value is the list object whose elements are strings of tags or text sections.
+   Because the object of the return value is created with the function `tclistnew', it should
+   be deleted with the function `tclistdel' when it is no longer in use.  Because this function
+   does not check validation, it can handle also HTML and SGML. */
+TCLIST *tcxmlbreak(const char *str);
+
+
+/* Get the map of attributes of an XML tag.
+   `str' specifies the pointer to the region of a tag string.
+   The return value is the map object containing attribute names and their values which are
+   unescaped.  You can get the name of the tag with the key of an empty string.
+   Because the object of the return value is created with the function `tcmapnew', it should
+   be deleted with the function `tcmapdel' when it is no longer in use. */
+TCMAP *tcxmlattrs(const char *str);
+
+
+
+/*************************************************************************************************
+ * bit operation utilities
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of structure for a bit stream object */
+  uint8_t *sp;                           /* start pointer */
+  uint8_t *cp;                           /* current pointer */
+  int idx;                               /* bit index */
+  int size;                              /* size of used region */
+} TCBITSTRM;
+
+
+/* Create a bitmap object. */
+#define TCBITMAPNEW(TC_num) \
+  tccalloc(((TC_num) >> 3) + 1, 1);
+
+
+/* Delete a bitmap object */
+#define TCBITMAPDEL(TC_bitmap) \
+  do { \
+    free((TC_bitmap)); \
+  } while(false);
+
+
+/* Turn on a field of a bitmap object. */
+#define TCBITMAPON(TC_bitmap, TC_idx) \
+  do { \
+    (TC_bitmap)[(TC_idx)>>3] |= 0x1 << ((TC_idx) & 0x7); \
+  } while(false);
+
+
+/* Turn off a field of a bitmap object. */
+#define TCBITMAPOFF(TC_bitmap, TC_idx) \
+  do { \
+    (TC_bitmap)[(TC_idx)>>3] &= ~(0x1 << ((TC_idx) & 0x7)); \
+  } while(false);
+
+
+/* Check a field of a bitmap object. */
+#define TCBITMAPCHECK(TC_bitmap, TC_idx) \
+  ((TC_bitmap)[(TC_idx)>>3] & 0x1 << ((TC_idx) & 0x7))
+
+
+/* Initialize a bit stream object as writer. */
+#define TCBITSTRMINITW(TC_bitstrm, TC_ptr) \
+  do { \
+    (TC_bitstrm).sp = (uint8_t *)(TC_ptr); \
+    (TC_bitstrm).cp = (TC_bitstrm).sp; \
+    *(TC_bitstrm).cp = 0; \
+    (TC_bitstrm).idx = 3; \
+    (TC_bitstrm).size = 1; \
+  } while(false);
+
+
+/* Concatenate a bit to a bit stream object. */
+#define TCBITSTRMCAT(TC_bitstrm, sign) \
+  do { \
+    if((TC_bitstrm).idx >= 8){ \
+      *(++(TC_bitstrm).cp) = 0; \
+      (TC_bitstrm).idx = 0; \
+      (TC_bitstrm).size++; \
+    } \
+    *(TC_bitstrm).cp |= (sign << (TC_bitstrm).idx); \
+    (TC_bitstrm).idx++; \
+  } while(false);
+
+
+/* Set the end mark to a bit stream object. */
+#define TCBITSTRMSETEND(TC_bitstrm) \
+  do { \
+    if((TC_bitstrm).idx >= 8){ \
+      *(++(TC_bitstrm).cp) = 0; \
+      (TC_bitstrm).idx = 0; \
+      (TC_bitstrm).size++; \
+    } \
+    *(TC_bitstrm).sp |= (TC_bitstrm).idx & 7; \
+  } while(false);
+
+
+/* Get the size of the used region of a bit stream object. */
+#define TCBITSTRMSIZE(TC_bitstrm) \
+  ((TC_bitstrm).size)
+
+
+/* Initialize a bit stream object as reader. */
+#define TCBITSTRMINITR(TC_bitstrm, TC_ptr, TC_size) \
+  do { \
+    (TC_bitstrm).sp = (uint8_t *)(TC_ptr); \
+    (TC_bitstrm).cp = (TC_bitstrm).sp; \
+    (TC_bitstrm).idx = 3; \
+    (TC_bitstrm).size = (TC_size); \
+  } while(false);
+
+
+/* Read a bit from a bit stream object. */
+#define TCBITSTRMREAD(TC_bitstrm, TC_sign) \
+  do { \
+    if((TC_bitstrm).idx >= 8){ \
+      (TC_bitstrm).cp++; \
+      (TC_bitstrm).idx = 0; \
+    } \
+    (TC_sign) = (*((TC_bitstrm).cp) & (1 << (TC_bitstrm).idx)) > 0; \
+    (TC_bitstrm).idx++; \
+  } while(false);
+
+
+/* Get the number of bits of a bit stream object. */
+#define TCBITSTRMNUM(TC_bitstrm) \
+  ((((TC_bitstrm).size - 1) << 3) + (*(TC_bitstrm).sp & 7) - 3)
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+#include <stdio.h>
+
+#define _TC_VERSION    "1.2.5"
+#define _TC_LIBVER     306
+#define _TC_FORMATVER  "1.0"
+
+
+/* Show error message on the standard error output and exit.
+   `message' specifies an error message.
+   This function does not return. */
+void *tcmyfatal(const char *message);
+
+
+/* Lock the global mutex object.
+   If successful, the return value is true, else, it is false. */
+bool tcglobalmutexlock(void);
+
+
+/* Lock the global mutex object by shared locking.
+   If successful, the return value is true, else, it is false. */
+bool tcglobalmutexlockshared(void);
+
+
+/* Unlock the global mutex object.
+   If successful, the return value is true, else, it is false. */
+bool tcglobalmutexunlock(void);
+
+
+/* Encode a serial object with BWT encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `idxp' specifies the pointer to the variable into which the index of the original string in
+   the rotation array is assigned.
+   The return value is the pointer to the result object.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when it
+   is no longer in use. */
+char *tcbwtencode(const char *ptr, int size, int *idxp);
+
+
+/* Decode a serial object encoded with BWT encoding.
+   `ptr' specifies the pointer to the region.
+   `size' specifies the size of the region.
+   `idx' specifies the index of the original string in the rotation array is assigned.
+   The return value is the pointer to the result object.
+   Because an additional zero code is appended at the end of the region of the return value,
+   the return value can be treated as a character string.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call when it
+   is no longer in use. */
+char *tcbwtdecode(const char *ptr, int size, int idx);
+
+
+/* Print debug information with a formatted string as with `printf'. */
+#if __STDC_VERSION__ >= 199901L
+#define TCDPRINTF(...) \
+  do { \
+    fprintf(stderr, "%s:%d:%s: ", __FILE__, __LINE__, __func__); \
+    fprintf(stderr, __VA_ARGS__); \
+    fprintf(stderr, "\n"); \
+  } while(false);
+#else
+#define TCDPRINTF(TC_str) \
+  do { \
+    fprintf(stderr, "%s:%d:%s: %s\n", __FILE__, __LINE__, __func__, TC_str); \
+  } while(false);
+#endif
+
+
+/* Print hexadecimal pattern of a binary region. */
+#define TCPRINTHEX(TC_ptr, TC_size) \
+  do { \
+    for(int TC_i = 0; TC_i < (TC_size); TC_i++){ \
+      if(TC_i > 0) putchar(' '); \
+      printf("%02X", ((unsigned char *)(TC_ptr))[TC_i]); \
+    } \
+    putchar('\n'); \
+  } while(false);
+
+
+/* Print an extensible string object. */
+#define TCPRINTXSTR(TC_xstr) \
+  do { \
+    fwrite(tcxstrptr((TC_xstr)), tcxstrsize((TC_xstr)), 1, stdout); \
+    putchar('\n'); \
+  } while(false);
+
+
+/* Print all elements of a list object. */
+#define TCPRINTLIST(TC_list) \
+  do { \
+    for(int TC_i = 0; TC_i < tclistnum((TC_list)); TC_i++){ \
+      int TC_size; \
+      const char *TC_ptr = tclistval((TC_list), TC_i, &TC_size); \
+      printf("%p\t", (void *)(TC_list)); \
+      fwrite(TC_ptr, TC_size, 1, stdout); \
+      putchar('\n'); \
+    } \
+    putchar('\n'); \
+  } while(false);
+
+
+/* Print all records of a list object. */
+#define TCPRINTMAP(TC_map) \
+  do { \
+    TCLIST *TC_keys = tcmapkeys((TC_map)); \
+    for(int TC_i = 0; TC_i < tclistnum(TC_keys); TC_i++){ \
+      int TC_ksiz; \
+      const char *TC_kbuf = tclistval(TC_keys, TC_i, &TC_ksiz); \
+      int TC_vsiz; \
+      const char *TC_vbuf = tcmapget((TC_map), TC_kbuf, TC_ksiz, &TC_vsiz); \
+      printf("%p\t", (void *)(TC_map)); \
+      fwrite(TC_kbuf, TC_ksiz, 1, stdout); \
+      putchar('\t'); \
+      fwrite(TC_vbuf, TC_vsiz, 1, stdout); \
+      putchar('\n'); \
+    } \
+    putchar('\n'); \
+    tclistdel(TC_keys); \
+  } while(false);
+
+
+/* Alias of `tcmemdup'. */
+#if defined(_MYFASTEST)
+#define TCMALLOC(TC_res, TC_size) \
+  do { \
+    (TC_res) = malloc(TC_size); \
+  } while(false)
+#else
+#define TCMALLOC(TC_res, TC_size) \
+  do { \
+    if(!((TC_res) = malloc(TC_size))) tcmyfatal("out of memory"); \
+  } while(false)
+#endif
+
+/* Alias of `tccalloc'. */
+#if defined(_MYFASTEST)
+#define TCCALLOC(TC_res, TC_nmemb, TC_size) \
+  do { \
+    (TC_res) = calloc((TC_nmemb), (TC_size)); \
+  } while(false)
+#else
+#define TCCALLOC(TC_res, TC_nmemb, TC_size) \
+  do { \
+    if(!((TC_res) = calloc((TC_nmemb), (TC_size)))) tcmyfatal("out of memory"); \
+  } while(false)
+#endif
+
+/* Alias of `tcrealloc'. */
+#if defined(_MYFASTEST)
+#define TCREALLOC(TC_res, TC_ptr, TC_size) \
+  do { \
+    (TC_res) = realloc((TC_ptr), (TC_size)); \
+  } while(false)
+#else
+#define TCREALLOC(TC_res, TC_ptr, TC_size) \
+  do { \
+    if(!((TC_res) = realloc((TC_ptr), (TC_size)))) tcmyfatal("out of memory"); \
+  } while(false)
+#endif
+
+/* Alias of `tcmemdup'. */
+#define TCMEMDUP(TC_res, TC_ptr, TC_size) \
+  do { \
+    TCMALLOC((TC_res), (TC_size) + 1);   \
+    memcpy((TC_res), (TC_ptr), (TC_size));      \
+    (TC_res)[TC_size] = '\0'; \
+  } while(false)
+
+
+/* Alias of `tcxstrcat'. */
+#define TCXSTRCAT(TC_xstr, TC_ptr, TC_size) \
+  do { \
+    int TC_mysize = (TC_size); \
+    int TC_nsize = (TC_xstr)->size + TC_mysize + 1; \
+    if((TC_xstr)->asize < TC_nsize){ \
+      while((TC_xstr)->asize < TC_nsize){ \
+        (TC_xstr)->asize *= 2; \
+        if((TC_xstr)->asize < TC_nsize) (TC_xstr)->asize = TC_nsize; \
+      } \
+      TCREALLOC((TC_xstr)->ptr, (TC_xstr)->ptr, (TC_xstr)->asize); \
+    } \
+    memcpy((TC_xstr)->ptr + (TC_xstr)->size, (TC_ptr), TC_mysize); \
+    (TC_xstr)->size += TC_mysize; \
+    (TC_xstr)->ptr[(TC_xstr)->size] = '\0'; \
+  } while(false)
+
+
+/* Alias of `tcxstrptr'. */
+#define TCXSTRPTR(TC_xstr) \
+  ((TC_xstr)->ptr)
+
+
+/* Alias of `tcxstrsize'. */
+#define TCXSTRSIZE(TC_xstr) \
+  ((TC_xstr)->size)
+
+
+/* Alias of `tclistnum'. */
+#define TCLISTNUM(TC_list) \
+  ((TC_list)->num)
+
+
+/* Alias of `tclistval' but not checking size and not using the third parameter. */
+#define TCLISTVALPTR(TC_list, TC_index) \
+  ((void *)((TC_list)->array[(TC_index)+(TC_list)->start].ptr))
+
+
+/* Alias of `tclistval' but not checking size and returning the size of the value. */
+#define TCLISTVALNUM(TC_list, TC_index) \
+  ((TC_list)->array[(TC_index)+(TC_list)->start].size)
+
+
+/* Add an element at the end of a list object. */
+#define TCLISTPUSH(TC_list, TC_ptr, TC_size) \
+  do { \
+    int TC_mysize = (TC_size); \
+    int TC_index = (TC_list)->start + (TC_list)->num; \
+    if(TC_index >= (TC_list)->anum){ \
+      (TC_list)->anum += (TC_list)->num + 1; \
+      TCREALLOC((TC_list)->array, (TC_list)->array, \
+                (TC_list)->anum * sizeof((TC_list)->array[0])); \
+    } \
+    TCLISTDATUM *array = (TC_list)->array; \
+    TCMALLOC(array[TC_index].ptr, TC_mysize + 1);     \
+    memcpy(array[TC_index].ptr, (TC_ptr), TC_mysize); \
+    array[TC_index].ptr[TC_mysize] = '\0'; \
+    array[TC_index].size = TC_mysize; \
+    (TC_list)->num++; \
+  } while(false)
+
+
+/* Alias of `tcmaprnum'. */
+#define TCMAPRNUM(TC_map) \
+  ((TC_map)->rnum)
+
+
+
+__TCUTIL_CLINKAGEEND
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */
diff --git a/bacula/src/lib/tokyocabinet/tokyocabinet.idl b/bacula/src/lib/tokyocabinet/tokyocabinet.idl
new file mode 100644 (file)
index 0000000..77cdf9f
--- /dev/null
@@ -0,0 +1,158 @@
+/*************************************************************************************************
+ * IDL for bindings of scripting languages
+ *                                                      Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU Lesser General Public License as published by the Free Software Foundation; either
+ * version 2.1 of the License or any later version.  Tokyo Cabinet is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+/**
+ * namespace of Tokyo Cabinet
+ */
+module TokyoCabinet {
+  //----------------------------------------------------------------
+  // list of strings (substituted for by the native mechanism).
+  //----------------------------------------------------------------
+  interface List {
+    string get(in long index);
+  };
+  //----------------------------------------------------------------
+  // the error codes
+  //----------------------------------------------------------------
+  interface ECODE {
+    const long ESUCCESS = 0;
+    const long ETHREAD = 1;
+    const long EINVALID = 2;
+    const long ENOFILE = 3;
+    const long ENOPERM = 4;
+    const long EMETA = 5;
+    const long ERHEAD = 6;
+    const long EOPEN = 7;
+    const long ECLOSE = 8;
+    const long ETRUNC = 9;
+    const long ESYNC = 10;
+    const long ESTAT = 11;
+    const long ESEEK = 12;
+    const long EREAD = 13;
+    const long EWRITE = 14;
+    const long EMMAP = 15;
+    const long ELOCK = 16;
+    const long EUNLINK = 17;
+    const long ERENAME = 18;
+    const long EMKDIR = 19;
+    const long ERMDIR = 20;
+    const long EKEEP = 21;
+    const long ENOREC = 22;
+    const long EMISC = 9999;
+    string errmsg(in long ecode);
+  };
+  //----------------------------------------------------------------
+  // the hash database API
+  //----------------------------------------------------------------
+  interface HDB :ECODE {
+    const long TLARGE = 1 << 0;
+    const long TDEFLATE = 1 << 1;
+    const long TTCBS = 1 << 2;
+    const long OREADER = 1 << 0;
+    const long OWRITER = 1 << 1;
+    const long OCREAT = 1 << 2;
+    const long OTRUNC = 1 << 3;
+    const long ONOLCK = 1 << 4;
+    const long OLCKNB = 1 << 5;
+    long ecode();
+    boolean tune(in long long bnum, in long apow, in long fpow, in long opts);
+    boolean setcache(in long rcnum);
+    boolean open(in string path, in long omode);
+    boolean close();
+    boolean put(in string key, in string value);
+    boolean putkeep(in string key, in string value);
+    boolean putcat(in string key, in string value);
+    boolean putasync(in string key, in string value);
+    boolean out(in string key);
+    string get(in string key);
+    long vsiz(in string key);
+    boolean iterinit();
+    string iternext();
+    List fwmkeys(in string prefix, in long max);
+    boolean sync();
+    boolean optimize(in long long bnum, in long apow, in long fpow, in long opts);
+    boolean vanish();
+    boolean copy(in string path);
+    string path();
+    long long rnum();
+    long long fsiz();
+  };
+  //----------------------------------------------------------------
+  // the B+ tree database API
+  //----------------------------------------------------------------
+  interface BDB :ECODE {
+    const long TLARGE = 1 << 0;
+    const long TDEFLATE = 1 << 1;
+    const long TTCBS = 1 << 2;
+    const long OREADER = 1 << 0;
+    const long OWRITER = 1 << 1;
+    const long OCREAT = 1 << 2;
+    const long OTRUNC = 1 << 3;
+    const long ONOLCK = 1 << 4;
+    const long OLCKNB = 1 << 5;
+    long ecode();
+    boolean tune(in long lmemb, in long nmemb,
+                 in long long bnum, in long apow, in long fpow, in long opts);
+    boolean setcache(in long lcnum, in long ncnum);
+    boolean open(in string path, in long omode);
+    boolean close();
+    boolean put(in string key, in string value);
+    boolean putkeep(in string key, in string value);
+    boolean putcat(in string key, in string value);
+    boolean putdup(in string key, in string value);
+    boolean putlist(in string key, in List values);
+    boolean out(in string key);
+    boolean outlist(in string key);
+    string get(in string key);
+    List getlist(in string key);
+    long vnum(in string key);
+    long vsiz(in string key);
+    List range(in string bkey, in boolean binc, in string ekey, in boolean einc, in long max);
+    List fwmkeys(in string prefix, in long max);
+    boolean sync();
+    boolean optimize(in long lmemb, in long nmemb,
+                     in long long bnum, in long apow, in long fpow, in long opts);
+    boolean vanish();
+    boolean copy(in string path);
+    boolean tranbegin();
+    boolean trancommit();
+    boolean tranabort();
+    string path();
+    long long rnum();
+    long long fsiz();
+  };
+  //----------------------------------------------------------------
+  // the B+ tree cursor API
+  //----------------------------------------------------------------
+  interface BDBCUR {
+    const long CPCURRENT = 0;
+    const long CPBEFORE = 1;
+    const long CPAFTER = 2;
+    boolean first();
+    boolean last();
+    boolean jump(in string key);
+    boolean prev();
+    boolean next();
+    boolean put(in string value, in long cpmode);
+    boolean out();
+    string key();
+    string val();
+  };
+};
+
+
+
+/* END OF FILE */
diff --git a/bacula/src/lib/tokyocabinet/tokyocabinet.pc.in b/bacula/src/lib/tokyocabinet/tokyocabinet.pc.in
new file mode 100644 (file)
index 0000000..e12d606
--- /dev/null
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datarootdir = @datarootdir@
+bindir=@bindir@
+libdir=@libdir@
+libexecdir=@libexecdir@
+includedir=@includedir@
+datadir=@datadir@
+
+Name: Tokyo Cabinet
+Description: a modern implementation of DBM
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -ltokyocabinet @LIBS@
+Cflags: -I${includedir}