[Box Backup-commit] COMMIT r2590 - in box/trunk: . qdbm qdbm/misc

subversion at boxbackup.org subversion at boxbackup.org
Sat Jan 9 16:15:41 GMT 2010


Author: chris
Date: 2010-01-09 16:15:40 +0000 (Sat, 09 Jan 2010)
New Revision: 2590

Added:
   box/trunk/qdbm/
   box/trunk/qdbm/COPYING
   box/trunk/qdbm/ChangeLog
   box/trunk/qdbm/LTmakefile.in
   box/trunk/qdbm/Makefile.in
   box/trunk/qdbm/NEWS
   box/trunk/qdbm/README
   box/trunk/qdbm/RISCmakefile
   box/trunk/qdbm/THANKS
   box/trunk/qdbm/VCmakefile
   box/trunk/qdbm/cabin.c
   box/trunk/qdbm/cabin.h
   box/trunk/qdbm/cbcodec.c
   box/trunk/qdbm/cbtest.c
   box/trunk/qdbm/configure
   box/trunk/qdbm/configure.in
   box/trunk/qdbm/crmgr.c
   box/trunk/qdbm/crtest.c
   box/trunk/qdbm/crtsv.c
   box/trunk/qdbm/curia.c
   box/trunk/qdbm/curia.h
   box/trunk/qdbm/depot.c
   box/trunk/qdbm/depot.h
   box/trunk/qdbm/dpmgr.c
   box/trunk/qdbm/dptest.c
   box/trunk/qdbm/dptsv.c
   box/trunk/qdbm/hovel.c
   box/trunk/qdbm/hovel.h
   box/trunk/qdbm/hvmgr.c
   box/trunk/qdbm/hvtest.c
   box/trunk/qdbm/misc/
   box/trunk/qdbm/misc/COPYING.txt
   box/trunk/qdbm/misc/README-win32.txt
   box/trunk/qdbm/misc/VCmakefile-old
   box/trunk/qdbm/misc/benchmark.pdf
   box/trunk/qdbm/misc/icon16.png
   box/trunk/qdbm/misc/icon20.png
   box/trunk/qdbm/misc/index.html
   box/trunk/qdbm/misc/index.ja.html
   box/trunk/qdbm/misc/logo.png
   box/trunk/qdbm/misc/makevcdef
   box/trunk/qdbm/misc/mymemo-ja.html
   box/trunk/qdbm/misc/tutorial-ja.html
   box/trunk/qdbm/misc/win32check.bat
   box/trunk/qdbm/myconf.c
   box/trunk/qdbm/myconf.h
   box/trunk/qdbm/odeum.c
   box/trunk/qdbm/odeum.h
   box/trunk/qdbm/odidx.c
   box/trunk/qdbm/odmgr.c
   box/trunk/qdbm/odtest.c
   box/trunk/qdbm/qdbm.def
   box/trunk/qdbm/qdbm.pc.in
   box/trunk/qdbm/qdbm.spec.in
   box/trunk/qdbm/qmttest.c
   box/trunk/qdbm/relic.c
   box/trunk/qdbm/relic.h
   box/trunk/qdbm/rlmgr.c
   box/trunk/qdbm/rltest.c
   box/trunk/qdbm/spex-ja.html
   box/trunk/qdbm/spex.html
   box/trunk/qdbm/villa.c
   box/trunk/qdbm/villa.h
   box/trunk/qdbm/vista.c
   box/trunk/qdbm/vista.h
   box/trunk/qdbm/vlmgr.c
   box/trunk/qdbm/vltest.c
   box/trunk/qdbm/vltsv.c
Log:
Add QDBM 1.8.77 dbm-compatible database library to sources, will 
probably be used as a default for the reference tracking implementation 
as it's smaller than BDB and apparently faster.


Added: box/trunk/qdbm/COPYING
===================================================================
--- box/trunk/qdbm/COPYING	                        (rev 0)
+++ box/trunk/qdbm/COPYING	2010-01-09 16:15:40 UTC (rev 2590)
@@ -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.
+
+  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.
+
+		  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.
+
+  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.
+
+  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.
+
+  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.
+
+  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.
+
+  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.
+
+  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
+
+           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!
+
+

Added: box/trunk/qdbm/ChangeLog
===================================================================
--- box/trunk/qdbm/ChangeLog	                        (rev 0)
+++ box/trunk/qdbm/ChangeLog	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,990 @@
+2007-10-18  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A potential defect related to leaf division of B+ tree was cleared.
+	- Release: 1.8.77
+
+2007-03-07  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The hash function of on-memory map was optimized.
+	- A bug related to large object was fixed.
+	- Release: 1.8.76
+
+2006-11-10  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- A bug related to B+ tree API for Ruby was fixed.
+	- Release: 1.8.75
+
+2006-11-02  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug related to optimization on Intel Mac series was escaped.
+	- Release: 1.8.74
+
+2006-10-20  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug related to BZIP2 encoding was fixed.
+	- Release: 1.8.73
+
+2006-10-06  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.8.72
+
+2006-08-24  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Makefile was modified to assure portability.
+	- Silent flag was added to the C++ API, the Java API, and the Ruby API.
+	- Release: 1.8.71
+
+2006-08-18  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug of segmentation fault on BSD was fixed.
+	- A test command for checking multi thread safety was added.
+	- Release: 1.8.70
+
+2006-08-15  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Mutex controll in C++ API became refined.
+	- Release: 1.8.69
+
+2006-08-08  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug about memory alignment was fixed.
+	- A bug of handling meta data on big endian platforms was fixed.
+	- Release: 1.8.68
+
+2006-08-05  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug about memory alignment was fixed.
+	- A bug about parsing MIME was fixed.
+	- Release: 1.8.67
+
+2006-08-05  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.8.66
+
+2006-08-03  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The extended API was enhanced.
+	- The extended advanced API was enhanced.
+	- Release: 1.8.65
+
+2006-07-28  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug of Makefile ralated to optimization was fixed.
+	- Release: 1.8.64
+
+2006-07-24  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A lot of functions were replaced by macros.
+	- The utility API was enhanced.
+	- Release: 1.8.63
+
+2006-07-20  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A lot of functions were replaced by macros.
+	- The URL resolver was to allow unescaped meta characters.
+	- The advanced API was enhanced.
+	- Release: 1.8.62
+
+2006-07-14  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug of the size checking function of the extended API was fixed.
+	- The advanced API was enhanced.
+	- Release: 1.8.61
+
+2006-06-03  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The basic API was enhanced.
+	- The extended API was enhanced.
+	- The advanced API was enhanced.
+	- Multiple cursor class is now supported on the java API.
+	- Release: 1.8.60
+
+2006-05-30  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The basic API was enhanced.
+	- The extended API was enhanced.
+	- The advanced API was enhanced.
+	- A bug of Makefile for Mac OS X support was fixed.
+	- Release: 1.8.59
+
+2006-05-28  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The basic API was enhanced.
+	- The advanced API was enhanced.
+	- Release: 1.8.58
+
+2006-05-20  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The basic API was enhanced.
+	- The utility API was enhanced.
+	- Release: 1.8.57
+
+2006-05-17  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug of URL decoder was fixed.
+	- Release: 1.8.56
+
+2006-05-15  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The extended API was enhanced.
+	- The utility API was enhanced.
+	- Release: 1.8.55
+
+2006-05-15  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The basic API was enhanced.
+	- Release: 1.8.54
+
+2006-05-10  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- AIX is now supported.
+	- The utility API was enhanced.
+	- Release: 1.8.53
+
+2006-05-04  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug about evaluating RFC822 date format was fixed.
+	- Release: 1.8.52
+
+2006-05-02  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug about evaluating RFC822 date format was fixed.
+	- Warings from GCC 4.1.x were dealt with.
+	- Missing functions in qdbm.def were supplied.
+	- Release: 1.8.51
+
+2006-04-28  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.8.50
+
+2006-04-19  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A parameter of dynamic linking library on Mac OS X was modified.
+	- The utility API was enhanced.
+	- Release: 1.8.49
+
+2006-03-27  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.8.48
+
+2006-03-10  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- LTmakefile was modified.
+	- The utility API was enhanced.
+	- Release: 1.8.47
+
+2006-02-20  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.8.46
+
+2006-01-28  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Alignment algorithm was improved.
+	- A bug of mmap emulation on Windows was fixed.
+	- Release: 1.8.45
+
+2006-01-24  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug of handling meta data on big endian platforms was fixed.
+	- The advanced API was enhanced.
+	- Release: 1.8.44
+
+2006-01-24  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug of mmap emulation on Windows was fixed.
+	- Release: 1.8.43
+
+2006-01-22  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- mmap emulation on Windows was enhanced.
+	- Release: 1.8.42
+
+2006-01-13  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Compression of pages of B+ tree with LZO and BZIP was added.
+	- Release: 1.8.41
+
+2006-01-08  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Configuration for VC++ was to build DLL.
+	- Release: 1.8.40
+
+2006-01-04  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The advanced API was enhanced.
+	- Release: 1.8.39
+
+2006-01-01  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The advanced API was enhanced.
+	- Release: 1.8.38
+
+2005-12-26  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Functions to set the size of the free block pool were added.
+	- Commands line tools were enhanced.
+	- Release: 1.8.37
+
+2005-12-24  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Free block pool was enhanced.
+	- Commands line tools were enhanced.
+	- Release: 1.8.36
+
+2005-11-30  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- The extended advanced API was enhanced.
+	- All stat calls were replaced with lstat calls.
+	- Release: 1.8.35
+
+2005-11-22  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug of i-node duplication on MinGW was fixed.
+	- Release: 1.8.34
+
+2005-09-09  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Compressing options of ZLIB was changed.
+	- The utility API was enhanced.
+	- Release: 1.8.33
+
+2005-08-30  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug of backward string matching was fixed.
+	- The utility API was enhanced.
+	- Release: 1.8.32
+
+2005-06-19  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug of the MIME parser was fixed.
+	- A bug about database repair on Win32 was fixed.
+	- A bug of Makefile for Mac OS X support was fixed.
+	- The type of error codes of GDBM-compatibility API was changed.
+	- Release: 1.8.31
+
+2005-06-09  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A Bug of APIs for C++ related to namespace was fixed.
+	- The library of shared objects of C++ APIs was added.
+	- The utility API was enhanced.
+	- Release: 1.8.30
+
+2005-06-01  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug about file locking on Win32 was fixed.
+	- Manuals of command line tools were added.
+	- The utility API was enhanced.
+	- Release: 1.8.29
+
+2005-05-27  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A compilation problem on hppa-linux was fixed.
+	- A configuration file for pkg-config was added.
+	- Release: 1.8.28
+
+2005-05-18  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Non-blocking lock was featured.
+	- Release: 1.8.27
+
+2005-05-12  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The inverted API was enhanced.
+	- The utility API was enhanced.
+	- Release: 1.8.26
+
+2005-05-10  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The inverted API was enhanced.
+	- The utility API was enhanced.
+	- Release: 1.8.25
+
+2005-04-25  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- A bug about database repair on Win32 was fixed.
+	- Release: 1.8.24
+
+2005-04-01  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- The extended advanced API was enhanced.
+	- Release: 1.8.23
+
+2005-03-24  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The extended advanced API was enhanced.
+	- The utility API was enhanced.
+	- The inverted API was enhanced.
+	- Test commands were enhanced.
+	- Release: 1.8.22
+
+2005-01-22  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Functions to dump endian independent data was added.
+	- Release: 1.8.21
+
+2005-01-05  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Sparse file support was added.
+	- Release: 1.8.20
+
+2005-01-02  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Building configurations were enhanced.
+	- Release: 1.8.19
+
+2004-11-10  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- The extended advanced API was enhanced.
+	- The inverted API was enhanced.
+	- Release: 1.8.18
+
+2004-09-20  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- The CGI script for file upload was enhanced.
+	- The spec file for RPM packages was enhanced.
+	- Release: 1.8.17
+
+2004-08-17  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug in the extended API was fixed.
+	- A bug about seek on Windows 9x was fixed.
+	- The CGI script for file upload was enhanced.
+	- Release: 1.8.16
+
+2004-08-04  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- A bug about mmap on Mac OS X was fixed.
+	- A CGI script for file upload was added.
+	- Building configurations were enhanced.
+	- Release: 1.8.15
+	
+2004-07-19  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The basic API was enhanced.
+	- The extended API was enhanced.
+	- The advanced API was enhanced.
+	- The inverted API was enhanced.
+	- Building configurations were enhanced.
+	- Release: 1.8.14
+
+2004-07-07  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- The inverted API was enhanced.
+	- Release: 1.8.13
+
+2004-05-18  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- The inverted API was enhanced.
+	- Building configurations were enhanced.
+	- Release: 1.8.12
+
+2004-05-12  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Source codes were cleaned up for Visual C++.
+	- Test commands were enhanced.
+	- Building configurations were enhanced.
+	- Release: 1.8.11
+
+2004-04-23  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Utility for performance test was enhanced.
+	- It was escaped from a bug about mmap on FreeBSD and NetBSD.
+	- Release: 1.8.10
+
+2004-04-20  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Logger for debugging is now featured.
+	- It was escaped from a bug about mmap on OpenBSD.
+	- Release: 1.8.9
+
+2004-04-11  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Processing speed was improved.
+	- The basic API was enhanced.
+	- The extended API was enhanced.
+	- The configuration for VC++ was enhanced.
+	- Release: 1.8.8
+
+2004-03-25  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The configuration of C++ API escaped from a bug of pthread.
+	- Release: 1.8.7
+
+2004-03-21  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The extended API was enhanced.
+	- Bugs in the utility API about memory management were fixed.
+	- Release: 1.8.6
+
+2004-03-12  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug in the utility API for 64-bit platforms was fixed.
+	- The utility API was enhanced.
+	- Release: 1.8.5
+
+2004-03-09  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Runtime switching of whether to use ZLIB was featured.
+	- The binary package for Win32 now features ZLIB and ICONV.
+	- The utility API was enhanced.
+	- Release: 1.8.4
+
+2004-03-06  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- A utility class of Java API was enhanced.
+	- Test commands were enhanced.
+	- Release: 1.8.3
+
+2004-03-01  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.8.2
+
+2004-02-22  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Processing speed of multi-thread support was improved.
+	- Release: 1.8.1
+
+2004-02-21  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Switch to make API for C thread-safe was added.
+	- Release: 1.8.0
+
+2004-02-18  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Potential bugs in APIs for C++ were fixed.
+	- Release: 1.7.34
+
+2004-01-28  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The extended API was enhanced.
+	- The inverted API was enhanced.
+	- Release: 1.7.33
+
+2004-01-17  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The inverted API was enhanced.
+	- Release: 1.7.32
+
+2004-01-16  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The inverted API was enhanced.
+	- Release: 1.7.31
+
+2004-01-12  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The inverted API was enhanced.
+	- Release: 1.7.30
+
+2004-01-11  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.7.29
+
+2004-01-09  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug in the utility API was fixed.
+	- Release: 1.7.28
+
+2004-01-06  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug in the advanced API was fixed.
+	- The utility API was enhanced.
+	- Release: 1.7.27
+
+2004-01-04  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The inverted API was enhanced.
+	- Release: 1.7.26
+
+2004-01-01  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The inverted API was enhanced.
+	- The utility API was enhanced.
+	- Release: 1.7.25
+
+2003-12-26  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- C++ export was supported in header files.
+	- The utility API was enhanced.
+	- Release: 1.7.24
+
+2003-12-21  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Repairing utility was added to the advanced API.
+	- The basic API was enhanced.
+	- The utility API was enhanced.
+	- Release: 1.7.23
+
+2003-12-16  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Repairing utility was added to the extended API.
+	- The utility API was enhanced.
+	- Release: 1.7.22
+
+2003-12-14  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A utility to repair a broken database file was added.
+	- A ulitity command for full-text search was enhanced.
+	- Release: 1.7.21
+
+2003-12-10  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Character encoding converter was added.
+	- Release: 1.7.20
+
+2003-12-10  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Format converter for older versions than 1.7.13 was added.
+	- Tuning paramters of the inverted API were modified.
+	- A CGI script for full-text search was enhanced.
+	- Release: 1.7.19
+
+2003-12-08  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- The GDBM-compatible API was enhanced.
+	- Release: 1.7.18
+
+2003-12-05  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- A CGI script for full-text search was enhanced.
+	- Release: 1.7.17
+
+2003-12-01  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A CGI script for full-text search was enhanced.
+	- A ulitity command for full-text search was enhanced.
+	- Building configuration files were modified.
+	- Release: 1.7.16
+
+2003-11-30  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A CGI script for full-text search was enhanced.
+	- A ulitity command for full-text search was enhanced.
+	- Release: 1.7.15
+
+2003-11-29  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The inverted API was enhanced.
+	- A ulitity command for full-text search was enhanced.
+	- A CGI script for full-text search was enhanced.
+	- Release: 1.7.14
+
+2003-11-27  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Format of headers of database was modified.
+	- Supporting endian conversion was abolished.
+	- Import/export features were added to utility commands.
+	- A ulitity command for full-text search was enhanced.
+	- A CGI script for full-text search was enhanced.
+	- Release: 1.7.13
+
+2003-11-25  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- The inverted API was enhanced.
+	- A ulitity command for full-text search was enhanced.
+	- A CGI script for full-text search was enhanced.
+	- Release: 1.7.12
+
+2003-11-23  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The inverted API was enhanced.
+	- A ulitity command for full-text search was enhanced.
+	- A CGI script for full-text search was enhanced.
+	- A utility class of Java API was enhanced.
+	- Release: 1.7.11
+
+2003-11-20  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A utility class of Java API was enhanced.
+	- Building configuration for CGI scripts was modified.
+	- Release: 1.7.10
+
+2003-11-20  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Tuning paramters of the inverted API were modified.
+	- A ulitity command for full-text search was enhanced.
+	- Release: 1.7.9
+
+2003-11-16  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Optional features of compressing with ZLIB were added.
+	- Release: 1.7.8
+
+2003-11-05  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The extended advanced API, Vista was added.
+	- Release: 1.7.7
+
+2003-11-03  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- C API compilation using Visual C++ was supported.
+	- Odeum and its commands were enhanced.
+	- Release: 1.7.6
+
+2003-10-25  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A ulitity command for full-text search was enhanced.
+	- A CGI script for full-text search was enhanced.
+	- Release: 1.7.5
+
+2003-10-23  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A CGI script for full-text search was added.
+	- A bug of BSD support in Makefile was fixed.
+	- Release: 1.7.4
+
+2003-10-19  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Processing speed of the inverted API was improved.
+	- A ulitity command for full-text search was enhanced.
+	- Release: 1.7.3
+
+2003-10-17  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The license was changed to LGPL.
+	- An indexing command for the inverted API was added.
+	- Release: 1.7.2
+
+2003-10-14  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A test command for the inverted API was added.
+	- A bug about optimization in the inverted API was fixed.
+	- Tuning paramters of the inverted API were modified.
+	- Release: 1.7.1
+
+2003-10-13  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The inverted API, Odeum was added.
+	- Comments in source codes increased.
+	- Release: 1.7.0
+
+2003-10-05  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Executable commands were modified for RISC OS.
+	- Release: 1.6.22
+
+2003-10-05  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Executable commands were modified for RISC OS.
+	- Release: 1.6.21
+
+2003-09-30  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Naming file in RISC OS style was supported.
+	- Hash functions were enhanced.
+	- Release: 1.6.20
+
+2003-09-26  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Test commands were added.
+	- Release: 1.6.19
+
+2003-09-22  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Makefile for RISC OS was added.
+	- C++ API was enhanced.
+	- Release: 1.6.18
+
+2003-09-14  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Test commands were enhanced.
+	- Source codes and Makefile were modified for RISC OS.
+	- The CGI script was enhanced.
+	- Release: 1.6.17
+
+2003-09-13  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The CGI script for administration was added.
+	- C++ API was enhanced.
+	- Release: 1.6.16
+
+2003-08-28  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.6.15
+
+2003-08-16  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.6.14
+
+2003-08-11  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.6.13
+
+2003-08-05  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- A bug in the utility API was fixed.
+	- Test commands were enhanced.
+	- Release: 1.6.12
+
+2003-07-31  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- C and Java APIs support compilation using MinGW.
+	- Release: 1.6.11
+
+2003-07-27  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Perl and Ruby APIs support transaction.
+	- Release: 1.6.10
+
+2003-07-26  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Speed of transaction was improved.
+	- Release: 1.6.9
+
+2003-07-24  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- C++ and Java APIs of B+ tree support transaction.
+	- Release: 1.6.8
+
+2003-07-21  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- C API of B+ tree supports transaction.
+	- Bugs about Mutex in C++ and Java APIs were fixed.
+	- Release: 1.6.7
+
+2003-07-18  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- C++ and Java APIs of database abstraction were simplified.
+	- Ruby API of B+ tree was simplified.
+	- Release: 1.6.6
+
+2003-07-16  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Ruby API of B+ tree was added.
+	- C, C++, Java and Perl APIs of B+ tree were enhanced.
+	- Release: 1.6.5
+
+2003-07-13  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Perl API of B+ tree was added.
+	- C, C++ and Java APIs of B+ tree were enhanced.
+	- Release: 1.6.4
+
+2003-07-08  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Java API of B++ tree was added.
+	- C API of B+ tree was enhanced.
+	- C++ API of B+ tree was enhanced.
+	- Release: 1.6.3
+
+2003-07-05  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Debug routines were removed and speed was improved.
+	- No locking mode was added.
+	- Endian converter of Villa was added.
+	- C++ API of B++ tree was added.
+	- Release: 1.6.2
+
+2003-07-03  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The advanced API was enhanced to improve its speed.
+	- Makefiles were enhanced for HP-UX.
+	- Release: 1.6.1
+
+2003-06-28  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The advanced API for B+ tree database is added.
+	- Release: 1.6.0
+
+2003-06-23  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Makefiles were enhanced for Mac OS X.
+	- Release: 1.5.13
+
+2003-06-18  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Makefile was enhanced for Solaris and Mac OS X.
+	- Release: 1.5.12
+
+2003-06-06  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.5.11
+
+2003-05-31  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was enhanced.
+	- Release: 1.5.10
+
+2003-05-29  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was changed.
+	- Release: 1.5.9
+
+2003-05-25  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Alignment mechanism was simplified.
+	- Release: 1.5.8
+
+2003-05-17  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Makefile was updated
+	- Release: 1.5.7
+
+2003-05-16  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Test tools were enhanced.
+	- Release: 1.5.6
+
+2003-05-14  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Makefile using libtool was added.
+	- Release: 1.5.5
+
+2003-05-12  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- API for Ruby was enhanced.
+	- Release: 1.5.4
+
+2003-05-08  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- API for Ruby was added.
+	- Release: 1.5.3
+
+2003-05-04  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- API for Perl was enhanced.
+	- Release: 1.5.2
+
+2003-04-30  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- API for Perl was enhanced.
+	- Release: 1.5.1
+
+2003-04-29  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- API for Perl was added.
+	- Release: 1.5.0
+
+2003-04-25  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The package was reorganized to be a GNU package.
+	- Release: 1.4.11
+
+2003-04-21  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The utility API was added.
+	- Release: 1.4.10
+
+2003-04-15  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Endian converters were added.
+	- Release: 1.4.9
+
+2003-04-12  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The GDBM-compatible API was enhanced.
+	- Release: 1.4.8
+
+2003-04-10  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Some bugs were fixed and C++ API was enhanced.
+	- Release: 1.4.7
+
+2003-04-09  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Coalescence of dead regions and reuse of them were featured.
+	- Release: 1.4.6
+
+2003-04-06  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The GDBM-compatible API and the C++ API were enhanced.
+	- Release: 1.4.5
+
+2003-04-04  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The API for C++ and the API for Java were enhanced.
+	- Release: 1.4.4
+
+2003-04-02  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Documents for C++ was enhanced.
+	- Release: 1.4.3
+
+2003-04-01  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The API for Java was enhanced.
+	- Release: 1.4.2
+
+2003-03-23  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Makefiles were modified.
+	- Release: 1.4.1
+
+2003-03-23  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- APIs for C++ and Java was enhanced.
+	- Release: 1.4.0
+
+2003-03-15  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The API for C++ was added.
+	- Release: 1.3.2
+
+2003-03-11  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The API for Java was supported on Solaris and Windows.
+	- Release: 1.3.1
+
+2003-03-04  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The API for Java was added.
+	- Release: 1.3.0
+
+2003-02-23  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The GDBM-compatible API was added.
+	- Release: 1.2.0
+
+2003-02-21  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Compatibility with NDBM was improved.
+	- Release: 1.1.3
+
+2003-02-18  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Optimizing suppoted on Windows.  DLL is supported.
+	- Release: 1.1.2
+
+2003-02-15  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- Windows is now supported.
+	- The compatible API was enhanced.
+	- Release: 1.1.1
+
+2003-02-11  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The NDBM-compatible API was added.
+	- Release: 1.1.0
+
+2003-02-08  Mikio Hirabayashi  <mikio at users.sourceforge.net>
+
+	- The initial version.
+	- Release: 1.0.0
+

Added: box/trunk/qdbm/LTmakefile.in
===================================================================
--- box/trunk/qdbm/LTmakefile.in	                        (rev 0)
+++ box/trunk/qdbm/LTmakefile.in	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,318 @@
+# Makefile to build QDBM using libtool
+
+
+
+#================================================================
+# Setting variables
+#================================================================
+
+
+# Generic settings
+SHELL = @SHELL@
+
+# Packaging
+PACKAGE = @PACKAGE_NAME@
+VERSION = @PACKAGE_VERSION@
+LIBVER = @LIBVER@
+LIBREV = @LIBREV@
+
+# Targets
+MYHEADS = depot.h curia.h relic.h hovel.h cabin.h villa.h vista.h odeum.h
+MYLIBOBJS = depot.lo curia.lo relic.lo hovel.lo cabin.lo villa.lo vista.lo odeum.lo myconf.lo
+MYLIBS = libqdbm.la
+MYBINS = dpmgr dptest dptsv crmgr crtest crtsv rlmgr rltest hvmgr hvtest \
+  cbtest cbcodec vlmgr vltest vltsv odmgr odtest odidx qmttest
+MYMAN1S = dpmgr.1 dptest.1 dptsv.1 crmgr.1 crtest.1 crtsv.1 rlmgr.1 rltest.1 hvmgr.1 hvtest.1 \
+  cbtest.1 cbcodec.1 vlmgr.1 vltest.1 vltsv.1 odmgr.1 odtest.1 odidx.1 qmttest.1
+MYMAN3S = qdbm.3 depot.3 dpopen.3 curia.3 cropen.3 relic.3 hovel.3 \
+  cabin.3 villa.3 vlopen.3 vista.3 odeum.3 odopen.3
+MYDOCS = spex.html spex-ja.html COPYING ChangeLog NEWS THANKS
+MYPCS = qdbm.pc
+
+# Install destinations
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+MYHEADDIR = @includedir@
+MYLIBDIR = @libdir@
+MYBINDIR = @bindir@
+MYMAN1DIR = @mandir@/man1
+MYMAN3DIR = @mandir@/man3
+MYSHAREDIR = $(prefix)/share/$(PACKAGE)
+MYPCDIR = @libdir@/pkgconfig
+
+# Building binaries
+LIBTOOL = libtool
+CC = gcc
+CFLAGS = -I. -I$(MYHEADDIR) -I$(HOME)/include -I/usr/local/include @MYDEFS@ \
+  -D_XOPEN_SOURCE_EXTENDED=1 -D_GNU_SOURCE=1 -D__EXTENSIONS__=1 -D_HPUX_SOURCE=1 \
+  -D_POSIX_MAPPED_FILES=1 -D_POSIX_SYNCHRONIZED_IO=1 \
+  -DPIC=1 -D_THREAD_SAFE=1 -D_REENTRANT=1 -DNDEBUG -O3
+LD = gcc
+LIBLDFLAGS = -rpath $(MYLIBDIR) -R $(MYLIBDIR) \
+  -version-info $$(($(LIBVER)+$(LIBREV))):0:$(LIBREV) @LIBS@
+LDFLAGS = -rpath $(MYLIBDIR) -R $(MYLIBDIR) -L. -lqdbm @LIBS@
+INSTALL = install
+MKDIR = mkdir -p
+CP = cp -rf
+RM = rm -rf
+
+
+
+#================================================================
+# Suffix rules
+#================================================================
+
+
+.SUFFIXES :
+.SUFFIXES : .c .lo
+
+.c.lo :
+	$(LIBTOOL) --mode=compile --tag=CC $(CC) -c $(CFLAGS) $<
+
+
+
+#================================================================
+# Actions
+#================================================================
+
+
+all : $(MYLIBS) $(MYBINS)
+	@printf '\n'
+	@printf '#================================================================\n'
+	@printf '# Ready to install.\n'
+	@printf '#================================================================\n'
+
+
+clean :
+	$(RM) $(MYLIBS) $(MYBINS) *.o *.a *.so *.lo *.la a.out .libs \
+	  *.exe *.dll.a *.dll TAGS srcdoc gmon.out leak.log casket casket.* *~
+
+
+install :
+	$(LIBTOOL) --mode=install $(INSTALL) $(MYHEADS) $(MYHEADDIR)
+	$(LIBTOOL) --mode=install $(INSTALL) $(MYLIBS) $(MYLIBDIR)
+	$(LIBTOOL) --mode=install $(INSTALL) $(MYBINS) $(MYBINDIR)
+	$(MKDIR) $(MYMAN1DIR)
+	cd man && $(CP) $(MYMAN1S) $(MYMAN1DIR)
+	$(MKDIR) $(MYMAN3DIR)
+	cd man && $(CP) $(MYMAN3S) $(MYMAN3DIR)
+	$(MKDIR) $(MYSHAREDIR)
+	$(CP) $(MYDOCS) $(MYSHAREDIR)
+	$(MKDIR) $(MYPCDIR)
+	$(CP) $(MYPCS) $(MYPCDIR)
+	@printf '\n'
+	@printf '#================================================================\n'
+	@printf '# Thanks for using QDBM.\n'
+	@printf '#================================================================\n'
+
+
+uninstall :
+	cd $(MYHEADDIR) && $(LIBTOOL) --mode=uninstall $(RM) $(MYHEADS)
+	cd $(MYLIBDIR) && $(LIBTOOL) --mode=uninstall $(RM) $(MYLIBS)
+	cd $(MYBINDIR) && $(LIBTOOL) --mode=uninstall $(RM) $(MYBINS)
+	cd $(MYMAN1DIR) && $(RM) $(MYMAN1S)
+	cd $(MYMAN3DIR) && $(RM) $(MYMAN3S)
+	$(RM) $(MYSHAREDIR)
+	cd $(MYPCDIR) && $(RM) $(MYPCS)
+
+
+distclean : clean
+	$(RM) Makefile LTmakefile rpmspec config.cache config.log config.status autom4te.cache
+
+
+check :
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./dptest write -s casket 500 500000
+	$(LIBTOOL) --mode=execute ./dptest write casket 50000 5000
+	$(LIBTOOL) --mode=execute ./dptest read casket
+	$(LIBTOOL) --mode=execute ./dptest read -wb casket
+	$(LIBTOOL) --mode=execute ./dptest rcat -c casket 50000 50 500 32 8
+	$(LIBTOOL) --mode=execute ./dptest combo casket
+	$(LIBTOOL) --mode=execute ./dptest wicked -c casket 5000
+	$(LIBTOOL) --mode=execute ./dptest wicked casket 500
+	$(LIBTOOL) --mode=execute ./dpmgr repair casket
+	$(LIBTOOL) --mode=execute ./dpmgr optimize casket
+	$(LIBTOOL) --mode=execute ./dpmgr list casket
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./crtest write -s casket 500 100000 5
+	$(LIBTOOL) --mode=execute ./crtest write casket 50000 500 10
+	$(LIBTOOL) --mode=execute ./crtest read casket
+	$(LIBTOOL) --mode=execute ./crtest read -wb casket
+	$(LIBTOOL) --mode=execute ./crtest rcat -c casket 50000 5 10 500 32 8
+	$(LIBTOOL) --mode=execute ./crtest combo casket
+	$(LIBTOOL) --mode=execute ./crtest wicked -c casket 5000
+	$(LIBTOOL) --mode=execute ./crtest wicked casket 500
+	$(LIBTOOL) --mode=execute ./crmgr repair casket
+	$(LIBTOOL) --mode=execute ./crmgr optimize casket
+	$(LIBTOOL) --mode=execute ./crmgr list casket
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./crtest write -lob casket 1000 50 10
+	$(LIBTOOL) --mode=execute ./crtest read -lob casket
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./rltest write casket 5000
+	$(LIBTOOL) --mode=execute ./rltest read casket 5000
+	$(LIBTOOL) --mode=execute ./rlmgr list casket
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./hvtest write casket 5000
+	$(LIBTOOL) --mode=execute ./hvtest read casket 5000
+	$(LIBTOOL) --mode=execute ./hvmgr optimize casket
+	$(LIBTOOL) --mode=execute ./hvmgr list casket
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./hvtest write -qdbm -s casket 500
+	$(LIBTOOL) --mode=execute ./hvtest write -qdbm casket 5000
+	$(LIBTOOL) --mode=execute ./hvtest read -qdbm casket 5000
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./cbtest sort 5000
+	$(LIBTOOL) --mode=execute ./cbtest strstr 500
+	$(LIBTOOL) --mode=execute ./cbtest list 50000
+	$(LIBTOOL) --mode=execute ./cbtest list -d 500
+	$(LIBTOOL) --mode=execute ./cbtest map 50000 500
+	$(LIBTOOL) --mode=execute ./cbtest map -d 500 5
+	$(LIBTOOL) --mode=execute ./cbtest heap 50000 500
+	$(LIBTOOL) --mode=execute ./cbtest heap -d 500 50
+	$(LIBTOOL) --mode=execute ./cbtest wicked 5000
+	$(LIBTOOL) --mode=execute ./cbtest misc
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./vltest write -tune 32 31 32 32 casket 50000
+	$(LIBTOOL) --mode=execute ./vltest read casket
+	$(LIBTOOL) --mode=execute ./vltest rdup -tune 32 31 512 256 casket 50000 50000
+	$(LIBTOOL) --mode=execute ./vltest combo casket
+	$(LIBTOOL) --mode=execute ./vltest wicked -c casket 5000
+	$(LIBTOOL) --mode=execute ./vltest wicked casket 500
+	$(LIBTOOL) --mode=execute ./vlmgr repair casket
+	$(LIBTOOL) --mode=execute ./vlmgr optimize casket
+	$(LIBTOOL) --mode=execute ./vlmgr list casket
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./vltest write -int -cz -tune 32 31 32 32 casket 50000
+	$(LIBTOOL) --mode=execute ./vltest read -int -vc casket
+	$(LIBTOOL) --mode=execute ./vltest rdup -int -cz -cc -tune 32 31 512 256 casket 50000 50000
+	$(LIBTOOL) --mode=execute ./vltest combo -cz casket
+	$(LIBTOOL) --mode=execute ./vltest wicked -cz -c casket 5000
+	$(LIBTOOL) --mode=execute ./vltest combo -cy casket
+	$(LIBTOOL) --mode=execute ./vltest wicked -cy -c casket 5000
+	$(LIBTOOL) --mode=execute ./vltest combo -cx casket
+	$(LIBTOOL) --mode=execute ./vltest wicked -cx -c casket 5000
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./odtest write casket 500 50 5000
+	$(LIBTOOL) --mode=execute ./odtest read casket
+	$(LIBTOOL) --mode=execute ./odtest combo casket
+	$(LIBTOOL) --mode=execute ./odtest wicked casket 500
+	$(RM) casket*
+	$(LIBTOOL) --mode=execute ./qmttest casket 50000 10
+	$(RM) casket*
+	@printf '\n'
+	@printf '#================================================================\n'
+	@printf '# Checking completed.\n'
+	@printf '#================================================================\n'
+
+
+.PHONY : all clean install check
+
+
+
+#================================================================
+# Building binaries
+#================================================================
+
+
+libqdbm.la : $(MYLIBOBJS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) $(MYLIBOBJS) -o $@ $(LIBLDFLAGS)
+
+
+dpmgr : dpmgr.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ dpmgr.lo $(LDFLAGS)
+
+
+dptest : dptest.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ dptest.lo $(LDFLAGS)
+
+
+dptsv : dptsv.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ dptsv.lo $(LDFLAGS)
+
+
+crmgr : crmgr.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ crmgr.lo $(LDFLAGS)
+
+
+crtest : crtest.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ crtest.lo $(LDFLAGS)
+
+
+crtsv : crtsv.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ crtsv.lo $(LDFLAGS)
+
+
+rlmgr : rlmgr.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ rlmgr.lo $(LDFLAGS)
+
+
+rltest : rltest.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ rltest.lo $(LDFLAGS)
+
+
+hvmgr : hvmgr.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ hvmgr.lo $(LDFLAGS)
+
+
+hvtest : hvtest.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ hvtest.lo $(LDFLAGS)
+
+
+cbtest : cbtest.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ cbtest.lo $(LDFLAGS)
+
+
+cbcodec : cbcodec.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ cbcodec.lo $(LDFLAGS)
+
+
+vlmgr : vlmgr.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ vlmgr.lo $(LDFLAGS)
+
+
+vltest : vltest.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ vltest.lo $(LDFLAGS)
+
+
+vltsv : vltsv.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ vltsv.lo $(LDFLAGS)
+
+
+odmgr : odmgr.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ odmgr.lo $(LDFLAGS)
+
+
+odtest : odtest.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ odtest.lo $(LDFLAGS)
+
+
+odidx : odidx.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ odidx.lo $(LDFLAGS)
+
+
+qmttest : qmttest.lo $(MYLIBS)
+	$(LIBTOOL) --mode=link --tag=CC $(LD) -o $@ qmttest.lo $(LDFLAGS)
+
+
+depot.lo dpmgr.lo dptest.lo dptsv.lo : depot.h myconf.h
+
+curia.lo crmgr.lo crtest.lo crtsv.lo : curia.h depot.h myconf.h
+
+relic.lo rlmgr.lo rltest.lo : relic.h depot.h myconf.h
+
+hovel.lo hvmgr.lo hvtest.lo : hovel.h depot.h curia.h myconf.h
+
+cabin.lo cbtest.lo : cabin.h myconf.h
+
+villa.lo vlmgr.lo vltest.lo vltsv.lo : villa.h depot.h cabin.h myconf.h
+
+vista.lo : vista.h villa.h depot.h curia.h cabin.h myconf.h villa.c
+
+odeum.lo odmgr.lo odtest.lo odidx.lo : odeum.h depot.h curia.h cabin.h villa.h myconf.h
+
+myconf.lo : myconf.h
+
+
+
+# END OF FILE

Added: box/trunk/qdbm/Makefile.in
===================================================================
--- box/trunk/qdbm/Makefile.in	                        (rev 0)
+++ box/trunk/qdbm/Makefile.in	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,646 @@
+# Makefile for QDBM
+
+
+
+#================================================================
+# Setting variables
+#================================================================
+
+
+# Generic settings
+SHELL = @SHELL@
+srcdir = @srcdir@
+VPATH = @srcdir@
+SUBDIRS = 
+
+# Packaging
+PACKAGE = @PACKAGE_NAME@
+VERSION = @PACKAGE_VERSION@
+PACKAGEDIR = $(PACKAGE)-$(VERSION)
+PACKAGETGZ = $(PACKAGE)-$(VERSION).tar.gz
+LIBVER = @LIBVER@
+LIBREV = @LIBREV@
+
+# Targets
+MYHEADS = depot.h curia.h relic.h hovel.h cabin.h villa.h vista.h odeum.h
+MYLIBOBJS = depot.o curia.o relic.o hovel.o cabin.o villa.o vista.o odeum.o myconf.o
+MYLIBS = libqdbm.a libqdbm.so.$(LIBVER).$(LIBREV).0 libqdbm.so.$(LIBVER) libqdbm.so
+MYBINS = dpmgr dptest dptsv crmgr crtest crtsv rlmgr rltest hvmgr hvtest \
+  cbtest cbcodec vlmgr vltest vltsv odmgr odtest odidx qmttest
+MYMAN1S = dpmgr.1 dptest.1 dptsv.1 crmgr.1 crtest.1 crtsv.1 rlmgr.1 rltest.1 hvmgr.1 hvtest.1 \
+  cbtest.1 cbcodec.1 vlmgr.1 vltest.1 vltsv.1 odmgr.1 odtest.1 odidx.1 qmttest.1
+MYMAN3S = qdbm.3 depot.3 dpopen.3 curia.3 cropen.3 relic.3 hovel.3 \
+  cabin.3 villa.3 vlopen.3 vista.3 odeum.3 odopen.3
+MYDOCS = spex.html spex-ja.html COPYING ChangeLog NEWS THANKS
+MYPCS = qdbm.pc
+MYWINLIBS = libqdbm.a libqdbm.dll.a
+MYMACLIBS = libqdbm.a libqdbm.$(LIBVER).$(LIBREV).0.dylib libqdbm.$(LIBVER).dylib libqdbm.dylib
+MYHPUXLIBS = libqdbm.a libqdbm.sl
+
+# Install destinations
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+MYHEADDIR = @includedir@
+MYLIBDIR = @libdir@
+MYBINDIR = @bindir@
+MYMAN1DIR = @mandir@/man1
+MYMAN3DIR = @mandir@/man3
+MYDATADIR = @datadir@/$(PACKAGE)
+MYPCDIR = @libdir@/pkgconfig
+DESTDIR =
+
+# Building binaries
+CC = @CC@
+CPPFLAGS = @CPPFLAGS@ -I$(srcdir) -I$(MYHEADDIR) \
+  -I$(HOME)/include -I/usr/local/include @MYDEFS@ \
+  -D_XOPEN_SOURCE_EXTENDED=1 -D_GNU_SOURCE=1 -D__EXTENSIONS__=1 -D_HPUX_SOURCE=1 \
+  -D_POSIX_MAPPED_FILES=1 -D_POSIX_SYNCHRONIZED_IO=1 \
+  -DPIC=1 -D_THREAD_SAFE=1 -D_REENTRANT=1 -DNDEBUG
+CFLAGS = -Wall -pedantic -fPIC -fsigned-char -O3 -fomit-frame-pointer -fforce-addr @MYOPTS@
+LD = @LD@
+LIBS = -lqdbm @LIBS@
+LIBLDFLAGS = @LDFLAGS@ -L. -L$(MYLIBDIR) -L$(HOME)/lib -L/usr/local/lib @LIBS@
+LDFLAGS = @LDFLAGS@ -L. -L$(MYLIBDIR) -L$(HOME)/lib -L/usr/local/lib $(LIBS)
+LDENV = LD_RUN_PATH=/lib:/usr/lib:$(MYLIBDIR):$(HOME)/lib:/usr/local/lib
+AR = @AR@
+ARFLAGS = rcsv
+RUNENV = LD_LIBRARY_PATH=.:/lib:/usr/lib:$(MYLIBDIR):$(HOME)/lib:/usr/local/lib
+
+
+
+#================================================================
+# Suffix rules
+#================================================================
+
+
+.SUFFIXES :
+.SUFFIXES : .c .o
+
+.c.o :
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
+
+
+
+#================================================================
+# Actions
+#================================================================
+
+
+targets : @TARGETS@
+
+
+all : $(MYLIBS) $(MYBINS)
+	@printf '\n'
+	@printf '#================================================================\n'
+	@printf '# Ready to install.\n'
+	@printf '#================================================================\n'
+
+
+static :
+	make MYLIBS="$(MYLIBS)" LDFLAGS="-static $(LDFLAGS)" all
+
+
+debug :
+	make MYLIBS="$(MYLIBS)" CPPFLAGS="$(CPPFLAGS) -UNDEBUG" \
+	  CFLAGS="-Wall -ansi -pedantic -fPIC -fsigned-char -O2 -g" \
+	  LDFLAGS="-static $(LDFLAGS)" all
+
+
+devel :
+	make MYLIBS="$(MYLIBS)" CPPFLAGS="$(CPPFLAGS) -UNDEBUG" \
+	  CFLAGS="-Wall -ansi -pedantic -fPIC -fsigned-char -O2 -g -pipe" all
+	sync ; sync
+
+
+stable :
+	make MYLIBS="$(MYLIBS)" CFLAGS="-Wall -ansi -pedantic -fPIC -fsigned-char -O2" all
+
+
+profile :
+	make MYLIBS="$(MYLIBS)" \
+	  CFLAGS="-Wall -pedantic -fPIC -fsigned-char -O3 -pg -g -Werror" \
+	  LDFLAGS="-static $(LDFLAGS)" all
+
+
+unsigned :
+	make MYLIBS="$(MYLIBS)" CPPFLAGS="$(CPPFLAGS) -UNDEBUG" \
+	  CFLAGS="-Wall -ansi -pedantic -fPIC -funsigned-char -g -O2" all
+
+
+m64 :
+	make MYLIBS="$(MYLIBS)" CPPFLAGS="$(CPPFLAGS) -UNDEBUG" \
+	  CFLAGS="-Wall -ansi -pedantic -fPIC -fsigned-char -O2 -m64 -g" all
+
+
+pen4 :
+	stdopt="-O3 -fomit-frame-pointer -minline-all-stringops" ; \
+	exopt="-march=pentium4 -minline-all-stringops -fprefetch-loop-arrays" ; \
+	  make MYLIBS="$(MYLIBS)" \
+	    CFLAGS="-Wall -pedantic -fPIC -fsigned-char $$stdopt $$exopt" all
+
+
+k8 :
+	stdopt="-O3 -fomit-frame-pointer -minline-all-stringops" ; \
+	exopt="-march=k8 -minline-all-stringops -fprefetch-loop-arrays" ; \
+	  make MYLIBS="$(MYLIBS)" \
+	    CFLAGS="-Wall -pedantic -fPIC -fsigned-char $$stdopt $$exopt" all
+
+
+clean :
+	rm -rf $(MYLIBS) $(MYBINS) *.o *.a *.so *.lo *.la a.out .libs \
+	  *.exe *.dll.a *.dll *.dylib *.sl TAGS srcdoc gmon.out leak.log \
+	  casket casket.* casket-* *~
+
+
+version :
+	vernum=`expr $(LIBVER)00 + $(LIBREV)` ; \
+	  sed -e 's/_QDBM_VERSION.*/_QDBM_VERSION  "$(VERSION)"/' \
+	    -e "s/_QDBM_LIBVER.*/_QDBM_LIBVER   $$vernum/" depot.h > depot.h~
+	[ -f depot.h~ ] && mv -f depot.h~ depot.h
+
+
+install :
+	mkdir -p $(DESTDIR)$(MYHEADDIR)
+	cd $(srcdir) && cp -Rf $(MYHEADS) $(DESTDIR)$(MYHEADDIR)
+	mkdir -p $(DESTDIR)$(MYLIBDIR)
+	cp -Rf $(MYLIBS) $(DESTDIR)$(MYLIBDIR)
+	mkdir -p $(DESTDIR)$(MYBINDIR)
+	cp -Rf $(MYBINS) $(DESTDIR)$(MYBINDIR)
+	mkdir -p $(DESTDIR)$(MYMAN1DIR)
+	cd $(srcdir)/man && cp -Rf $(MYMAN1S) $(DESTDIR)$(MYMAN1DIR)
+	mkdir -p $(DESTDIR)$(MYMAN3DIR)
+	cd $(srcdir)/man && cp -Rf $(MYMAN3S) $(DESTDIR)$(MYMAN3DIR)
+	mkdir -p $(DESTDIR)$(MYDATADIR)
+	cd $(srcdir) && cp -Rf $(MYDOCS) $(DESTDIR)$(MYDATADIR)
+	mkdir -p $(DESTDIR)$(MYPCDIR)
+	cd $(srcdir) && cp -Rf $(MYPCS) $(DESTDIR)$(MYPCDIR)
+	@printf '\n'
+	@printf '#================================================================\n'
+	@printf '# Thanks for using QDBM.\n'
+	@printf '#================================================================\n'
+
+
+install-strip :
+	make DESTDIR=$(DESTDIR) install
+	cd $(DESTDIR)$(MYBINDIR) && strip $(MYBINS)
+
+
+uninstall :
+	cd $(DESTDIR)$(MYHEADDIR) && rm -f $(MYHEADS)
+	cd $(DESTDIR)$(MYLIBDIR) && rm -f $(MYLIBS)
+	cd $(DESTDIR)$(MYBINDIR) && rm -f $(MYBINS)
+	cd $(DESTDIR)$(MYMAN1DIR) && rm -f $(MYMAN1S)
+	cd $(DESTDIR)$(MYMAN3DIR) && rm -f $(MYMAN3S)
+	rm -rf $(DESTDIR)$(MYDATADIR)
+	cd $(DESTDIR)$(MYPCDIR) && rm -f $(MYPCS)
+
+
+dist :
+	sync ; sync
+	for dir in $(SUBDIRS) ; \
+	  do \
+	    if [ -d $$dir ] ; \
+	    then \
+	      echo Making $@ in $$dir ; \
+	      ( cd $$dir && if [ -f configure.in ] ; then autoconf ; ./configure ; \
+	        make dist ; fi ) ; \
+	    fi ; \
+	  done
+	make version
+	make distclean
+	cd .. && tar cvf - $(PACKAGEDIR) | gzip -c > $(PACKAGETGZ)
+	sync ; sync
+
+
+distclean : clean
+	for dir in $(SUBDIRS) ; \
+	  do \
+	    if [ -d $$dir ] ; \
+	    then \
+	      echo Making $@ in $$dir ; \
+	      ( cd $$dir && if [ -f Makefile ] ; then make distclean ; fi ) ; \
+	    fi ; \
+	  done
+	rm -rf Makefile LTmakefile qdbm.spec qdbm.pc config.cache config.log config.status \
+	  autom4te.cache rpm-tmp *-win32
+
+
+TAGS :
+	etags -o $@ *.c *.h
+
+
+sdoc :
+	rm -rf srcdoc
+	./lab/ccdoc -d srcdoc -t "Source Documents of QDBM" *.h *.c
+
+
+check :
+	sync ; sync
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./dptest write -s casket 500 500000
+	$(RUNENV) $(RUNCMD) ./dptest write casket 50000 5000
+	$(RUNENV) $(RUNCMD) ./dptest read casket
+	$(RUNENV) $(RUNCMD) ./dptest read -wb casket
+	$(RUNENV) $(RUNCMD) ./dptest rcat -c casket 50000 50 500 32 8
+	$(RUNENV) $(RUNCMD) ./dptest combo casket
+	$(RUNENV) $(RUNCMD) ./dptest wicked -c casket 5000
+	$(RUNENV) $(RUNCMD) ./dptest wicked casket 500
+	$(RUNENV) $(RUNCMD) ./dpmgr repair casket
+	$(RUNENV) $(RUNCMD) ./dpmgr optimize casket
+	$(RUNENV) $(RUNCMD) ./dpmgr list casket
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./crtest write -s casket 500 100000 5
+	$(RUNENV) $(RUNCMD) ./crtest write casket 50000 500 10
+	$(RUNENV) $(RUNCMD) ./crtest read casket
+	$(RUNENV) $(RUNCMD) ./crtest read -wb casket
+	$(RUNENV) $(RUNCMD) ./crtest rcat -c casket 50000 5 10 500 32 8
+	$(RUNENV) $(RUNCMD) ./crtest combo casket
+	$(RUNENV) $(RUNCMD) ./crtest wicked -c casket 5000
+	$(RUNENV) $(RUNCMD) ./crtest wicked casket 500
+	$(RUNENV) $(RUNCMD) ./crmgr repair casket
+	$(RUNENV) $(RUNCMD) ./crmgr optimize casket
+	$(RUNENV) $(RUNCMD) ./crmgr list casket
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./crtest write -lob casket 1000 50 10
+	$(RUNENV) $(RUNCMD) ./crtest read -lob casket
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./rltest write casket 5000
+	$(RUNENV) $(RUNCMD) ./rltest read casket 5000
+	$(RUNENV) $(RUNCMD) ./rlmgr list casket
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./hvtest write casket 5000
+	$(RUNENV) $(RUNCMD) ./hvtest read casket 5000
+	$(RUNENV) $(RUNCMD) ./hvmgr optimize casket
+	$(RUNENV) $(RUNCMD) ./hvmgr list casket
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./hvtest write -qdbm -s casket 500
+	$(RUNENV) $(RUNCMD) ./hvtest write -qdbm casket 5000
+	$(RUNENV) $(RUNCMD) ./hvtest read -qdbm casket 5000
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./cbtest sort 5000
+	$(RUNENV) $(RUNCMD) ./cbtest strstr 500
+	$(RUNENV) $(RUNCMD) ./cbtest list 50000
+	$(RUNENV) $(RUNCMD) ./cbtest list -d 500
+	$(RUNENV) $(RUNCMD) ./cbtest map 50000 500
+	$(RUNENV) $(RUNCMD) ./cbtest map -d 500 5
+	$(RUNENV) $(RUNCMD) ./cbtest heap 50000 500
+	$(RUNENV) $(RUNCMD) ./cbtest heap -d 500 50
+	$(RUNENV) $(RUNCMD) ./cbtest wicked 5000
+	$(RUNENV) $(RUNCMD) ./cbtest misc
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./vltest write -tune 32 31 32 32 casket 50000
+	$(RUNENV) $(RUNCMD) ./vltest read casket
+	$(RUNENV) $(RUNCMD) ./vltest rdup -tune 32 31 512 256 casket 50000 50000
+	$(RUNENV) $(RUNCMD) ./vltest combo casket
+	$(RUNENV) $(RUNCMD) ./vltest wicked -c casket 5000
+	$(RUNENV) $(RUNCMD) ./vltest wicked casket 500
+	$(RUNENV) $(RUNCMD) ./vlmgr repair casket
+	$(RUNENV) $(RUNCMD) ./vlmgr optimize casket
+	$(RUNENV) $(RUNCMD) ./vlmgr list casket
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./vltest write -int -cz -tune 32 31 32 32 casket 50000
+	$(RUNENV) $(RUNCMD) ./vltest read -int -vc casket
+	$(RUNENV) $(RUNCMD) ./vltest rdup -int -cz -cc -tune 32 31 512 256 casket 50000 50000
+	$(RUNENV) $(RUNCMD) ./vltest combo -cz casket
+	$(RUNENV) $(RUNCMD) ./vltest wicked -cz -c casket 5000
+	$(RUNENV) $(RUNCMD) ./vltest combo -cy casket
+	$(RUNENV) $(RUNCMD) ./vltest wicked -cy -c casket 5000
+	$(RUNENV) $(RUNCMD) ./vltest combo -cx casket
+	$(RUNENV) $(RUNCMD) ./vltest wicked -cx -c casket 5000
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./odtest write casket 500 50 5000
+	$(RUNENV) $(RUNCMD) ./odtest read casket
+	$(RUNENV) $(RUNCMD) ./odtest combo casket
+	$(RUNENV) $(RUNCMD) ./odtest wicked casket 500
+	rm -rf casket*
+	$(RUNENV) $(RUNCMD) ./qmttest casket 50000 10
+	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
+
+
+world :
+	make clean ; make
+	cd plus ; [ -f Makefile ] || ./configure ; make clean ; make
+	cd java ; [ -f Makefile ] || ./configure ; make clean ; make
+	cd perl ; [ -f Makefile ] || ./configure ; make clean ; make
+	cd ruby ; [ -f Makefile ] || ./configure ; make clean ; make
+	cd cgi ; [ -f Makefile ] || ./configure ; make clean ; make
+
+
+install-world :
+	make install
+	cd plus ; [ -f Makefile ] || ./configure ; make install
+	cd java ; [ -f Makefile ] || ./configure ; make install
+	cd perl ; [ -f Makefile ] || ./configure ; make install
+	cd ruby ; [ -f Makefile ] || ./configure ; make install
+	cd cgi ; [ -f Makefile ] || ./configure ; make install
+
+
+uninstall-world :
+	make uninstall
+	cd plus ; [ -f Makefile ] || ./configure ; make uninstall
+	cd java ; [ -f Makefile ] || ./configure ; make uninstall
+	cd perl ; [ -f Makefile ] || ./configure ; make uninstall
+	cd ruby ; [ -f Makefile ] || ./configure ; make uninstall
+	cd cgi ; [ -f Makefile ] || ./configure ; make uninstall
+
+
+check-world :
+	make check
+	cd plus ; [ -f Makefile ] || ./configure ; make check
+	cd java ; [ -f Makefile ] || ./configure ; make check
+	cd perl ; [ -f Makefile ] || ./configure ; make check
+	cd ruby ; [ -f Makefile ] || ./configure ; make check
+
+
+rpm : ../$(PACKAGETGZ) qdbm.spec
+	mkdir -p rpm-tmp/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
+	mkdir -p rpm-tmp/RPMS/i386
+	cp ../$(PACKAGETGZ) rpm-tmp/SOURCES
+	rpmbuild -bb --target i386 --define "_topdir `pwd`/rpm-tmp" qdbm.spec
+	mv -f rpm-tmp/RPMS/i386/$(PACKAGE)-*$(VERSION)*.rpm ..
+	rm -rf rpm-tmp
+
+
+win32pkg :
+	test -f /bin/mgwz.dll
+	test -f /bin/libiconv-2.dll
+	make uninstall && make uninstall-win && make clean
+	make mingw && strip *.exe && make install-win
+	cd java && ./configure
+	cd java && make uninstall && make uninstall-win && make clean
+	cd java && make mingw
+	cd cgi && ./configure
+	cd cgi && make clean
+	cd cgi && make mingw && strip *.cgi
+	mkdir -p $(PACKAGE)-$(VERSION)-win32
+	cp -Rf $(MYHEADS) libqdbm.dll.a qdbm.dll *.exe *.html \
+	  misc/README-win32.txt misc/COPYING.txt misc/win32check.bat \
+	  /bin/mgwz.dll /bin/libiconv-2.dll \
+	  $(PACKAGE)-$(VERSION)-win32
+	cp -Rf java/jqdbm.dll java/qdbm.jar java/*.html java/japidoc $(PACKAGE)-$(VERSION)-win32
+	mkdir -p $(PACKAGE)-$(VERSION)-win32/cgi
+	cp -Rf cgi/*.cgi cgi/*.conf cgi/*.html $(PACKAGE)-$(VERSION)-win32/cgi
+	zip -r $(PACKAGE)-$(VERSION)-win32.zip $(PACKAGE)-$(VERSION)-win32
+	mv -f $(PACKAGE)-$(VERSION)-win32.zip ..
+	rm -rf $(PACKAGE)-$(VERSION)-win32
+	make uninstall && make uninstall-win && make clean
+	cd java ; make uninstall && make uninstall-win && make clean
+
+
+win :
+	make MYLIBS="$(MYWINLIBS)" CFLAGS="-Wall -ansi -pedantic -fsigned-char -O2"
+
+
+mingw :
+	make CC="gcc -mno-cygwin" MYLIBS="$(MYWINLIBS)" \
+	  CFLAGS="-Wall -fsigned-char -O2" LIBLDFLAGS="@MGWLIBS@" LDFLAGS="-L. -lqdbm @MGWLIBS@"
+
+
+check-win :
+	make check
+
+
+install-win :
+	make MYBINS="`for file in $(MYBINS) ; do echo $$file.exe ; done | tr '\n' ' '`" \
+	  MYLIBS="$(MYWINLIBS)" install
+	cp -Rf qdbm.dll $(DESTDIR)$(MYBINDIR)
+
+
+uninstall-win :
+	make MYBINS="`for file in $(MYBINS) ; do echo $$file.exe ; done | tr '\n' ' '`" \
+	  MYLIBS="$(MYWINLIBS)" uninstall
+	rm -f $(DESTDIR)$(MYBINDIR)/qdbm.dll
+
+
+def : libqdbm.a
+	./misc/makevcdef libqdbm.a > qdbm.def
+
+
+mac :
+	make MYLIBS="$(MYMACLIBS)" CFLAGS="-Wall -fsigned-char -fno-common -O2"
+
+
+check-mac :
+	make RUNENV="DYLD_LIBRARY_PATH=." check
+
+
+install-mac :
+	make MYLIBS="$(MYMACLIBS)" install
+
+
+uninstall-mac :
+	make MYLIBS="$(MYMACLIBS)" uninstall
+
+
+hpux :
+	make MYLIBS="$(MYHPUXLIBS)"
+
+
+check-hpux :
+	make RUNENV="SHLIB_PATH=." check
+
+
+install-hpux :
+	make MYLIBS="$(MYHPUXLIBS)" install
+
+
+uninstall-hpux :
+	make MYLIBS="$(MYHPUXLIBS)" uninstall
+
+
+no-so :
+	make MYLIBS="libqdbm.a" all
+
+
+install-no-so :
+	make MYLIBS="libqdbm.a" install
+
+
+uninstall-no-so :
+	make MYLIBS="libqdbm.a" uninstall
+
+
+.PHONY : all clean install check
+
+
+
+#================================================================
+# Building binaries
+#================================================================
+
+
+libqdbm.a : $(MYLIBOBJS)
+	$(AR) $(ARFLAGS) $@ $(MYLIBOBJS)
+
+
+libqdbm.so.$(LIBVER).$(LIBREV).0 : $(MYLIBOBJS)
+	if uname -a | egrep -i 'SunOS' > /dev/null ; \
+	  then \
+	    $(CC) -shared -Wl,-G,-h,libqdbm.so.$(LIBVER) -o $@ $(MYLIBOBJS) $(LIBLDFLAGS) ; \
+	  else \
+	    $(CC) -shared -Wl,-soname,libqdbm.so.$(LIBVER) -o $@ $(MYLIBOBJS) $(LIBLDFLAGS) ; \
+	  fi
+
+
+libqdbm.so.$(LIBVER) : libqdbm.so.$(LIBVER).$(LIBREV).0
+	ln -f -s libqdbm.so.$(LIBVER).$(LIBREV).0 $@
+
+
+libqdbm.so : libqdbm.so.$(LIBVER).$(LIBREV).0
+	ln -f -s libqdbm.so.$(LIBVER).$(LIBREV).0 $@
+
+
+libqdbm.dll.a : qdbm.dll
+
+
+qdbm.dll : $(MYLIBOBJS)
+	$(CC) -shared -o $@ \
+	  -Wl,--out-implib=lib$@.a \
+	  -Wl,--export-all-symbols \
+	  -Wl,--enable-auto-import \
+	  -Wl,--add-stdcall-alias \
+	  -Wl,--whole-archive \
+	  -Wl,--no-whole-archive $(MYLIBOBJS) $(LIBLDFLAGS)
+
+
+libqdbm.$(LIBVER).$(LIBREV).0.dylib : $(MYLIBOBJS)
+	$(CC) -dynamiclib -o $@ \
+	  -install_name $(MYLIBDIR)/libqdbm.$(LIBVER).dylib \
+	  -current_version $(LIBVER).$(LIBREV).0 \
+	  -compatibility_version $(LIBVER) \
+	  $(MYLIBOBJS) $(LIBLDFLAGS)
+
+
+libqdbm.$(LIBVER).dylib : libqdbm.$(LIBVER).$(LIBREV).0.dylib
+	ln -f -s libqdbm.$(LIBVER).$(LIBREV).0.dylib $@
+
+
+libqdbm.dylib : libqdbm.$(LIBVER).$(LIBREV).0.dylib
+	ln -f -s libqdbm.$(LIBVER).$(LIBREV).0.dylib $@
+
+
+libqdbm.sl : $(MYLIBOBJS)
+	$(CC) -shared -Wl,-b -o $@ $(MYLIBOBJS) $(LIBLDFLAGS)
+
+
+dpmgr : dpmgr.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ dpmgr.o $(LDFLAGS)
+
+
+dptest : dptest.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ dptest.o $(LDFLAGS)
+
+
+dptsv : dptsv.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ dptsv.o $(LDFLAGS)
+
+
+crmgr : crmgr.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ crmgr.o $(LDFLAGS)
+
+
+crtest : crtest.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ crtest.o $(LDFLAGS)
+
+
+crtsv : crtsv.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ crtsv.o $(LDFLAGS)
+
+
+rlmgr : rlmgr.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ rlmgr.o $(LDFLAGS)
+
+
+rltest : rltest.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ rltest.o $(LDFLAGS)
+
+
+hvmgr : hvmgr.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ hvmgr.o $(LDFLAGS)
+
+
+hvtest : hvtest.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ hvtest.o $(LDFLAGS)
+
+
+cbtest : cbtest.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ cbtest.o $(LDFLAGS)
+
+
+cbcodec : cbcodec.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ cbcodec.o $(LDFLAGS)
+
+
+vlmgr : vlmgr.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ vlmgr.o $(LDFLAGS)
+
+
+vltest : vltest.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ vltest.o $(LDFLAGS)
+
+
+vltsv : vltsv.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ vltsv.o $(LDFLAGS)
+
+
+odmgr : odmgr.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ odmgr.o $(LDFLAGS)
+
+
+odtest : odtest.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ odtest.o $(LDFLAGS)
+
+
+odidx : odidx.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ odidx.o $(LDFLAGS)
+
+
+qmttest : qmttest.o $(MYLIBS)
+	$(LDENV) $(CC) $(CFLAGS) -o $@ qmttest.o $(LDFLAGS)
+
+
+depot.o : depot.h myconf.h
+
+curia.o : depot.h curia.h myconf.h
+
+relic.o : depot.h relic.h myconf.h
+
+hovel.o : depot.h curia.h hovel.h myconf.h
+
+cabin.o : cabin.h myconf.h
+
+villa.o : depot.h cabin.h villa.h myconf.h
+
+vista.o : depot.h curia.h cabin.h villa.h vista.h myconf.h villa.c
+
+odeum.o : depot.h curia.h cabin.h villa.h myconf.h
+
+myconf.o : myconf.h
+
+dpmgr.o dptest.o dptsv.o : depot.h cabin.h
+
+crmgr.o crtest.o crtsv.o : depot.h curia.h cabin.h
+
+rlmgr.o rltest.o : depot.h relic.h cabin.h
+
+hvmgr.o hvtest.o : depot.h curia.h hovel.h cabin.h
+
+cbtest.o cbcodec.o : cabin.h
+
+vlmgr.o vltest.o vltsv.o : depot.h cabin.h villa.h
+
+odmgr.o odtest.o odidx.o : depot.h curia.h cabin.h villa.h odeum.h
+
+
+
+# END OF FILE

Added: box/trunk/qdbm/NEWS
===================================================================
--- box/trunk/qdbm/NEWS	                        (rev 0)
+++ box/trunk/qdbm/NEWS	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,43 @@
+== Thu, 08 Sep 2005 13:13:58 +0900 ==
+
+Compressing options of ZLIB was changed.  If you use villa with the option
+`VL_OZCOMP', databases of the earlier versions are not compatible with the
+current version of QDBM.  To convert the old database to new format,
+export endian independent data by "vlmgr exportdb" with the old version,
+and then import it by "vlmgr importdb" with the latest version.
+
+
+
+== Wed, 10 Mar 2004 23:24:24 +0900 ==
+
+API of B+ tree was changed a bit.  Even if you build QDBM with ZLIB
+enabled, records are not compressed.  Instead of it, the function `vlopen'
+has the option `VL_OZCOMP'.  If it is specified, records are compressed.
+So, you can switch whether to compress records or not, on runtime.
+
+Users who have used ZLIB feature should modify their source codes to
+specify that option.
+
+
+
+== Wed, 10 Dec 2003 09:24:12 +0900 ==
+
+The database format was changed with QDBM 1.7.13.  Newer versions do not
+have backward compatibility to old format.  You can convert old databases
+with the command `fmtcnv031127' in the sub directory `lab'.  To build it,
+perform the following steps.
+
+  cd lab
+  make fmtcnv031127
+
+About usage of it, see the file `README' in `lab'.  Typically, to convert
+a Depot database named as `old' and create a database named as `new',
+perform the following step.
+
+  ./fmtcnv031127 < old > new
+
+I'm sorry for bothering you.
+
+
+
+== END OF FILE ==

Added: box/trunk/qdbm/README
===================================================================
--- box/trunk/qdbm/README	                        (rev 0)
+++ box/trunk/qdbm/README	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,50 @@
+================================================================
+ QDBM: Quick Database Manager
+ Copyright (C) 2000-2007 Mikio Hirabayashi
+================================================================
+
+
+Please read the following documents with a WWW browser.
+How to install QDBM is explained in the specification.
+
+  README         - this file
+  COPYING        - license
+  ChangeLog      - history of enhancement
+  NEWS           - news for users
+  THANKS         - list of contributors
+  spex.html      - specification
+  spex-ja.html   - specification in Japanese
+
+
+Contents of the directory tree is below.
+
+  ./             - sources of QDBM
+  ./plus/        - API for C++ (read `./plus/xspex.html')
+  ./java/        - API for Java (read `./java/jspex.html')
+  ./perl/        - API for Perl (read `./perl/plspex.html')
+  ./ruby/        - API for Ruby (read `./ruby/rbspex.html')
+  ./cgi/         - CGI scripts (read `./cgi/cgispex.html')
+  ./man1/        - manual pages for commands
+  ./man3/        - manual pages for C API
+  ./lab/         - for test and experiment
+  ./bros/        - for comparison with other database managers
+  ./misc/        - miscellaneous files
+
+
+QDBM is released under the terms of the GNU Lesser General Public
+License.  See the file `COPYING' for details.
+
+QDBM was written by Mikio Hirabayashi.  You can contact the author
+by e-mail to `mikio at users.sourceforge.net'.  However, as for
+topics which can be shared among other users, pleae send it to
+the mailing list. To join the mailing list, refer to the following
+URL.
+
+  http://lists.sourceforge.net/lists/listinfo/qdbm-users
+
+
+Thanks.
+
+
+
+== END OF FILE ==

Added: box/trunk/qdbm/RISCmakefile
===================================================================
--- box/trunk/qdbm/RISCmakefile	                        (rev 0)
+++ box/trunk/qdbm/RISCmakefile	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,140 @@
+# Makefile for the RISC OS version of QDBM
+
+
+# Define which compiler to use:
+
+CC = cc
+#CC = gcc
+
+
+#########################################
+# DO NOT EDIT ANYTHING BELOW THIS LINE! #
+#########################################
+
+ifeq (${CC},cc)
+CC = cc
+LD = link
+AR = libfile
+DEPEND = -depend !Depend
+CC_FLAGS = -Wdp -throwback -Otime -I@,Unix: -JUnix
+UNIXLIB = Unix:o.UnixLib
+else
+ifeq (${CC},gcc)
+CC = gcc
+LD = gcc
+AR = ar
+CC_FLAGS = -mthrowback -O3 -I.
+else
+# No other compiler supported!
+endif
+endif
+
+QDBM_OBJS = o.depot o.curia o.relic o.hovel o.cabin o.villa o.vista o.odeum o.myconf
+
+.INIT :
+	@cdir o
+
+## Rule Patterns ##
+
+.SUFFIXES : .c .o
+
+.c.o :
+	$(CC) $(CC_FLAGS) $(DEPEND) -c -o $@ $<
+
+# Static dependencies:
+
+all : libqdbm testcases managers converters
+
+libqdbm : $(QDBM_OBJS)
+	$(AR) $(AR_FLAGS) -c -o libqdbm $(QDBM_OBJS)
+
+testcases : dptest crtest rltest hvtest cbtest vltest odtest
+	create testcases
+
+managers : dpmgr crmgr rlmgr hvmgr vlmgr odmgr
+	create managers
+
+converters : dptsv crtsv cbcodec vltsv odidx
+	create converters
+
+dptest : o.dptest libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+crtest : o.crtest libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+rltest : o.rltest libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+hvtest : o.hvtest libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+cbtest : o.cbtest libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+vltest : o.vltest libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+odtest : o.odtest libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+dpmgr : o.dpmgr libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+crmgr : o.crmgr libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+rlmgr : o.rlmgr libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+hvmgr : o.hvmgr libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+vlmgr : o.vlmgr libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+odmgr : o.odmgr libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+dptsv : o.dptsv libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+crtsv : o.crtsv libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+cbcodec : o.cbcodec libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+vltsv : o.vltsv libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+odidx : o.odidx libqdbm
+	$(LD) $(LD_FLAGS) -o $@ o.$* libqdbm $(UNIXLIB)
+
+clean:
+	-ifthere libqdbm then wipe libqdbm ~CFR~V
+	-ifthere dptest then wipe dptest ~CFR~V
+	-ifthere crtest then wipe crtest ~CFR~V
+	-ifthere rltest then wipe rltest ~CFR~V
+	-ifthere hvtest then wipe hvtest ~CFR~V
+	-ifthere cbtest then wipe cbtest ~CFR~V
+	-ifthere vltest then wipe vltest ~CFR~V
+	-ifthere odtest then wipe odtest ~CFR~V
+	-ifthere dpmgr then wipe dpmgr ~CFR~V
+	-ifthere crmgr then wipe crmgr ~CFR~V
+	-ifthere rlmgr then wipe rlmgr ~CFR~V
+	-ifthere hvmgr then wipe hvmgr ~CFR~V
+	-ifthere cbmgr then wipe cbmgr ~CFR~V
+	-ifthere vlmgr then wipe vlmgr ~CFR~V
+	-ifthere odmgr then wipe odmgr ~CFR~V
+	-ifthere dptsv then wipe dptsv ~CFR~V
+	-ifthere crtsv then wipe crtsv ~CFR~V
+	-ifthere cbcodec then wipe cbcodec ~CFR~V
+	-ifthere vltsv then wipe vltsv ~CFR~V
+	-ifthere odidx then wipe odidx ~CFR~V
+	-ifthere testcases then wipe testcases ~CFR~V
+	-ifthere managers then wipe managers ~CFR~V
+	-ifthere converters then wipe converters ~CFR~V
+	-ifthere o.* then wipe o.* ~CFR~V
+
+# Dynamic dependencies:

Added: box/trunk/qdbm/THANKS
===================================================================
--- box/trunk/qdbm/THANKS	                        (rev 0)
+++ box/trunk/qdbm/THANKS	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,45 @@
+================================================================
+ Thanks to all of the following for their valuable suggestions
+ or contributions.
+================================================================
+
+
+Kang-Jin Lee
+  - suggestions about the GDBM-compatible API
+  - contributions about Makefile
+
+Pat Podenski
+  - suggestions about porting to Mac OS X, Solaris, and HP-UX 
+
+BERO
+  - contributions about supporting MinGW
+
+Stefan Bellon
+  - contributions about porting to RISC OS
+
+Donald Gobin
+  - contributions about supporting Visual C++
+
+Emanuel Dejanu
+  - contributions about supporting Visual C++
+
+Keith Bostic
+  - suggestions about the performance test suite
+
+William Lachance
+  - contributions about RPM spec file
+
+Zed A. Shaw
+  - contributions about a text analyzer in Odeum
+  - contributions about a query language in Odeum
+
+Chris Bilderback
+  - contributions about cursor functions in Villa.
+
+Fumitoshi Ukai
+  - contributions of troff manuals
+  - making Debian packages
+
+
+
+== END OF FILE ==

Added: box/trunk/qdbm/VCmakefile
===================================================================
--- box/trunk/qdbm/VCmakefile	                        (rev 0)
+++ box/trunk/qdbm/VCmakefile	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,248 @@
+# Makefile to build QDBM using Microsoft Visual C++
+
+
+
+#================================================================
+# Setting variables
+#================================================================
+
+
+# VC++ directory
+VCPATH = C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7
+
+# User options
+YOUR_CLFLAGS =
+YOUR_LIBFLAGS =
+YOUR_LINKFLAGS=
+
+# Configurations
+!IF "$(CFG)" == "ld"
+!MESSAGE Build using static debug configuration
+BASE_FLAGS = /MLd /W3 /ZI /Od /FD /GZ /Fo"$(OUTDIR)\\" /Fd"$(OUTDIR)\\"
+BASE_DEFS = /D_DEBUG /D__DEBUG__
+OUTDIR = .\tmp_ld
+LIB_APPEND = _ld
+EXE_APPEND = _ld
+!ELSEIF "$(CFG)" == "l"
+!MESSAGE Build using static release configuration
+BASE_DEFS = /DNDEBUG
+BASE_FLAGS = /ML /W3 /O2 /FD /Fo"$(OUTDIR)\\" /Fd"$(OUTDIR)\\"
+OUTDIR = .\tmp_l
+LIB_APPEND = _l
+EXE_APPEND = _l
+!ELSEIF "$(CFG)" == "td"
+!MESSAGE Build using static threaded debug configuration
+BASE_FLAGS = /MTd /W3 /ZI /Od /FD /GZ /Fo"$(OUTDIR)\\" /Fd"$(OUTDIR)\\"
+BASE_DEFS = /D_DEBUG /D__DEBUG__
+OUTDIR = .\tmp_td
+LIB_APPEND = _td
+EXE_APPEND = _td
+!ELSEIF "$(CFG)" == "t"
+!MESSAGE Build using static threaded release configuration
+BASE_DEFS = /DNDEBUG
+BASE_FLAGS = /MT /W3 /O2 /FD /Fo"$(OUTDIR)\\" /Fd"$(OUTDIR)\\"
+OUTDIR = .\tmp_t
+LIB_APPEND = _t
+EXE_APPEND = _t
+!ELSEIF "$(CFG)" == "dd"
+!MESSAGE Build using dynamic threaded debug configuration
+BASE_FLAGS = /MDd /W3 /ZI /Od /FD /GZ /Fo"$(OUTDIR)\\" /Fd"$(OUTDIR)\\"
+BASE_DEFS = /D_DEBUG /D__DEBUG__
+OUTDIR = .\tmp_dd
+LIB_APPEND = _dd
+EXE_APPEND = _dd
+!ELSE
+!MESSAGE Build using dynamic threaded release configuration
+BASE_DEFS = /DNDEBUG
+BASE_FLAGS = /MD /W3 /O2 /FD /Fo"$(OUTDIR)\\" /Fd"$(OUTDIR)\\"
+OUTDIR = .\tmp
+LIB_APPEND =
+EXE_APPEND =
+!ENDIF
+
+# Building binaries
+CLFLAGS = /I "$(VCPATH)\Include" /I "$(VCPATH)\PlatformSDK\Include" /I "." \
+  /nologo $(YOUR_CLFLAGS) $(BASE_FLAGS) $(BASE_DEFS) /D_CRT_SECURE_NO_DEPRECATE=1
+LIBFLAGS = /libpath:"$(VCPATH)\lib" /libpath:"$(VCPATH)\PlatformSDK\Lib" /libpath:"." \
+  /nologo $(YOUR_LIBFLAGS)
+LINKFLAGS = /libpath:"$(VCPATH)\lib" /libpath:"$(VCPATH)\PlatformSDK\Lib" /libpath:"." \
+  /nologo $(YOUR_LINKFLAGS)
+
+# Targets
+MYLIBS = qdbm$(LIB_APPEND).dll qdbm$(LIB_APPEND).lib qdbm$(LIB_APPEND)_static.lib
+LIBOBJS = $(OUTDIR)\depot.obj $(OUTDIR)\curia.obj $(OUTDIR)\relic.obj \
+  $(OUTDIR)\hovel.obj $(OUTDIR)\cabin.obj $(OUTDIR)\villa.obj \
+  $(OUTDIR)\vista.obj $(OUTDIR)\odeum.obj $(OUTDIR)\myconf.obj
+MYBINS = dpmgr$(EXE_APPEND).exe dptest$(EXE_APPEND).exe dptsv$(EXE_APPEND).exe \
+  crmgr$(EXE_APPEND).exe crtest$(EXE_APPEND).exe crtsv$(EXE_APPEND).exe \
+  rlmgr$(EXE_APPEND).exe rltest$(EXE_APPEND).exe hvmgr$(EXE_APPEND).exe \
+  hvtest$(EXE_APPEND).exe cbtest$(EXE_APPEND).exe cbcodec$(EXE_APPEND).exe \
+  vlmgr$(EXE_APPEND).exe vltest$(EXE_APPEND).exe vltsv$(EXE_APPEND).exe \
+  odmgr$(EXE_APPEND).exe odtest$(EXE_APPEND).exe odidx$(EXE_APPEND).exe
+
+
+
+#================================================================
+# Suffix rules
+#================================================================
+
+
+.SUFFIXES :
+.SUFFIXES : .c .obj
+
+.c{$(OUTDIR)}.obj :
+	cl /c $(CLFLAGS) $<
+
+.c.obj:
+	cl /c $(CLFLAGS) $<
+
+
+
+#================================================================
+# Actions
+#================================================================
+
+
+all : $(OUTDIR) $(MYLIBS) $(MYBINS)
+
+
+allcfg:
+	nmake /NOLOGO /f VCmakefile CFG=ld
+	nmake /NOLOGO /f VCmakefile CFG=l
+	nmake /NOLOGO /f VCmakefile CFG=td
+	nmake /NOLOGO /f VCmakefile CFG=t
+	nmake /NOLOGO /f VCmakefile CFG=dd
+	nmake /NOLOGO /f VCmakefile
+
+
+clean :
+	-rd tmp_ld /S /Q > NUL: 2>&1
+	-rd tmp_l /S /Q > NUL: 2>&1
+	-rd tmp_td /S /Q > NUL: 2>&1
+	-rd tmp_t /S /Q > NUL: 2>&1
+	-rd tmp_dd /S /Q > NUL: 2>&1
+	-rd tmp /S /Q > NUL: 2>&1
+	-del *.obj *.lib *.dll *.exp *.exe casket /F /Q > NUL: 2>&1
+
+
+"$(OUTDIR)" :
+	if not exist "$(OUTDIR)/" mkdir "$(OUTDIR)"
+
+
+
+#================================================================
+# Building binaries
+#================================================================
+
+
+qdbm$(LIB_APPEND).dll : $(LIBOBJS) qdbm.def
+	link /DLL /DEF:qdbm.def $(LINKFLAGS) /OUT:$@ /IMPLIB:qdbm$(LIB_APPEND).lib $(LIBOBJS)
+
+
+qdbm$(LIB_APPEND).lib : qdbm$(LIB_APPEND).dll
+
+
+qdbm$(LIB_APPEND)_static.lib : $(LIBOBJS)
+	lib $(LIBFLAGS) /OUT:$@ $(LIBOBJS)
+
+
+dpmgr$(EXE_APPEND).exe : $(OUTDIR)\dpmgr.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\dpmgr.obj qdbm$(LIB_APPEND).lib
+
+
+dptest$(EXE_APPEND).exe : $(OUTDIR)\dptest.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\dptest.obj qdbm$(LIB_APPEND).lib
+
+
+dptsv$(EXE_APPEND).exe : $(OUTDIR)\dptsv.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\dptsv.obj qdbm$(LIB_APPEND).lib
+
+
+crmgr$(EXE_APPEND).exe : $(OUTDIR)\crmgr.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\crmgr.obj qdbm$(LIB_APPEND).lib
+
+
+crtest$(EXE_APPEND).exe : $(OUTDIR)\crtest.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\crtest.obj qdbm$(LIB_APPEND).lib
+
+
+crtsv$(EXE_APPEND).exe : $(OUTDIR)\crtsv.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\crtsv.obj qdbm$(LIB_APPEND).lib
+
+
+rlmgr$(EXE_APPEND).exe : $(OUTDIR)\rlmgr.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\rlmgr.obj qdbm$(LIB_APPEND).lib
+
+
+rltest$(EXE_APPEND).exe : $(OUTDIR)\rltest.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\rltest.obj qdbm$(LIB_APPEND).lib
+
+
+hvmgr$(EXE_APPEND).exe : $(OUTDIR)\hvmgr.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\hvmgr.obj qdbm$(LIB_APPEND).lib
+
+
+hvtest$(EXE_APPEND).exe : $(OUTDIR)\hvtest.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\hvtest.obj qdbm$(LIB_APPEND).lib
+
+
+cbtest$(EXE_APPEND).exe : $(OUTDIR)\cbtest.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\cbtest.obj qdbm$(LIB_APPEND).lib
+
+
+cbcodec$(EXE_APPEND).exe : $(OUTDIR)\cbcodec.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\cbcodec.obj qdbm$(LIB_APPEND).lib
+
+
+vlmgr$(EXE_APPEND).exe : $(OUTDIR)\vlmgr.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\vlmgr.obj qdbm$(LIB_APPEND).lib
+
+
+vltest$(EXE_APPEND).exe : $(OUTDIR)\vltest.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\vltest.obj qdbm$(LIB_APPEND).lib
+
+
+vltsv$(EXE_APPEND).exe : $(OUTDIR)\vltsv.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\vltsv.obj qdbm$(LIB_APPEND).lib
+
+
+odmgr$(EXE_APPEND).exe : $(OUTDIR)\odmgr.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\odmgr.obj qdbm$(LIB_APPEND).lib
+
+
+odtest$(EXE_APPEND).exe : $(OUTDIR)\odtest.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\odtest.obj qdbm$(LIB_APPEND).lib
+
+
+odidx$(EXE_APPEND).exe : $(OUTDIR)\odidx.obj qdbm$(LIB_APPEND).lib
+	link $(LINKFLAGS) /OUT:$@ $(OUTDIR)\odidx.obj qdbm$(LIB_APPEND).lib
+
+
+$(OUTDIR)\depot.obj $(OUTDIR)\dpmgr.obj $(OUTDIR)\dptest.obj $(OUTDIR)\dptsv.obj : \
+  depot.h myconf.h
+
+$(OUTDIR)\curia.obj $(OUTDIR)\crmgr.obj $(OUTDIR)\crtest.obj $(OUTDIR)\crtsv.obj : \
+  curia.h depot.h myconf.h
+
+$(OUTDIR)\relic.obj $(OUTDIR)\rlmgr.obj $(OUTDIR)\rltest.obj : \
+  relic.h depot.h myconf.h
+
+$(OUTDIR)\hovel.obj $(OUTDIR)\hvmgr.obj $(OUTDIR)\hvtest.obj : \
+  hovel.h depot.h curia.h myconf.h
+
+$(OUTDIR)\cabin.obj $(OUTDIR)\cbtest.obj $(OUTDIR)\cbcodec.obj : \
+  cabin.h myconf.h
+
+$(OUTDIR)\villa.obj $(OUTDIR)\vlmgr.obj $(OUTDIR)\vltest.obj $(OUTDIR)\vltsv.obj : \
+  villa.h depot.h cabin.h myconf.h
+
+$(OUTDIR)\vista.obj : vista.h villa.h depot.h curia.h cabin.h myconf.h
+
+$(OUTDIR)\odeum.obj $(OUTDIR)\odmgr.obj $(OUTDIR)\odtest.obj $(OUTDIR)\odidx.obj : \
+  odeum.h depot.h curia.h cabin.h villa.h myconf.h
+
+$(OUTDIR)\myconf.obj : myconf.h
+
+
+
+# END OF FILE

Added: box/trunk/qdbm/cabin.c
===================================================================
--- box/trunk/qdbm/cabin.c	                        (rev 0)
+++ box/trunk/qdbm/cabin.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,3529 @@
+/*************************************************************************************************
+ * Implementation of Cabin
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#define QDBM_INTERNAL  1
+
+#include "cabin.h"
+#include "myconf.h"
+
+#define CB_GCUNIT      64                /* allocation unit size of a buffer in gc */
+#define CB_SPBUFSIZ    32                /* size of a buffer for sprintf */
+#define CB_SPMAXWIDTH  128               /* max width of a column for sprintf */
+#define CB_MAPPBNUM    251               /* bucket size of a petit map handle */
+#define CB_MAPCSUNIT   52                /* small allocation unit size of map concatenation */
+#define CB_MAPCBUNIT   252               /* big allocation unit size of map concatenation */
+#define CB_MSGBUFSIZ   256               /* size of a buffer for log message */
+#define CB_IOBUFSIZ    8192              /* size of an I/O buffer */
+#define CB_FILEMODE    00644             /* permission of a creating file */
+#define CB_NUMBUFSIZ   32                /* size of a buffer for a number */
+#define CB_ENCBUFSIZ   32                /* size of a buffer for encoding name */
+#define CB_DATEBUFSIZ  64                /* size of a buffer for date expression */
+#define CB_VNUMBUFSIZ  8                 /* size of a buffer for variable length number */
+
+/* set a buffer for a variable length number */
+#define CB_SETVNUMBUF(CB_len, CB_buf, CB_num) \
+  do { \
+    int _CB_num; \
+    _CB_num = (CB_num); \
+    if(_CB_num == 0){ \
+      ((signed char *)(CB_buf))[0] = 0; \
+      (CB_len) = 1; \
+    } else { \
+      (CB_len) = 0; \
+      while(_CB_num > 0){ \
+        int _CB_rem = _CB_num & 0x7f; \
+        _CB_num >>= 7; \
+        if(_CB_num > 0){ \
+          ((signed char *)(CB_buf))[(CB_len)] = -_CB_rem - 1; \
+        } else { \
+          ((signed char *)(CB_buf))[(CB_len)] = _CB_rem; \
+        } \
+        (CB_len)++; \
+      } \
+    } \
+  } while(FALSE)
+
+/* read a variable length buffer */
+#define CB_READVNUMBUF(CB_buf, CB_size, CB_num, CB_step) \
+  do { \
+    int _CB_i, _CB_base; \
+    CB_num = 0; \
+    _CB_base = 1; \
+    if((size) < 2){ \
+      CB_num = ((signed char *)(CB_buf))[0]; \
+      (CB_step) = 1; \
+    } else { \
+      for(_CB_i = 0; _CB_i < (size); _CB_i++){ \
+        if(((signed char *)(CB_buf))[_CB_i] >= 0){ \
+          CB_num += ((signed char *)(CB_buf))[_CB_i] * _CB_base; \
+          break; \
+        } \
+        CB_num += _CB_base * (((signed char *)(CB_buf))[_CB_i] + 1) * -1; \
+        _CB_base *= 128; \
+      } \
+      (CB_step) = _CB_i + 1; \
+    } \
+  } while(FALSE)
+
+/* get the first hash value */
+#define CB_FIRSTHASH(CB_res, CB_kbuf, CB_ksiz) \
+  do { \
+    const unsigned char *_CB_p; \
+    int _CB_ksiz; \
+    _CB_p = (const unsigned char *)(CB_kbuf); \
+    _CB_ksiz = CB_ksiz; \
+    for((CB_res) = 19780211; _CB_ksiz--;){ \
+      (CB_res) = (CB_res) * 37 + *(_CB_p)++; \
+    } \
+    (CB_res) &= INT_MAX; \
+  } while(FALSE)
+
+/* get the second hash value */
+#define CB_SECONDHASH(CB_res, CB_kbuf, CB_ksiz) \
+  do { \
+    const unsigned char *_CB_p; \
+    int _CB_ksiz; \
+    _CB_p = (const unsigned char *)(CB_kbuf) + CB_ksiz - 1; \
+    _CB_ksiz = CB_ksiz; \
+    for((CB_res) = 0x13579bdf; _CB_ksiz--;){ \
+      (CB_res) = (CB_res) * 31 + *(_CB_p)--; \
+    } \
+    (CB_res) &= INT_MAX; \
+  } while(FALSE)
+
+
+/* private function prototypes */
+static void cbggchandler(void);
+static void cbggckeeper(void *ptr, void (*func)(void *));
+static void cbqsortsub(char *bp, int nmemb, int size, char *pswap, char *vswap,
+                       int(*compar)(const void *, const void *));
+static int cblistelemcmp(const void *a, const void *b);
+static int cbkeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz);
+
+
+
+/*************************************************************************************************
+ * public objects
+ *************************************************************************************************/
+
+
+/* Call back function for handling a fatal error. */
+void (*cbfatalfunc)(const char *message) = NULL;
+
+
+/* Allocate a region on memory. */
+void *cbmalloc(size_t size){
+  char *p;
+  assert(size > 0 && size < INT_MAX);
+  if(!(p = malloc(size))) cbmyfatal("out of memory");
+  return p;
+}
+
+
+/* Re-allocate a region on memory. */
+void *cbrealloc(void *ptr, size_t size){
+  char *p;
+  assert(size > 0);
+  if(!(p = realloc(ptr, size))) cbmyfatal("out of memory");
+  return p;
+}
+
+
+/* Duplicate a region on memory. */
+char *cbmemdup(const char *ptr, int size){
+  char *p;
+  assert(ptr);
+  if(size < 0) size = strlen(ptr);
+  CB_MALLOC(p, size + 1);
+  memcpy(p, ptr, size);
+  p[size] = '\0';
+  return p;
+}
+
+
+/* Free a region on memory. */
+void cbfree(void *ptr){
+  free(ptr);
+}
+
+
+/* Register the pointer or handle of an object to the global garbage collector. */
+void cbglobalgc(void *ptr, void (*func)(void *)){
+  assert(ptr && func);
+  cbggckeeper(ptr, func);
+}
+
+
+/* Exercise the global garbage collector explicitly. */
+void cbggcsweep(void){
+  cbggckeeper(NULL, NULL);
+}
+
+
+/* Check availability of allocation of the virtual memory. */
+int cbvmemavail(size_t size){
+  assert(size >= 0);
+  return _qdbm_vmemavail(size);
+}
+
+
+/* Sort an array using insert sort. */
+void cbisort(void *base, int nmemb, int size, int(*compar)(const void *, const void *)){
+  char *bp, *swap;
+  int i, j;
+  assert(base && nmemb >= 0 && size > 0 && compar);
+  bp = (char *)base;
+  CB_MALLOC(swap, size);
+  for(i = 1; i < nmemb; i++){
+    if(compar(bp + (i - 1) * size, bp + i * size) > 0){
+      memcpy(swap, bp + i * size, size);
+      for(j = i; j > 0; j--){
+        if(compar(bp + (j - 1) * size, swap) < 0) break;
+        memcpy(bp + j * size, bp + (j - 1) * size, size);
+      }
+      memcpy(bp + j * size, swap, size);
+    }
+  }
+  free(swap);
+}
+
+
+/* Sort an array using shell sort. */
+void cbssort(void *base, int nmemb, int size, int(*compar)(const void *, const void *)){
+  char *bp, *swap;
+  int step, bottom, i, j;
+  assert(base && nmemb >= 0 && size > 0 && compar);
+  bp = (char *)base;
+  CB_MALLOC(swap, size);
+  for(step = (nmemb - 1) / 3; step >= 0; step = (step - 1) / 3){
+    if(step < 5) step = 1;
+    for(bottom = 0; bottom < step; bottom++){
+      for(i = bottom + step; i < nmemb; i += step){
+        if(compar(bp + (i - step) * size, bp + i * size) > 0){
+          memcpy(swap, bp + i * size, size);
+          for(j = i; j > step - 1; j -= step){
+            if(compar(bp + (j - step) * size, swap) < 0) break;
+            memcpy(bp + j * size, bp + (j - step) * size, size);
+          }
+          memcpy(bp + j * size, swap, size);
+        }
+      }
+    }
+    if(step < 2) break;
+  }
+  free(swap);
+}
+
+
+/* Sort an array using heap sort. */
+void cbhsort(void *base, int nmemb, int size, int(*compar)(const void *, const void *)){
+  char *bp, *swap;
+  int top, bottom, mybot, i;
+  assert(base && nmemb >= 0 && size > 0 && compar);
+  bp = (char *)base;
+  nmemb--;
+  bottom = nmemb / 2 + 1;
+  top = nmemb;
+  CB_MALLOC(swap, size);
+  while(bottom > 0){
+    bottom--;
+    mybot = bottom;
+    i = 2 * mybot;
+    while(i <= top) {
+      if(i < top && compar(bp + (i + 1) * size, bp + i * size) > 0) i++;
+      if(compar(bp + mybot * size, bp + i * size) >= 0) break;
+      memcpy(swap, bp + mybot * size, size);
+      memcpy(bp + mybot * size, bp + i * size, size);
+      memcpy(bp + i * size, swap, size);
+      mybot = i;
+      i = 2 * mybot;
+    }
+  }
+  while(top > 0){
+    memcpy(swap, bp, size);
+    memcpy(bp, bp + top * size, size);
+    memcpy(bp + top * size, swap, size);
+    top--;
+    mybot = bottom;
+    i = 2 * mybot;
+    while(i <= top){
+      if(i < top && compar(bp + (i + 1) * size, bp + i * size) > 0) i++;
+      if(compar(bp + mybot * size, bp + i * size) >= 0) break;
+      memcpy(swap, bp + mybot * size, size);
+      memcpy(bp + mybot * size, bp + i * size, size);
+      memcpy(bp + i * size, swap, size);
+      mybot = i;
+      i = 2 * mybot;
+    }
+  }
+  free(swap);
+}
+
+
+/* Sort an array using quick sort. */
+void cbqsort(void *base, int nmemb, int size, int(*compar)(const void *, const void *)){
+  char *pswap, *vswap;
+  assert(base && nmemb >= 0 && size > 0 && compar);
+  CB_MALLOC(pswap, size);
+  CB_MALLOC(vswap, size);
+  cbqsortsub(base, nmemb, size, pswap, vswap, compar);
+  free(vswap);
+  free(pswap);
+}
+
+
+/* Compare two strings with case insensitive evaluation. */
+int cbstricmp(const char *astr, const char *bstr){
+  int ac, bc;
+  assert(astr && bstr);
+  while(*astr != '\0'){
+    if(*bstr == '\0') return 1;
+    ac = (*astr >= 'A' && *astr <= 'Z') ? *astr + ('a' - 'A') : *(unsigned char *)astr;
+    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. */
+int cbstrfwmatch(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. */
+int cbstrfwimatch(const char *str, const char *key){
+  int sc, kc;
+  assert(str && key);
+  while(*key != '\0'){
+    if(*str == '\0') return FALSE;
+    sc = *str;
+    if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
+    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. */
+int cbstrbwmatch(const char *str, const char *key){
+  int slen, klen, i;
+  assert(str && key);
+  slen = strlen(str);
+  klen = strlen(key);
+  for(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. */
+int cbstrbwimatch(const char *str, const char *key){
+  int slen, klen, i, sc, kc;
+  assert(str && key);
+  slen = strlen(str);
+  klen = strlen(key);
+  for(i = 1; i <= klen; i++){
+    if(i > slen) return FALSE;
+    sc = str[slen-i];
+    if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
+    kc = key[klen-i];
+    if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
+    if(sc != kc) return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Locate a substring in a string using KMP method. */
+char *cbstrstrkmp(const char *haystack, const char *needle){
+  int i, j, hlen, nlen;
+  signed char tbl[0x100];
+  assert(haystack && needle);
+  nlen = strlen(needle);
+  if(nlen >= 0x100) return strstr(haystack, needle);
+  tbl[0] = -1;
+  i = 0;
+  j = -1;
+  while(i < nlen){
+    while((j >= 0) && (needle[i] != needle[j])){
+      j = tbl[j];
+    }
+    i++;
+    j++;
+    tbl[i] = j;
+  }
+  hlen = strlen(haystack);
+  i = 0;
+  j = 0;
+  while(i < hlen && j < nlen){
+    while((j >= 0) && (haystack[i] != needle[j])){
+      j = tbl[j];
+    }
+    i++;
+    j++;
+  }
+  if(j == nlen) return (char *)(haystack + i - nlen);
+  return NULL;
+}
+
+
+/* Locate a substring in a string using BM method. */
+char *cbstrstrbm(const char *haystack, const char *needle){
+  const unsigned char *rp;
+  const char *ep;
+  unsigned char tbl[0x100];
+  int i, j, nlen, len, idx;
+  assert(haystack && needle);
+  nlen = strlen(needle);
+  if(nlen < 3 || nlen >= 0x100) return strstr(haystack, needle);
+  for(i = 0; i < 0x100; i++){
+    tbl[i] = nlen;
+  }
+  len = nlen;
+  rp = (const unsigned char *)needle;
+  while(len > 0){
+    tbl[*rp++] = --len;
+  }
+  nlen--;
+  ep = haystack + strlen(haystack) - nlen;
+  while(haystack < ep){
+    for(i = nlen; haystack[i] == needle[i]; i--){
+      if(i == 0) return (char *)haystack;
+    }
+    idx = ((unsigned char *)haystack)[i];
+    j = tbl[idx] - nlen + i;
+    haystack += j > 0 ? j : 2;
+  }
+  return NULL;
+}
+
+
+/* Convert the letters of a string to upper case. */
+char *cbstrtoupper(char *str){
+  int i;
+  assert(str);
+  for(i = 0; str[i] != '\0'; i++){
+    if(str[i] >= 'a' && str[i] <= 'z') str[i] -= 'a' - 'A';
+  }
+  return str;
+}
+
+
+/* Convert the letters of a string to lower case. */
+char *cbstrtolower(char *str){
+  int i;
+  assert(str);
+  for(i = 0; str[i] != '\0'; i++){
+    if(str[i] >= 'A' && str[i] <= 'Z') str[i] += 'a' - 'A';
+  }
+  return str;
+}
+
+
+/* Cut space characters at head or tail of a string. */
+char *cbstrtrim(char *str){
+  char *wp;
+  int i, head;
+  assert(str);
+  wp = str;
+  head = TRUE;
+  for(i = 0; str[i] != '\0'; i++){
+    if((str[i] >= 0x07 && str[i] <= 0x0d) || str[i] == 0x20){
+      if(!head) *(wp++) = str[i];
+    } else {
+      *(wp++) = str[i];
+      head = FALSE;
+    }
+  }
+  *wp = '\0';
+  while(wp > str && ((wp[-1] >= 0x07 && wp[-1] <= 0x0d) || wp[-1] == 0x20)){
+    *(--wp) = '\0';
+  }
+  return str;
+}
+
+
+/* Squeeze space characters in a string and trim it. */
+char *cbstrsqzspc(char *str){
+  char *wp;
+  int i, spc;
+  assert(str);
+  wp = str;
+  spc = TRUE;
+  for(i = 0; str[i] != '\0'; i++){
+    if(str[i] > 0 && str[i] <= ' '){
+      if(!spc) *(wp++) = str[i];
+      spc = TRUE;
+    } else {
+      *(wp++) = str[i];
+      spc = FALSE;
+    }
+  }
+  *wp = '\0';
+  for(wp--; wp >= str; wp--){
+    if(*wp > 0 && *wp <= ' '){
+      *wp = '\0';
+    } else {
+      break;
+    }
+  }
+  return str;
+}
+
+
+/* Count the number of characters in a string of UTF-8. */
+int cbstrcountutf(const char *str){
+  const unsigned char *rp;
+  int cnt;
+  assert(str);
+  rp = (unsigned char *)str;
+  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 *cbstrcututf(char *str, int num){
+  unsigned char *wp;
+  int cnt;
+  assert(str && num >= 0);
+  wp = (unsigned char *)str;
+  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;
+}
+
+
+/* Get a datum handle. */
+CBDATUM *cbdatumopen(const char *ptr, int size){
+  CBDATUM *datum;
+  CB_MALLOC(datum, sizeof(*datum));
+  CB_MALLOC(datum->dptr, CB_DATUMUNIT);
+  datum->dptr[0] = '\0';
+  datum->dsize = 0;
+  datum->asize = CB_DATUMUNIT;
+  if(ptr) CB_DATUMCAT(datum, ptr, (size >= 0 ? size : strlen(ptr)));
+  return datum;
+}
+
+
+/* Copy a datum. */
+CBDATUM *cbdatumdup(const CBDATUM *datum){
+  assert(datum);
+  return cbdatumopen(datum->dptr, datum->dsize);
+}
+
+
+/* Free a datum handle. */
+void cbdatumclose(CBDATUM *datum){
+  assert(datum);
+  free(datum->dptr);
+  free(datum);
+}
+
+
+/* Concatenate a datum and a region. */
+void cbdatumcat(CBDATUM *datum, const char *ptr, int size){
+  assert(datum && ptr);
+  if(size < 0) size = strlen(ptr);
+  if(datum->dsize + size >= datum->asize){
+    datum->asize = datum->asize * 2 + size + 1;
+    CB_REALLOC(datum->dptr, datum->asize);
+  }
+  memcpy(datum->dptr + datum->dsize, ptr, size);
+  datum->dsize += size;
+  datum->dptr[datum->dsize] = '\0';
+}
+
+
+/* Get the pointer of the region of a datum. */
+const char *cbdatumptr(const CBDATUM *datum){
+  assert(datum);
+  return datum->dptr;
+}
+
+
+/* Get the size of the region of a datum. */
+int cbdatumsize(const CBDATUM *datum){
+  assert(datum);
+  return datum->dsize;
+}
+
+
+/* Set the size of the region of a datum. */
+void cbdatumsetsize(CBDATUM *datum, int size){
+  assert(datum && size >= 0);
+  if(size <= datum->dsize){
+    datum->dsize = size;
+    datum->dptr[size] = '\0';
+  } else {
+    if(size >= datum->asize){
+      datum->asize = datum->asize * 2 + size + 1;
+      CB_REALLOC(datum->dptr, datum->asize);
+    }
+    memset(datum->dptr + datum->dsize, 0, (size - datum->dsize) + 1);
+    datum->dsize = size;
+  }
+}
+
+
+/* Perform formatted output into a datum. */
+void cbdatumprintf(CBDATUM *datum, const char *format, ...){
+  va_list ap;
+  char *tmp, cbuf[CB_NUMBUFSIZ], tbuf[CB_NUMBUFSIZ*2];
+  unsigned char c;
+  int cblen, tlen;
+  assert(datum && format);
+  va_start(ap, format);
+  while(*format != '\0'){
+    if(*format == '%'){
+      cbuf[0] = '%';
+      cblen = 1;
+      format++;
+      while(strchr("0123456789 .+-", *format) && *format != '\0' && cblen < CB_NUMBUFSIZ - 1){
+        cbuf[cblen++] = *format;
+        format++;
+      }
+      cbuf[cblen++] = *format;
+      cbuf[cblen] = '\0';
+      switch(*format){
+      case 's':
+        tmp = va_arg(ap, char *);
+        if(!tmp) tmp = "(null)";
+        cbdatumcat(datum, tmp, -1);
+        break;
+      case 'd':
+        tlen = sprintf(tbuf, cbuf, va_arg(ap, int));
+        cbdatumcat(datum, tbuf, tlen);
+        break;
+      case 'o': case 'u': case 'x': case 'X': case 'c':
+        tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned int));
+        cbdatumcat(datum, tbuf, tlen);
+        break;
+      case 'e': case 'E': case 'f': case 'g': case 'G':
+        tlen = sprintf(tbuf, cbuf, va_arg(ap, double));
+        cbdatumcat(datum, tbuf, tlen);
+        break;
+      case '@':
+        tmp = va_arg(ap, char *);
+        if(!tmp) tmp = "(null)";
+        while(*tmp){
+          switch(*tmp){
+          case '&': cbdatumcat(datum, "&", 5); break;
+          case '<': cbdatumcat(datum, "<", 4); break;
+          case '>': cbdatumcat(datum, ">", 4); break;
+          case '"': cbdatumcat(datum, """, 6); break;
+          default:
+            if(!((*tmp >= 0 && *tmp <= 0x8) || (*tmp >= 0x0e && *tmp <= 0x1f)))
+              cbdatumcat(datum, tmp, 1);
+            break;
+          }
+          tmp++;
+        }
+        break;
+      case '?':
+        tmp = va_arg(ap, char *);
+        if(!tmp) tmp = "(null)";
+        while(*tmp){
+          c = *(unsigned char *)tmp;
+          if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+             (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.", c))){
+            cbdatumcat(datum, tmp, 1);
+          } else {
+            tlen = sprintf(tbuf, "%%%02X", c);
+            cbdatumcat(datum, tbuf, tlen);
+          }
+          tmp++;
+        }
+        break;
+      case ':':
+        tmp = va_arg(ap, char *);
+        if(!tmp) tmp = "";
+        tmp = cbmimeencode(tmp, "UTF-8", TRUE);
+        cbdatumcat(datum, tmp, -1);
+        free(tmp);
+        break;
+      case '%':
+        cbdatumcat(datum, "%", 1);
+        break;
+      }
+    } else {
+      cbdatumcat(datum, format, 1);
+    }
+    format++;
+  }
+  va_end(ap);
+}
+
+
+/* Convert a datum to an allocated region. */
+char *cbdatumtomalloc(CBDATUM *datum, int *sp){
+  char *ptr;
+  assert(datum);
+  ptr = datum->dptr;
+  if(sp) *sp = datum->dsize;
+  free(datum);
+  return ptr;
+}
+
+
+/* Get a list handle. */
+CBLIST *cblistopen(void){
+  CBLIST *list;
+  CB_MALLOC(list, sizeof(*list));
+  list->anum = CB_LISTUNIT;
+  CB_MALLOC(list->array, sizeof(list->array[0]) * list->anum);
+  list->start = 0;
+  list->num = 0;
+  return list;
+}
+
+
+/* Copy a list. */
+CBLIST *cblistdup(const CBLIST *list){
+  CBLIST *newlist;
+  int i, size;
+  const char *val;
+  assert(list);
+  CB_LISTOPEN2(newlist, CB_LISTNUM(list));
+  for(i = 0; i < CB_LISTNUM(list); i++){
+    val = CB_LISTVAL2(list, i, size);
+    CB_LISTPUSH(newlist, val, size);
+  }
+  return newlist;
+}
+
+
+/* Close a list handle. */
+void cblistclose(CBLIST *list){
+  int i, end;
+  assert(list);
+  end = list->start + list->num;
+  for(i = list->start; i < end; i++){
+    free(list->array[i].dptr);
+  }
+  free(list->array);
+  free(list);
+}
+
+
+/* Get the number of elements of a list. */
+int cblistnum(const CBLIST *list){
+  assert(list);
+  return list->num;
+}
+
+
+/* Get the pointer to the region of an element. */
+const char *cblistval(const CBLIST *list, int index, int *sp){
+  assert(list && index >= 0);
+  if(index >= list->num) return NULL;
+  index += list->start;
+  if(sp) *sp = list->array[index].dsize;
+  return list->array[index].dptr;
+}
+
+
+/* Add an element at the end of a list. */
+void cblistpush(CBLIST *list, const char *ptr, int size){
+  int index;
+  assert(list && ptr);
+  if(size < 0) size = strlen(ptr);
+  index = list->start + list->num;
+  if(index >= list->anum){
+    list->anum *= 2;
+    CB_REALLOC(list->array, list->anum * sizeof(list->array[0]));
+  }
+  CB_MALLOC(list->array[index].dptr, (size < CB_DATUMUNIT ? CB_DATUMUNIT : size) + 1);
+  memcpy(list->array[index].dptr, ptr, size);
+  list->array[index].dptr[size] = '\0';
+  list->array[index].dsize = size;
+  list->num++;
+}
+
+
+/* Remove an element of the end of a list. */
+char *cblistpop(CBLIST *list, int *sp){
+  int index;
+  assert(list);
+  if(list->num < 1) return NULL;
+  index = list->start + list->num - 1;
+  list->num--;
+  if(sp) *sp = list->array[index].dsize;
+  return list->array[index].dptr;
+}
+
+
+/* Add an element at the top of a list. */
+void cblistunshift(CBLIST *list, const char *ptr, int size){
+  int index;
+  assert(list && ptr);
+  if(size < 0) size = strlen(ptr);
+  if(list->start < 1){
+    if(list->start + list->num >= list->anum){
+      list->anum *= 2;
+      CB_REALLOC(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]));
+  }
+  index = list->start - 1;
+  CB_MALLOC(list->array[index].dptr, (size < CB_DATUMUNIT ? CB_DATUMUNIT : size) + 1);
+  memcpy(list->array[index].dptr, ptr, size);
+  list->array[index].dptr[size] = '\0';
+  list->array[index].dsize = size;
+  list->start--;
+  list->num++;
+}
+
+
+/* Remove an element of the top of a list. */
+char *cblistshift(CBLIST *list, int *sp){
+  int index;
+  assert(list);
+  if(list->num < 1) return NULL;
+  index = list->start;
+  list->start++;
+  list->num--;
+  if(sp) *sp = list->array[index].dsize;
+  return list->array[index].dptr;
+}
+
+
+/* Add an element at the specified location of a list. */
+void cblistinsert(CBLIST *list, int index, const char *ptr, int size){
+  assert(list && index >= 0);
+  if(index > list->num) return;
+  if(size < 0) size = strlen(ptr);
+  index += list->start;
+  if(list->start + list->num >= list->anum){
+    list->anum *= 2;
+    CB_REALLOC(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));
+  CB_MEMDUP(list->array[index].dptr, ptr, size);
+  list->array[index].dsize = size;
+  list->num++;
+}
+
+
+/* Remove an element at the specified location of a list. */
+char *cblistremove(CBLIST *list, int index, int *sp){
+  char *dptr;
+  assert(list && index >= 0);
+  if(index >= list->num) return NULL;
+  index += list->start;
+  dptr = list->array[index].dptr;
+  if(sp) *sp = list->array[index].dsize;
+  list->num--;
+  memmove(list->array + index, list->array + index + 1,
+          sizeof(list->array[0]) * (list->start + list->num - index));
+  return dptr;
+}
+
+
+/* Overwrite an element at the specified location of a list. */
+void cblistover(CBLIST *list, int index, const char *ptr, int size){
+  assert(list && index >= 0);
+  if(index >= list->num) return;
+  if(size < 0) size = strlen(ptr);
+  index += list->start;
+  if(size > list->array[index].dsize)
+    CB_REALLOC(list->array[index].dptr, size + 1);
+  memcpy(list->array[index].dptr, ptr, size);
+  list->array[index].dsize = size;
+  list->array[index].dptr[size] = '\0';
+}
+
+
+/* Sort elements of a list in lexical order. */
+void cblistsort(CBLIST *list){
+  assert(list);
+  qsort(list->array + list->start, list->num, sizeof(list->array[0]), cblistelemcmp);
+}
+
+
+/* Search a list for an element using liner search. */
+int cblistlsearch(const CBLIST *list, const char *ptr, int size){
+  int i, end;
+  assert(list && ptr);
+  if(size < 0) size = strlen(ptr);
+  end = list->start + list->num;
+  for(i = list->start; i < end; i++){
+    if(list->array[i].dsize == size && !memcmp(list->array[i].dptr, ptr, size))
+      return i - list->start;
+  }
+  return -1;
+}
+
+
+/* Search a list for an element using binary search. */
+int cblistbsearch(const CBLIST *list, const char *ptr, int size){
+  CBLISTDATUM key, *res;
+  assert(list && ptr);
+  if(size < 0) size = strlen(ptr);
+  CB_MEMDUP(key.dptr, ptr, size);
+  key.dsize = size;
+  res = bsearch(&key, list->array + list->start, list->num, sizeof(list->array[0]), cblistelemcmp);
+  free(key.dptr);
+  return res ? (res - list->array - list->start) : -1;
+}
+
+
+/* Serialize a list into a byte array. */
+char *cblistdump(const CBLIST *list, int *sp){
+  char *buf, vnumbuf[CB_VNUMBUFSIZ];
+  const char *vbuf;
+  int i, bsiz, vnumsiz, ln, vsiz;
+  assert(list && sp);
+  ln = CB_LISTNUM(list);
+  CB_SETVNUMBUF(vnumsiz, vnumbuf, ln);
+  CB_MALLOC(buf, vnumsiz + 1);
+  memcpy(buf, vnumbuf, vnumsiz);
+  bsiz = vnumsiz;
+  for(i = 0; i < ln; i++){
+    vbuf = CB_LISTVAL2(list, i, vsiz);
+    CB_SETVNUMBUF(vnumsiz, vnumbuf, vsiz);
+    CB_REALLOC(buf, bsiz + vnumsiz + vsiz + 1);
+    memcpy(buf + bsiz, vnumbuf, vnumsiz);
+    bsiz += vnumsiz;
+    memcpy(buf + bsiz, vbuf, vsiz);
+    bsiz += vsiz;
+  }
+  *sp = bsiz;
+  return buf;
+}
+
+
+/* Redintegrate a serialized list. */
+CBLIST *cblistload(const char *ptr, int size){
+  CBLIST *list;
+  const char *rp;
+  int i, anum, step, ln, vsiz;
+  assert(ptr && size >= 0);
+  anum = size / (sizeof(CBLISTDATUM) + 1);
+  CB_LISTOPEN2(list, anum);
+  rp = ptr;
+  CB_READVNUMBUF(rp, size, ln, step);
+  rp += step;
+  size -= step;
+  if(ln > size) return list;
+  for(i = 0; i < ln; i++){
+    if(size < 1) break;
+    CB_READVNUMBUF(rp, size, vsiz, step);
+    rp += step;
+    size -= step;
+    if(vsiz > size) break;
+    CB_LISTPUSH(list, rp, vsiz);
+    rp += vsiz;
+  }
+  return list;
+}
+
+
+/* Get a map handle. */
+CBMAP *cbmapopen(void){
+  return cbmapopenex(CB_MAPBNUM);
+}
+
+
+/* Copy a map. */
+CBMAP *cbmapdup(CBMAP *map){
+  CBMAP *newmap;
+  const char *kbuf, *vbuf;
+  int ksiz, vsiz;
+  assert(map);
+  cbmapiterinit(map);
+  newmap = map->rnum > CB_MAPPBNUM ? cbmapopen() : cbmapopenex(CB_MAPPBNUM);
+  while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
+    CB_MAPITERVAL(vbuf, kbuf, vsiz);
+    cbmapput(newmap, kbuf, ksiz, vbuf, vsiz, FALSE);
+  }
+  cbmapiterinit(map);
+  return newmap;
+}
+
+
+/* Close a map handle. */
+void cbmapclose(CBMAP *map){
+  CBMAPDATUM *datum, *next;
+  datum = map->first;
+  while(datum){
+    next = datum->next;
+    free(datum);
+    datum = next;
+  }
+  free(map->buckets);
+  free(map);
+}
+
+
+/* Store a record. */
+int cbmapput(CBMAP *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int over){
+  CBMAPDATUM *datum, **entp, *old;
+  char *dbuf;
+  int bidx, hash, kcmp, psiz;
+  assert(map && kbuf && vbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(vsiz < 0) vsiz = strlen(vbuf);
+  CB_FIRSTHASH(hash, kbuf, ksiz);
+  bidx = hash % map->bnum;
+  datum = map->buckets[bidx];
+  entp = map->buckets + bidx;
+  CB_SECONDHASH(hash, kbuf, ksiz);
+  while(datum){
+    if(hash > datum->hash){
+      entp = &(datum->left);
+      datum = datum->left;
+    } else if(hash < datum->hash){
+      entp = &(datum->right);
+      datum = datum->right;
+    } else {
+      dbuf = (char *)datum + sizeof(*datum);
+      kcmp = cbkeycmp(kbuf, ksiz, dbuf, datum->ksiz);
+      if(kcmp < 0){
+        entp = &(datum->left);
+        datum = datum->left;
+      } else if(kcmp > 0){
+        entp = &(datum->right);
+        datum = datum->right;
+      } else {
+        if(!over) return FALSE;
+        psiz = CB_ALIGNPAD(ksiz);
+        if(vsiz > datum->vsiz){
+          old = datum;
+          CB_REALLOC(datum, sizeof(*datum) + ksiz + psiz + vsiz + 1);
+          if(datum != old){
+            if(map->first == old) map->first = datum;
+            if(map->last == old) map->last = datum;
+            if(*entp == old) *entp = datum;
+            if(datum->prev) datum->prev->next = datum;
+            if(datum->next) datum->next->prev = datum;
+            dbuf = (char *)datum + sizeof(*datum);
+          }
+        }
+        memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+        dbuf[ksiz+psiz+vsiz] = '\0';
+        datum->vsiz = vsiz;
+        return TRUE;
+      }
+    }
+  }
+  psiz = CB_ALIGNPAD(ksiz);
+  CB_MALLOC(datum, sizeof(*datum) + ksiz + psiz + vsiz + 1);
+  dbuf = (char *)datum + sizeof(*datum);
+  memcpy(dbuf, kbuf, ksiz);
+  dbuf[ksiz] = '\0';
+  datum->ksiz = ksiz;
+  memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+  dbuf[ksiz+psiz+vsiz] = '\0';
+  datum->vsiz = vsiz;
+  datum->hash = hash;
+  datum->left = NULL;
+  datum->right = NULL;
+  datum->prev = map->last;
+  datum->next = NULL;
+  *entp = datum;
+  if(!map->first) map->first = datum;
+  if(map->last) map->last->next = datum;
+  map->last = datum;
+  map->rnum++;
+  return TRUE;
+}
+
+
+/* Concatenate a value at the end of the value of the existing record. */
+void cbmapputcat(CBMAP *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz){
+  CBMAPDATUM *datum, **entp, *old;
+  char *dbuf;
+  int bidx, hash, kcmp, psiz, asiz, unit;
+  assert(map && kbuf && vbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(vsiz < 0) vsiz = strlen(vbuf);
+  CB_FIRSTHASH(hash, kbuf, ksiz);
+  bidx = hash % map->bnum;
+  datum = map->buckets[bidx];
+  entp = map->buckets + bidx;
+  CB_SECONDHASH(hash, kbuf, ksiz);
+  while(datum){
+    if(hash > datum->hash){
+      entp = &(datum->left);
+      datum = datum->left;
+    } else if(hash < datum->hash){
+      entp = &(datum->right);
+      datum = datum->right;
+    } else {
+      dbuf = (char *)datum + sizeof(*datum);
+      kcmp = cbkeycmp(kbuf, ksiz, dbuf, datum->ksiz);
+      if(kcmp < 0){
+        entp = &(datum->left);
+        datum = datum->left;
+      } else if(kcmp > 0){
+        entp = &(datum->right);
+        datum = datum->right;
+      } else {
+        psiz = CB_ALIGNPAD(ksiz);
+        asiz = sizeof(*datum) + ksiz + psiz + datum->vsiz + vsiz + 1;
+        unit = asiz <= CB_MAPCSUNIT ? CB_MAPCSUNIT : CB_MAPCBUNIT;
+        asiz = (asiz - 1) + unit - (asiz - 1) % unit;
+        old = datum;
+        CB_REALLOC(datum, asiz);
+        if(datum != old){
+          if(map->first == old) map->first = datum;
+          if(map->last == old) map->last = datum;
+          if(*entp == old) *entp = datum;
+          if(datum->prev) datum->prev->next = datum;
+          if(datum->next) datum->next->prev = datum;
+          dbuf = (char *)datum + sizeof(*datum);
+        }
+        memcpy(dbuf + ksiz + psiz + datum->vsiz, vbuf, vsiz);
+        dbuf[ksiz+psiz+datum->vsiz+vsiz] = '\0';
+        datum->vsiz += vsiz;
+        return;
+      }
+    }
+  }
+  psiz = CB_ALIGNPAD(ksiz);
+  asiz = sizeof(*datum) + ksiz + psiz + vsiz + 1;
+  unit = asiz <= CB_MAPCSUNIT ? CB_MAPCSUNIT : CB_MAPCBUNIT;
+  asiz = (asiz - 1) + unit - (asiz - 1) % unit;
+  CB_MALLOC(datum, asiz);
+  dbuf = (char *)datum + sizeof(*datum);
+  memcpy(dbuf, kbuf, ksiz);
+  dbuf[ksiz] = '\0';
+  datum->ksiz = ksiz;
+  memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+  dbuf[ksiz+psiz+vsiz] = '\0';
+  datum->vsiz = vsiz;
+  datum->hash = hash;
+  datum->left = NULL;
+  datum->right = NULL;
+  datum->prev = map->last;
+  datum->next = NULL;
+  *entp = datum;
+  if(!map->first) map->first = datum;
+  if(map->last) map->last->next = datum;
+  map->last = datum;
+  map->rnum++;
+}
+
+
+/* Delete a record. */
+int cbmapout(CBMAP *map, const char *kbuf, int ksiz){
+  CBMAPDATUM *datum, **entp, *tmp;
+  char *dbuf;
+  int bidx, hash, kcmp;
+  assert(map && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  CB_FIRSTHASH(hash, kbuf, ksiz);
+  bidx = hash % map->bnum;
+  datum = map->buckets[bidx];
+  entp = map->buckets + bidx;
+  CB_SECONDHASH(hash, kbuf, ksiz);
+  while(datum){
+    if(hash > datum->hash){
+      entp = &(datum->left);
+      datum = datum->left;
+    } else if(hash < datum->hash){
+      entp = &(datum->right);
+      datum = datum->right;
+    } else {
+      dbuf = (char *)datum + sizeof(*datum);
+      kcmp = cbkeycmp(kbuf, ksiz, dbuf, datum->ksiz);
+      if(kcmp < 0){
+        entp = &(datum->left);
+        datum = datum->left;
+      } else if(kcmp > 0){
+        entp = &(datum->right);
+        datum = datum->right;
+      } else {
+        if(datum->prev) datum->prev->next = datum->next;
+        if(datum->next) datum->next->prev = datum->prev;
+        if(datum == map->first) map->first = datum->next;
+        if(datum == map->last) map->last = datum->prev;
+        if(datum->left && !datum->right){
+          *entp = datum->left;
+        } else if(!datum->left && datum->right){
+          *entp = datum->right;
+        } else if(!datum->left && !datum->left){
+          *entp = NULL;
+        } else {
+          *entp = datum->left;
+          tmp = *entp;
+          while(TRUE){
+            if(hash > tmp->hash){
+              if(tmp->left){
+                tmp = tmp->left;
+              } else {
+                tmp->left = datum->right;
+                break;
+              }
+            } else if(hash < tmp->hash){
+              if(tmp->right){
+                tmp = tmp->right;
+              } else {
+                tmp->right = datum->right;
+                break;
+              }
+            } else {
+              kcmp = cbkeycmp(kbuf, ksiz, dbuf, datum->ksiz);
+              if(kcmp < 0){
+                if(tmp->left){
+                  tmp = tmp->left;
+                } else {
+                  tmp->left = datum->right;
+                  break;
+                }
+              } else {
+                if(tmp->right){
+                  tmp = tmp->right;
+                } else {
+                  tmp->right = datum->right;
+                  break;
+                }
+              }
+            }
+          }
+        }
+        free(datum);
+        map->rnum--;
+        return TRUE;
+      }
+    }
+  }
+  return FALSE;
+}
+
+
+/* Retrieve a record. */
+const char *cbmapget(const CBMAP *map, const char *kbuf, int ksiz, int *sp){
+  CBMAPDATUM *datum;
+  char *dbuf;
+  int hash, kcmp;
+  assert(map && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  CB_FIRSTHASH(hash, kbuf, ksiz);
+  datum = map->buckets[hash%map->bnum];
+  CB_SECONDHASH(hash, kbuf, ksiz);
+  while(datum){
+    if(hash > datum->hash){
+      datum = datum->left;
+    } else if(hash < datum->hash){
+      datum = datum->right;
+    } else {
+      dbuf = (char *)datum + sizeof(*datum);
+      kcmp = cbkeycmp(kbuf, ksiz, dbuf, datum->ksiz);
+      if(kcmp < 0){
+        datum = datum->left;
+      } else if(kcmp > 0){
+        datum = datum->right;
+      } else {
+        if(sp) *sp = datum->vsiz;
+        return dbuf + datum->ksiz + CB_ALIGNPAD(datum->ksiz);
+      }
+    }
+  }
+  return NULL;
+}
+
+
+/* Move a record to the edge. */
+int cbmapmove(CBMAP *map, const char *kbuf, int ksiz, int head){
+  CBMAPDATUM *datum;
+  char *dbuf;
+  int hash, kcmp;
+  assert(map && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  CB_FIRSTHASH(hash, kbuf, ksiz);
+  datum = map->buckets[hash%map->bnum];
+  CB_SECONDHASH(hash, kbuf, ksiz);
+  while(datum){
+    if(hash > datum->hash){
+      datum = datum->left;
+    } else if(hash < datum->hash){
+      datum = datum->right;
+    } else {
+      dbuf = (char *)datum + sizeof(*datum);
+      kcmp = cbkeycmp(kbuf, ksiz, dbuf, datum->ksiz);
+      if(kcmp < 0){
+        datum = datum->left;
+      } else if(kcmp > 0){
+        datum = datum->right;
+      } else {
+        if(head){
+          if(map->first == datum) return TRUE;
+          if(map->last == datum) map->last = datum->prev;
+          if(datum->prev) datum->prev->next = datum->next;
+          if(datum->next) datum->next->prev = datum->prev;
+          datum->prev = NULL;
+          datum->next = map->first;
+          map->first->prev = datum;
+          map->first = datum;
+        } else {
+          if(map->last == datum) return TRUE;
+          if(map->first == datum) map->first = datum->next;
+          if(datum->prev) datum->prev->next = datum->next;
+          if(datum->next) datum->next->prev = datum->prev;
+          datum->prev = map->last;
+          datum->next = NULL;
+          map->last->next = datum;
+          map->last = datum;
+        }
+        return TRUE;
+      }
+    }
+  }
+  return FALSE;
+}
+
+
+/* Initialize the iterator of a map handle. */
+void cbmapiterinit(CBMAP *map){
+  assert(map);
+  map->cur = map->first;
+}
+
+
+/* Get the next key of the iterator. */
+const char *cbmapiternext(CBMAP *map, int *sp){
+  CBMAPDATUM *datum;
+  assert(map);
+  if(!map->cur) return NULL;
+  datum = map->cur;
+  map->cur = datum->next;
+  if(sp) *sp = datum->ksiz;
+  return (char *)datum + sizeof(*datum);
+}
+
+
+/* Get the value binded to the key fetched from the iterator. */
+const char *cbmapiterval(const char *kbuf, int *sp){
+  CBMAPDATUM *datum;
+  assert(kbuf);
+  datum = (CBMAPDATUM *)(kbuf - sizeof(*datum));
+  if(sp) *sp = datum->vsiz;
+  return (char *)datum + sizeof(*datum) + datum->ksiz + CB_ALIGNPAD(datum->ksiz);
+}
+
+
+/* Get the number of the records stored in a map. */
+int cbmaprnum(const CBMAP *map){
+  assert(map);
+  return map->rnum;
+}
+
+
+/* Get the list handle contains all keys in a map. */
+CBLIST *cbmapkeys(CBMAP *map){
+  CBLIST *list;
+  const char *kbuf;
+  int anum, ksiz;
+  assert(map);
+  anum = cbmaprnum(map);
+  CB_LISTOPEN2(list, anum);
+  cbmapiterinit(map);
+  while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
+    CB_LISTPUSH(list, kbuf, ksiz);
+  }
+  return list;
+}
+
+
+/* Get the list handle contains all values in a map. */
+CBLIST *cbmapvals(CBMAP *map){
+  CBLIST *list;
+  const char *kbuf, *vbuf;
+  int anum, ksiz, vsiz;
+  assert(map);
+  anum = cbmaprnum(map);
+  CB_LISTOPEN2(list, anum);
+  cbmapiterinit(map);
+  while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
+    CB_MAPITERVAL(vbuf, kbuf, vsiz);
+    CB_LISTPUSH(list, vbuf, vsiz);
+  }
+  return list;
+}
+
+
+/* Serialize a map into a byte array. */
+char *cbmapdump(CBMAP *map, int *sp){
+  char *buf, vnumbuf[CB_VNUMBUFSIZ];
+  const char *kbuf, *vbuf;
+  int bsiz, vnumsiz, rn, ksiz, vsiz;
+  assert(map && sp);
+  rn = cbmaprnum(map);
+  CB_SETVNUMBUF(vnumsiz, vnumbuf, rn);
+  CB_MALLOC(buf, vnumsiz + 1);
+  memcpy(buf, vnumbuf, vnumsiz);
+  bsiz = vnumsiz;
+  cbmapiterinit(map);
+  while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
+    CB_MAPITERVAL(vbuf, kbuf, vsiz);
+    CB_SETVNUMBUF(vnumsiz, vnumbuf, ksiz);
+    CB_REALLOC(buf, bsiz + vnumsiz + ksiz + 1);
+    memcpy(buf + bsiz, vnumbuf, vnumsiz);
+    bsiz += vnumsiz;
+    memcpy(buf + bsiz, kbuf, ksiz);
+    bsiz += ksiz;
+    CB_SETVNUMBUF(vnumsiz, vnumbuf, vsiz);
+    CB_REALLOC(buf, bsiz + vnumsiz + vsiz + 1);
+    memcpy(buf + bsiz, vnumbuf, vnumsiz);
+    bsiz += vnumsiz;
+    memcpy(buf + bsiz, vbuf, vsiz);
+    bsiz += vsiz;
+  }
+  *sp = bsiz;
+  return buf;
+}
+
+
+/* Redintegrate a serialized map. */
+CBMAP *cbmapload(const char *ptr, int size){
+  CBMAP *map;
+  const char *rp, *kbuf, *vbuf;
+  int i, step, rn, ksiz, vsiz;
+  assert(ptr && size >= 0);
+  map = cbmapopenex(CB_MAPPBNUM);
+  rp = ptr;
+  CB_READVNUMBUF(rp, size, rn, step);
+  rp += step;
+  size -= step;
+  if(rn > size) return map;
+  for(i = 0; i < rn; i++){
+    if(size < 1) break;
+    CB_READVNUMBUF(rp, size, ksiz, step);
+    rp += step;
+    size -= step;
+    if(ksiz > size) break;
+    kbuf = rp;
+    rp += ksiz;
+    if(size < 1) break;
+    CB_READVNUMBUF(rp, size, vsiz, step);
+    rp += step;
+    size -= step;
+    if(vsiz > size) break;
+    vbuf = rp;
+    rp += vsiz;
+    cbmapput(map, kbuf, ksiz, vbuf, vsiz, TRUE);
+  }
+  return map;
+}
+
+
+/* Redintegrate a serialized map and get one of the records. */
+char *cbmaploadone(const char *ptr, int size, const char *kbuf, int ksiz, int *sp){
+  const char *rp, *tkbuf, *vbuf;
+  char *rv;
+  int i, step, rn, tksiz, vsiz;
+  assert(ptr && size >= 0 && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  rp = ptr;
+  CB_READVNUMBUF(rp, size, rn, step);
+  rp += step;
+  size -= step;
+  if(rn > size) return NULL;
+  for(i = 0; i < rn; i++){
+    if(size < 1) break;
+    CB_READVNUMBUF(rp, size, tksiz, step);
+    rp += step;
+    size -= step;
+    if(tksiz > size) break;
+    tkbuf = rp;
+    rp += tksiz;
+    if(size < 1) break;
+    CB_READVNUMBUF(rp, size, vsiz, step);
+    rp += step;
+    size -= step;
+    if(vsiz > size) break;
+    vbuf = rp;
+    rp += vsiz;
+    if(tksiz == ksiz && !memcmp(tkbuf, kbuf, ksiz)){
+      if(sp) *sp = vsiz;
+      CB_MEMDUP(rv, vbuf, vsiz);
+      return rv;
+    }
+  }
+  return NULL;
+}
+
+
+/* Get a heap handle. */
+CBHEAP *cbheapopen(int size, int max, int(*compar)(const void *, const void *)){
+  CBHEAP *heap;
+  assert(size > 0 && max >= 0 && compar);
+  CB_MALLOC(heap, sizeof(*heap));
+  CB_MALLOC(heap->base, size * max + 1);
+  CB_MALLOC(heap->swap, size);
+  heap->size = size;
+  heap->num = 0;
+  heap->max = max;
+  heap->compar = compar;
+  return heap;
+}
+
+
+/* Copy a heap. */
+CBHEAP *cbheapdup(CBHEAP *heap){
+  CBHEAP *newheap;
+  assert(heap);
+  CB_MALLOC(newheap, sizeof(*newheap));
+  CB_MEMDUP(newheap->base, heap->base, heap->size * heap->max);
+  CB_MALLOC(newheap->swap, heap->size);
+  newheap->size = heap->size;
+  newheap->num = heap->num;
+  newheap->max = heap->max;
+  newheap->compar = heap->compar;
+  return newheap;
+}
+
+
+/* Close a heap handle. */
+void cbheapclose(CBHEAP *heap){
+  assert(heap);
+  free(heap->swap);
+  free(heap->base);
+  free(heap);
+}
+
+
+/* Get the number of the records stored in a heap. */
+int cbheapnum(CBHEAP *heap){
+  assert(heap);
+  return heap->num;
+}
+
+
+/* Insert a record into a heap. */
+int cbheapinsert(CBHEAP *heap, const void *ptr){
+  char *base;
+  int size, pidx, cidx, bot;
+  assert(heap && ptr);
+  if(heap->max < 1) return FALSE;
+  base = heap->base;
+  size = heap->size;
+  if(heap->num >= heap->max){
+    if(heap->compar(ptr, base) > 0) return FALSE;
+    memcpy(base, ptr, size);
+    pidx = 0;
+    bot = heap->num / 2;
+    while(pidx < bot){
+      cidx = pidx * 2 + 1;
+      if(cidx < heap->num - 1 && heap->compar(base + cidx * size, base + (cidx + 1) * size) < 0)
+        cidx++;
+      if(heap->compar(base + pidx * size, base + cidx * size) > 0) break;
+      memcpy(heap->swap, base + pidx * size, size);
+      memcpy(base + pidx * size, base + cidx * size, size);
+      memcpy(base + cidx * size, heap->swap, size);
+      pidx = cidx;
+    }
+  } else {
+    memcpy(base + size * heap->num, ptr, size);
+    cidx = heap->num;
+    while(cidx > 0){
+      pidx = (cidx - 1) / 2;
+      if(heap->compar(base + cidx * size, base + pidx * size) <= 0) break;
+      memcpy(heap->swap, base + cidx * size, size);
+      memcpy(base + cidx * size, base + pidx * size, size);
+      memcpy(base + pidx * size, heap->swap, size);
+      cidx = pidx;
+    }
+    heap->num++;
+  }
+  return TRUE;
+}
+
+
+/* Get the pointer to the region of a record in a heap. */
+const void *cbheapval(CBHEAP *heap, int index){
+  assert(heap && index >= 0);
+  if(index >= heap->num) return NULL;
+  return heap->base + index * heap->size;
+}
+
+
+/* Convert a heap to an allocated region. */
+void *cbheaptomalloc(CBHEAP *heap, int *np){
+  char *ptr;
+  assert(heap);
+  qsort(heap->base, heap->num, heap->size, heap->compar);
+  ptr = heap->base;
+  if(np) *np = heap->num;
+  free(heap->swap);
+  free(heap);
+  return ptr;
+}
+
+
+/* Allocate a formatted string on memory. */
+char *cbsprintf(const char *format, ...){
+  va_list ap;
+  char *buf, cbuf[CB_SPBUFSIZ], *str;
+  int len, cblen, num, slen;
+  unsigned int unum;
+  double dnum;
+  va_start(ap, format);
+  assert(format);
+  CB_MALLOC(buf, 1);
+  len = 0;
+  while(*format != '\0'){
+    if(*format == '%'){
+      cbuf[0] = '%';
+      cblen = 1;
+      format++;
+      while(strchr("0123456789 .+-", *format) && *format != '\0' && cblen < CB_SPBUFSIZ - 1){
+        cbuf[cblen++] = *format;
+        format++;
+      }
+      cbuf[cblen] = '\0';
+      if(atoi(cbuf + 1) > CB_SPMAXWIDTH - 16){
+        sprintf(cbuf, "(err)");
+      } else {
+        cbuf[cblen++] = *format;
+        cbuf[cblen] = '\0';
+      }
+      switch(*format){
+      case 'd':
+        num = va_arg(ap, int);
+        CB_REALLOC(buf, len + CB_SPMAXWIDTH + 2);
+        len += sprintf(buf + len, cbuf, num);
+        break;
+      case 'o': case 'u': case 'x': case 'X': case 'c':
+        unum = va_arg(ap, unsigned int);
+        CB_REALLOC(buf, len + CB_SPMAXWIDTH + 2);
+        len += sprintf(buf + len, cbuf, unum);
+        break;
+      case 'e': case 'E': case 'f': case 'g': case 'G':
+        dnum = va_arg(ap, double);
+        CB_REALLOC(buf, len + CB_SPMAXWIDTH + 2);
+        len += sprintf(buf + len, cbuf, dnum);
+        break;
+      case 's':
+        str = va_arg(ap, char *);
+        slen = strlen(str);
+        CB_REALLOC(buf, len + slen + 2);
+        memcpy(buf + len, str, slen);
+        len += slen;
+        break;
+      case '%':
+        CB_REALLOC(buf, len + 2);
+        buf[len++] = '%';
+        break;
+      default:
+        break;
+      }
+    } else {
+      CB_REALLOC(buf, len + 2);
+      buf[len++] = *format;
+    }
+    format++;
+  }
+  buf[len] = '\0';
+  va_end(ap);
+  return buf;
+}
+
+
+/* Replace some patterns in a string. */
+char *cbreplace(const char *str, CBMAP *pairs){
+  int i, bsiz, wi, rep, ksiz, vsiz;
+  char *buf;
+  const char *key, *val;
+  assert(str && pairs);
+  bsiz = CB_DATUMUNIT;
+  CB_MALLOC(buf, bsiz);
+  wi = 0;
+  while(*str != '\0'){
+    rep = FALSE;
+    cbmapiterinit(pairs);
+    while((key = cbmapiternext(pairs, &ksiz)) != NULL){
+      for(i = 0; i < ksiz; i++){
+        if(str[i] == '\0' || str[i] != key[i]) break;
+      }
+      if(i == ksiz){
+        CB_MAPITERVAL(val, key, vsiz);
+        if(wi + vsiz >= bsiz){
+          bsiz = bsiz * 2 + vsiz;
+          CB_REALLOC(buf, bsiz);
+        }
+        memcpy(buf + wi, val, vsiz);
+        wi += vsiz;
+        str += ksiz;
+        rep = TRUE;
+        break;
+      }
+    }
+    if(!rep){
+      if(wi + 1 >= bsiz){
+        bsiz = bsiz * 2 + 1;
+        CB_REALLOC(buf, bsiz);
+      }
+      buf[wi++] = *str;
+      str++;
+    }
+  }
+  CB_REALLOC(buf, wi + 1);
+  buf[wi] = '\0';
+  return buf;
+}
+
+
+/* Make a list by split a serial datum. */
+CBLIST *cbsplit(const char *ptr, int size, const char *delim){
+  CBLIST *list;
+  int bi, step;
+  assert(ptr);
+  CB_LISTOPEN(list);
+  if(size < 0) size = strlen(ptr);
+  if(delim){
+    for(bi = 0; bi < size; bi += step){
+      step = 0;
+      while(bi + step < size && !strchr(delim, ptr[bi+step])){
+        step++;
+      }
+      cblistpush(list, ptr + bi, step);
+      step++;
+    }
+    if(size > 0 && strchr(delim, ptr[size-1])) cblistpush(list, "", 0);
+  } else {
+    for(bi = 0; bi < size; bi += step){
+      step = 0;
+      while(bi + step < size && ptr[bi+step]){
+        step++;
+      }
+      cblistpush(list, ptr + bi, step);
+      step++;
+    }
+    if(size > 0 && ptr[size-1] == 0) cblistpush(list, "", 0);
+  }
+  return list;
+}
+
+
+/* Read whole data of a file. */
+char *cbreadfile(const char *name, int *sp){
+  struct stat sbuf;
+  char iobuf[CB_IOBUFSIZ], *buf;
+  int fd, size, asiz, rv;
+  asiz = CB_IOBUFSIZ * 2;
+  if(name){
+    if((fd = open(name, O_RDONLY, 0)) == -1) return NULL;
+    if(fstat(fd, &sbuf) != -1) asiz = sbuf.st_size + 1;
+  } else {
+    fd = 0;
+  }
+  CB_MALLOC(buf, asiz + 1);
+  size = 0;
+  while((rv = read(fd, iobuf, CB_IOBUFSIZ)) > 0){
+    if(size + rv >= asiz){
+      asiz = asiz * 2 + size;
+      CB_REALLOC(buf, asiz + 1);
+    }
+    memcpy(buf + size, iobuf, rv);
+    size += rv;
+  }
+  buf[size] = '\0';
+  if(close(fd) == -1 || rv == -1){
+    free(buf);
+    return NULL;
+  }
+  if(sp) *sp = size;
+  return buf;
+}
+
+
+/* Write data of a region into a file. */
+int cbwritefile(const char *name, const char *ptr, int size){
+  int fd, err, wb;
+  assert(ptr);
+  if(size < 0) size = strlen(ptr);
+  if(name){
+    if((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, CB_FILEMODE)) == -1) return FALSE;
+  } else {
+    fd = 1;
+  }
+  err = FALSE;
+  wb = 0;
+  do {
+    wb = write(fd, ptr, size);
+    switch(wb){
+    case -1: err = errno != EINTR ? TRUE : FALSE; break;
+    case 0: break;
+    default:
+      ptr += wb;
+      size -= wb;
+      break;
+    }
+  } while(size > 0);
+  if(close(fd) == -1) err = TRUE;
+  return err ? FALSE : TRUE;
+}
+
+
+/* Read every line of a file. */
+CBLIST *cbreadlines(const char *name){
+  char *buf, *tmp;
+  int vsiz;
+  CBMAP *pairs;
+  CBLIST *list;
+  if(!(buf = cbreadfile(name, NULL))) return NULL;
+  pairs = cbmapopenex(3);
+  cbmapput(pairs, "\r\n", 2, "\n", 1, TRUE);
+  cbmapput(pairs, "\r", 1, "\n", 1, TRUE);
+  tmp = cbreplace(buf, pairs);
+  list = cbsplit(tmp, strlen(tmp), "\n");
+  free(tmp);
+  cbmapclose(pairs);
+  free(buf);
+  if(CB_LISTNUM(list) > 0){
+    cblistval(list, CB_LISTNUM(list) - 1, &vsiz);
+    if(vsiz < 1) CB_LISTDROP(list);
+  }
+  return list;
+}
+
+
+/* Read names of files in a directory. */
+CBLIST *cbdirlist(const char *name){
+  DIR *DD;
+  struct dirent *dp;
+  CBLIST *list;
+  assert(name);
+  if(!(DD = opendir(name))) return NULL;
+  CB_LISTOPEN(list);
+  while((dp = readdir(DD)) != NULL){
+    CB_LISTPUSH(list, dp->d_name, strlen(dp->d_name));
+  }
+  if(closedir(DD) == -1){
+    CB_LISTCLOSE(list);
+    return NULL;
+  }
+  return list;
+}
+
+
+/* Get the status of a file or a directory. */
+int cbfilestat(const char *name, int *isdirp, int *sizep, time_t *mtimep){
+  struct stat sbuf;
+  assert(name);
+  if(lstat(name, &sbuf) == -1) return FALSE;
+  if(isdirp) *isdirp = S_ISDIR(sbuf.st_mode);
+  if(sizep) *sizep = (int)sbuf.st_size;
+  if(mtimep) *mtimep = sbuf.st_mtime;
+  return TRUE;
+}
+
+
+/* Remove a file or a directory and its sub ones recursively. */
+int cbremove(const char *name){
+  CBLIST *list;
+  const char *elem;
+  char *path;
+  int i, err, tail;
+  struct stat sbuf;
+  assert(name);
+  if(lstat(name, &sbuf) == -1) return FALSE;
+  if(unlink(name) == 0) return TRUE;
+  if(!S_ISDIR(sbuf.st_mode) || !(list = cbdirlist(name))) return FALSE;
+  err = FALSE;
+  tail = name[0] != '\0' && name[strlen(name)-1] == MYPATHCHR;
+  for(i = 0; i < CB_LISTNUM(list); i++){
+    elem = CB_LISTVAL(list, i);
+    if(!strcmp(MYCDIRSTR, elem) || !strcmp(MYPDIRSTR, elem)) continue;
+    if(tail){
+      path = cbsprintf("%s%s", name, elem);
+    } else {
+      path = cbsprintf("%s%c%s", name, MYPATHCHR, elem);
+    }
+    if(!cbremove(path)) err = TRUE;
+    free(path);
+  }
+  CB_LISTCLOSE(list);
+  return rmdir(name) == 0 ? TRUE : FALSE;
+}
+
+
+/* Break up a URL into elements. */
+CBMAP *cburlbreak(const char *str){
+  CBMAP *map;
+  char *tmp, *ep;
+  const char *rp;
+  int serv;
+  assert(str);
+  map = cbmapopenex(CB_MAPPBNUM);
+  CB_MEMDUP(tmp, str, strlen(str));
+  rp = cbstrtrim(tmp);
+  cbmapput(map, "self", -1, rp, -1, TRUE);
+  serv = FALSE;
+  if(cbstrfwimatch(rp, "http://")){
+    cbmapput(map, "scheme", -1, "http", -1, TRUE);
+    rp += 7;
+    serv = TRUE;
+  } else if(cbstrfwimatch(rp, "https://")){
+    cbmapput(map, "scheme", -1, "https", -1, TRUE);
+    rp += 8;
+    serv = TRUE;
+  } else if(cbstrfwimatch(rp, "ftp://")){
+    cbmapput(map, "scheme", -1, "ftp", -1, TRUE);
+    rp += 6;
+    serv = TRUE;
+  } else if(cbstrfwimatch(rp, "sftp://")){
+    cbmapput(map, "scheme", -1, "sftp", -1, TRUE);
+    rp += 7;
+    serv = TRUE;
+  } else if(cbstrfwimatch(rp, "ftps://")){
+    cbmapput(map, "scheme", -1, "ftps", -1, TRUE);
+    rp += 7;
+    serv = TRUE;
+  } else if(cbstrfwimatch(rp, "tftp://")){
+    cbmapput(map, "scheme", -1, "tftp", -1, TRUE);
+    rp += 7;
+    serv = TRUE;
+  } else if(cbstrfwimatch(rp, "ldap://")){
+    cbmapput(map, "scheme", -1, "ldap", -1, TRUE);
+    rp += 7;
+    serv = TRUE;
+  } else if(cbstrfwimatch(rp, "ldaps://")){
+    cbmapput(map, "scheme", -1, "ldaps", -1, TRUE);
+    rp += 8;
+    serv = TRUE;
+  } else if(cbstrfwimatch(rp, "file://")){
+    cbmapput(map, "scheme", -1, "file", -1, TRUE);
+    rp += 7;
+    serv = TRUE;
+  }
+  if((ep = strchr(rp, '#')) != NULL){
+    cbmapput(map, "fragment", -1, ep + 1, -1, TRUE);
+    *ep = '\0';
+  }
+  if((ep = strchr(rp, '?')) != NULL){
+    cbmapput(map, "query", -1, ep + 1, -1, TRUE);
+    *ep = '\0';
+  }
+  if(serv){
+    if((ep = strchr(rp, '/')) != NULL){
+      cbmapput(map, "path", -1, ep, -1, TRUE);
+      *ep = '\0';
+    } else {
+      cbmapput(map, "path", -1, "/", -1, TRUE);
+    }
+    if((ep = strchr(rp, '@')) != NULL){
+      *ep = '\0';
+      if(rp[0] != '\0') cbmapput(map, "authority", -1, rp, -1, TRUE);
+      rp = ep + 1;
+    }
+    if((ep = strchr(rp, ':')) != NULL){
+      if(ep[1] != '\0') cbmapput(map, "port", -1, ep + 1, -1, TRUE);
+      *ep = '\0';
+    }
+    if(rp[0] != '\0') cbmapput(map, "host", -1, rp, -1, TRUE);
+  } else {
+    cbmapput(map, "path", -1, rp, -1, TRUE);
+  }
+  free(tmp);
+  if((rp = cbmapget(map, "path", -1, NULL)) != NULL){
+    if((ep = strrchr(rp, '/')) != NULL){
+      if(ep[1] != '\0') cbmapput(map, "file", -1, ep + 1, -1, TRUE);
+    } else {
+      cbmapput(map, "file", -1, rp, -1, TRUE);
+    }
+  }
+  if((rp = cbmapget(map, "file", -1, NULL)) != NULL && (!strcmp(rp, ".") || !strcmp(rp, "..")))
+    cbmapout(map, "file", -1);
+  return map;
+}
+
+
+/* Resolve a relative URL with another absolute URL. */
+char *cburlresolve(const char *base, const char *target){
+  CBMAP *telems, *belems;
+  CBLIST *bpaths, *opaths, *qelems;
+  CBDATUM *rbuf;
+  const char *vbuf, *path;
+  char *tmp, *wp, *enc, numbuf[CB_NUMBUFSIZ];
+  int i, vsiz, port, num;
+  assert(base && target);
+  while(*base > '\0' && *base <= ' '){
+    base++;
+  }
+  while(*target > '\0' && *target <= ' '){
+    target++;
+  }
+  if(*target == '\0') target = base;
+  CB_DATUMOPEN(rbuf);
+  telems = cburlbreak(target);
+  port = 80;
+  belems = cburlbreak(cbmapget(telems, "scheme", -1, &vsiz) ? target : base);
+  if((vbuf = cbmapget(belems, "scheme", -1, &vsiz)) != NULL){
+    CB_DATUMCAT(rbuf, vbuf, vsiz);
+    CB_DATUMCAT(rbuf, "://", 3);
+    if(!cbstricmp(vbuf, "https")){
+      port = 443;
+    } else if(!cbstricmp(vbuf, "ftp")){
+      port = 21;
+    } else if(!cbstricmp(vbuf, "sftp")){
+      port = 115;
+    } else if(!cbstricmp(vbuf, "ftps")){
+      port = 22;
+    } else if(!cbstricmp(vbuf, "tftp")){
+      port = 69;
+    } else if(!cbstricmp(vbuf, "ldap")){
+      port = 389;
+    } else if(!cbstricmp(vbuf, "ldaps")){
+      port = 636;
+    }
+  } else {
+    CB_DATUMCAT(rbuf, "http://", 7);
+  }
+  if((vbuf = cbmapget(belems, "authority", -1, &vsiz)) != NULL){
+    if((wp = strchr(vbuf, ':')) != NULL){
+      *wp = '\0';
+      tmp = cburldecode(vbuf, NULL);
+      enc = cburlencode(tmp, -1);
+      CB_DATUMCAT(rbuf, enc, strlen(enc));
+      free(enc);
+      free(tmp);
+      CB_DATUMCAT(rbuf, ":", 1);
+      wp++;
+      tmp = cburldecode(wp, NULL);
+      enc = cburlencode(tmp, -1);
+      CB_DATUMCAT(rbuf, enc, strlen(enc));
+      free(enc);
+      free(tmp);
+    } else {
+      tmp = cburldecode(vbuf, NULL);
+      enc = cburlencode(tmp, -1);
+      CB_DATUMCAT(rbuf, enc, strlen(enc));
+      free(enc);
+      free(tmp);
+    }
+    CB_DATUMCAT(rbuf, "@", 1);
+  }
+  if((vbuf = cbmapget(belems, "host", -1, &vsiz)) != NULL){
+    tmp = cburldecode(vbuf, NULL);
+    cbstrtolower(tmp);
+    enc = cburlencode(tmp, -1);
+    CB_DATUMCAT(rbuf, enc, strlen(enc));
+    free(enc);
+    free(tmp);
+  } else {
+    CB_DATUMCAT(rbuf, "localhost", 9);
+  }
+  if((vbuf = cbmapget(belems, "port", -1, &vsiz)) != NULL &&
+     (num = atoi(vbuf)) != port && num > 1){
+    sprintf(numbuf, ":%d", num);
+    CB_DATUMCAT(rbuf, numbuf, strlen(numbuf));
+  }
+  if(!(path = cbmapget(telems, "path", -1, NULL))) path = "/";
+  if(path[0] == '\0' && (vbuf = cbmapget(belems, "path", -1, NULL)) != NULL) path = vbuf;
+  if(path[0] == '\0') path = "/";
+  CB_LISTOPEN(bpaths);
+  if(path[0] != '/' && (vbuf = cbmapget(belems, "path", -1, &vsiz)) != NULL){
+    opaths = cbsplit(vbuf, vsiz, "/");
+  } else {
+    opaths = cbsplit("/", 1, "/");
+  }
+  CB_LISTDROP(opaths);
+  for(i = 0; i < CB_LISTNUM(opaths); i++){
+    vbuf = CB_LISTVAL2(opaths, i, vsiz);
+    if(vsiz < 1 || !strcmp(vbuf, ".")) continue;
+    if(!strcmp(vbuf, "..")){
+      CB_LISTDROP(bpaths);
+    } else {
+      CB_LISTPUSH(bpaths, vbuf, vsiz);
+    }
+  }
+  CB_LISTCLOSE(opaths);
+  opaths = cbsplit(path, -1, "/");
+  for(i = 0; i < CB_LISTNUM(opaths); i++){
+    vbuf = CB_LISTVAL2(opaths, i, vsiz);
+    if(vsiz < 1 || !strcmp(vbuf, ".")) continue;
+    if(!strcmp(vbuf, "..")){
+      CB_LISTDROP(bpaths);
+    } else {
+      CB_LISTPUSH(bpaths, vbuf, vsiz);
+    }
+  }
+  CB_LISTCLOSE(opaths);
+  for(i = 0; i < CB_LISTNUM(bpaths); i++){
+    vbuf = CB_LISTVAL(bpaths, i);
+    if(strchr(vbuf, '%')){
+      tmp = cburldecode(vbuf, NULL);
+    } else {
+      CB_MEMDUP(tmp, vbuf, strlen(vbuf));
+    }
+    enc = cburlencode(tmp, -1);
+    CB_DATUMCAT(rbuf, "/", 1);
+    CB_DATUMCAT(rbuf, enc, strlen(enc));
+    free(enc);
+    free(tmp);
+  }
+  if(cbstrbwmatch(path, "/")) CB_DATUMCAT(rbuf, "/", 1);
+  CB_LISTCLOSE(bpaths);
+  if((vbuf = cbmapget(telems, "query", -1, &vsiz)) != NULL){
+    CB_DATUMCAT(rbuf, "?", 1);
+    qelems = cbsplit(vbuf, vsiz, "&;");
+    for(i = 0; i < CB_LISTNUM(qelems); i++){
+      vbuf = CB_LISTVAL(qelems, i);
+      if(i > 0) CB_DATUMCAT(rbuf, "&", 1);
+      if((wp = strchr(vbuf, '=')) != NULL){
+        *wp = '\0';
+        tmp = cburldecode(vbuf, NULL);
+        enc = cburlencode(tmp, -1);
+        CB_DATUMCAT(rbuf, enc, strlen(enc));
+        free(enc);
+        free(tmp);
+        CB_DATUMCAT(rbuf, "=", 1);
+        wp++;
+        tmp = cburldecode(wp, NULL);
+        enc = cburlencode(tmp, -1);
+        CB_DATUMCAT(rbuf, enc, strlen(enc));
+        free(enc);
+        free(tmp);
+      } else {
+        tmp = cburldecode(vbuf, NULL);
+        enc = cburlencode(tmp, -1);
+        CB_DATUMCAT(rbuf, enc, strlen(enc));
+        free(enc);
+        free(tmp);
+      }
+    }
+    CB_LISTCLOSE(qelems);
+  }
+  if((vbuf = cbmapget(telems, "fragment", -1, &vsiz)) != NULL){
+    tmp = cburldecode(vbuf, NULL);
+    enc = cburlencode(tmp, -1);
+    CB_DATUMCAT(rbuf, "#", 1);
+    CB_DATUMCAT(rbuf, enc, strlen(enc));
+    free(enc);
+    free(tmp);
+  }
+  cbmapclose(belems);
+  cbmapclose(telems);
+  return cbdatumtomalloc(rbuf, NULL);
+}
+
+
+/* Encode a serial object with URL encoding. */
+char *cburlencode(const char *ptr, int size){
+  char *buf, *wp;
+  int i, c;
+  assert(ptr);
+  if(size < 0) size = strlen(ptr);
+  CB_MALLOC(buf, size * 3 + 1);
+  wp = buf;
+  for(i = 0; i < size; i++){
+    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 *cburldecode(const char *str, int *sp){
+  char *buf, *wp;
+  unsigned char c;
+  CB_MEMDUP(buf, str, strlen(str));
+  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'))){
+        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';
+  if(sp) *sp = wp - buf;
+  return buf;
+}
+
+
+/* Encode a serial object with Base64 encoding. */
+char *cbbaseencode(const char *ptr, int size){
+  char *tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  char *buf, *wp;
+  const unsigned char *obj;
+  int i;
+  assert(ptr);
+  if(size < 0) size = strlen(ptr);
+  CB_MALLOC(buf, 4 * (size + 2) / 3 + 1);
+  obj = (const unsigned char *)ptr;
+  wp = buf;
+  for(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 *cbbasedecode(const char *str, int *sp){
+  unsigned char *obj, *wp;
+  int len, cnt, bpos, i, bits, eqcnt;
+  assert(str);
+  cnt = 0;
+  bpos = 0;
+  eqcnt = 0;
+  len = strlen(str);
+  CB_MALLOC(obj, len + 4);
+  wp = obj;
+  while(bpos < len && eqcnt == 0){
+    bits = 0;
+    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';
+  if(sp) *sp = cnt;
+  return (char *)obj;
+}
+
+
+/* Encode a serial object with quoted-printable encoding. */
+char *cbquoteencode(const char *ptr, int size){
+  const unsigned char *rp;
+  char *buf, *wp;
+  int i, cols;
+  assert(ptr);
+  if(size < 0) size = strlen(ptr);
+  rp = (const unsigned char *)ptr;
+  CB_MALLOC(buf, size * 3 + 1);
+  wp = buf;
+  cols = 0;
+  for(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 *cbquotedecode(const char *str, int *sp){
+  char *buf, *wp;
+  assert(str);
+  CB_MALLOC(buf, strlen(str) + 1);
+  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';
+  if(sp) *sp = wp - buf;
+  return buf;
+}
+
+
+/* Split a string of MIME into headers and the body. */
+char *cbmimebreak(const char *ptr, int size, CBMAP *attrs, int *sp){
+  CBLIST *list;
+  const char *head, *line, *pv, *ep;
+  char *hbuf, *name, *rv;
+  int i, j, wi, hlen;
+  assert(ptr);
+  if(size < 0) size = strlen(ptr);
+  head = NULL;
+  hlen = 0;
+  for(i = 0; i < size; i++){
+    if(i < size - 4 && ptr[i] == '\r' && ptr[i+1] == '\n' &&
+       ptr[i+2] == '\r' && ptr[i+3] == '\n'){
+      head = ptr;
+      hlen = i;
+      ptr += i + 4;
+      size -= i + 4;
+      break;
+    } else if(i < size - 2 && ptr[i] == '\n' && ptr[i+1] == '\n'){
+      head = ptr;
+      hlen = i;
+      ptr += i + 2;
+      size -= i + 2;
+      break;
+    }
+  }
+  if(head && attrs){
+    CB_MALLOC(hbuf, hlen + 1);
+    wi = 0;
+    for(i = 0; i < hlen; i++){
+      if(head[i] == '\r') continue;
+      if(i < hlen - 1 && head[i] == '\n' && (head[i+1] == ' ' || head[i+1] == '\t')){
+        hbuf[wi++] = ' ';
+        i++;
+      } else {
+        hbuf[wi++] = head[i];
+      }
+    }
+    list = cbsplit(hbuf, wi, "\n");
+    for(i = 0; i < CB_LISTNUM(list); i++){
+      line = CB_LISTVAL(list, i);
+      if((pv = strchr(line, ':')) != NULL){
+        CB_MEMDUP(name, line, pv - line);
+        for(j = 0; name[j] != '\0'; j++){
+          if(name[j] >= 'A' && name[j] <= 'Z') name[j] -= 'A' - 'a';
+        }
+        pv++;
+        while(*pv == ' ' || *pv == '\t'){
+          pv++;
+        }
+        cbmapput(attrs, name, -1, pv, -1, TRUE);
+        free(name);
+      }
+
+    }
+    CB_LISTCLOSE(list);
+    free(hbuf);
+    if((pv = cbmapget(attrs, "content-type", -1, NULL)) != NULL){
+      if((ep = strchr(pv, ';')) != NULL){
+        cbmapput(attrs, "TYPE", -1, pv, ep - pv, TRUE);
+        do {
+          ep++;
+          while(ep[0] == ' '){
+            ep++;
+          }
+          if(cbstrfwimatch(ep, "charset=")){
+            ep += 8;
+            while(*ep > '\0' && *ep <= ' '){
+              ep++;
+            }
+            if(ep[0] == '"') ep++;
+            pv = ep;
+            while(ep[0] != '\0' && ep[0] != ' ' && ep[0] != '"' && ep[0] != ';'){
+              ep++;
+            }
+            cbmapput(attrs, "CHARSET", -1, pv, ep - pv, TRUE);
+          } else if(cbstrfwimatch(ep, "boundary=")){
+            ep += 9;
+            while(*ep > '\0' && *ep <= ' '){
+              ep++;
+            }
+            if(ep[0] == '"'){
+              ep++;
+              pv = ep;
+              while(ep[0] != '\0' && ep[0] != '"'){
+                ep++;
+              }
+            } else {
+              pv = ep;
+              while(ep[0] != '\0' && ep[0] != ' ' && ep[0] != '"' && ep[0] != ';'){
+                ep++;
+              }
+            }
+            cbmapput(attrs, "BOUNDARY", -1, pv, ep - pv, TRUE);
+          }
+        } while((ep = strchr(ep, ';')) != NULL);
+      } else {
+        cbmapput(attrs, "TYPE", -1, pv, -1, TRUE);
+      }
+    }
+    if((pv = cbmapget(attrs, "content-disposition", -1, NULL)) != NULL){
+      if((ep = strchr(pv, ';')) != NULL){
+        cbmapput(attrs, "DISPOSITION", -1, pv, ep - pv, TRUE);
+        do {
+          ep++;
+          while(ep[0] == ' '){
+            ep++;
+          }
+          if(cbstrfwimatch(ep, "filename=")){
+            ep += 9;
+            if(ep[0] == '"') ep++;
+            pv = ep;
+            while(ep[0] != '\0' && ep[0] != '"'){
+              ep++;
+            }
+            cbmapput(attrs, "FILENAME", -1, pv, ep - pv, TRUE);
+          } else if(cbstrfwimatch(ep, "name=")){
+            ep += 5;
+            if(ep[0] == '"') ep++;
+            pv = ep;
+            while(ep[0] != '\0' && ep[0] != '"'){
+              ep++;
+            }
+            cbmapput(attrs, "NAME", -1, pv, ep - pv, TRUE);
+          }
+        } while((ep = strchr(ep, ';')) != NULL);
+      } else {
+        cbmapput(attrs, "DISPOSITION", -1, pv, -1, TRUE);
+      }
+    }
+  }
+  if(sp) *sp = size;
+  CB_MEMDUP(rv, ptr, size);
+  return rv;
+}
+
+
+/* Split multipart data in MIME into its parts. */
+CBLIST *cbmimeparts(const char *ptr, int size, const char *boundary){
+  CBLIST *list;
+  const char *pv, *ep;
+  int i, blen;
+  assert(ptr && boundary);
+  if(size < 0) size = strlen(ptr);
+  CB_LISTOPEN(list);
+  if((blen = strlen(boundary)) < 1) return list;
+  pv = NULL;
+  for(i = 0; i < size; i++){
+    if(ptr[i] == '-' && ptr[i+1] == '-' && i + 2 + blen < size &&
+       cbstrfwmatch(ptr + i + 2, boundary) && strchr("\t\n\v\f\r ", ptr[i+2+blen])){
+      pv = ptr + i + 2 + blen;
+      if(*pv == '\r') pv++;
+      if(*pv == '\n') pv++;
+      size -= pv - ptr;
+      ptr = pv;
+      break;
+    }
+  }
+  if(!pv) return list;
+  for(i = 0; i < size; i++){
+    if(ptr[i] == '-' && ptr[i+1] == '-' && i + 2 + blen < size &&
+       cbstrfwmatch(ptr + i + 2, boundary) && strchr("\t\n\v\f\r -", ptr[i+2+blen])){
+      ep = ptr + i;
+      if(ep > ptr && ep[-1] == '\n') ep--;
+      if(ep > ptr && ep[-1] == '\r') ep--;
+      if(ep > pv) CB_LISTPUSH(list, pv, ep - pv);
+      pv = ptr + i + 2 + blen;
+      if(*pv == '\r') pv++;
+      if(*pv == '\n') pv++;
+    }
+  }
+  return list;
+}
+
+
+/* Encode a string with MIME encoding. */
+char *cbmimeencode(const char *str, const char *encname, int base){
+  char *buf, *wp, *enc;
+  int len;
+  assert(str && encname);
+  len = strlen(str);
+  CB_MALLOC(buf, len * 3 + strlen(encname) + 16);
+  wp = buf;
+  wp += sprintf(wp, "=?%s?%c?", encname, base ? 'B' : 'Q');
+  enc = base ? cbbaseencode(str, len) : cbquoteencode(str, len);
+  wp += sprintf(wp, "%s?=", enc);
+  free(enc);
+  return buf;
+}
+
+
+/* Decode a string encoded with MIME encoding. */
+char *cbmimedecode(const char *str, char *enp){
+  char *buf, *wp, *tmp, *dec;
+  const char *pv, *ep;
+  int quoted;
+  assert(str);
+  if(enp) sprintf(enp, "US-ASCII");
+  CB_MALLOC(buf, strlen(str) + 1);
+  wp = buf;
+  while(*str != '\0'){
+    if(cbstrfwmatch(str, "=?")){
+      str += 2;
+      pv = str;
+      if(!(ep = strchr(str, '?'))) continue;
+      if(enp && ep - pv < CB_ENCBUFSIZ){
+        memcpy(enp, pv, ep - pv);
+        enp[ep-pv] = '\0';
+      }
+      pv = ep + 1;
+      quoted = (*pv == 'Q' || *pv == 'q');
+      if(*pv != '\0') pv++;
+      if(*pv != '\0') pv++;
+      if(!(ep = strchr(pv, '?'))) continue;
+      CB_MEMDUP(tmp, pv, ep - pv);
+      dec = quoted ? cbquotedecode(tmp, NULL) : cbbasedecode(tmp, NULL);
+      wp += sprintf(wp, "%s", dec);
+      free(dec);
+      free(tmp);
+      str = ep + 1;
+      if(*str != '\0') str++;
+    } else {
+      *(wp++) = *str;
+      str++;
+    }
+  }
+  *wp = '\0';
+  return buf;
+}
+
+
+/* Split a string of CSV into rows. */
+CBLIST *cbcsvrows(const char *str){
+  CBLIST *list;
+  const char *pv;
+  int quoted;
+  assert(str);
+  CB_LISTOPEN(list);
+  pv = str;
+  quoted = FALSE;
+  while(TRUE){
+    if(*str == '"') quoted = !quoted;
+    if(!quoted && (*str == '\r' || *str == '\n')){
+      CB_LISTPUSH(list, pv, str - pv);
+      if(str[0] == '\r' && str[1] == '\n') str++;
+      str++;
+      pv = str;
+    } else if(*str == '\0'){
+      if(str > pv) CB_LISTPUSH(list, pv, str - pv);
+      break;
+    } else {
+      str++;
+    }
+  }
+  return list;
+}
+
+
+/* Split a string of a row of CSV into cells. */
+CBLIST *cbcsvcells(const char *str){
+  CBLIST *list, *uelist;
+  const char *pv;
+  char *tmp;
+  int i, quoted;
+  assert(str);
+  CB_LISTOPEN(list);
+  pv = str;
+  quoted = FALSE;
+  while(TRUE){
+    if(*str == '"') quoted = !quoted;
+    if(!quoted && *str == ','){
+      CB_LISTPUSH(list, pv, str - pv);
+      str++;
+      pv = str;
+    } else if(*str == '\0'){
+      CB_LISTPUSH(list, pv, str - pv);
+      break;
+    } else {
+      str++;
+    }
+  }
+  CB_LISTOPEN(uelist);
+  for(i = 0; i < CB_LISTNUM(list); i++){
+    tmp = cbcsvunescape(CB_LISTVAL(list, i));
+    CB_LISTPUSH(uelist, tmp, strlen(tmp));
+    free(tmp);
+  }
+  CB_LISTCLOSE(list);
+  return uelist;
+}
+
+
+/* Escape a string with the meta characters of CSV. */
+char *cbcsvescape(const char *str){
+  char *buf, *wp;
+  int i;
+  assert(str);
+  CB_MALLOC(buf, strlen(str) * 2 + 3);
+  wp = buf;
+  *(wp++) = '"';
+  for(i = 0; str[i] != '\0'; i++){
+    if(str[i] == '"') *(wp++) = '"';
+    *(wp++) = str[i];
+  }
+  *(wp++) = '"';
+  *wp = '\0';
+  return buf;
+}
+
+
+/* Unescape a string with the escaped meta characters of CSV. */
+char *cbcsvunescape(const char *str){
+  char *buf, *wp;
+  int i, len;
+  assert(str);
+  len = strlen(str);
+  if(str[0] == '"'){
+    str++;
+    len--;
+    if(str[len-1] == '"') len--;
+  }
+  CB_MALLOC(buf, len + 1);
+  wp = buf;
+  for(i = 0; i < len; i++){
+    if(str[i] == '"'){
+      if(str[i+1] == '"') *(wp++) = str[i++];
+    } else {
+      *(wp++) = str[i];
+    }
+  }
+  *wp = '\0';
+  return buf;
+}
+
+
+/* Split a string of XML into tags and text sections. */
+CBLIST *cbxmlbreak(const char *str, int cr){
+  CBLIST *list;
+  CBDATUM *datum;
+  int i, pv, tag;
+  char *ep;
+  assert(str);
+  CB_LISTOPEN(list);
+  i = 0;
+  pv = 0;
+  tag = FALSE;
+  while(TRUE){
+    if(str[i] == '\0'){
+      if(i > pv) CB_LISTPUSH(list, str + pv, i - pv);
+      break;
+    } else if(!tag && str[i] == '<'){
+      if(str[i+1] == '!' && str[i+2] == '-' && str[i+3] == '-'){
+        if(i > pv) CB_LISTPUSH(list, str + pv, i - pv);
+        if((ep = strstr(str + i, "-->")) != NULL){
+          if(!cr) CB_LISTPUSH(list, str + i, ep - str - i + 3);
+          i = ep - str + 2;
+          pv = i + 1;
+        }
+      } else if(str[i+1] == '!' && str[i+2] == '[' && cbstrfwimatch(str + i, "<![CDATA[")){
+        if(i > pv) CB_LISTPUSH(list, str + pv, i - pv);
+        if((ep = strstr(str + i, "]]>")) != NULL){
+          i += 9;
+          CB_DATUMOPEN(datum);
+          while(str + i < ep){
+            if(str[i] == '&'){
+              CB_DATUMCAT(datum, "&", 5);
+            } else if(str[i] == '<'){
+              CB_DATUMCAT(datum, "<", 4);
+            } else if(str[i] == '>'){
+              CB_DATUMCAT(datum, ">", 4);
+            } else {
+              CB_DATUMCAT(datum, str + i, 1);
+            }
+            i++;
+          }
+          if(CB_DATUMSIZE(datum) > 0) CB_LISTPUSH(list, CB_DATUMPTR(datum), CB_DATUMSIZE(datum));
+          CB_DATUMCLOSE(datum);
+          i = ep - str + 2;
+          pv = i + 1;
+        }
+      } else {
+        if(i > pv) CB_LISTPUSH(list, str + pv, i - pv);
+        tag = TRUE;
+        pv = i;
+      }
+    } else if(tag && str[i] == '>'){
+      if(i > pv) CB_LISTPUSH(list, str + pv, i - pv + 1);
+      tag = FALSE;
+      pv = i + 1;
+    }
+    i++;
+  }
+  return list;
+}
+
+
+/* Get the map of attributes of a XML tag. */
+CBMAP *cbxmlattrs(const char *str){
+  CBMAP *map;
+  const unsigned char *rp, *key, *val;
+  char *copy, *raw;
+  int ksiz, vsiz;
+  assert(str);
+  map = cbmapopenex(CB_MAPPBNUM);
+  rp = (unsigned char *)str;
+  while(*rp == '<' || *rp == '/' || *rp == '?' || *rp == '!' || *rp == ' '){
+    rp++;
+  }
+  key = rp;
+  while(*rp > 0x20 && *rp != '/' && *rp != '>'){
+    rp++;
+  }
+  cbmapput(map, "", -1, (char *)key, rp - key, FALSE);
+  while(*rp != '\0'){
+    while(*rp != '\0' && (*rp <= 0x20 || *rp == '/' || *rp == '?' || *rp == '>')){
+      rp++;
+    }
+    key = rp;
+    while(*rp > 0x20 && *rp != '/' && *rp != '>' && *rp != '='){
+      rp++;
+    }
+    ksiz = rp - key;
+    while(*rp != '\0' && (*rp == '=' || *rp <= 0x20)){
+      rp++;
+    }
+    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){
+      CB_MEMDUP(copy, (char *)val, vsiz);
+      raw = cbxmlunescape(copy);
+      cbmapput(map, (char *)key, ksiz, raw, -1, FALSE);
+      free(raw);
+      free(copy);
+    }
+  }
+  return map;
+}
+
+
+/* Escape a string with the meta characters of XML. */
+char *cbxmlescape(const char *str){
+  CBDATUM *datum;
+  assert(str);
+  CB_DATUMOPEN(datum);
+  while(*str != '\0'){
+    switch(*str){
+    case '&':
+      CB_DATUMCAT(datum, "&", 5);
+      break;
+    case '<':
+      CB_DATUMCAT(datum, "<", 4);
+      break;
+    case '>':
+      CB_DATUMCAT(datum, ">", 4);
+      break;
+    case '"':
+      CB_DATUMCAT(datum, """, 6);
+      break;
+    case '\'':
+      CB_DATUMCAT(datum, "'", 6);
+      break;
+    default:
+      CB_DATUMCAT(datum, str, 1);
+      break;
+    }
+    str++;
+  }
+  return cbdatumtomalloc(datum, NULL);
+}
+
+
+/* Unescape a string with the entity references of XML. */
+char *cbxmlunescape(const char *str){
+  CBDATUM *datum;
+  assert(str);
+  CB_DATUMOPEN(datum);
+  while(*str != '\0'){
+    if(*str == '&'){
+      if(cbstrfwmatch(str, "&")){
+        CB_DATUMCAT(datum, "&", 1);
+        str += 5;
+      } else if(cbstrfwmatch(str, "<")){
+        CB_DATUMCAT(datum, "<", 1);
+        str += 4;
+      } else if(cbstrfwmatch(str, ">")){
+        CB_DATUMCAT(datum, ">", 1);
+        str += 4;
+      } else if(cbstrfwmatch(str, """)){
+        CB_DATUMCAT(datum, "\"", 1);
+        str += 6;
+      } else if(cbstrfwmatch(str, "'")){
+        CB_DATUMCAT(datum, "'", 1);
+        str += 6;
+      } else {
+        CB_DATUMCAT(datum, str, 1);
+        str++;
+      }
+    } else {
+      CB_DATUMCAT(datum, str, 1);
+      str++;
+    }
+  }
+  return cbdatumtomalloc(datum, NULL);
+}
+
+
+/* Compress a serial object with ZLIB. */
+char *cbdeflate(const char *ptr, int size, int *sp){
+  assert(ptr && sp);
+  if(!_qdbm_deflate) return NULL;
+  return _qdbm_deflate(ptr, size, sp, _QDBM_ZMZLIB);
+}
+
+
+/* Decompress a serial object compressed with ZLIB. */
+char *cbinflate(const char *ptr, int size, int *sp){
+  assert(ptr && size >= 0);
+  if(!_qdbm_inflate) return NULL;
+  return _qdbm_inflate(ptr, size, sp, _QDBM_ZMZLIB);
+}
+
+
+/* Compress a serial object with GZIP. */
+char *cbgzencode(const char *ptr, int size, int *sp){
+  assert(ptr && sp);
+  if(!_qdbm_deflate) return NULL;
+  return _qdbm_deflate(ptr, size, sp, _QDBM_ZMGZIP);
+}
+
+
+/* Decompress a serial object compressed with GZIP. */
+char *cbgzdecode(const char *ptr, int size, int *sp){
+  assert(ptr && size >= 0);
+  if(!_qdbm_inflate) return NULL;
+  return _qdbm_inflate(ptr, size, sp, _QDBM_ZMGZIP);
+}
+
+
+/* Get the CRC32 checksum of a serial object. */
+unsigned int cbgetcrc(const char *ptr, int size){
+  assert(ptr);
+  if(!_qdbm_inflate) return 0;
+  return _qdbm_getcrc(ptr, size);
+}
+
+
+/* Compress a serial object with LZO. */
+char *cblzoencode(const char *ptr, int size, int *sp){
+  assert(ptr && sp);
+  if(!_qdbm_lzoencode) return NULL;
+  return _qdbm_lzoencode(ptr, size, sp);
+}
+
+
+/* Decompress a serial object compressed with LZO. */
+char *cblzodecode(const char *ptr, int size, int *sp){
+  assert(ptr && size >= 0);
+  if(!_qdbm_lzodecode) return NULL;
+  return _qdbm_lzodecode(ptr, size, sp);
+}
+
+
+/* Compress a serial object with BZIP2. */
+char *cbbzencode(const char *ptr, int size, int *sp){
+  assert(ptr && sp);
+  if(!_qdbm_bzencode) return NULL;
+  return _qdbm_bzencode(ptr, size, sp);
+}
+
+
+/* Decompress a serial object compressed with BZIP2. */
+char *cbbzdecode(const char *ptr, int size, int *sp){
+  assert(ptr && size >= 0);
+  if(!_qdbm_bzdecode) return NULL;
+  return _qdbm_bzdecode(ptr, size, sp);
+}
+
+
+/* Convert the character encoding of a string. */
+char *cbiconv(const char *ptr, int size, const char *icode, const char *ocode, int *sp, int *mp){
+  char *res;
+  assert(ptr && icode && ocode);
+  if(!_qdbm_iconv) return NULL;
+  if((res = _qdbm_iconv(ptr, size, icode, ocode, sp, mp)) != NULL) return res;
+  if(!cbstricmp(icode, ocode)){
+    if(sp) *sp = size;
+    if(mp) *mp = 0;
+    CB_MEMDUP(res, ptr, size < 0 ? strlen(ptr) : size);
+    return res;
+  }
+  return NULL;
+}
+
+
+/* Detect the encoding of a string automatically. */
+const char *cbencname(const char *ptr, int size){
+  assert(ptr);
+  if(!_qdbm_encname) return "ISO-8859-1";
+  return _qdbm_encname(ptr, size);
+}
+
+
+/* Get the jet lag of the local time in seconds. */
+int cbjetlag(void){
+  struct tm ts, *tp;
+  time_t t, gt, lt;
+  if((t = time(NULL)) < 0) return 0;
+  if(!(tp = _qdbm_gmtime(&t, &ts))) return 0;
+  if((gt = mktime(tp)) < 0) return 0;
+  if(!(tp = _qdbm_localtime(&t, &ts))) return 0;
+  if((lt = mktime(tp)) < 0) return 0;
+  return lt - gt;
+}
+
+
+/* Get the Gregorian calendar of a time. */
+void cbcalendar(time_t t, int jl, int *yearp, int *monp, int *dayp,
+                int *hourp, int *minp, int *secp){
+  struct tm ts, *tp;
+  if(t < 0) t = time(NULL);
+  t += jl;
+  if(!(tp = _qdbm_gmtime(&t, &ts))) return;
+  if(yearp) *yearp = tp->tm_year + 1900;
+  if(monp) *monp = tp->tm_mon + 1;
+  if(dayp) *dayp = tp->tm_mday;
+  if(hourp) *hourp = tp->tm_hour;
+  if(minp) *minp = tp->tm_min;
+  if(secp) *secp = tp->tm_sec;
+}
+
+
+/* Get the day of week of a date. */
+int cbdayofweek(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;
+}
+
+
+/* Get the string for a date in W3CDTF. */
+char *cbdatestrwww(time_t t, int jl){
+  char date[CB_DATEBUFSIZ], tzone[CB_DATEBUFSIZ], *rv;
+  int year, mon, day, hour, min, sec;
+  cbcalendar(t, jl, &year, &mon, &day, &hour, &min, &sec);
+  jl /= 60;
+  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(date, "%04d-%02d-%02dT%02d:%02d:%02d%s", year, mon, day, hour, min, sec, tzone);
+  CB_MEMDUP(rv, date, strlen(date));
+  return rv;
+}
+
+
+/* Get the string for a date in RFC 1123 format. */
+char *cbdatestrhttp(time_t t, int jl){
+  char date[CB_DATEBUFSIZ], *wp, *rv;
+  int year, mon, day, hour, min, sec;
+  cbcalendar(t, jl, &year, &mon, &day, &hour, &min, &sec);
+  jl /= 60;
+  wp = date;
+  switch(cbdayofweek(year, mon, day)){
+  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 ", day);
+  switch(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 ", year, hour, min, sec);
+  if(jl == 0){
+    wp += sprintf(wp, "GMT");
+  } else if(jl < 0){
+    jl *= -1;
+    wp += sprintf(wp, "-%02d%02d", jl / 60, jl % 60);
+  } else {
+    wp += sprintf(wp, "+%02d%02d", jl / 60, jl % 60);
+  }
+  CB_MEMDUP(rv, date, strlen(date));
+  return rv;
+}
+
+
+/* Get the time value of a date string in decimal, W3CDTF, or RFC 1123. */
+time_t cbstrmktime(const char *str){
+  const char *crp;
+  char *pv, *rp;
+  int len, clen;
+  time_t t;
+  struct tm ts;
+  assert(str);
+  while(*str > '\0' && *str <= ' '){
+    str++;
+  }
+  if(*str == '\0') return -1;
+  if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) return (time_t)strtol(str + 2, NULL, 16);
+  memset(&ts, 0, sizeof(struct tm));
+  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;
+  len = strlen(str);
+  t = (time_t)strtol(str, &pv, 10);
+  if(*(signed char *)pv >= '\0' && *pv <= ' '){
+    while(*pv > '\0' && *pv <= ' '){
+      pv++;
+    }
+    if(*pv == '\0') return t;
+  }
+  if((pv[0] == 's' || pv[0] == 'S') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+    return t;
+  if((pv[0] == 'm' || pv[0] == 'M') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+    return t * 60;
+  if((pv[0] == 'h' || pv[0] == 'H') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+    return t * 60 * 60;
+  if((pv[0] == 'd' || pv[0] == 'D') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+    return t * 60 * 60 * 24;
+  if(len > 4 && str[4] == '-'){
+    ts.tm_year = atoi(str) - 1900;
+    if((pv = strchr(str, '-')) != NULL && pv - str == 4){
+      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);
+        }
+      }
+    }
+    ts.tm_sec += cbjetlag();
+    return mktime(&ts);
+  }
+  if(len > 4 && str[4] == '/'){
+    ts.tm_year = atoi(str) - 1900;
+    if((pv = strchr(str, '/')) != NULL && pv - str == 4){
+      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);
+        }
+      }
+    }
+    ts.tm_sec += cbjetlag();
+    return mktime(&ts);
+  }
+  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(cbstrfwimatch(crp, "Jan")){
+    ts.tm_mon = 0;
+  } else if(cbstrfwimatch(crp, "Feb")){
+    ts.tm_mon = 1;
+  } else if(cbstrfwimatch(crp, "Mar")){
+    ts.tm_mon = 2;
+  } else if(cbstrfwimatch(crp, "Apr")){
+    ts.tm_mon = 3;
+  } else if(cbstrfwimatch(crp, "May")){
+    ts.tm_mon = 4;
+  } else if(cbstrfwimatch(crp, "Jun")){
+    ts.tm_mon = 5;
+  } else if(cbstrfwimatch(crp, "Jul")){
+    ts.tm_mon = 6;
+  } else if(cbstrfwimatch(crp, "Aug")){
+    ts.tm_mon = 7;
+  } else if(cbstrfwimatch(crp, "Sep")){
+    ts.tm_mon = 8;
+  } else if(cbstrfwimatch(crp, "Oct")){
+    ts.tm_mon = 9;
+  } else if(cbstrfwimatch(crp, "Nov")){
+    ts.tm_mon = 10;
+  } else if(cbstrfwimatch(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){
+    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;
+        }
+      }
+    }
+    ts.tm_sec += cbjetlag();
+    return mktime(&ts);
+  }
+  return -1;
+}
+
+
+/* Get user and system processing times. */
+void cbproctime(double *usrp, double *sysp){
+  struct tms buf;
+  times(&buf);
+  if(usrp) *usrp = (double)buf.tms_utime / sysconf(_SC_CLK_TCK);
+  if(sysp) *sysp = (double)buf.tms_stime / sysconf(_SC_CLK_TCK);
+}
+
+
+/* Ensure that the standard I/O is binary mode. */
+void cbstdiobin(void){
+  if(setmode(0, O_BINARY) == -1 || setmode(1, O_BINARY) == -1 || setmode(2, O_BINARY) == -1)
+    cbmyfatal("setmode failed");
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Show error message on the standard error output and exit. */
+void *cbmyfatal(const char *message){
+  char buf[CB_MSGBUFSIZ];
+  assert(message);
+  if(cbfatalfunc){
+    cbfatalfunc(message);
+  } else {
+    sprintf(buf, "fatal error: %s\n", message);
+    write(2, buf, strlen(buf));
+  }
+  exit(1);
+  return NULL;
+}
+
+
+/* Create a datum handle from an allocated region. */
+CBDATUM *cbdatumopenbuf(char *ptr, int size){
+  CBDATUM *datum;
+  assert(ptr && size >= 0);
+  CB_REALLOC(ptr, size + 1);
+  CB_MALLOC(datum, sizeof(*datum));
+  datum->dptr = ptr;
+  datum->dptr[size] = '\0';
+  datum->dsize = size;
+  datum->asize = size;
+  return datum;
+}
+
+
+/* Set a buffer to a datum handle. */
+void cbdatumsetbuf(CBDATUM *datum, char *ptr, int size){
+  assert(datum && ptr && size >= 0);
+  free(datum->dptr);
+  CB_REALLOC(ptr, size + 1);
+  datum->dptr = ptr;
+  datum->dptr[size] = '\0';
+  datum->dsize = size;
+  datum->asize = size;
+}
+
+
+/* Add an allocated element at the end of a list. */
+void cblistpushbuf(CBLIST *list, char *ptr, int size){
+  int index;
+  assert(list && ptr && size >= 0);
+  index = list->start + list->num;
+  if(index >= list->anum){
+    list->anum *= 2;
+    CB_REALLOC(list->array, list->anum * sizeof(list->array[0]));
+  }
+  list->array[index].dptr = ptr;
+  list->array[index].dsize = size;
+  list->num++;
+}
+
+
+/* Get a map handle with specifying the number of buckets. */
+CBMAP *cbmapopenex(int bnum){
+  CBMAP *map;
+  int i;
+  assert(bnum > 0);
+  CB_MALLOC(map, sizeof(*map));
+  CB_MALLOC(map->buckets, sizeof(map->buckets[0]) * bnum);
+  for(i = 0; i < bnum; i++){
+    map->buckets[i] = NULL;
+  }
+  map->first = NULL;
+  map->last = NULL;
+  map->cur = NULL;
+  map->bnum = bnum;
+  map->rnum = 0;
+  return map;
+}
+
+
+
+/*************************************************************************************************
+ * private objects
+ *************************************************************************************************/
+
+
+/* Handler to invoke the global garbage collector. */
+static void cbggchandler(void){
+  cbggckeeper(NULL, NULL);
+}
+
+
+/* Manage resources of the global garbage collector.
+   `ptr' specifies the pointer to add to the collection.  If it is `NULL', all resources are
+   released.
+   `func' specifies the pointer to the function to release the resources. */
+static void cbggckeeper(void *ptr, void (*func)(void *)){
+  static void **parray = NULL;
+  static void (**farray)(void *) = NULL;
+  static int onum = 0;
+  static int asiz = CB_GCUNIT;
+  int i;
+  if(!ptr){
+    if(!parray) return;
+    for(i = onum - 1; i >= 0; i--){
+      farray[i](parray[i]);
+    }
+    free(parray);
+    free(farray);
+    parray = NULL;
+    farray = NULL;
+    onum = 0;
+    asiz = CB_GCUNIT;
+    return;
+  }
+  if(!parray){
+    CB_MALLOC(parray, sizeof(void *) * asiz);
+    CB_MALLOC(farray, sizeof(void *) * asiz);
+    if(atexit(cbggchandler) != 0) cbmyfatal("gc failed");
+  }
+  if(onum >= asiz){
+    asiz *= 2;
+    CB_REALLOC(parray, sizeof(void *) * asiz);
+    CB_REALLOC(farray, sizeof(void *) * asiz);
+  }
+  parray[onum] = ptr;
+  farray[onum] = func;
+  onum++;
+}
+
+
+/* Utility function for quick sort.
+   `bp' specifies the pointer to the pointer to an array.
+   `nmemb' specifies the number of elements of the array.
+   `size' specifies the size of each element.
+   `pswap' specifies the pointer to the swap region for a pivot.
+   `vswap' specifies the pointer to the swap region for elements.
+   `compar' specifies the pointer to comparing function. */
+static void cbqsortsub(char *bp, int nmemb, int size, char *pswap, char *vswap,
+                       int(*compar)(const void *, const void *)){
+  int top, bottom;
+  assert(bp && nmemb >= 0 && size > 0 && pswap && vswap && compar);
+  if(nmemb < 10){
+    if(nmemb > 1) cbisort(bp, nmemb, size, compar);
+    return;
+  }
+  top = 0;
+  bottom = nmemb - 1;
+  memcpy(pswap, bp + (nmemb / 2) * size, size);
+  while(top - 1 < bottom){
+    if(compar(bp + top * size, pswap) < 0){
+      top++;
+    } else if(compar(bp + bottom * size, pswap) > 0){
+      bottom--;
+    } else {
+      if(top != bottom){
+        memcpy(vswap, bp + top * size, size);
+        memcpy(bp + top * size, bp + bottom * size, size);
+        memcpy(bp + bottom * size, vswap, size);
+      }
+      top++;
+      bottom--;
+    }
+  }
+  cbqsortsub(bp, top, size, pswap, vswap, compar);
+  cbqsortsub(bp + (bottom + 1) * size, nmemb - bottom - 1, size, pswap, vswap, compar);
+}
+
+
+/* Compare two list elements.
+   `a' specifies the pointer to one element.
+   `b' specifies the pointer to the other element.
+   The return value is positive if a is big, negative if b is big, else, it is 0. */
+static int cblistelemcmp(const void *a, const void *b){
+  int i, size;
+  CBLISTDATUM *ap, *bp;
+  char *ao, *bo;
+  assert(a && b);
+  ap = (CBLISTDATUM *)a;
+  bp = (CBLISTDATUM *)b;
+  ao = ap->dptr;
+  bo = bp->dptr;
+  size = ap->dsize < bp->dsize ? ap->dsize : bp->dsize;
+  for(i = 0; i < size; i++){
+    if(ao[i] > bo[i]) return 1;
+    if(ao[i] < bo[i]) return -1;
+  }
+  return ap->dsize - bp->dsize;
+}
+
+
+/* Compare two keys.
+   `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 cbkeycmp(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);
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/cabin.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/cabin.h
===================================================================
--- box/trunk/qdbm/cabin.h	                        (rev 0)
+++ box/trunk/qdbm/cabin.h	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,1544 @@
+/*************************************************************************************************
+ * The utitlity API of QDBM
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _CABIN_H                         /* duplication check */
+#define _CABIN_H
+
+#if defined(__cplusplus)                 /* export for C++ */
+extern "C" {
+#endif
+
+
+#include <stdlib.h>
+#include <time.h>
+
+
+#if defined(_MSC_VER) && !defined(QDBM_INTERNAL) && !defined(QDBM_STATIC)
+#define MYEXTERN extern __declspec(dllimport)
+#else
+#define MYEXTERN extern
+#endif
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+#define CB_DATUMUNIT   12                /* allocation unit size of a datum handle */
+#define CB_LISTUNIT    64                /* allocation unit number of a list handle */
+#define CB_MAPBNUM     4093              /* bucket size of a map handle */
+
+typedef struct {                         /* type of structure for a basic datum */
+  char *dptr;                            /* pointer to the region */
+  int dsize;                             /* size of the region */
+  int asize;                             /* size of the allocated region */
+} CBDATUM;
+
+typedef struct {                         /* type of structure for an element of a list */
+  char *dptr;                            /* pointer to the region */
+  int dsize;                             /* size of the effective region */
+} CBLISTDATUM;
+
+typedef struct {                         /* type of structure for a list */
+  CBLISTDATUM *array;                    /* array of data */
+  int anum;                              /* number of the elements of the array */
+  int start;                             /* start index of using elements */
+  int num;                               /* number of using elements */
+} CBLIST;
+
+typedef struct _CBMAPDATUM {             /* 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 */
+  int hash;                              /* second hash value */
+  struct _CBMAPDATUM *left;              /* pointer to the left child */
+  struct _CBMAPDATUM *right;             /* pointer to the right child */
+  struct _CBMAPDATUM *prev;              /* pointer to the previous element */
+  struct _CBMAPDATUM *next;              /* pointer to the next element */
+} CBMAPDATUM;
+
+typedef struct {                         /* type of structure for a map */
+  CBMAPDATUM **buckets;                  /* bucket array */
+  CBMAPDATUM *first;                     /* pointer to the first element */
+  CBMAPDATUM *last;                      /* pointer to the last element */
+  CBMAPDATUM *cur;                       /* pointer to the current element */
+  int bnum;                              /* number of buckets */
+  int rnum;                              /* number of records */
+} CBMAP;
+
+typedef struct {                         /* type of structure for a heap */
+  char *base;                            /* base pointer */
+  char *swap;                            /* region for swapping */
+  int size;                              /* size of each record */
+  int num;                               /* currnet number of records */
+  int max;                               /* maximum number of records */
+  int(*compar)(const void *, const void *);  /* comparing function */
+} CBHEAP;
+
+
+/* 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. */
+MYEXTERN void (*cbfatalfunc)(const char *);
+
+
+/* Allocate a region on memory.
+   `size' specifies the size of the region.
+   The return value is the pointer to the allocated region.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use. */
+void *cbmalloc(size_t size);
+
+
+/* Re-allocate a region on memory.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.
+   The return value is the pointer to the re-allocated region.
+   Because the region of the return value is allocated with the `realloc' call, it should be
+   released with the `free' call if it is no longer in use. */
+void *cbrealloc(void *ptr, size_t size);
+
+
+/* Duplicate a region on memory.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   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 if
+   it is no longer in use. */
+char *cbmemdup(const char *ptr, int size);
+
+
+/* Free a region on memory.
+   `ptr' specifies the pointer to a 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 cbfree(void *ptr);
+
+
+/* Register the pointer or handle of an object to the global garbage collector.
+   `ptr' specifies the pointer or handle of an object.
+   `func' specifies the pointer to a function to release resources of the object.  Its argument
+   is the pointer or handle of the object to release.
+   This function assures that resources of an object are released when the process exits
+   normally by returning from the `main' function or calling the `exit' function. */
+void cbglobalgc(void *ptr, void (*func)(void *));
+
+
+/* Exercise the global garbage collector explicitly.
+   Note that you should not use objects registered to the global garbage collector any longer
+   after calling this function.  Because the global garbage collecter is initialized and you
+   can register new objects into it. */
+void cbggcsweep(void);
+
+
+/* Check availability of allocation of the virtual memory.
+   `size' specifies the size of region to be allocated newly.
+   The return value is true if allocation should be success, or false if not. */
+int cbvmemavail(size_t size);
+
+
+/* Sort an array using insert sort.
+   `base' spacifies the pointer to an array.
+   `nmemb' specifies the number of elements of the array.
+   `size' specifies the size of each element.
+   `compar' specifies the pointer to comparing function.  The two arguments specify the pointers
+   of elements.  The comparing function should returns positive if the former is big, negative
+   if the latter is big, 0 if both are equal.
+   Insert sort is useful only if most elements have been sorted already. */
+void cbisort(void *base, int nmemb, int size, int(*compar)(const void *, const void *));
+
+
+/* Sort an array using shell sort.
+   `base' spacifies the pointer to an array.
+   `nmemb' specifies the number of elements of the array.
+   `size' specifies the size of each element.
+   `compar' specifies the pointer to comparing function.  The two arguments specify the pointers
+   of elements.  The comparing function should returns positive if the former is big, negative
+   if the latter is big, 0 if both are equal.
+   If most elements have been sorted, shell sort may be faster than heap sort or quick sort. */
+void cbssort(void *base, int nmemb, int size, int(*compar)(const void *, const void *));
+
+
+/* Sort an array using heap sort.
+   `base' spacifies the pointer to an array.
+   `nmemb' specifies the number of elements of the array.
+   `size' specifies the size of each element.
+   `compar' specifies the pointer to comparing function.  The two arguments specify the pointers
+   of elements.  The comparing function should returns positive if the former is big, negative
+   if the latter is big, 0 if both are equal.
+   Although heap sort is robust against bias of input, quick sort is faster in most cases. */
+void cbhsort(void *base, int nmemb, int size, int(*compar)(const void *, const void *));
+
+
+/* Sort an array using quick sort.
+   `base' spacifies the pointer to an array.
+   `nmemb' specifies the number of elements of the array.
+   `size' specifies the size of each element.
+   `compar' specifies the pointer to comparing function.  The two arguments specify the pointers
+   of elements.  The comparing function should returns positive if the former is big, negative
+   if the latter is big, 0 if both are equal.
+   Being sensitive to bias of input, quick sort is the fastest sorting algorithm. */
+void cbqsort(void *base, int nmemb, int size, int(*compar)(const void *, const void *));
+
+
+/* Compare two strings with case insensitive evaluation.
+   `astr' specifies the pointer of one string.
+   `astr' specifies the pointer 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.
+   Upper cases and lower cases of alphabets in ASCII code are not distinguished. */
+int cbstricmp(const char *astr, const char *bstr);
+
+
+/* Check whether a string begins with a key.
+   `str' specifies the pointer of a target string.
+   `key' specifies the pointer of a forward matching key string.
+   The return value is true if the target string begins with the key, else, it is false. */
+int cbstrfwmatch(const char *str, const char *key);
+
+
+/* Check whether a string begins with a key, with case insensitive evaluation.
+   `str' specifies the pointer of a target string.
+   `key' specifies the pointer of a forward matching key string.
+   The return value is true if the target string begins with the key, else, it is false.
+   Upper cases and lower cases of alphabets in ASCII code are not distinguished. */
+int cbstrfwimatch(const char *str, const char *key);
+
+
+/* Check whether a string ends with a key.
+   `str' specifies the pointer of a target string.
+   `key' specifies the pointer of a backward matching key string.
+   The return value is true if the target string ends with the key, else, it is false. */
+int cbstrbwmatch(const char *str, const char *key);
+
+
+/* Check whether a string ends with a key, with case insensitive evaluation.
+   `str' specifies the pointer of a target string.
+   `key' specifies the pointer of a backward matching key string.
+   The return value is true if the target string ends with the key, else, it is false.
+   Upper cases and lower cases of alphabets in ASCII code are not distinguished. */
+int cbstrbwimatch(const char *str, const char *key);
+
+
+/* Locate a substring in a string using KMP method.
+   `haystack' specifies the pointer of a target string.
+   `needle' specifies the pointer of a substring to be found.
+   The return value is the pointer to the beginning of the substring or `NULL' if the substring
+   is not found.
+   In most cases, `strstr' as a built-in function of the compiler is faster than this function. */
+char *cbstrstrkmp(const char *haystack, const char *needle);
+
+
+/* Locate a substring in a string using BM method.
+   `haystack' specifies the pointer of a target string.
+   `needle' specifies the pointer of a substring to be found.
+   The return value is the pointer to the beginning of the substring or `NULL' if the substring
+   is not found.
+   In most cases, `strstr' as a built-in function of the compiler is faster than this function. */
+char *cbstrstrbm(const char *haystack, const char *needle);
+
+
+/* Convert the letters of a string to upper case.
+   `str' specifies the pointer of a string to convert.
+   The return value is the pointer to the string. */
+char *cbstrtoupper(char *str);
+
+
+/* Convert the letters of a string to lower case.
+   `str' specifies the pointer of a string to convert.
+   The return value is the pointer to the string. */
+char *cbstrtolower(char *str);
+
+
+/* Cut space characters at head or tail of a string.
+   `str' specifies the pointer of a string to convert.
+   The return value is the pointer to the string. */
+char *cbstrtrim(char *str);
+
+
+/* Squeeze space characters in a string and trim it.
+   `str' specifies the pointer of a string to convert.
+   The return value is the pointer to the string. */
+char *cbstrsqzspc(char *str);
+
+
+/* Count the number of characters in a string of UTF-8.
+   `str' specifies the pointer of a string of UTF-8.
+   The return value is the number of characters in the string. */
+int cbstrcountutf(const char *str);
+
+
+/* Cut a string of UTF-8 at the specified number of characters.
+   `str' specifies the pointer of a string of UTF-8.
+   `num' specifies the number of characters to be kept.
+   The return value is the pointer to the string. */
+char *cbstrcututf(char *str, int num);
+
+
+/* Get a datum handle.
+   `ptr' specifies the pointer to the region of the initial content.  If it is `NULL', an empty
+   datum is created.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   The return value is a datum handle. */
+CBDATUM *cbdatumopen(const char *ptr, int size);
+
+
+/* Copy a datum.
+   `datum' specifies a datum handle.
+   The return value is a new datum handle. */
+CBDATUM *cbdatumdup(const CBDATUM *datum);
+
+
+/* Free a datum handle.
+   `datum' specifies a datum handle.
+   Because the region of a closed handle is released, it becomes impossible to use the handle. */
+void cbdatumclose(CBDATUM *datum);
+
+
+/* Concatenate a datum and a region.
+   `datum' specifies a datum handle.
+   `ptr' specifies the pointer to the region to be appended.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'. */
+void cbdatumcat(CBDATUM *datum, const char *ptr, int size);
+
+
+/* Get the pointer of the region of a datum.
+   `datum' specifies a datum handle.
+   The return value is the pointer of the region of a datum.
+   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 char *cbdatumptr(const CBDATUM *datum);
+
+
+/* Get the size of the region of a datum.
+   `datum' specifies a datum handle.
+   The return value is the size of the region of a datum. */
+int cbdatumsize(const CBDATUM *datum);
+
+
+/* Change the size of the region of a datum.
+   `datum' specifies a datum handle.
+   `size' specifies the new size of the region.
+   If the new size is bigger than the one of old, the surplus region is filled with zero codes. */
+void cbdatumsetsize(CBDATUM *datum, int size);
+
+
+/* Perform formatted output into a datum.
+   `format' specifies a 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.  `:' works
+   as with `s' but performs MIME encoding as UTF-8.  The other conversion character work as with
+   each original. */
+void cbdatumprintf(CBDATUM *datum, const char *format, ...);
+
+
+/* Convert a datum to an allocated region.
+   `datum' specifies a datum handle.
+   `sp' specifies the pointer to a variable to 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 region of the datum.
+   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 if it
+   is no longer in use.  Because the region of the original datam is released, it should not be
+   released again. */
+char *cbdatumtomalloc(CBDATUM *datum, int *sp);
+
+
+/* Get a list handle.
+   The return value is a list handle. */
+CBLIST *cblistopen(void);
+
+
+/* Copy a list.
+   `list' specifies a list handle.
+   The return value is a new list handle. */
+CBLIST *cblistdup(const CBLIST *list);
+
+
+/* Close a list handle.
+   `list' specifies a list handle.
+   Because the region of a closed handle is released, it becomes impossible to use the handle. */
+void cblistclose(CBLIST *list);
+
+
+/* Get the number of elements of a list.
+   `list' specifies a list handle.
+   The return value is the number of elements of the list. */
+int cblistnum(const CBLIST *list);
+
+
+/* Get the pointer to the region of an element of a list.
+   `list' specifies a list handle.
+   `index' specifies the index of an element.
+   `sp' specifies the pointer to a variable to 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 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 char *cblistval(const CBLIST *list, int index, int *sp);
+
+
+/* Add an element at the end of a list.
+   `list' specifies a list handle.
+   `ptr' specifies the pointer to the region of an element.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'. */
+void cblistpush(CBLIST *list, const char *ptr, int size);
+
+
+/* Remove an element of the end of a list.
+   `list' specifies a list handle.
+   `sp' specifies the pointer to a variable to 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 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.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call if it
+   is no longer in use.  If the list is empty, the return value is `NULL'. */
+char *cblistpop(CBLIST *list, int *sp);
+
+
+/* Add an element at the top of a list.
+   `list' specifies a list handle.
+   `ptr' specifies the pointer to the region of an element.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'. */
+void cblistunshift(CBLIST *list, const char *ptr, int size);
+
+
+/* Remove an element of the top of a list.
+   `list' specifies a list handle.
+   `sp' specifies the pointer to a variable to 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 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.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call if it
+   is no longer in use.  If the list is empty, the return value is `NULL'. */
+char *cblistshift(CBLIST *list, int *sp);
+
+
+/* Add an element at the specified location of a list.
+   `list' specifies a list handle.
+   `index' specifies the index of an element.
+   `ptr' specifies the pointer to the region of the element.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'. */
+void cblistinsert(CBLIST *list, int index, const char *ptr, int size);
+
+
+/* Remove an element at the specified location of a list.
+   `list' specifies a list handle.
+   `index' specifies the index of an element.
+   `sp' specifies the pointer to a variable to 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 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.  Because the region of the return
+   value is allocated with the `malloc' call, it should be released with the `free' call if 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 *cblistremove(CBLIST *list, int index, int *sp);
+
+
+/* Overwrite an element at the specified location of a list.
+   `list' specifies a list handle.
+   `index' specifies the index of an element.
+   `ptr' specifies the pointer to the region of the new content.
+   `size' specifies the size of the new content.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   If `index' is equal to or more than the number of elements, this function has no effect. */
+void cblistover(CBLIST *list, int index, const char *ptr, int size);
+
+
+/* Sort elements of a list in lexical order.
+   `list' specifies a list handle.
+   Quick sort is used for sorting. */
+void cblistsort(CBLIST *list);
+
+
+/* Search a list for an element using liner search.
+   `list' specifies a list handle.
+   `ptr' specifies the pointer to the region of a key.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   The return value is the index of a corresponding element or -1 if there is no corresponding
+   element.  If two or more elements corresponds, the former returns. */
+int cblistlsearch(const CBLIST *list, const char *ptr, int size);
+
+
+/* Search a list for an element using binary search.
+   `list' specifies a list handle.  It should be sorted in lexical order.
+   `ptr' specifies the pointer to the region of a key.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   The return value is the index of a corresponding element or -1 if there is no corresponding
+   element.  If two or more elements corresponds, which returns is not defined. */
+int cblistbsearch(const CBLIST *list, const char *ptr, int size);
+
+
+/* Serialize a list into a byte array.
+   `list' specifies a list handle.
+   `sp' specifies the pointer to a variable to 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 if it is no longer in use. */
+char *cblistdump(const CBLIST *list, int *sp);
+
+
+/* Redintegrate a serialized list.
+   `ptr' specifies the pointer to a byte array.
+   `size' specifies the size of the region.
+   The return value is a new list handle. */
+CBLIST *cblistload(const char *ptr, int size);
+
+
+/* Get a map handle.
+   The return value is a map handle. */
+CBMAP *cbmapopen(void);
+
+
+/* Copy a map.
+   `map' specifies a map handle.
+   The return value is a new map handle.
+   The iterator of the source map is initialized. */
+CBMAP *cbmapdup(CBMAP *map);
+
+
+/* Close a map handle.
+   `map' specifies a map handle.
+   Because the region of a closed handle is released, it becomes impossible to use the handle. */
+void cbmapclose(CBMAP *map);
+
+
+/* Store a record into a map.
+   `map' specifies a map handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region of the value.  If it is negative, the size is
+   assigned with `strlen(vbuf)'.
+   `over' specifies whether the value of the duplicated record is overwritten or not.
+   If `over' is false and the key is duplicated, the return value is false, else, it is true. */
+int cbmapput(CBMAP *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int over);
+
+
+/* Concatenate a value at the end of the value of the existing record.
+   `map' specifies a map handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region of the value.  If it is negative, the size is
+   assigned with `strlen(vbuf)'.
+   If there is no corresponding record, a new record is created. */
+void cbmapputcat(CBMAP *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz);
+
+
+/* Delete a record in a map.
+   `map' specifies a map handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is true.  False is returned when no record corresponds to
+   the specified key. */
+int cbmapout(CBMAP *map, const char *kbuf, int ksiz);
+
+
+/* Retrieve a record in a map.
+   `map' specifies a map handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   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 char *cbmapget(const CBMAP *map, const char *kbuf, int ksiz, int *sp);
+
+
+/* Move a record to the edge of a map.
+   `map' specifies a map handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `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. */
+int cbmapmove(CBMAP *map, const char *kbuf, int ksiz, int head);
+
+
+/* Initialize the iterator of a map.
+   `map' specifies a map handle.
+   The iterator is used in order to access the key of every record stored in a map. */
+void cbmapiterinit(CBMAP *map);
+
+
+/* Get the next key of the iterator of a map.
+   `map' specifies a map handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   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.  The order of iteration is assured
+   to be the same of the one of storing. */
+const char *cbmapiternext(CBMAP *map, int *sp);
+
+
+/* Get the value binded to the key fetched from the iterator of a map.
+   `kbuf' specifies the pointer to the region of a iteration key.
+   `sp' specifies the pointer to a variable to 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 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 char *cbmapiterval(const char *kbuf, int *sp);
+
+
+/* Get the number of the records stored in a map.
+   `map' specifies a map handle.
+   The return value is the number of the records stored in the map. */
+int cbmaprnum(const CBMAP *map);
+
+
+/* Get the list handle contains all keys in a map.
+   `map' specifies a map handle.
+   The return value is the list handle contains all keys in the map.
+   Because the handle of the return value is opened with the function `cblistopen', it should
+   be closed with the function `cblistclose' if it is no longer in use. */
+CBLIST *cbmapkeys(CBMAP *map);
+
+
+/* Get the list handle contains all values in a map.
+   `map' specifies a map handle.
+   The return value is the list handle contains all values in the map.
+   Because the handle of the return value is opened with the function `cblistopen', it should
+   be closed with the function `cblistclose' if it is no longer in use. */
+CBLIST *cbmapvals(CBMAP *map);
+
+
+/* Serialize a map into a byte array.
+   `map' specifies a map handle.
+   `sp' specifies the pointer to a variable to 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 if it is no longer in use. */
+char *cbmapdump(CBMAP *map, int *sp);
+
+
+/* Redintegrate a serialized map.
+   `ptr' specifies the pointer to a byte array.
+   `size' specifies the size of the region.
+   The return value is a new map handle. */
+CBMAP *cbmapload(const char *ptr, int size);
+
+
+/* Extract a record from a serialized map.
+   `ptr' specifies the pointer to a byte array.
+   `size' specifies the size of the region.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   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. */
+char *cbmaploadone(const char *ptr, int size, const char *kbuf, int ksiz, int *sp);
+
+
+/* Get a heap handle.
+   `size' specifies the size of each record.
+   `max' specifies the maximum number of records in the heap.
+   `compar' specifies the pointer to comparing function.  The two arguments specify the pointers
+   of records.  The comparing function should returns positive if the former is big, negative
+   if the latter is big, 0 if both are equal.
+   The return value is a heap handle. */
+CBHEAP *cbheapopen(int size, int max, int(*compar)(const void *, const void *));
+
+
+/* Copy a heap.
+   `heap' specifies a heap handle.
+   The return value is a new heap handle. */
+CBHEAP *cbheapdup(CBHEAP *heap);
+
+
+/* Close a heap handle.
+   `heap' specifies a heap handle.
+   Because the region of a closed handle is released, it becomes impossible to use the handle. */
+void cbheapclose(CBHEAP *heap);
+
+
+/* Get the number of the records stored in a heap.
+   `heap' specifies a heap handle.
+   The return value is the number of the records stored in the heap. */
+int cbheapnum(CBHEAP *heap);
+
+
+/* Insert a record into a heap.
+   `heap' specifies a heap handle.
+   `ptr' specifies the pointer to the region of a record.
+   The return value is true if the record is added, else false.
+   If the new record is bigger than the biggest existing regord, the new record is not added.
+   If the new record is added and the number of records exceeds the maximum number, the biggest
+   existing record is removed. */
+int cbheapinsert(CBHEAP *heap, const void *ptr);
+
+
+/* Get the pointer to the region of a record in a heap.
+   `heap' specifies a heap handle.
+   `index' specifies the index of a record.
+   The return value is the pointer to the region of the record.
+   If `index' is equal to or more than the number of records, the return value is `NULL'.  Note
+   that records are organized by the nagative order the comparing function. */
+const void *cbheapval(CBHEAP *heap, int index);
+
+
+/* Convert a heap to an allocated region.
+   `heap' specifies a heap handle.
+   `np' specifies the pointer to a variable to which the number of records of the return value
+   is assigned.  If it is `NULL', it is not used.
+   The return value is the pointer to the region of the heap.  Records are sorted.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use.  Because the region of the original
+   heap is released, it should not be released again. */
+void *cbheaptomalloc(CBHEAP *heap, int *np);
+
+
+/* Allocate a formatted string on memory.
+   `format' specifies a printf-like format string.  The conversion character `%' can be used
+   with such flag characters as `d', `o', `u', `x', `X', `e', `E', `f', `g', `G', `c', `s', and
+   `%'.  Specifiers of the field length and the precision can be put between the conversion
+   characters and the flag characters.  The specifiers consist of decimal characters, `.', `+',
+   `-', and the space character.
+   The other arguments are used according to the format string.
+   The return value is the pointer to the allocated region of 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 it is no longer in use. */
+char *cbsprintf(const char *format, ...);
+
+
+/* Replace some patterns in a string.
+   `str' specifies the pointer to a source string.
+   `pairs' specifies the handle of a map composed of pairs of replacement.  The key of each pair
+   specifies a pattern before replacement and its value specifies the pattern after replacement.
+   The return value is the pointer to the allocated region of 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 it is no longer in use. */
+char *cbreplace(const char *str, CBMAP *pairs);
+
+
+/* Make a list by splitting a serial datum.
+   `ptr' specifies the pointer to the region of the source content.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   `delim' specifies a string containing delimiting characters.  If it is `NULL', zero code is
+   used as a delimiter.
+   The return value is a list handle.
+   If two delimiters are successive, it is assumed that an empty element is between the two.
+   Because the handle of the return value is opened with the function `cblistopen', it should
+   be closed with the function `cblistclose'. */
+CBLIST *cbsplit(const char *ptr, int size, const char *delim);
+
+
+/* Read whole data of a file.
+   `name' specifies the name of a file.  If it is `NULL', the standard input is specified.
+   `sp' specifies the pointer to a variable to 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.  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 if it is no
+   longer in use.  */
+char *cbreadfile(const char *name, int *sp);
+
+
+/* Write a serial datum into a file.
+   `name specifies the name of a file.  If it is `NULL', the standard output is specified.
+   `ptr' specifies the pointer to the region of the source content.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   If successful, the return value is true, else, it is false.
+   If the file exists, it is overwritten.  Else, a new file is created. */
+int cbwritefile(const char *name, const char *ptr, int size);
+
+
+/* Read every line of a file.
+   `name' specifies the name of a file.  If it is `NULL', the standard input is specified.
+   The return value is a list handle of the lines if successful, else it is NULL.  Line
+   separators are cut out.  Because the handle of the return value is opened with the function
+   `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use. */
+CBLIST *cbreadlines(const char *name);
+
+
+/* Read names of files in a directory.
+   `name' specifies the name of a directory.
+   The return value is a list handle of names if successful, else it is NULL.
+   Because the handle of the return value is opened with the function `cblistopen', it should
+   be closed with the function `cblistclose' if it is no longer in use. */
+CBLIST *cbdirlist(const char *name);
+
+
+/* Get the status of a file or a directory.
+   `name' specifies the name of a file or a directory.
+   `dirp' specifies the pointer to a variable to which whether the file is a directory is
+   assigned.  If it is `NULL', it is not used.
+   `sizep' specifies the pointer to a variable to which the size of the file is assigned.  If it
+   is `NULL', it is not used.
+   `mtimep' specifies the pointer to a variable to which the last modified time of the file is
+   assigned.  If it is `NULL', it is not used.
+   If successful, the return value is true, else, false.  False is returned when the file does
+   not exist or the permission is denied. */
+int cbfilestat(const char *name, int *isdirp, int *sizep, time_t *mtimep);
+
+
+/* Remove a file or a directory and its sub ones recursively.
+   `name' specifies the name of a file or a directory.
+   If successful, the return value is true, else, false.  False is returned when the file does
+   not exist or the permission is denied. */
+int cbremove(const char *name);
+
+
+/* Break up a URL into elements.
+   `str' specifies the pointer to a string of URL.
+   The return value is a map handle.  Each key of the map is the name of an element.  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 handle of the return value is opened with the function `cbmapopen', it should
+   be closed with the function `cbmapclose' if it is no longer in use. */
+CBMAP *cburlbreak(const char *str);
+
+
+/* Resolve a relative URL with another absolute URL.
+   `base' specifies an absolute URL of a base location.
+   `target' specifies a URL to be resolved.
+   The return value is a 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 if it is no longer in use. */
+char *cburlresolve(const char *base, const char *target);
+
+
+/* Encode a serial object with URL encoding.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   The return value is the pointer to 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 it is no longer in use. */
+char *cburlencode(const char *ptr, int size);
+
+
+/* Decode a string encoded with URL encoding.
+   `str' specifies the pointer to an encoded string.
+   `sp' specifies the pointer to a variable to 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 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 if
+   it is no longer in use. */
+char *cburldecode(const char *str, int *sp);
+
+
+/* Encode a serial object with Base64 encoding.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   The return value is the pointer to 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 it is no longer in use. */
+char *cbbaseencode(const char *ptr, int size);
+
+
+/* Decode a string encoded with Base64 encoding.
+   `str' specifies the pointer to an encoded string.
+   `sp' specifies the pointer to a variable to 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 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 if
+   it is no longer in use. */
+char *cbbasedecode(const char *str, int *sp);
+
+
+/* Encode a serial object with quoted-printable encoding.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   The return value is the pointer to 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 it is no longer in use. */
+char *cbquoteencode(const char *ptr, int size);
+
+
+/* Decode a string encoded with quoted-printable encoding.
+   `str' specifies the pointer to an encoded string.
+   `sp' specifies the pointer to a variable to 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 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 if
+   it is no longer in use. */
+char *cbquotedecode(const char *str, int *sp);
+
+
+/* Split a string of MIME into headers and the body.
+   `ptr' specifies the pointer to the region of MIME data.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   `attrs' specifies a map handle to store attributes.  If it is `NULL', it is not used.  Each
+   key of the map is an attribute name uncapitalized.
+   `sp' specifies the pointer to a variable to 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 of the body data.
+   If the content type is defined, the attribute map has the key "TYPE" specifying the type.  If
+   the character encoding is defined, the key "CHARSET" specifies the encoding name.  If the
+   boundary string of multipart is defined, the key "BOUNDARY" specifies the string.  If the
+   content disposition is defined, the key "DISPOSITION" specifies the direction.  If the file
+   name is defined, the key "FILENAME" specifies the name.  If the attribute name is defined,
+   the key "NAME" specifies the name.  Because the region of the return value is allocated with
+   the `malloc' call, it should be released with the `free' call if it is no longer in use. */
+char *cbmimebreak(const char *ptr, int size, CBMAP *attrs, int *sp);
+
+
+/* Split multipart data of MIME into its parts.
+   `ptr' specifies the pointer to the region of multipart data of MIME.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   `boundary' specifies the pointer to the region of the boundary string.
+   The return value is a list handle.  Each element of the list is the string of a part.
+   Because the handle of the return value is opened with the function `cblistopen', it should
+   be closed with the function `cblistclose' if it is no longer in use. */
+CBLIST *cbmimeparts(const char *ptr, int size, const char *boundary);
+
+
+/* Encode a string with MIME encoding.
+   `str' specifies the pointer to a string.
+   `encname' specifies a string of the name of the character encoding.
+   The return value is the pointer to the result string.
+   `base' specifies whether to use Base64 encoding.  If it is false, quoted-printable is used.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use. */
+char *cbmimeencode(const char *str, const char *encname, int base);
+
+
+/* Decode a string encoded with MIME encoding.
+   `str' specifies the pointer to an encoded string.
+   `enp' specifies the pointer to a 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 pointer to 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 it is no longer in use. */
+char *cbmimedecode(const char *str, char *enp);
+
+
+/* Split a string of CSV into rows.
+   `str' specifies the pointer to the region of an CSV string.
+   The return value is a list handle.  Each element of the list is a string of a row.
+   Because the handle of the return value is opened with the function `cblistopen', it should
+   be closed with the function `cblistclose' if it is no longer in use.  The character encoding
+   of the input string should be US-ASCII, UTF-8, ISO-8859-*, EUC-*, or Shift_JIS.  Being
+   compatible with MS-Excel, these functions for CSV can handle cells including such meta
+   characters as comma, between double quotation marks. */
+CBLIST *cbcsvrows(const char *str);
+
+
+/* Split the string of a row of CSV into cells.
+   `str' specifies the pointer to the region of a row of CSV.
+   The return value is a list handle.  Each element of the list is the unescaped string of a
+   cell of the row.
+   Because the handle of the return value is opened with the function `cblistopen', it should
+   be closed with the function `cblistclose' if it is no longer in use. */
+CBLIST *cbcsvcells(const char *str);
+
+
+/* Escape a string with the meta characters of CSV.
+   `str' specifies the pointer to the region of a string.
+   The return value is the pointer to the escaped string sanitized of meta characters.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use. */
+char *cbcsvescape(const char *str);
+
+
+/* Unescape a string with the escaped meta characters of CSV.
+   `str' specifies the pointer to the region of a string with meta characters.
+   The return value is the pointer to the unescaped string.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use. */
+char *cbcsvunescape(const char *str);
+
+
+/* Split a string of XML into tags and text sections.
+   `str' specifies the pointer to the region of an XML string.
+   `cr' specifies whether to remove comments.
+   The return value is a list handle.  Each element of the list is the string of a tag or a
+   text section.
+   Because the handle of the return value is opened with the function `cblistopen', it should
+   be closed with the function `cblistclose' if it is no longer in use.  The character encoding
+   of the input string should be US-ASCII, UTF-8, ISO-8859-*, EUC-*, or Shift_JIS.  Because
+   these functions for XML are not XML parser with validation check, it can handle also HTML
+   and SGML. */
+CBLIST *cbxmlbreak(const char *str, int cr);
+
+
+/* Get the map of attributes of an XML tag.
+   `str' specifies the pointer to the region of a tag string.
+   The return value is a map handle.  Each key of the map is the name of an attribute.  Each
+   value is unescaped.  You can get the name of the tag with the key of an empty string.
+   Because the handle of the return value is opened with the function `cbmapopen', it should
+   be closed with the function `cbmapclose' if it is no longer in use. */
+CBMAP *cbxmlattrs(const char *str);
+
+
+/* Escape a string with the meta characters of XML.
+   `str' specifies the pointer to the region of a string.
+   The return value is the pointer to the escaped string sanitized of meta characters.
+   This function converts only `&', `<', `>', and `"'.  Because the region of the return value
+   is allocated with the `malloc' call, it should be released with the `free' call if it is no
+   longer in use. */
+char *cbxmlescape(const char *str);
+
+
+/* Unescape a string with the entity references of XML.
+   `str' specifies the pointer to the region of a string with meta characters.
+   The return value is the pointer to the unescaped string.
+   This function restores only `&', `<', `>', and `"'.  Because the region of the
+   return value is allocated with the `malloc' call, it should be released with the `free' call
+   if it is no longer in use. */
+char *cbxmlunescape(const char *str);
+
+
+/* Compress a serial object with ZLIB.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   `sp' specifies the pointer to a variable to 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 if it is no longer in use.  This function is available only if
+   QDBM was built with ZLIB enabled. */
+char *cbdeflate(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with ZLIB.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   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 if it
+   is no longer in use.  This function is available only if QDBM was built with ZLIB enabled. */
+char *cbinflate(const char *ptr, int size, int *sp);
+
+
+/* Compress a serial object with GZIP.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   `sp' specifies the pointer to a variable to 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 if it is no longer in use.  This function is available only if
+   QDBM was built with ZLIB enabled. */
+char *cbgzencode(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with GZIP.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   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 if it
+   is no longer in use.  This function is available only if QDBM was built with ZLIB enabled. */
+char *cbgzdecode(const char *ptr, int size, int *sp);
+
+
+/* Get the CRC32 checksum of a serial object.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   The return value is the CRC32 checksum of the object.
+   This function is available only if QDBM was built with ZLIB enabled. */
+unsigned int cbgetcrc(const char *ptr, int size);
+
+
+/* Compress a serial object with LZO.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   `sp' specifies the pointer to a variable to 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 if it is no longer in use.  This function is available only if
+   QDBM was built with LZO enabled. */
+char *cblzoencode(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with LZO.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   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 if it
+   is no longer in use.  This function is available only if QDBM was built with LZO enabled. */
+char *cblzodecode(const char *ptr, int size, int *sp);
+
+
+/* Compress a serial object with BZIP2.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   `sp' specifies the pointer to a variable to 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 if it is no longer in use.  This function is available only if
+   QDBM was built with LZO enabled. */
+char *cbbzencode(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with BZIP2.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   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 if it
+   is no longer in use.  This function is available only if QDBM was built with LZO enabled. */
+char *cbbzdecode(const char *ptr, int size, int *sp);
+
+
+/* Convert the character encoding of a string.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   `icode' specifies the name of encoding of the input string.
+   `ocode' specifies the name of encoding of the output string.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   `mp' specifies the pointer to a variable to which the number of missing characters by failure
+   of conversion is assigned.  If it is `NULL', it is not used.
+   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 if it
+   is no longer in use.  This function is available only if QDBM was built with ICONV enabled. */
+char *cbiconv(const char *ptr, int size, const char *icode, const char *ocode, int *sp, int *mp);
+
+
+/* Detect the encoding of a string automatically.
+   `ptr' specifies the pointer to a region.
+   `size' specifies the size of the region.  If it is negative, the size is assigned with
+   `strlen(ptr)'.
+   The return value is the string of the encoding name of the string.
+   As it stands, US-ASCII, ISO-2022-JP, Shift_JIS, CP932, EUC-JP, UTF-8, UTF-16, UTF-16BE,
+   and UTF-16LE are supported.  If none of them matches, ISO-8859-1 is selected.  This function
+   is available only if QDBM was built with ICONV enabled. */
+const char *cbencname(const char *ptr, int size);
+
+
+/* Get the jet lag of the local time in seconds.
+   The return value is the jet lag of the local time in seconds. */
+int cbjetlag(void);
+
+
+/* Get the Gregorian calendar of a time.
+   `t' specifies a source time.  If it is negative, the current time is specified.
+   `jl' specifies the jet lag of a location in seconds.
+   `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 cbcalendar(time_t t, int jl, int *yearp, int *monp, int *dayp,
+                int *hourp, int *minp, int *secp);
+
+
+/* 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 cbdayofweek(int year, int mon, int day);
+
+
+/* Get the string for a date in W3CDTF.
+   `t' specifies a source time.  If it is negative, the current time is specified.
+   `jl' specifies the jet lag of a location in seconds.
+   The return value is the string of the date in W3CDTF (YYYY-MM-DDThh:mm:ddTZD).
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use. */
+char *cbdatestrwww(time_t t, int jl);
+
+
+/* Get the string for a date in RFC 1123 format.
+   `t' specifies a source time.  If it is negative, the current time is specified.
+   `jl' specifies the jet lag of a location in seconds.
+   The return value is the string of the date in RFC 1123 format (Wdy, DD-Mon-YYYY hh:mm:dd TZD).
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use. */
+char *cbdatestrhttp(time_t t, int jl);
+
+
+/* Get the time value of a date string in decimal, hexadecimal, W3CDTF, or RFC 822 (1123).
+   `str' specifies a date string in decimal, hexadecimal, W3CDTF, or RFC 822 (1123).
+   The return value is the time value of the date or -1 if the format is invalid.
+   Decimal can be trailed by "s" for in seconds, "m" for in minutes, "h" for in hours,
+   and "d" for in days. */
+time_t cbstrmktime(const char *str);
+
+
+/* Get user and system processing times.
+   `usrp' specifies the pointer to a variable to which the user processing time is assigned.
+   If it is `NULL', it is not used.  The unit of time is seconds.
+   `sysp' specifies the pointer to a variable to which the system processing time is assigned.
+   If it is `NULL', it is not used.  The unit of time is seconds. */
+void cbproctime(double *usrp, double *sysp);
+
+
+/* Ensure that the standard I/O is binary mode.
+   This function is useful for applications on dosish file systems. */
+void cbstdiobin(void);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Show error message on the standard error output and exit.
+   `message' specifies an error message.
+   This function does not return. */
+void *cbmyfatal(const char *message);
+
+
+/* Create a datum handle from an allocated region.
+   `ptr' specifies the pointer to the region of an element.  The region should be allocated with
+   malloc and it is released by the function.
+   `size' specifies the size of the region. */
+CBDATUM *cbdatumopenbuf(char *ptr, int size);
+
+
+/* Set a buffer to a datum handle.
+   `ptr' specifies the pointer to the region of an element.  The region should be allocated with
+   malloc and it is released by the function.
+   `size' specifies the size of the region. */
+void cbdatumsetbuf(CBDATUM *datum, char *ptr, int size);
+
+
+/* Add an allocated element at the end of a list.
+   `list' specifies a list handle.
+   `ptr' specifies the pointer to the region of an element.  The region should be allocated with
+   malloc and it is released by the function.
+   `size' specifies the size of the region. */
+void cblistpushbuf(CBLIST *list, char *ptr, int size);
+
+
+/* Get a map handle with specifying the number of buckets.
+   `bnum' specifies the number of buckets.
+   The return value is a map handle. */
+CBMAP *cbmapopenex(int bnum);
+
+
+/* Alias of `cbmalloc'. */
+#define CB_MALLOC(CB_ptr, CB_size) \
+  (((CB_ptr) = malloc(CB_size)) ? (CB_ptr) : cbmyfatal("out of memory"))
+
+
+/* Alias of `cbrealloc'. */
+#define CB_REALLOC(CB_ptr, CB_size) \
+  (((CB_ptr) = realloc((CB_ptr), (CB_size))) ? (CB_ptr) : cbmyfatal("out of memory"))
+
+
+/* Alias of `cbmemdup'.
+   However, `size' should not be negative. */
+#define CB_MEMDUP(CB_res, CB_ptr, CB_size) \
+  do { \
+    CB_MALLOC((CB_res), (CB_size) + 1); \
+    memcpy((CB_res), (CB_ptr), (CB_size)); \
+    (CB_res)[(CB_size)] = '\0'; \
+  } while(FALSE)
+
+
+/* Get the size of padding bytes for pointer alignment.
+   `hsiz' specifies the header size of the object.
+   The return value is the size of padding bytes. */
+#define CB_ALIGNPAD(CB_hsiz) \
+  (((CB_hsiz | ~-(int)sizeof(void *)) + 1) - CB_hsiz)
+
+
+/* Alias of `cbdatumopen'.
+   However, no dafault data is specified. */
+#define CB_DATUMOPEN(CB_datum) \
+  do { \
+    CB_MALLOC((CB_datum), sizeof(*(CB_datum))); \
+    CB_MALLOC((CB_datum)->dptr, CB_DATUMUNIT); \
+    (CB_datum)->dptr[0] = '\0'; \
+    (CB_datum)->dsize = 0; \
+    (CB_datum)->asize = CB_DATUMUNIT; \
+  } while(FALSE)
+
+
+/* Alias of `cbdatumopen'.
+   However, `size' should not be negative. */
+#define CB_DATUMOPEN2(CB_datum, CB_ptr, CB_size) \
+  do { \
+    CB_DATUMOPEN((CB_datum)); \
+    CB_DATUMCAT((CB_datum), (CB_ptr), (CB_size)); \
+  } while(FALSE)
+
+
+/* Alias of `cbdatumclose'. */
+#define CB_DATUMCLOSE(CB_datum) \
+  do { \
+    free((CB_datum)->dptr); \
+    free((CB_datum)); \
+  } while(FALSE)
+
+
+/* Alias of `cbdatumcat'.
+   However, `size' should not be negative. */
+#define CB_DATUMCAT(CB_datum, CB_ptr, CB_size) \
+  do { \
+    if((CB_datum)->dsize + (CB_size) >= (CB_datum)->asize){ \
+      (CB_datum)->asize = (CB_datum)->asize * 2 + (CB_size) + 1; \
+      CB_REALLOC((CB_datum)->dptr, (CB_datum)->asize); \
+    } \
+    memcpy((CB_datum)->dptr + (CB_datum)->dsize, (CB_ptr), (CB_size)); \
+    (CB_datum)->dsize += (CB_size); \
+    (CB_datum)->dptr[(CB_datum)->dsize] = '\0'; \
+  } while(FALSE)
+
+
+/* Alias of `cbdatumptr'. */
+#define CB_DATUMPTR(CB_datum) ((const char *)((CB_datum)->dptr))
+
+
+/* Alias of `cbdatumsize'. */
+#define CB_DATUMSIZE(CB_datum) ((int)((CB_datum)->dsize))
+
+
+/* Alias of `cbdatumsetsize'. */
+#define CB_DATUMSETSIZE(CB_datum, CB_size) \
+  do { \
+    if((CB_size) <= (CB_datum)->dsize){ \
+      (CB_datum)->dsize = (CB_size); \
+      (CB_datum)->dptr[(CB_size)] = '\0'; \
+    } else { \
+      if((CB_size) >= (CB_datum)->asize){ \
+        (CB_datum)->asize = (CB_datum)->asize * 2 + (CB_size) + 1; \
+        CB_REALLOC((CB_datum)->dptr, (CB_datum)->asize); \
+      } \
+      memset((CB_datum)->dptr + (CB_datum)->dsize, 0, ((CB_size) - (CB_datum)->dsize) + 1); \
+      (CB_datum)->dsize = (CB_size); \
+    } \
+  } while(FALSE)
+
+
+/* Alias of `cbdatumtomalloc'. */
+#define CB_DATUMTOMALLOC(CB_datum, CB_ptr, CB_size) \
+  do { \
+    (CB_ptr) = (CB_datum)->dptr; \
+    (CB_size) = (CB_datum)->dsize; \
+    free((CB_datum)); \
+  } while(FALSE)
+
+
+/* Alias of `cblistopen'. */
+#define CB_LISTOPEN(CB_list) \
+  do { \
+    CB_MALLOC((CB_list), sizeof(*(CB_list))); \
+    (CB_list)->anum = CB_LISTUNIT; \
+    CB_MALLOC((CB_list)->array, sizeof((CB_list)->array[0]) * (CB_list)->anum); \
+    (CB_list)->start = 0; \
+    (CB_list)->num = 0; \
+  } while(FALSE)
+
+
+/* Alias of `cblistopen'.
+   However, `anum' is specified for the number of initial allocated elements. */
+#define CB_LISTOPEN2(CB_list, CB_anum) \
+  do { \
+    CB_MALLOC((CB_list), sizeof(*(CB_list))); \
+    (CB_list)->anum = (CB_anum) > 4 ? (CB_anum) : 4; \
+    CB_MALLOC((CB_list)->array, sizeof((CB_list)->array[0]) * (CB_list)->anum); \
+    (CB_list)->start = 0; \
+    (CB_list)->num = 0; \
+  } while(FALSE)
+
+
+/* Alias of `cblistclose'. */
+#define CB_LISTCLOSE(CB_list) \
+  do { \
+    int _CB_i, _CB_end; \
+    _CB_end = (CB_list)->start + (CB_list)->num; \
+    for(_CB_i = (CB_list)->start; _CB_i < _CB_end; _CB_i++){ \
+      free((CB_list)->array[_CB_i].dptr); \
+    } \
+    free((CB_list)->array); \
+    free((CB_list)); \
+  } while(FALSE)
+
+
+/* Alias of `cblistnum'. */
+#define CB_LISTNUM(CB_list) \
+  ((int)((CB_list)->num))
+
+
+/* Alias of `cblistval'.
+   However, `sp' is ignored. */
+#define CB_LISTVAL(CB_list, CB_index) \
+  ((const char *)((CB_list)->array[(CB_list)->start+(CB_index)].dptr))
+
+
+/* Alias of `cblistval'.
+   However, `size' is used instead of `sp'. */
+#define CB_LISTVAL2(CB_list, CB_index, CB_size) \
+  ((CB_size) = (CB_list)->array[(CB_list)->start+(CB_index)].dsize, \
+  (const char *)((CB_list)->array[(CB_list)->start+(CB_index)].dptr))
+
+
+/* Alias of `cblistpush'.
+   However, `size' should not be negative. */
+#define CB_LISTPUSH(CB_list, CB_ptr, CB_size) \
+  do { \
+    int _CB_index; \
+    _CB_index = (CB_list)->start + (CB_list)->num; \
+    if(_CB_index >= (CB_list)->anum){ \
+      (CB_list)->anum *= 2; \
+      CB_REALLOC((CB_list)->array, (CB_list)->anum * sizeof((CB_list)->array[0])); \
+    } \
+    CB_MALLOC((CB_list)->array[_CB_index].dptr, \
+              ((CB_size) < CB_DATUMUNIT ? CB_DATUMUNIT : (CB_size)) + 1); \
+    memcpy((CB_list)->array[_CB_index].dptr, (CB_ptr), (CB_size)); \
+    (CB_list)->array[_CB_index].dptr[(CB_size)] = '\0'; \
+    (CB_list)->array[_CB_index].dsize = (CB_size); \
+    (CB_list)->num++; \
+  } while(FALSE)
+
+
+/* Remove and free an element of the end of a list.
+   `list' specifies a list handle. */
+#define CB_LISTDROP(CB_list) \
+  do { \
+    if((CB_list)->num > 0){ \
+      free((CB_list)->array[(CB_list)->start+(CB_list)->num-1].dptr); \
+      (CB_list)->num--; \
+    } \
+  } while(FALSE)
+
+
+/* Alias of `cblistinsert'.
+   However, `index' is not checked and `size' should not be negative. */
+#define CB_LISTINSERT(CB_list, CB_index, CB_ptr, CB_size) \
+  do { \
+    int _CB_index = (CB_index); \
+    _CB_index += (CB_list)->start; \
+    if((CB_list)->start + (CB_list)->num >= (CB_list)->anum){ \
+      (CB_list)->anum *= 2; \
+      CB_REALLOC((CB_list)->array, (CB_list)->anum * sizeof((CB_list)->array[0])); \
+    } \
+    memmove((CB_list)->array + _CB_index + 1, (CB_list)->array + _CB_index, \
+            sizeof((CB_list)->array[0]) * ((CB_list)->start + (CB_list)->num - _CB_index)); \
+    CB_MEMDUP((CB_list)->array[_CB_index].dptr, (CB_ptr), (CB_size)); \
+    (CB_list)->array[_CB_index].dsize = (CB_size); \
+    (CB_list)->num++; \
+  } while(FALSE)
+
+
+/* Alias of `cblistpushbuf'. */
+#define CB_LISTPUSHBUF(CB_list, CB_ptr, CB_size) \
+  do{ \
+    int _CB_index; \
+    _CB_index = (CB_list)->start + (CB_list)->num; \
+    if(_CB_index >= (CB_list)->anum){ \
+      (CB_list)->anum *= 2; \
+      CB_REALLOC((CB_list)->array, (CB_list)->anum * sizeof((CB_list)->array[0])); \
+    } \
+    (CB_list)->array[_CB_index].dptr = (CB_ptr); \
+    (CB_list)->array[_CB_index].dsize = (CB_size); \
+    (CB_list)->num++; \
+  } while(FALSE) \
+
+
+/* Alias of `cbmapiterval'.
+   However, `size' is used instead of `sp'. */
+#define CB_MAPITERVAL(CB_vbuf, CB_kbuf, CB_vsiz) \
+  do { \
+    CBMAPDATUM *_CB_datum; \
+    _CB_datum = (CBMAPDATUM *)((CB_kbuf) - sizeof(*_CB_datum)); \
+    (CB_vsiz) = _CB_datum->vsiz; \
+    (CB_vbuf) = (char *)_CB_datum + sizeof(*_CB_datum) + \
+      _CB_datum->ksiz + CB_ALIGNPAD(_CB_datum->ksiz); \
+  } while(FALSE)
+
+
+
+#undef MYEXTERN
+
+#if defined(__cplusplus)                 /* export for C++ */
+}
+#endif
+
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/cabin.h
___________________________________________________________________
Added: svn:mime-type
   + text/x-chdr
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/cbcodec.c
===================================================================
--- box/trunk/qdbm/cbcodec.c	                        (rev 0)
+++ box/trunk/qdbm/cbcodec.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,1079 @@
+/*************************************************************************************************
+ * Popular encoders and decoders
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <cabin.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define DEFCODE        "UTF-8"           /* default encoding */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+char *readstdin(int *sp);
+int runurl(int argc, char **argv);
+int runbase(int argc, char **argv);
+int runquote(int argc, char **argv);
+int runmime(int argc, char **argv);
+int runcsv(int argc, char **argv);
+int runxml(int argc, char **argv);
+int runzlib(int argc, char **argv);
+int runlzo(int argc, char **argv);
+int runbzip(int argc, char **argv);
+int runiconv(int argc, char **argv);
+int rundate(int argc, char **argv);
+void shouucsmap(void);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if(argc < 2) usage();
+  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], "csv")){
+    rv = runcsv(argc, argv);
+  } else if(!strcmp(argv[1], "xml")){
+    rv = runxml(argc, argv);
+  } else if(!strcmp(argv[1], "zlib")){
+    rv = runzlib(argc, argv);
+  } else if(!strcmp(argv[1], "lzo")){
+    rv = runlzo(argc, argv);
+  } else if(!strcmp(argv[1], "bzip")){
+    rv = runbzip(argc, argv);
+  } else if(!strcmp(argv[1], "iconv")){
+    rv = runiconv(argc, argv);
+  } else if(!strcmp(argv[1], "date")){
+    rv = rundate(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  char *tmp;
+  int tsiz;
+  fprintf(stderr, "%s: popular encoders and decoders\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s url [-d] [-br] [-rs base target] [-l] [-e expr] [file]\n", progname);
+  fprintf(stderr, "  %s base [-d] [-l] [-c num] [-e expr] [file]\n", progname);
+  fprintf(stderr, "  %s quote [-d] [-l] [-c num] [-e expr] [file]\n", progname);
+  fprintf(stderr, "  %s mime [-d] [-hd] [-bd] [-part num] [-l] [-ec code] [-qp] [-dc] [-e expr]"
+          " [file]\n", progname);
+  fprintf(stderr, "  %s csv [-d] [-t] [-l] [-e expr] [-html] [file]\n", progname);
+  fprintf(stderr, "  %s xml [-d] [-p] [-l] [-e expr] [-tsv] [file]\n", progname);
+  if((tmp = cbdeflate("", 0, &tsiz)) != NULL){
+    fprintf(stderr, "  %s zlib [-d] [-gz] [-crc] [file]\n", progname);
+    free(tmp);
+  }
+  if((tmp = cblzoencode("", 0, &tsiz)) != NULL){
+    fprintf(stderr, "  %s lzo [-d] [file]\n", progname);
+    free(tmp);
+  }
+  if((tmp = cbbzencode("", 0, &tsiz)) != NULL){
+    fprintf(stderr, "  %s bzip [-d] [file]\n", progname);
+    free(tmp);
+  }
+  if((tmp = cbiconv("", 0, "US-ASCII", "US-ASCII", NULL, NULL)) != NULL){
+    fprintf(stderr, "  %s iconv [-ic code] [-oc code] [-ol ltype] [-cn] [-wc] [-um] [file]\n",
+            progname);
+    free(tmp);
+  }
+  fprintf(stderr, "  %s date [-wf] [-rf] [-utc] [str]\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* read the standard input */
+char *readstdin(int *sp){
+  char *buf;
+  int i, blen, c;
+  blen = 256;
+  buf = cbmalloc(blen);
+  for(i = 0; (c = getchar()) != EOF; i++){
+    if(i >= blen - 1) buf = cbrealloc(buf, blen *= 2);
+    buf[i] = c;
+  }
+  buf[i] = '\0';
+  if(sp) *sp = i;
+  return buf;
+}
+
+
+/* parse arguments of url command */
+int runurl(int argc, char **argv){
+  CBMAP *map;
+  int i, size, dec, br, line;
+  const char *val;
+  char *base, *target, *expr, *file, *buf, *res;
+  dec = FALSE;
+  br = FALSE;
+  line = FALSE;
+  base = NULL;
+  target = NULL;
+  expr = NULL;
+  file = NULL;
+  for(i = 2; i < argc; i++){
+    if(!file && 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];
+        if(++i >= argc) usage();
+        target = argv[i];
+      } else if(!strcmp(argv[i], "-l")){
+        line = TRUE;
+      } else if(!strcmp(argv[i], "-e")){
+        if(++i >= argc) usage();
+        expr = argv[i];
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  buf = NULL;
+  if(base){
+    size = strlen(base);
+    buf = cbmemdup(base, size);
+  } else if(expr){
+    size = strlen(expr);
+    buf = cbmemdup(expr, size);
+  } else if(file){
+    if(!(buf = cbreadfile(file, &size))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    buf = readstdin(&size);
+  }
+  if(target){
+    res = cburlresolve(base, target);
+    printf("%s", res);
+    free(res);
+  } else if(br){
+    map = cburlbreak(buf);
+    if((val = cbmapget(map, "self", -1, NULL))) printf("self\t%s\n", val);
+    if((val = cbmapget(map, "scheme", -1, NULL))) printf("scheme\t%s\n", val);
+    if((val = cbmapget(map, "host", -1, NULL))) printf("host\t%s\n", val);
+    if((val = cbmapget(map, "port", -1, NULL))) printf("port\t%s\n", val);
+    if((val = cbmapget(map, "authority", -1, NULL))) printf("authority\t%s\n", val);
+    if((val = cbmapget(map, "path", -1, NULL))) printf("path\t%s\n", val);
+    if((val = cbmapget(map, "file", -1, NULL))) printf("file\t%s\n", val);
+    if((val = cbmapget(map, "query", -1, NULL))) printf("query\t%s\n", val);
+    if((val = cbmapget(map, "fragment", -1, NULL))) printf("fragment\t%s\n", val);
+    cbmapclose(map);
+  } else if(dec){
+    res = cburldecode(buf, &size);
+    for(i = 0; i < size; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  } else {
+    res = cburlencode(buf, size);
+    for(i = 0; res[i] != '\0'; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  }
+  if(line) putchar('\n');
+  free(buf);
+  return 0;
+}
+
+
+/* parse arguments of base command */
+int runbase(int argc, char **argv){
+  int i, ci, size, dec, line, cols;
+  char *expr, *file, *buf, *res;
+  dec = FALSE;
+  line = FALSE;
+  cols = -1;
+  expr = NULL;
+  file = NULL;
+  for(i = 2; i < argc; i++){
+    if(!file && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = TRUE;
+      } else if(!strcmp(argv[i], "-l")){
+        line = TRUE;
+      } else if(!strcmp(argv[i], "-c")){
+        if(++i >= argc) usage();
+        cols = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-e")){
+        if(++i >= argc) usage();
+        expr = argv[i];
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  buf = NULL;
+  if(expr){
+    size = strlen(expr);
+    buf = cbmemdup(expr, size);
+  } else if(file){
+    if(!(buf = cbreadfile(file, &size))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    buf = readstdin(&size);
+  }
+  if(dec){
+    res = cbbasedecode(buf, &size);
+    for(i = 0; i < size; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  } else {
+    res = cbbaseencode(buf, size);
+    ci = 0;
+    for(i = 0; res[i] != '\0'; i++){
+      if(cols > 0 && ci >= cols){
+        putchar('\n');
+        ci = 0;
+      }
+      putchar(res[i]);
+      ci++;
+    }
+    free(res);
+  }
+  if(line) putchar('\n');
+  free(buf);
+  return 0;
+}
+
+
+/* parse arguments of quote command */
+int runquote(int argc, char **argv){
+  int i, ci, size, dec, line, cols;
+  char *expr, *file, *buf, *res;
+  dec = FALSE;
+  line = FALSE;
+  cols = -1;
+  expr = NULL;
+  file = NULL;
+  for(i = 2; i < argc; i++){
+    if(!file && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = TRUE;
+      } else if(!strcmp(argv[i], "-l")){
+        line = TRUE;
+      } else if(!strcmp(argv[i], "-c")){
+        if(++i >= argc) usage();
+        cols = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-e")){
+        if(++i >= argc) usage();
+        expr = argv[i];
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  buf = NULL;
+  if(expr){
+    size = strlen(expr);
+    buf = cbmemdup(expr, size);
+  } else if(file){
+    if(!(buf = cbreadfile(file, &size))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    buf = readstdin(&size);
+  }
+  if(dec){
+    res = cbquotedecode(buf, &size);
+    for(i = 0; i < size; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  } else {
+    res = cbquoteencode(buf, size);
+    ci = 0;
+    for(i = 0; res[i] != '\0'; i++){
+      if(cols > 0 && (ci >= cols || (ci >= cols - 2 && res[i] == '='))){
+        printf("=\n");
+        ci = 0;
+      }
+      if(res[i] == '\r' || res[i] == '\n') ci = 0;
+      putchar(res[i]);
+      ci++;
+    }
+    free(res);
+  }
+  if(line) putchar('\n');
+  free(buf);
+  return 0;
+}
+
+
+/* parse arguments of mime command */
+int runmime(int argc, char **argv){
+  CBMAP *attrs;
+  CBLIST *parts;
+  int i, size, dec, line, qp, dc, hd, bd, pnum, rsiz, bsiz;
+  const char *key, *body;
+  char *code, *expr, *file, *buf, *res, renc[64];
+  dec = FALSE;
+  hd = FALSE;
+  bd = FALSE;
+  pnum = 0;
+  line = FALSE;
+  dc = FALSE;
+  qp = FALSE;
+  code = NULL;
+  expr = NULL;
+  file = NULL;
+  for(i = 2; i < argc; i++){
+    if(!file && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = TRUE;
+      } else if(!strcmp(argv[i], "-hd")){
+        hd = TRUE;
+      } else if(!strcmp(argv[i], "-bd")){
+        bd = TRUE;
+      } else if(!strcmp(argv[i], "-part")){
+        if(++i >= argc) usage();
+        pnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-l")){
+        line = TRUE;
+      } else if(!strcmp(argv[i], "-ec")){
+        if(++i >= argc) usage();
+        code = argv[i];
+      } else if(!strcmp(argv[i], "-qp")){
+        qp = TRUE;
+      } else if(!strcmp(argv[i], "-dc")){
+        dc = TRUE;
+      } else if(!strcmp(argv[i], "-e")){
+        if(++i >= argc) usage();
+        expr = argv[i];
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  buf = NULL;
+  if(expr){
+    size = strlen(expr);
+    buf = cbmemdup(expr, size);
+  } else if(file){
+    if(!(buf = cbreadfile(file, &size))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    buf = readstdin(&size);
+  }
+  if(hd || bd || pnum > 0){
+    attrs = cbmapopen();
+    res = cbmimebreak(buf, size, attrs, &rsiz);
+    if(pnum > 0){
+      parts = NULL;
+      if(!(key = cbmapget(attrs, "TYPE", -1, NULL)) || !cbstrfwimatch(key, "multipart/") ||
+         !(key = cbmapget(attrs, "BOUNDARY", -1, NULL)) ||
+         !(parts = cbmimeparts(res, rsiz, key)) || cblistnum(parts) < pnum){
+        fprintf(stderr, "%s: not multipart or no such part\n", progname);
+        if(parts) cblistclose(parts);
+        free(res);
+        cbmapclose(attrs);
+        free(buf);
+        return 1;
+      }
+      body = cblistval(parts, pnum - 1, &bsiz);
+      for(i = 0; i < bsiz; i++){
+        putchar(body[i]);
+      }
+      cblistclose(parts);
+    } else if(hd){
+      cbmapiterinit(attrs);
+      while((key = cbmapiternext(attrs, NULL)) != NULL){
+        printf("%s\t%s\n", key, cbmapget(attrs, key, -1, NULL));
+      }
+    } else {
+      for(i = 0; i < rsiz; i++){
+        putchar(res[i]);
+      }
+    }
+    free(res);
+    cbmapclose(attrs);
+  } else if(dec){
+    res = cbmimedecode(buf, renc);
+    printf("%s", dc ? renc : res);
+    free(res);
+  } else {
+    res = cbmimeencode(buf, code ? code : DEFCODE, !qp);
+    printf("%s", res);
+    free(res);
+  }
+  if(line) putchar('\n');
+  free(buf);
+  return 0;
+}
+
+
+/* parse arguments of csv command */
+int runcsv(int argc, char **argv){
+  CBLIST *rows, *cells;
+  int i, j, k, dec, tb, line, html;
+  const char *row, *cell;
+  char *expr, *file, *buf, *res;
+  dec = FALSE;
+  tb = FALSE;
+  line = FALSE;
+  html = FALSE;
+  expr = NULL;
+  file = NULL;
+  for(i = 2; i < argc; i++){
+    if(!file && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = TRUE;
+      } else if(!strcmp(argv[i], "-t")){
+        tb = TRUE;
+      } else if(!strcmp(argv[i], "-l")){
+        line = TRUE;
+      } else if(!strcmp(argv[i], "-e")){
+        if(++i >= argc) usage();
+        expr = argv[i];
+      } else if(!strcmp(argv[i], "-html")){
+        html = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  buf = NULL;
+  if(expr){
+    buf = cbmemdup(expr, -1);
+  } else if(file){
+    if(!(buf = cbreadfile(file, NULL))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    buf = readstdin(NULL);
+  }
+  if(tb || html){
+    if(html) printf("<table border=\"1\">\n");
+    rows = cbcsvrows(buf);
+    for(i = 0; i < cblistnum(rows); i++){
+      if(html) printf("<tr>");
+      row = cblistval(rows, i, NULL);
+      cells = cbcsvcells(row);
+      for(j = 0; j < cblistnum(cells); j++){
+        cell = cblistval(cells, j, NULL);
+        if(html){
+          printf("<td>");
+          for(k = 0; cell[k] != '\0'; k++){
+            if(cell[k] == '\r' || cell[k] == '\n'){
+              printf("<br>");
+              if(cell[k] == '\r' && cell[k] == '\n') k++;
+            } else {
+              switch(cell[k]){
+              case '&': printf("&"); break;
+              case '<': printf("<"); break;
+              case '>': printf(">"); break;
+              default: putchar(cell[k]); break;
+              }
+            }
+          }
+          printf("</td>");
+        } else {
+          if(j > 0) putchar('\t');
+          for(k = 0; cell[k] != '\0'; k++){
+            if(((unsigned char *)cell)[k] >= 0x20) putchar(cell[k]);
+          }
+        }
+      }
+      cblistclose(cells);
+      if(html) printf("</tr>");
+      putchar('\n');
+    }
+    cblistclose(rows);
+    if(html) printf("</table>\n");
+  } else if(dec){
+    res = cbcsvunescape(buf);
+    for(i = 0; res[i] != '\0'; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  } else {
+    res = cbcsvescape(buf);
+    for(i = 0; res[i] != '\0'; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  }
+  if(line) putchar('\n');
+  free(buf);
+  return 0;
+}
+
+
+/* parse arguments of xml command */
+int runxml(int argc, char **argv){
+  CBLIST *elems;
+  CBMAP *attrs;
+  int i, j, dec, pb, line, tsv, div;
+  const char *elem, *attr;
+  char *expr, *file, *buf, *res;
+  dec = FALSE;
+  pb = FALSE;
+  line = FALSE;
+  tsv = FALSE;
+  expr = NULL;
+  file = NULL;
+  for(i = 2; i < argc; i++){
+    if(!file && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = TRUE;
+      } else if(!strcmp(argv[i], "-p")){
+        pb = TRUE;
+      } else if(!strcmp(argv[i], "-l")){
+        line = TRUE;
+      } else if(!strcmp(argv[i], "-e")){
+        if(++i >= argc) usage();
+        expr = argv[i];
+      } else if(!strcmp(argv[i], "-tsv")){
+        tsv = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  buf = NULL;
+  if(expr){
+    buf = cbmemdup(expr, -1);
+  } else if(file){
+    if(!(buf = cbreadfile(file, NULL))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    buf = readstdin(NULL);
+  }
+  if(pb || tsv){
+    elems = cbxmlbreak(buf, FALSE);
+    for(i = 0; i < cblistnum(elems); i++){
+      elem = cblistval(elems, i, NULL);
+      div = FALSE;
+      if(elem[0] == '<'){
+        if(cbstrfwimatch(elem, "<?xml")){
+          printf("XMLDECL");
+          div = TRUE;
+        } else if(cbstrfwimatch(elem, "<!DOCTYPE")){
+          printf("DOCTYPE");
+        } else if(cbstrfwimatch(elem, "<!--")){
+          printf("COMMENT");
+        } else if(cbstrfwimatch(elem, "</")){
+          printf("ENDTAG");
+          div = TRUE;
+        } else if(cbstrbwimatch(elem, "/>")){
+          printf("EMPTAG");
+          div = TRUE;
+        } else {
+          printf("BEGTAG");
+          div = TRUE;
+        }
+      } else {
+        printf("TEXT");
+      }
+      putchar('\t');
+      if(tsv){
+        if(div){
+          attrs = cbxmlattrs(elem);
+          cbmapiterinit(attrs);
+          for(j = 0; (attr = cbmapiternext(attrs, NULL)) != NULL; j++){
+            if(j < 1){
+              printf("%s", cbmapget(attrs, attr, -1, NULL));
+            } else {
+              printf("\t%s\t%s", attr, cbmapget(attrs, attr, -1, NULL));
+            }
+          }
+          cbmapclose(attrs);
+        } else {
+          res = cbxmlunescape(elem);
+          for(j = 0; elem[j] != '\0'; j++){
+            if(((unsigned char *)elem)[j] < 0x20 || elem[j] == '%'){
+              printf("%%%02X", elem[j]);
+            } else {
+              putchar(elem[j]);
+            }
+          }
+          free(res);
+        }
+      } else {
+        printf("%s", elem);
+      }
+      putchar('\n');
+    }
+    cblistclose(elems);
+  } else if(dec){
+    res = cbxmlunescape(buf);
+    for(i = 0; res[i] != '\0'; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  } else {
+    res = cbxmlescape(buf);
+    for(i = 0; res[i] != '\0'; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  }
+  if(line) putchar('\n');
+  free(buf);
+  return 0;
+}
+
+
+/* parse arguments of zlib command */
+int runzlib(int argc, char **argv){
+  unsigned int sum;
+  int i, bsiz, rsiz, dec, gz, crc;
+  char *file, *buf, *res;
+  dec = FALSE;
+  gz = FALSE;
+  crc = FALSE;
+  file = NULL;
+  for(i = 2; i < argc; i++){
+    if(!file && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = TRUE;
+      } else if(!strcmp(argv[i], "-gz")){
+        gz = TRUE;
+      } else if(!strcmp(argv[i], "-crc")){
+        crc = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  buf = NULL;
+  if(file){
+    if(!(buf = cbreadfile(file, &bsiz))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    buf = readstdin(&bsiz);
+  }
+  if(crc){
+    sum = cbgetcrc(buf, bsiz);
+    for(i = 0; i < sizeof(int); i++){
+      printf("%02x", sum / 0x1000000);
+      sum <<= 8;
+    }
+    putchar('\n');
+  } else if(dec){
+    if(!(res = gz ? cbgzdecode(buf, bsiz, &rsiz) : cbinflate(buf, bsiz, &rsiz))){
+      fprintf(stderr, "%s: inflate failed\n", progname);
+      free(buf);
+      return 1;
+    }
+    for(i = 0; i < rsiz; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  } else {
+    if(!(res = gz ? cbgzencode(buf, bsiz, &rsiz) : cbdeflate(buf, bsiz, &rsiz))){
+      fprintf(stderr, "%s: deflate failed\n", progname);
+      free(buf);
+      return 1;
+    }
+    for(i = 0; i < rsiz; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  }
+  free(buf);
+  return 0;
+}
+
+
+/* parse arguments of lzo command */
+int runlzo(int argc, char **argv){
+  int i, bsiz, rsiz, dec;
+  char *file, *buf, *res;
+  dec = FALSE;
+  file = NULL;
+  for(i = 2; i < argc; i++){
+    if(!file && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  buf = NULL;
+  if(file){
+    if(!(buf = cbreadfile(file, &bsiz))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    buf = readstdin(&bsiz);
+  }
+  if(dec){
+    if(!(res = cblzodecode(buf, bsiz, &rsiz))){
+      fprintf(stderr, "%s: decode failed\n", progname);
+      free(buf);
+      return 1;
+    }
+    for(i = 0; i < rsiz; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  } else {
+    if(!(res = cblzoencode(buf, bsiz, &rsiz))){
+      fprintf(stderr, "%s: encode failed\n", progname);
+      free(buf);
+      return 1;
+    }
+    for(i = 0; i < rsiz; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  }
+  free(buf);
+  return 0;
+}
+
+
+/* parse arguments of bzip command */
+int runbzip(int argc, char **argv){
+  int i, bsiz, rsiz, dec;
+  char *file, *buf, *res;
+  dec = FALSE;
+  file = NULL;
+  for(i = 2; i < argc; i++){
+    if(!file && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        dec = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  buf = NULL;
+  if(file){
+    if(!(buf = cbreadfile(file, &bsiz))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    buf = readstdin(&bsiz);
+  }
+  if(dec){
+    if(!(res = cbbzdecode(buf, bsiz, &rsiz))){
+      fprintf(stderr, "%s: decode failed\n", progname);
+      free(buf);
+      return 1;
+    }
+    for(i = 0; i < rsiz; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  } else {
+    if(!(res = cbbzencode(buf, bsiz, &rsiz))){
+      fprintf(stderr, "%s: encode failed\n", progname);
+      free(buf);
+      return 1;
+    }
+    for(i = 0; i < rsiz; i++){
+      putchar(res[i]);
+    }
+    free(res);
+  }
+  free(buf);
+  return 0;
+}
+
+
+/* parse arguments of iconv command */
+int runiconv(int argc, char **argv){
+  CBDATUM *datum;
+  const char *rcode;
+  char *icode, *ocode, *ltype, *file, *buf, *res, *norm, *orig;
+  int i, cn, wc, bsiz, rsiz, nsiz, osiz, miss;
+  icode = NULL;
+  ocode = NULL;
+  ltype = NULL;
+  file = NULL;
+  cn = FALSE;
+  wc = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!file && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-ic")){
+        if(++i >= argc) usage();
+        icode = argv[i];
+      } else if(!strcmp(argv[i], "-oc")){
+        if(++i >= argc) usage();
+        ocode = argv[i];
+      } else if(!strcmp(argv[i], "-ol")){
+        if(++i >= argc) usage();
+        ltype = argv[i];
+      } else if(!strcmp(argv[i], "-cn")){
+        cn = TRUE;
+      } else if(!strcmp(argv[i], "-wc")){
+        wc = TRUE;
+      } else if(!strcmp(argv[i], "-um")){
+        shouucsmap();
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  buf = NULL;
+  if(file){
+    if(!(buf = cbreadfile(file, &bsiz))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    buf = readstdin(&bsiz);
+  }
+  miss = 0;
+  if(cn){
+    printf("%s\n", cbencname(buf, bsiz));
+  } else if(wc){
+    printf("%d\n", cbstrcountutf(buf));
+  } else {
+    rcode = icode ? icode : cbencname(buf, bsiz);
+    if(!(res = cbiconv(buf, bsiz, rcode, ocode ? ocode : DEFCODE,
+                       &rsiz, &miss))){
+      fprintf(stderr, "%s: iconv failed\n", progname);
+      free(buf);
+      return 1;
+    }
+    if(miss > 0) fprintf(stderr, "%s: missing %d characters\n", progname, miss);
+    if(ltype && (!cbstricmp(ltype, "u") || !cbstricmp(ltype, "unix") ||
+                 !cbstricmp(ltype, "lf"))){
+      ltype = "\n";
+    } else if(ltype && (!cbstricmp(ltype, "d") || !cbstricmp(ltype, "dos") ||
+                        !cbstricmp(ltype, "crlf"))){
+      ltype = "\r\n";
+    } else if(ltype && (!cbstricmp(ltype, "m") || !cbstricmp(ltype, "mac") ||
+                        !cbstricmp(ltype, "cr"))){
+      ltype = "\r";
+    } else {
+      ltype = NULL;
+    }
+    if(ltype){
+      if(!(norm = cbiconv(res, rsiz, ocode, "UTF-8", &nsiz, NULL))){
+        fprintf(stderr, "%s: iconv failed\n", progname);
+        free(res);
+        free(buf);
+        return 1;
+      }
+      datum = cbdatumopen(NULL, -1);
+      for(i = 0; i < nsiz; i++){
+        if(norm[i] == '\r'){
+          if(norm[i+1] == '\n') i++;
+          cbdatumcat(datum, ltype, -1);
+        } else if(norm[i] == '\n'){
+          cbdatumcat(datum, ltype, -1);
+        } else {
+          cbdatumcat(datum, norm + i, 1);
+        }
+      }
+      if(!(orig = cbiconv(cbdatumptr(datum), cbdatumsize(datum), "UTF-8", ocode, &osiz, NULL))){
+        fprintf(stderr, "%s: iconv failed\n", progname);
+        cbdatumclose(datum);
+        free(norm);
+        free(res);
+        free(buf);
+        return 1;
+      }
+      for(i = 0; i < osiz; i++){
+        putchar(orig[i]);
+      }
+      free(orig);
+      cbdatumclose(datum);
+      free(norm);
+    } else {
+      for(i = 0; i < rsiz; i++){
+        putchar(res[i]);
+      }
+    }
+    free(res);
+  }
+  free(buf);
+  return miss > 0 ? 1 : 0;
+}
+
+
+/* parse arguments of date command */
+int rundate(int argc, char **argv){
+  int i, wb, rb, utc, jl;
+  char *date, *res;
+  time_t t;
+  wb = FALSE;
+  rb = FALSE;
+  utc = FALSE;
+  date = NULL;
+  for(i = 2; i < argc; i++){
+    if(!date && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-wf")){
+        wb = TRUE;
+      } else if(!strcmp(argv[i], "-rf")){
+        rb = TRUE;
+      } else if(!strcmp(argv[i], "-utc")){
+        utc = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!date){
+      date = argv[i];
+    } else {
+      usage();
+    }
+  }
+  jl = utc ? 0 : cbjetlag();
+  if(date){
+    t = cbstrmktime(date);
+  } else {
+    t = time(NULL);
+  }
+  if(wb){
+    res = cbdatestrwww(t, jl);
+  } else if(rb){
+    res = cbdatestrhttp(t, jl);
+  } else {
+    res = cbsprintf("%d", (int)t);
+  }
+  if(t >= 0){
+    printf("%s\n", res);
+  } else {
+    if(date){
+      fprintf(stderr, "%s: %s: invalid date format\n", progname, date);
+    } else {
+      fprintf(stderr, "%s: invalid time setting\n", progname);
+    }
+  }
+  free(res);
+  return 0;
+}
+
+
+/* show mapping of UCS-2 and exit. */
+void shouucsmap(void){
+  unsigned char buf[2], *tmp;
+  int i, j, tsiz;
+  for(i = 0; i < 65536; i++){
+    buf[0] = i / 256;
+    buf[1] = i % 256;
+    printf("%d\t", i);
+    printf("U+%02X%02X\t", buf[0], buf[1]);
+    printf("\"\\x%x\\x%x\"\t", buf[0], buf[1]);
+    if((tmp = (unsigned char *)cbiconv((char *)buf, 2, "UTF-16BE", "UTF-8",
+                                       &tsiz, NULL)) != NULL){
+      if(tsiz > 0){
+        printf("\"");
+        for(j = 0; j < tsiz; j++){
+          printf("\\x%x", tmp[j]);
+        }
+        printf("\"");
+      } else {
+        printf("NULL");
+      }
+      free(tmp);
+    }
+    printf("\n");
+  }
+  exit(0);
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/cbcodec.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/cbtest.c
===================================================================
--- box/trunk/qdbm/cbtest.c	                        (rev 0)
+++ box/trunk/qdbm/cbtest.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,924 @@
+/*************************************************************************************************
+ * Test cases of Cabin
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <cabin.h>
+#include <stdio.h>
+#include <cabin.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <time.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define RECBUFSIZ      32                /* buffer for records */
+#define TEXTBUFSIZ     262144            /* buffer for text */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+int runsort(int argc, char **argv);
+int runstrstr(int argc, char **argv);
+int runlist(int argc, char **argv);
+int runmap(int argc, char **argv);
+int runheap(int argc, char **argv);
+int runwicked(int argc, char **argv);
+int runmisc(int argc, char **argv);
+int printfflush(const char *format, ...);
+int strpcmp(const void *ap, const void *bp);
+int intpcmp(const void *ap, const void *bp);
+int myrand(void);
+int dosort(int rnum, int disp);
+int dostrstr(int rnum, int disp);
+int dolist(int rnum, int disp);
+int domap(int rnum, int bnum, int disp);
+int doheap(int rnum, int max, int disp);
+int dowicked(int rnum);
+int domisc(void);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "sort")){
+    rv = runsort(argc, argv);
+  } else if(!strcmp(argv[1], "strstr")){
+    rv = runstrstr(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], "heap")){
+    rv = runheap(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else if(!strcmp(argv[1], "misc")){
+    rv = runmisc(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: test cases for Cabin\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s sort [-d] rnum\n", progname);
+  fprintf(stderr, "  %s strstr [-d] rnum\n", progname);
+  fprintf(stderr, "  %s list [-d] rnum\n", progname);
+  fprintf(stderr, "  %s map [-d] rnum [bnum]\n", progname);
+  fprintf(stderr, "  %s heap [-d] rnum [top]\n", progname);
+  fprintf(stderr, "  %s wicked rnum\n", progname);
+  fprintf(stderr, "  %s misc\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of sort command */
+int runsort(int argc, char **argv){
+  int i, rnum, disp, rv;
+  char *rstr;
+  rstr = NULL;
+  rnum = 0;
+  disp = FALSE;
+  for(i = 2; i < argc; i++){
+    if(argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        disp = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = dosort(rnum, disp);
+  return rv;
+}
+
+
+/* parse arguments of strstr command */
+int runstrstr(int argc, char **argv){
+  int i, rnum, disp, rv;
+  char *rstr;
+  rstr = NULL;
+  rnum = 0;
+  disp = FALSE;
+  for(i = 2; i < argc; i++){
+    if(argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        disp = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = dostrstr(rnum, disp);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+int runlist(int argc, char **argv){
+  int i, rnum, disp, rv;
+  char *rstr;
+  rstr = NULL;
+  rnum = 0;
+  disp = FALSE;
+  for(i = 2; i < argc; i++){
+    if(argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        disp = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = dolist(rnum, disp);
+  return rv;
+}
+
+
+/* parse arguments of map command */
+int runmap(int argc, char **argv){
+  int i, rnum, bnum, disp, rv;
+  char *rstr, *bstr;
+  rstr = NULL;
+  bstr = NULL;
+  rnum = 0;
+  bnum = -1;
+  disp = FALSE;
+  for(i = 2; i < argc; i++){
+    if(argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        disp = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  if(bstr) bnum = atoi(bstr);
+  rv = domap(rnum, bnum, disp);
+  return rv;
+}
+
+
+/* parse arguments of heap command */
+int runheap(int argc, char **argv){
+  int i, rnum, max, disp, rv;
+  char *rstr, *mstr;
+  rstr = NULL;
+  mstr = NULL;
+  rnum = 0;
+  max = -1;
+  disp = FALSE;
+  for(i = 2; i < argc; i++){
+    if(argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-d")){
+        disp = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!mstr){
+      mstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  if(mstr) max = atoi(mstr);
+  if(max < 0) max = rnum;
+  rv = doheap(rnum, max, disp);
+  rv = 0;
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+int runwicked(int argc, char **argv){
+  int i, rnum, rv;
+  char *rstr;
+  rstr = NULL;
+  rnum = 0;
+  for(i = 2; i < argc; i++){
+    if(argv[i][0] == '-'){
+      usage();
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = dowicked(rnum);
+  return rv;
+}
+
+
+/* parse arguments of misc command */
+int runmisc(int argc, char **argv){
+  int rv;
+  rv = domisc();
+  return rv;
+}
+
+
+/* print formatted string and flush the buffer */
+int printfflush(const char *format, ...){
+  va_list ap;
+  int rv;
+  va_start(ap, format);
+  rv = vprintf(format, ap);
+  if(fflush(stdout) == EOF) rv = -1;
+  va_end(ap);
+  return rv;
+}
+
+
+/* comparing function for strings */
+int strpcmp(const void *ap, const void *bp){
+  return strcmp(*(char **)ap, *(char **)bp);
+}
+
+
+/* comparing function for integers */
+int intpcmp(const void *ap, const void *bp){
+  return *(int *)ap - *(int *)bp;
+}
+
+
+/* pseudo random number generator */
+int myrand(void){
+  static int cnt = 0;
+  if(cnt == 0) srand(time(NULL));
+  return (rand() * rand() + (rand() >> (sizeof(int) * 4)) + (cnt++)) & INT_MAX;
+}
+
+
+/* perform sort command */
+int dosort(int rnum, int disp){
+  char **ivector1, **ivector2, **ivector3, **ivector4, **ivector5;
+  char buf[RECBUFSIZ];
+  int i, len, err;
+  if(!disp) printfflush("<Sorting Test>\n  rnum=%d\n\n", rnum);
+  ivector1 = cbmalloc(rnum * sizeof(ivector1[0]));
+  ivector2 = cbmalloc(rnum * sizeof(ivector2[0]));
+  ivector3 = cbmalloc(rnum * sizeof(ivector3[0]));
+  ivector4 = cbmalloc(rnum * sizeof(ivector4[0]));
+  ivector5 = cbmalloc(rnum * sizeof(ivector5[0]));
+  err = FALSE;
+  for(i = 0; i < rnum; i++){
+    len = sprintf(buf, "%08d", myrand() % rnum + 1);
+    ivector1[i] = cbmemdup(buf, len);
+    ivector2[i] = cbmemdup(buf, len);
+    ivector3[i] = cbmemdup(buf, len);
+    ivector4[i] = cbmemdup(buf, len);
+    ivector5[i] = cbmemdup(buf, len);
+  }
+  if(!disp) printfflush("Sorting with insert sort ... ");
+  cbisort(ivector1, rnum, sizeof(ivector1[0]), strpcmp);
+  if(!disp) printfflush("ok\n");
+  if(!disp) printfflush("Sorting with shell sort ... ");
+  cbssort(ivector2, rnum, sizeof(ivector2[0]), strpcmp);
+  if(!disp) printfflush("ok\n");
+  if(!disp) printfflush("Sorting with heap sort ... ");
+  cbhsort(ivector3, rnum, sizeof(ivector3[0]), strpcmp);
+  if(!disp) printfflush("ok\n");
+  if(!disp) printfflush("Sorting with quick sort ... ");
+  cbqsort(ivector4, rnum, sizeof(ivector4[0]), strpcmp);
+  if(!disp) printfflush("ok\n");
+  for(i = 0; i < rnum; i++){
+    if(disp) printfflush("%s\t%s\t%s\t%s\t[%s]\n",
+                        ivector1[i], ivector2[i], ivector3[i], ivector4[i], ivector5[i]);
+    if(strcmp(ivector1[i], ivector2[i])) err = TRUE;
+    if(strcmp(ivector1[i], ivector3[i])) err = TRUE;
+    if(strcmp(ivector1[i], ivector4[i])) err = TRUE;
+    free(ivector1[i]);
+    free(ivector2[i]);
+    free(ivector3[i]);
+    free(ivector4[i]);
+    free(ivector5[i]);
+  }
+  free(ivector1);
+  free(ivector2);
+  free(ivector3);
+  free(ivector4);
+  free(ivector5);
+  if(err) fprintf(stderr, "%s: sorting failed\n", progname);
+  if(!disp && !err) printfflush("all ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform strstr command */
+int dostrstr(int rnum, int disp){
+  char *text, buf[RECBUFSIZ], *std, *kmp, *bm;
+  int i, j, len, err;
+  text = cbmalloc(TEXTBUFSIZ);
+  for(i = 0; i < TEXTBUFSIZ - 1; i++){
+    text[i] = 'a' + myrand() % ('z' - 'a');
+  }
+  text[i] = '\0';
+  err = FALSE;
+  if(!disp) printfflush("Locating substrings ... ");
+  for(i = 0; i < rnum; i++){
+    len = myrand() % (RECBUFSIZ - 1);
+    for(j = 0; j < len; j++){
+      buf[j] = 'a' + myrand() % ('z' - 'a');
+    }
+    buf[j] = 0;
+    std = strstr(text, buf);
+    kmp = cbstrstrkmp(text, buf);
+    bm = cbstrstrbm(text, buf);
+    if(kmp != std || bm != std){
+      err = TRUE;
+      break;
+    }
+    if(disp && std) printf("%s\n", buf);
+  }
+  if(err) fprintf(stderr, "%s: string scanning failed\n", progname);
+  if(!disp && !err){
+    printfflush("ok\n");
+    printfflush("all ok\n\n");
+  }
+  free(text);
+  return err ? 1 : 0;
+}
+
+
+/* perform list command */
+int dolist(int rnum, int disp){
+  CBLIST *list;
+  const char *vbuf;
+  char buf[RECBUFSIZ], *tmp;
+  int i, err, len, vsiz;
+  if(!disp) printfflush("<List Writing Test>\n  rnum=%d\n\n", rnum);
+  list = cblistopen();
+  err = FALSE;
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", i);
+    cblistpush(list, buf, len);
+    if(!disp && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  if(disp){
+    for(i = 0; i < cblistnum(list); i++){
+      if((vbuf = cblistval(list, i, &vsiz)) != NULL){
+        printfflush("%s:%d\n", vbuf, vsiz);
+      } else {
+        fprintf(stderr, "%s: val error\n", progname);
+        err = TRUE;
+        break;
+      }
+    }
+    printfflush("\n");
+    while((tmp = cblistpop(list, &vsiz)) != NULL){
+      printfflush("%s:%d\n", tmp, vsiz);
+      free(tmp);
+    }
+  }
+  cblistclose(list);
+  if(!disp && !err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform list command */
+int domap(int rnum, int bnum, int disp){
+  CBMAP *map;
+  const char *kbuf, *vbuf;
+  char buf[RECBUFSIZ];
+  int i, err, len, ksiz, vsiz;
+  if(!disp) printfflush("<Map Writing Test>\n  rnum=%d  bnum=%d\n\n", rnum, bnum);
+  map = bnum > 0 ? cbmapopenex(bnum) : cbmapopen();
+  err = FALSE;
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!cbmapput(map, buf, len, buf, len, FALSE)){
+      fprintf(stderr, "%s: put error\n", progname);
+      err = TRUE;
+      break;
+    }
+    if(!disp && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  if(disp){
+    for(i = 1; i <= rnum; i++){
+      len = sprintf(buf, "%08d", i);
+      if((vbuf = cbmapget(map, buf, len, &vsiz)) != NULL){
+        printfflush("%s:%d\t%s:%d\n", buf, len, vbuf, vsiz);
+      } else {
+        fprintf(stderr, "%s: get error\n", progname);
+      }
+    }
+    printfflush("\n");
+    cbmapiterinit(map);
+    while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
+      vbuf = cbmapiterval(kbuf, &vsiz);
+      printfflush("%s:%d\t%s:%d\n", kbuf, ksiz, vbuf, vsiz);
+    }
+  }
+  cbmapclose(map);
+  if(!disp && !err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform heap command */
+int doheap(int rnum, int max, int disp){
+  CBHEAP *heap;
+  int *orig, *ary;
+  int i, err, num, anum;
+  if(!disp) printfflush("<Heap Writing Test>\n  rnum=%d  max=%d\n\n", rnum, max);
+  orig = disp ? cbmalloc(rnum * sizeof(int) + 1) : NULL;
+  heap = cbheapopen(sizeof(int), max, intpcmp);
+  err = FALSE;
+  for(i = 1; i <= rnum; i++){
+    num = myrand() % rnum + 1;
+    if(orig) orig[i-1] = num;
+    cbheapinsert(heap, &num);
+    if(!disp && rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  if(disp){
+    for(i = 0; i < cbheapnum(heap); i++){
+      printf("%d\n", *(int *)cbheapval(heap, i));
+    }
+    printf("\n");
+    qsort(orig, rnum, sizeof(int), intpcmp);
+    ary = (int *)cbheaptomalloc(heap, &anum);
+    if(anum != rnum && anum != max) err = TRUE;
+    for(i = 0; i < anum; i++){
+      printf("%d\t%d\n", ary[i], orig[i]);
+      if(ary[i] != orig[i]) err = TRUE;
+    }
+    free(ary);
+  } else {
+    cbheapclose(heap);
+  }
+  free(orig);
+  if(!disp && !err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+int dowicked(int rnum){
+  CBLIST *list;
+  CBMAP *map;
+  int i, len;
+  char buf[RECBUFSIZ], *tmp;
+  printfflush("<Wicked Writing Test>\n  rnum=%d\n\n", rnum);
+  list = cblistopen();
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%d", myrand() % rnum + 1);
+    switch(myrand() % 16){
+    case 0:
+      free(cblistpop(list, NULL));
+      putchar('O');
+      break;
+    case 1:
+      cblistunshift(list, buf, len);
+      putchar('U');
+      break;
+    case 2:
+      free(cblistshift(list, NULL));
+      putchar('S');
+      break;
+    case 3:
+      cblistinsert(list, myrand() % (cblistnum(list) + 1), buf, len);
+      putchar('I');
+      break;
+    case 4:
+      free(cblistremove(list, myrand() % (cblistnum(list) + 1), NULL));
+      putchar('R');
+      break;
+    case 5:
+      cblistover(list, myrand() % (cblistnum(list) + 1), buf, len);
+      putchar('V');
+      break;
+    case 6:
+      tmp = cbmemdup(buf, len);
+      cblistpushbuf(list, tmp, len);
+      putchar('B');
+      break;
+    default:
+      cblistpush(list, buf, len);
+      putchar('P');
+      break;
+    }
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  cblistclose(list);
+  map = cbmapopen();
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%d", myrand() % rnum + 1);
+    switch(myrand() % 16){
+    case 0:
+      cbmapput(map, buf, len, buf, len, FALSE);
+      putchar('I');
+      break;
+    case 1:
+      cbmapputcat(map, buf, len, buf, len);
+      putchar('C');
+      break;
+    case 2:
+      cbmapget(map, buf, len, NULL);
+      putchar('V');
+      break;
+    case 3:
+      cbmapout(map, buf, len);
+      putchar('D');
+      break;
+    case 4:
+      cbmapmove(map, buf, len, myrand() % 2);
+      putchar('M');
+      break;
+    default:
+      cbmapput(map, buf, len, buf, len, TRUE);
+      putchar('O');
+      break;
+    }
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  cbmapclose(map);
+  printfflush("ok\n\n");
+  return 0;
+}
+
+
+/* perform misc command */
+int domisc(void){
+  CBDATUM *odatum, *ndatum;
+  CBLIST *olist, *nlist, *elems, *glist;
+  CBMAP *omap, *nmap, *pairs, *gmap;
+  int i, j, ssiz, osiz, tsiz, jl;
+  char kbuf[RECBUFSIZ], vbuf[RECBUFSIZ], *sbuf, spbuf[1024], *tmp, *orig, renc[64];
+  const char *op, *np;
+  time_t t;
+  printfflush("<Miscellaneous Test>\n\n");
+  printfflush("Checking memory allocation ... ");
+  tmp = cbmalloc(1024);
+  for(i = 1; i <= 65536; i *= 2){
+    tmp = cbrealloc(tmp, i);
+  }
+  cbfree(tmp);
+  printfflush("ok\n");
+  printfflush("Checking basic datum ... ");
+  odatum = cbdatumopen("x", -1);
+  for(i = 0; i < 1000; i++){
+    cbdatumcat(odatum, "x", 1);
+  }
+  cbdatumclose(odatum);
+  tmp = cbmalloc(3);
+  memcpy(tmp, "abc", 3);
+  odatum = cbdatumopenbuf(tmp, 3);
+  for(i = 0; i < 1000; i++){
+    cbdatumcat(odatum, ".", 1);
+  }
+  ndatum = cbdatumdup(odatum);
+  for(i = 0; i < 1000; i++){
+    cbdatumcat(ndatum, "*", 1);
+  }
+  for(i = 0; i < 1000; i++){
+    tmp = cbmalloc(3);
+    memcpy(tmp, "123", 3);
+    cbdatumsetbuf(ndatum, tmp, 3);
+  }
+  cbdatumprintf(ndatum, "[%s\t%08d\t%08o\t%08x\t%08.1f\t%@\t%?\t%:]",
+                "mikio", 1978, 1978, 1978, 1978.0211, "<>&#!+-*/%", "<>&#!+-*/%", "<>&#!+-*/%");
+  cbdatumclose(ndatum);
+  cbdatumclose(odatum);
+  printfflush("ok\n");
+  printfflush("Checking serialization of list ... ");
+  olist = cblistopen();
+  for(i = 1; i <= 1000; i++){
+    sprintf(vbuf, "%d", i);
+    cblistpush(olist, vbuf, -1);
+  }
+  sbuf = cblistdump(olist, &ssiz);
+  nlist = cblistload(sbuf, ssiz);
+  free(sbuf);
+  for(i = 0; i < cblistnum(olist); i++){
+    op = cblistval(olist, i, NULL);
+    np = cblistval(nlist, i, NULL);
+    if(!op || !np || strcmp(op, np)){
+      cblistclose(nlist);
+      cblistclose(olist);
+      fprintf(stderr, "%s: validation failed\n", progname);
+      return 1;
+    }
+  }
+  cblistclose(nlist);
+  cblistclose(olist);
+  printfflush("ok\n");
+  printfflush("Checking serialization of map ... ");
+  omap = cbmapopen();
+  for(i = 1; i <= 1000; i++){
+    sprintf(kbuf, "%X", i);
+    sprintf(vbuf, "[%d]", i);
+    cbmapput(omap, kbuf, -1, vbuf, -1, TRUE);
+  }
+  sbuf = cbmapdump(omap, &ssiz);
+  nmap = cbmapload(sbuf, ssiz);
+  free(cbmaploadone(sbuf, ssiz, "1", 2, &tsiz));
+  free(cbmaploadone(sbuf, ssiz, "33", 2, &tsiz));
+  free(sbuf);
+  cbmapiterinit(omap);
+  while((op = cbmapiternext(omap, NULL)) != NULL){
+    if(!(np = cbmapget(nmap, op, -1, NULL))){
+      cbmapclose(nmap);
+      cbmapclose(omap);
+      fprintf(stderr, "%s: validation failed\n", progname);
+      return 1;
+    }
+  }
+  cbmapclose(nmap);
+  cbmapclose(omap);
+  printfflush("ok\n");
+  printfflush("Checking string utilities ... ");
+  sprintf(spbuf, "[%08d/%08o/%08u/%08x/%08X/%08.3e/%08.3E/%08.3f/%08.3g/%08.3G/%c/%s/%%]",
+          123456, 123456, 123456, 123456, 123456,
+          123456.789, 123456.789, 123456.789, 123456.789, 123456.789,
+          'A', "hoge");
+  tmp = cbsprintf("[%08d/%08o/%08u/%08x/%08X/%08.3e/%08.3E/%08.3f/%08.3g/%08.3G/%c/%s/%%]",
+                  123456, 123456, 123456, 123456, 123456,
+                  123456.789, 123456.789, 123456.789, 123456.789, 123456.789,
+                  'A', "hoge");
+  while(strcmp(spbuf, tmp)){
+    free(tmp);
+    fprintf(stderr, "%s: cbsprintf is invalid\n", progname);
+    return 1;
+  }
+  free(tmp);
+  pairs = cbmapopen();
+  cbmapput(pairs, "aa", -1, "AAA", -1, TRUE);
+  cbmapput(pairs, "bb", -1, "BBB", -1, TRUE);
+  cbmapput(pairs, "cc", -1, "CCC", -1, TRUE);
+  cbmapput(pairs, "ZZZ", -1, "z", -1, TRUE);
+  tmp = cbreplace("[aaaaabbbbbcccccdddddZZZZ]", pairs);
+  if(strcmp(tmp, "[AAAAAAaBBBBBBbCCCCCCcdddddzZ]")){
+    free(tmp);
+    cbmapclose(pairs);
+    fprintf(stderr, "%s: cbreplace is invalid\n", progname);
+    return 1;
+  }
+  free(tmp);
+  cbmapclose(pairs);
+  elems = cbsplit("aa bb,ccc-dd,", -1, " ,-");
+  if(cblistnum(elems) != 5 || strcmp(cblistval(elems, 0, NULL), "aa") ||
+     strcmp(cblistval(elems, 1, NULL), "bb") || strcmp(cblistval(elems, 2, NULL), "ccc") ||
+     strcmp(cblistval(elems, 3, NULL), "dd") || strcmp(cblistval(elems, 4, NULL), "")){
+    cblistclose(elems);
+    fprintf(stderr, "%s: cbsplit is invalid\n", progname);
+    return 1;
+  }
+  cblistclose(elems);
+  if(cbstricmp("abc", "ABC") || !cbstricmp("abc", "abcd")){
+    fprintf(stderr, "%s: cbstricmp is invalid\n", progname);
+    return 1;
+  }
+  if(!cbstrfwmatch("abcd", "abc") || cbstrfwmatch("abc", "abcd")){
+    fprintf(stderr, "%s: cbstrfwmatch is invalid\n", progname);
+    return 1;
+  }
+  if(!cbstrfwimatch("abcd", "ABC") || cbstrfwmatch("abc", "ABCD")){
+    fprintf(stderr, "%s: cbstrfwimatch is invalid\n", progname);
+    return 1;
+  }
+  if(!cbstrbwmatch("dcba", "cba") || cbstrbwmatch("cba", "dcba")){
+    fprintf(stderr, "%s: cbstrbwmatch is invalid\n", progname);
+    return 1;
+  }
+  if(!cbstrbwimatch("dcba", "CBA") || cbstrbwimatch("cba", "DCBA")){
+    fprintf(stderr, "%s: cbstrbwimatch is invalid\n", progname);
+    return 1;
+  }
+  tmp = cbmemdup(" \r\n[Quick   Database Manager]\r\n ", -1);
+  if(cbstrtoupper(tmp) != tmp || strcmp(tmp, " \r\n[QUICK   DATABASE MANAGER]\r\n ")){
+    free(tmp);
+    fprintf(stderr, "%s: cbstrtoupper is invalid\n", progname);
+    return 1;
+  }
+  if(cbstrtolower(tmp) != tmp || strcmp(tmp, " \r\n[quick   database manager]\r\n ")){
+    free(tmp);
+    fprintf(stderr, "%s: cbstrtolower is invalid\n", progname);
+    return 1;
+  }
+  if(cbstrtrim(tmp) != tmp || strcmp(tmp, "[quick   database manager]")){
+    free(tmp);
+    fprintf(stderr, "%s: cbstrtrim is invalid\n", progname);
+    return 1;
+  }
+  if(cbstrsqzspc(tmp) != tmp || strcmp(tmp, "[quick database manager]")){
+    free(tmp);
+    fprintf(stderr, "%s: cbstrsqzspc is invalid\n", progname);
+    return 1;
+  }
+  cbstrcututf(tmp, 5);
+  if(cbstrcountutf(tmp) != 5){
+    free(tmp);
+    fprintf(stderr, "%s: cbstrcututf or cbstrcountutf is invalid\n", progname);
+    return 1;
+  }
+  free(tmp);
+  printfflush("ok\n");
+  printfflush("Checking encoding utilities ... ");
+  strcpy(spbuf, "My name is \xca\xbf\xce\xd3\xb4\xb4\xcd\xba.\n\n<Love & Peace!>\n");
+  tmp = cbbaseencode(spbuf, -1);
+  orig = cbbasedecode(tmp, &osiz);
+  if(osiz != strlen(spbuf) || strcmp(orig, spbuf)){
+    free(orig);
+    free(tmp);
+    fprintf(stderr, "%s: Base64 encoding is invalid\n", progname);
+    return 1;
+  }
+  free(orig);
+  free(tmp);
+  strcpy(spbuf, "My name is \xca\xbf\xce\xd3\xb4\xb4\xcd\xba.\n\n<Love & Peace!>\n");
+  tmp = cbquoteencode(spbuf, -1);
+  orig = cbquotedecode(tmp, &osiz);
+  if(osiz != strlen(spbuf) || strcmp(orig, spbuf)){
+    free(orig);
+    free(tmp);
+    fprintf(stderr, "%s: quoted-printable encoding is invalid\n", progname);
+    return 1;
+  }
+  free(orig);
+  free(tmp);
+  strcpy(spbuf, "My name is \xca\xbf\xce\xd3\xb4\xb4\xcd\xba.\n\n<Love & Peace!>\n");
+  tmp = cbmimeencode(spbuf, "ISO-8859-1", TRUE);
+  orig = cbmimedecode(tmp, renc);
+  if(osiz != strlen(spbuf) || strcmp(orig, spbuf) || strcmp(renc, "ISO-8859-1")){
+    free(orig);
+    free(tmp);
+    fprintf(stderr, "%s: MIME encoding is invalid\n", progname);
+    return 1;
+  }
+  free(orig);
+  free(tmp);
+  strcpy(spbuf, "\"He says...\r\n\"\"What PROGRAM are they watching?\"\"\"");
+  tmp = cbcsvunescape(spbuf);
+  orig = cbcsvescape(tmp);
+  if(strcmp(orig, spbuf)){
+    free(orig);
+    free(tmp);
+    fprintf(stderr, "%s: CSV escaping is invalid\n", progname);
+    return 1;
+  }
+  free(orig);
+  free(tmp);
+  strcpy(spbuf, "<Nuts&Milk> is "very" surfin'!");
+  tmp = cbxmlunescape(spbuf);
+  orig = cbxmlescape(tmp);
+  if(strcmp(orig, spbuf)){
+    free(orig);
+    free(tmp);
+    fprintf(stderr, "%s: XML escaping is invalid\n", progname);
+    return 1;
+  }
+  free(orig);
+  free(tmp);
+  printfflush("ok\n");
+  printfflush("Checking date utilities ... ");
+  for(i = 0; i < 200; i++){
+    jl = (myrand() % 23) * 1800;
+    if(myrand() % 2 == 0) jl *= -1;
+    t = myrand() % (INT_MAX - 3600 * 24 * 365 * 6) + 3600 * 24 * 365 * 5;
+    tmp = cbdatestrwww(t, jl);
+    t = cbstrmktime(tmp);
+    orig = cbdatestrwww(t, jl);
+    if(strcmp(orig, tmp)){
+      free(orig);
+      free(tmp);
+      fprintf(stderr, "%s: W3CDTF formatter is invalid\n", progname);
+      return 1;
+    }
+    free(orig);
+    free(tmp);
+    tmp = cbdatestrhttp(t, jl);
+    t = cbstrmktime(tmp);
+    orig = cbdatestrhttp(t, jl);
+    if(strcmp(orig, tmp)){
+      free(orig);
+      free(tmp);
+      fprintf(stderr, "%s: RFC 822 date formatter is invalid\n", progname);
+      return 1;
+    }
+    free(orig);
+    free(tmp);
+  }
+  printfflush("ok\n");
+  printfflush("Checking the global garbage collector ... ");
+  for(i = 0; i < 512; i++){
+    glist = cblistopen();
+    cbglobalgc(glist, (void (*)(void *))cblistclose);
+    for(j = 0; j < 10; j++){
+      sprintf(kbuf, "%08d", j);
+      cblistpush(glist, kbuf, -1);
+    }
+    gmap = cbmapopen();
+    cbglobalgc(gmap, (void (*)(void *))cbmapclose);
+    for(j = 0; j < 10; j++){
+      sprintf(kbuf, "%08d", j);
+      cbmapput(gmap, kbuf, -1, kbuf, -1, TRUE);
+    }
+    if(myrand() % 64 == 0){
+      cbvmemavail(100);
+      cbggcsweep();
+    }
+  }
+  printfflush("ok\n");
+  printfflush("all ok\n\n");
+  return 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/cbtest.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/configure
===================================================================
--- box/trunk/qdbm/configure	                        (rev 0)
+++ box/trunk/qdbm/configure	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,3913 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59 for qdbm 1.8.77.
+#
+# 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='qdbm'
+PACKAGE_TARNAME='qdbm'
+PACKAGE_VERSION='1.8.77'
+PACKAGE_STRING='qdbm 1.8.77'
+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 LIBVER LIBREV TARGETS MYDEFS MYOPTS MGWLIBS LD AR 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 qdbm 1.8.77 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 qdbm 1.8.77:";;
+   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-stable         build for stable release
+  --enable-pthread        use POSIX thread and make APIs thread-safe
+  --disable-lock          build for environments without file locking
+  --disable-mmap          build for environments without memory mapping
+  --enable-zlib           feature ZLIB for B+ tree and inverted index
+  --enable-lzo            feature LZO for B+ tree and inverted index
+  --enable-bzip           feature BZIP2 for B+ tree and inverted index
+  --enable-iconv          feature ICONV utilities
+  --disable-warn          hide warnings in the configuration
+
+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
+qdbm configure 1.8.77
+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 qdbm $as_me 1.8.77, 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Export variables
+LIBVER=14
+LIBREV=13
+TARGETS="all"
+MYDEFS=""
+MYOPTS=""
+MGWLIBS=""
+LD="ld"
+AR="ar"
+
+# Building paths
+pathtmp="$PATH"
+PATH="$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/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"
+export PATH LIBRARY_PATH LD_LIBRARY_PATH CPATH
+
+
+
+#================================================================
+# Options
+#================================================================
+
+
+# Internal variables
+enables=""
+ispthread=""
+iszlib=""
+isiconv=""
+isnowarn=""
+
+# 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
+  TARGETS="debug"
+  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
+  TARGETS="devel"
+  enables="$enables (devel)"
+fi
+
+# Stable mode
+# Check whether --enable-stable or --disable-stable was given.
+if test "${enable_stable+set}" = set; then
+  enableval="$enable_stable"
+
+fi;
+if test "$enable_stable" = "yes"
+then
+  TARGETS="stable"
+  enables="$enables (stable)"
+fi
+
+# Enable 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" = "yes"
+then
+  MYDEFS="$MYDEFS -DMYPTHREAD"
+  enables="$enables (pthread)"
+  ispthread="yes"
+fi
+
+# Disable file locking
+# Check whether --enable-lock or --disable-lock was given.
+if test "${enable_lock+set}" = set; then
+  enableval="$enable_lock"
+
+fi;
+if test "$enable_lock" = "no"
+then
+  MYDEFS="$MYDEFS -DMYNOLOCK"
+  enables="$enables (no-lock)"
+fi
+
+# Disable memory mapping
+# Check whether --enable-mmap or --disable-mmap was given.
+if test "${enable_mmap+set}" = set; then
+  enableval="$enable_mmap"
+
+fi;
+if test "$enable_mmap" = "no"
+then
+  MYDEFS="$MYDEFS -DMYNOMMAP"
+  enables="$enables (no-mmap)"
+fi
+
+# Enable 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" = "yes"
+then
+  MYDEFS="$MYDEFS -DMYZLIB"
+  MGWLIBS="-lz $MGWLIBS"
+  enables="$enables (zlib)"
+  iszlib="yes"
+fi
+
+# Enable LZO compression
+# Check whether --enable-lzo or --disable-lzo was given.
+if test "${enable_lzo+set}" = set; then
+  enableval="$enable_lzo"
+
+fi;
+if test "$enable_lzo" = "yes"
+then
+  MYDEFS="$MYDEFS -DMYLZO"
+  MGWLIBS="-llzo2 $MGWLIBS"
+  enables="$enables (lzo)"
+  islzo="yes"
+fi
+
+# Enable BZIP2 compression
+# Check whether --enable-bzip or --disable-bzip was given.
+if test "${enable_bzip+set}" = set; then
+  enableval="$enable_bzip"
+
+fi;
+if test "$enable_bzip" = "yes"
+then
+  MYDEFS="$MYDEFS -DMYBZIP"
+  MGWLIBS="-lbz2 $MGWLIBS"
+  enables="$enables (bzip)"
+  isbzip="yes"
+fi
+
+# Enable ICONV utilities
+# Check whether --enable-iconv or --disable-iconv was given.
+if test "${enable_iconv+set}" = set; then
+  enableval="$enable_iconv"
+
+fi;
+if test "$enable_iconv" = "yes"
+then
+  MYDEFS="$MYDEFS -DMYICONV"
+  MGWLIBS="-liconv $MGWLIBS"
+  enables="$enables (iconv)"
+  isiconv="yes"
+fi
+
+# No warning configuration
+# Check whether --enable-warn or --disable-warn was given.
+if test "${enable_warn+set}" = set; then
+  enableval="$enable_warn"
+
+fi;
+if test "$enable_warn" = "no"
+then
+  isnowarn="yes"
+fi
+
+# Messages
+printf '#================================================================\n'
+printf '# Configuring QDBM version %s%s.\n' "$PACKAGE_VERSION" "$enables"
+printf '#================================================================\n'
+
+
+
+#================================================================
+# Checking Commands to Build with
+#================================================================
+
+
+# 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
+
+if test "$GCC" != "yes"
+then
+  if test "$isnowarn" != "yes"
+  then
+    printf '#================================================================\n' 1>&2
+    printf '# WARNING: GCC is required to build this package.\n' 1>&2
+    printf '#================================================================\n' 1>&2
+  fi
+fi
+if uname | egrep -i 'Linux' > /dev/null 2>&1 &&
+  uname -m | egrep '(x|i)(3|4|5|6|7|8|9)?86' > /dev/null 2>&1
+then
+  MYOPTS="-minline-all-stringops"
+fi
+if uname | egrep -i 'SunOS' > /dev/null 2>&1
+then
+  MYOPTS="-O1 -fno-omit-frame-pointer -fno-force-addr"
+fi
+if uname | egrep -i 'BSD' > /dev/null 2>&1
+then
+  MYOPTS="-O1 -fno-omit-frame-pointer -fno-force-addr"
+fi
+if gcc --version | egrep -i '^2\.(8|9)' > /dev/null 2>&1
+then
+  MYOPTS="-O1 -fno-omit-frame-pointer -fno-force-addr"
+fi
+
+# Linker
+printf 'checking for ld... '
+if which ld | grep '/ld$' > /dev/null 2>&1
+then
+  LD=`which ld`
+  printf '%s\n' "$LD"
+else
+  printf 'no\n'
+  if test "$isnowarn" != "yes"
+  then
+    printf '#================================================================\n' 1>&2
+    printf '# WARNING: ld is not found in PATH.\n' 1>&2
+    printf '#================================================================\n' 1>&2
+  fi
+fi
+
+# Archiver
+printf 'checking for ar... '
+if which ar | grep '/ar$' > /dev/null 2>&1
+then
+  AR=`which ar`
+  printf '%s\n' "$AR"
+else
+  printf 'no\n'
+  if test "$isnowarn" != "yes"
+  then
+    printf '#================================================================\n' 1>&2
+    printf '# WARNING: ar is not found in PATH.\n' 1>&2
+    printf '#================================================================\n' 1>&2
+  fi
+fi
+
+
+
+#================================================================
+# Checking Libraries
+#================================================================
+
+
+# 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
+
+
+# for pthread
+if test "$ispthread" = "yes"
+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
+
+# for zlib
+if test "$iszlib" = "yes"
+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
+
+# for lzo
+if test "$islzo" = "yes"
+then
+
+echo "$as_me:$LINENO: checking for main in -llzo2" >&5
+echo $ECHO_N "checking for main in -llzo2... $ECHO_C" >&6
+if test "${ac_cv_lib_lzo2_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-llzo2  $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_lzo2_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_lzo2_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_lzo2_main" >&5
+echo "${ECHO_T}$ac_cv_lib_lzo2_main" >&6
+if test $ac_cv_lib_lzo2_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBLZO2 1
+_ACEOF
+
+  LIBS="-llzo2 $LIBS"
+
+fi
+
+fi
+
+# for bzip
+if test "$isbzip" = "yes"
+then
+
+echo "$as_me:$LINENO: checking for main in -lbz2" >&5
+echo $ECHO_N "checking for main in -lbz2... $ECHO_C" >&6
+if test "${ac_cv_lib_bz2_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lbz2  $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_bz2_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_bz2_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_bz2_main" >&5
+echo "${ECHO_T}$ac_cv_lib_bz2_main" >&6
+if test $ac_cv_lib_bz2_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBBZ2 1
+_ACEOF
+
+  LIBS="-lbz2 $LIBS"
+
+fi
+
+fi
+
+# for iconv
+if test "$isiconv" = "yes"
+then
+
+echo "$as_me:$LINENO: checking for main in -liconv" >&5
+echo $ECHO_N "checking for main in -liconv... $ECHO_C" >&6
+if test "${ac_cv_lib_iconv_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-liconv  $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_iconv_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_iconv_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_iconv_main" >&5
+echo "${ECHO_T}$ac_cv_lib_iconv_main" >&6
+if test $ac_cv_lib_iconv_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBICONV 1
+_ACEOF
+
+  LIBS="-liconv $LIBS"
+
+fi
+
+fi
+
+# For old BSDs
+if test "$ispthread" = "yes" && uname -a | grep BSD > /dev/null &&
+  test -f /usr/lib/libc_r.a && test ! -f /usr/lib/libpthread.a
+then
+  LIBS=`printf '%s' "$LIBS" | sed 's/-lc/-lc_r/g'`
+fi
+
+# Duplication of QDBM for C
+echo "$as_me:$LINENO: checking for main in -lqdbm" >&5
+echo $ECHO_N "checking for main in -lqdbm... $ECHO_C" >&6
+if test "${ac_cv_lib_qdbm_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lqdbm  $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_qdbm_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_qdbm_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_qdbm_main" >&5
+echo "${ECHO_T}$ac_cv_lib_qdbm_main" >&6
+if test $ac_cv_lib_qdbm_main = yes; then
+  if test "$isnowarn" != "yes"
+  then
+    printf '#================================================================\n' 1>&2
+    printf '# WARNING: The existing library was detected.\n' 1>&2
+    printf '#================================================================\n' 1>&2
+  fi
+
+fi
+
+
+
+
+#================================================================
+# Generic Settings
+#================================================================
+
+
+# Export variables
+
+
+
+
+
+
+
+
+
+
+
+# Targets
+                                        ac_config_files="$ac_config_files Makefile LTmakefile qdbm.spec qdbm.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 qdbm $as_me 1.8.77, 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 at gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+qdbm config.status 1.8.77
+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" ;;
+  "LTmakefile" ) CONFIG_FILES="$CONFIG_FILES LTmakefile" ;;
+  "qdbm.spec" ) CONFIG_FILES="$CONFIG_FILES qdbm.spec" ;;
+  "qdbm.pc" ) CONFIG_FILES="$CONFIG_FILES qdbm.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, at SHELL@,$SHELL,;t t
+s, at PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s, at PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s, at PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s, at PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s, at PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s, at PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s, at exec_prefix@,$exec_prefix,;t t
+s, at prefix@,$prefix,;t t
+s, at program_transform_name@,$program_transform_name,;t t
+s, at bindir@,$bindir,;t t
+s, at sbindir@,$sbindir,;t t
+s, at libexecdir@,$libexecdir,;t t
+s, at datadir@,$datadir,;t t
+s, at sysconfdir@,$sysconfdir,;t t
+s, at sharedstatedir@,$sharedstatedir,;t t
+s, at localstatedir@,$localstatedir,;t t
+s, at libdir@,$libdir,;t t
+s, at includedir@,$includedir,;t t
+s, at oldincludedir@,$oldincludedir,;t t
+s, at infodir@,$infodir,;t t
+s, at mandir@,$mandir,;t t
+s, at build_alias@,$build_alias,;t t
+s, at host_alias@,$host_alias,;t t
+s, at target_alias@,$target_alias,;t t
+s, at DEFS@,$DEFS,;t t
+s, at ECHO_C@,$ECHO_C,;t t
+s, at ECHO_N@,$ECHO_N,;t t
+s, at ECHO_T@,$ECHO_T,;t t
+s, at LIBS@,$LIBS,;t t
+s, at CC@,$CC,;t t
+s, at CFLAGS@,$CFLAGS,;t t
+s, at LDFLAGS@,$LDFLAGS,;t t
+s, at CPPFLAGS@,$CPPFLAGS,;t t
+s, at ac_ct_CC@,$ac_ct_CC,;t t
+s, at EXEEXT@,$EXEEXT,;t t
+s, at OBJEXT@,$OBJEXT,;t t
+s, at LIBVER@,$LIBVER,;t t
+s, at LIBREV@,$LIBREV,;t t
+s, at TARGETS@,$TARGETS,;t t
+s, at MYDEFS@,$MYDEFS,;t t
+s, at MYOPTS@,$MYOPTS,;t t
+s, at MGWLIBS@,$MGWLIBS,;t t
+s, at LD@,$LD,;t t
+s, at AR@,$AR,;t t
+s, at LIBOBJS@,$LIBOBJS,;t t
+s, at 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, at configure_input@,$configure_input,;t t
+s, at srcdir@,$ac_srcdir,;t t
+s, at abs_srcdir@,$ac_abs_srcdir,;t t
+s, at top_srcdir@,$ac_top_srcdir,;t t
+s, at abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s, at builddir@,$ac_builddir,;t t
+s, at abs_builddir@,$ac_abs_builddir,;t t
+s, at top_builddir@,$ac_top_builddir,;t t
+s, at 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


Property changes on: box/trunk/qdbm/configure
___________________________________________________________________
Added: svn:executable
   + *

Added: box/trunk/qdbm/configure.in
===================================================================
--- box/trunk/qdbm/configure.in	                        (rev 0)
+++ box/trunk/qdbm/configure.in	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,312 @@
+# Source of configuration for QDBM
+
+
+
+#================================================================
+# Generic Settings
+#================================================================
+
+
+# Targets
+AC_INIT(qdbm, 1.8.77)
+
+# Export variables
+LIBVER=14
+LIBREV=13
+TARGETS="all"
+MYDEFS=""
+MYOPTS=""
+MGWLIBS=""
+LD="ld"
+AR="ar"
+
+# Building paths
+pathtmp="$PATH"
+PATH="$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/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"
+export PATH LIBRARY_PATH LD_LIBRARY_PATH CPATH
+
+
+
+#================================================================
+# Options
+#================================================================
+
+
+# Internal variables
+enables=""
+ispthread=""
+iszlib=""
+isiconv=""
+isnowarn=""
+
+# Debug mode
+AC_ARG_ENABLE(debug,
+  AC_HELP_STRING([--enable-debug], [build for debugging]))
+if test "$enable_debug" = "yes"
+then
+  TARGETS="debug"
+  enables="$enables (debug)"
+fi
+
+# Developping mode
+AC_ARG_ENABLE(devel,
+  AC_HELP_STRING([--enable-devel], [build for development]))
+if test "$enable_devel" = "yes"
+then
+  TARGETS="devel"
+  enables="$enables (devel)"
+fi
+
+# Stable mode
+AC_ARG_ENABLE(stable,
+  AC_HELP_STRING([--enable-stable], [build for stable release]))
+if test "$enable_stable" = "yes"
+then
+  TARGETS="stable"
+  enables="$enables (stable)"
+fi
+
+# Enable POSIX thread
+AC_ARG_ENABLE(pthread,
+  AC_HELP_STRING([--enable-pthread], [use POSIX thread and make APIs thread-safe]))
+if test "$enable_pthread" = "yes"
+then
+  MYDEFS="$MYDEFS -DMYPTHREAD"
+  enables="$enables (pthread)"
+  ispthread="yes"
+fi
+
+# Disable file locking
+AC_ARG_ENABLE(lock,
+  AC_HELP_STRING([--disable-lock], [build for environments without file locking]))
+if test "$enable_lock" = "no"
+then
+  MYDEFS="$MYDEFS -DMYNOLOCK"
+  enables="$enables (no-lock)"
+fi
+
+# Disable memory mapping
+AC_ARG_ENABLE(mmap,
+  AC_HELP_STRING([--disable-mmap], [build for environments without memory mapping]))
+if test "$enable_mmap" = "no"
+then
+  MYDEFS="$MYDEFS -DMYNOMMAP"
+  enables="$enables (no-mmap)"
+fi
+
+# Enable ZLIB compression
+AC_ARG_ENABLE(zlib,
+  AC_HELP_STRING([--enable-zlib], [feature ZLIB for B+ tree and inverted index]))
+if test "$enable_zlib" = "yes"
+then
+  MYDEFS="$MYDEFS -DMYZLIB"
+  MGWLIBS="-lz $MGWLIBS"
+  enables="$enables (zlib)"
+  iszlib="yes"
+fi
+
+# Enable LZO compression
+AC_ARG_ENABLE(lzo,
+  AC_HELP_STRING([--enable-lzo], [feature LZO for B+ tree and inverted index]))
+if test "$enable_lzo" = "yes"
+then
+  MYDEFS="$MYDEFS -DMYLZO"
+  MGWLIBS="-llzo2 $MGWLIBS"
+  enables="$enables (lzo)"
+  islzo="yes"
+fi
+
+# Enable BZIP2 compression
+AC_ARG_ENABLE(bzip,
+  AC_HELP_STRING([--enable-bzip], [feature BZIP2 for B+ tree and inverted index]))
+if test "$enable_bzip" = "yes"
+then
+  MYDEFS="$MYDEFS -DMYBZIP"
+  MGWLIBS="-lbz2 $MGWLIBS"
+  enables="$enables (bzip)"
+  isbzip="yes"
+fi
+
+# Enable ICONV utilities
+AC_ARG_ENABLE(iconv,
+  AC_HELP_STRING([--enable-iconv], [feature ICONV utilities]))
+if test "$enable_iconv" = "yes"
+then
+  MYDEFS="$MYDEFS -DMYICONV"
+  MGWLIBS="-liconv $MGWLIBS"
+  enables="$enables (iconv)"
+  isiconv="yes"
+fi
+
+# No warning configuration
+AC_ARG_ENABLE(warn,
+  AC_HELP_STRING([--disable-warn], [hide warnings in the configuration]))
+if test "$enable_warn" = "no"
+then
+  isnowarn="yes"
+fi
+
+# Messages
+printf '#================================================================\n'
+printf '# Configuring QDBM version %s%s.\n' "$PACKAGE_VERSION" "$enables"
+printf '#================================================================\n'
+
+
+
+#================================================================
+# Checking Commands to Build with
+#================================================================
+
+
+# C compiler
+AC_PROG_CC
+if test "$GCC" != "yes"
+then
+  if test "$isnowarn" != "yes"
+  then
+    printf '#================================================================\n' 1>&2
+    printf '# WARNING: GCC is required to build this package.\n' 1>&2
+    printf '#================================================================\n' 1>&2
+  fi
+fi
+if uname | egrep -i 'Linux' > /dev/null 2>&1 &&
+  uname -m | egrep '(x|i)(3|4|5|6|7|8|9)?86' > /dev/null 2>&1
+then
+  MYOPTS="-minline-all-stringops"
+fi
+if uname | egrep -i 'SunOS' > /dev/null 2>&1
+then
+  MYOPTS="-O1 -fno-omit-frame-pointer -fno-force-addr"
+fi
+if uname | egrep -i 'BSD' > /dev/null 2>&1
+then
+  MYOPTS="-O1 -fno-omit-frame-pointer -fno-force-addr"
+fi
+if gcc --version | egrep -i '^2\.(8|9)' > /dev/null 2>&1
+then
+  MYOPTS="-O1 -fno-omit-frame-pointer -fno-force-addr"
+fi
+
+# Linker
+printf 'checking for ld... '
+if which ld | grep '/ld$' > /dev/null 2>&1
+then
+  LD=`which ld`
+  printf '%s\n' "$LD"
+else
+  printf 'no\n'
+  if test "$isnowarn" != "yes"
+  then
+    printf '#================================================================\n' 1>&2
+    printf '# WARNING: ld is not found in PATH.\n' 1>&2
+    printf '#================================================================\n' 1>&2
+  fi
+fi
+
+# Archiver
+printf 'checking for ar... '
+if which ar | grep '/ar$' > /dev/null 2>&1
+then
+  AR=`which ar`
+  printf '%s\n' "$AR"
+else
+  printf 'no\n'
+  if test "$isnowarn" != "yes"
+  then
+    printf '#================================================================\n' 1>&2
+    printf '# WARNING: ar is not found in PATH.\n' 1>&2
+    printf '#================================================================\n' 1>&2
+  fi
+fi
+
+
+
+#================================================================
+# Checking Libraries
+#================================================================
+
+
+# Underlying libraries
+AC_CHECK_LIB(c, main)
+
+# for pthread
+if test "$ispthread" = "yes"
+then
+  AC_CHECK_LIB(pthread, main)
+fi
+
+# for zlib
+if test "$iszlib" = "yes"
+then
+  AC_CHECK_LIB(z, main)
+fi
+
+# for lzo
+if test "$islzo" = "yes"
+then
+  AC_CHECK_LIB(lzo2, main)
+fi
+
+# for bzip
+if test "$isbzip" = "yes"
+then
+  AC_CHECK_LIB(bz2, main)
+fi
+
+# for iconv
+if test "$isiconv" = "yes"
+then
+  AC_CHECK_LIB(iconv, main)
+fi
+
+# For old BSDs
+if test "$ispthread" = "yes" && uname -a | grep BSD > /dev/null &&
+  test -f /usr/lib/libc_r.a && test ! -f /usr/lib/libpthread.a
+then
+  LIBS=`printf '%s' "$LIBS" | sed 's/-lc/-lc_r/g'`
+fi
+
+# Duplication of QDBM for C
+AC_CHECK_LIB(qdbm, main,
+  if test "$isnowarn" != "yes"
+  then
+    printf '#================================================================\n' 1>&2
+    printf '# WARNING: The existing library was detected.\n' 1>&2
+    printf '#================================================================\n' 1>&2
+  fi
+)
+
+
+
+#================================================================
+# Generic Settings
+#================================================================
+
+
+# Export variables
+AC_SUBST(LIBVER)
+AC_SUBST(LIBREV)
+AC_SUBST(TARGETS)
+AC_SUBST(MYDEFS)
+AC_SUBST(MYOPTS)
+AC_SUBST(MGWLIBS)
+AC_SUBST(LD)
+AC_SUBST(AR)
+AC_SUBST(CPPFLAGS)
+AC_SUBST(LDFLAGS)
+
+# Targets
+AC_OUTPUT(Makefile LTmakefile qdbm.spec qdbm.pc)
+
+# Messages
+printf '#================================================================\n'
+printf '# Ready to make.\n'
+printf '#================================================================\n'
+
+
+
+# END OF FILE

Added: box/trunk/qdbm/crmgr.c
===================================================================
--- box/trunk/qdbm/crmgr.c	                        (rev 0)
+++ box/trunk/qdbm/crmgr.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,956 @@
+/*************************************************************************************************
+ * Utility for debugging Curia and its applications
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <curia.h>
+#include <cabin.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define ALIGNSIZ       32                /* basic size of alignment */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+char *hextoobj(const char *str, int *sp);
+char *dectoiobj(const char *str, int *sp);
+int runcreate(int argc, char **argv);
+int runput(int argc, char **argv);
+int runout(int argc, char **argv);
+int runget(int argc, char **argv);
+int runlist(int argc, char **argv);
+int runoptimize(int argc, char **argv);
+int runinform(int argc, char **argv);
+int runremove(int argc, char **argv);
+int runrepair(int argc, char **argv);
+int runexportdb(int argc, char **argv);
+int runimportdb(int argc, char **argv);
+int runsnaffle(int argc, char **argv);
+void pdperror(const char *name);
+void printobj(const char *obj, int size);
+void printobjhex(const char *obj, int size);
+int docreate(const char *name, int bnum, int dnum, int sparse);
+int doput(const char *name, const char *kbuf, int ksiz,
+          const char *vbuf, int vsiz, int dmode, int lob, int align);
+int doout(const char *name, const char *kbuf, int ksiz, int lob);
+int doget(const char *name, int opts, const char *kbuf, int ksiz,
+          int start, int max, int ox, int lob, int nb);
+int dolist(const char *name, int opts, int kb, int vb, int ox);
+int dooptimize(const char *name, int bnum, int align);
+int doinform(const char *name, int opts);
+int doremove(const char *name);
+int dorepair(const char *name);
+int doexportdb(const char *name, const char *dir);
+int doimportdb(const char *name, const char *dir, int bnum, int dnum);
+int dosnaffle(const char *name, const char *kbuf, int ksiz, int ox, int nb);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  char *env;
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if((env = getenv("QDBMDBGFD")) != NULL) dpdbgfd = atoi(env);
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "create")){
+    rv = runcreate(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], "inform")){
+    rv = runinform(argc, argv);
+  } else if(!strcmp(argv[1], "remove")){
+    rv = runremove(argc, argv);
+  } else if(!strcmp(argv[1], "repair")){
+    rv = runrepair(argc, argv);
+  } else if(!strcmp(argv[1], "exportdb")){
+    rv = runexportdb(argc, argv);
+  } else if(!strcmp(argv[1], "importdb")){
+    rv = runimportdb(argc, argv);
+  } else if(!strcmp(argv[1], "snaffle")){
+    rv = runsnaffle(argc, argv);
+  } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){
+    printf("Powered by QDBM version %s on %s%s\n",
+           dpversion, dpsysname, dpisreentrant ? " (reentrant)" : "");
+    printf("Copyright (c) 2000-2007 Mikio Hirabayashi\n");
+    rv = 0;
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: administration utility for Curia\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s create [-s] [-bnum num] [-dnum num] name\n", progname);
+  fprintf(stderr, "  %s put [-kx|-ki] [-vx|-vi|-vf] [-keep|-cat] [-lob] [-na] "
+          "name key val\n", progname);
+  fprintf(stderr, "  %s out [-kx|-ki] [-lob] name key\n", progname);
+  fprintf(stderr, "  %s get [-nl] [-kx|-ki] [-start num] [-max num] [-ox] [-lob] [-n] name key\n",
+          progname);
+  fprintf(stderr, "  %s list [-nl] [-k|-v] [-ox] name\n", progname);
+  fprintf(stderr, "  %s optimize [-bnum num] [-na] name\n", progname);
+  fprintf(stderr, "  %s inform [-nl] name\n", progname);
+  fprintf(stderr, "  %s remove name\n", progname);
+  fprintf(stderr, "  %s repair name\n", progname);
+  fprintf(stderr, "  %s exportdb name dir\n", progname);
+  fprintf(stderr, "  %s importdb [-bnum num] [-dnum num] name dir\n", progname);
+  fprintf(stderr, "  %s snaffle [-kx|-ki] [-ox] [-n] name key\n", progname);
+  fprintf(stderr, "  %s version\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* create a binary object from a hexadecimal string */
+char *hextoobj(const char *str, int *sp){
+  char *buf, mbuf[3];
+  int len, i, j;
+  len = strlen(str);
+  if(!(buf = malloc(len + 1))) return NULL;
+  j = 0;
+  for(i = 0; i < len; i += 2){
+    while(strchr(" \n\r\t\f\v", str[i])){
+      i++;
+    }
+    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;
+}
+
+
+/* create a integer object from a decimal string */
+char *dectoiobj(const char *str, int *sp){
+  char *buf;
+  int num;
+  num = atoi(str);
+  if(!(buf = malloc(sizeof(int)))) return NULL;
+  *(int *)buf = num;
+  *sp = sizeof(int);
+  return buf;
+}
+
+
+/* parse arguments of create command */
+int runcreate(int argc, char **argv){
+  char *name;
+  int i, sb, bnum, dnum, rv;
+  name = NULL;
+  sb = FALSE;
+  bnum = -1;
+  dnum = -1;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-s")){
+        sb = TRUE;
+      } else if(!strcmp(argv[i], "-bnum")){
+        if(++i >= argc) usage();
+        bnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-dnum")){
+        if(++i >= argc) usage();
+        dnum = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = docreate(name, bnum, dnum, sb);
+  return rv;
+}
+
+
+/* parse arguments of put command */
+int runput(int argc, char **argv){
+  char *name, *key, *val, *kbuf, *vbuf;
+  int i, kx, ki, vx, vi, vf, lob, ksiz, vsiz, align, rv;
+  int dmode;
+  name = NULL;
+  kx = FALSE;
+  ki = FALSE;
+  vx = FALSE;
+  vi = FALSE;
+  vf = FALSE;
+  lob = FALSE;
+  align = ALIGNSIZ;
+  key = NULL;
+  val = NULL;
+  dmode = CR_DOVER;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else if(!strcmp(argv[i], "-vx")){
+        vx = TRUE;
+      } else if(!strcmp(argv[i], "-vi")){
+        vi = TRUE;
+      } else if(!strcmp(argv[i], "-vf")){
+        vf = TRUE;
+      } else if(!strcmp(argv[i], "-keep")){
+        dmode = CR_DKEEP;
+      } else if(!strcmp(argv[i], "-cat")){
+        dmode = CR_DCAT;
+      } else if(!strcmp(argv[i], "-lob")){
+        lob = TRUE;
+      } else if(!strcmp(argv[i], "-na")){
+        align = 0;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else if(!val){
+      val = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key || !val) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(vx){
+    vbuf = hextoobj(val, &vsiz);
+  } else if(vi){
+    vbuf = dectoiobj(val, &vsiz);
+  } else if(vf){
+    vbuf = cbreadfile(val, &vsiz);
+  } else {
+    vbuf = cbmemdup(val, -1);
+    vsiz = -1;
+  }
+  if(kbuf && vbuf){
+    rv = doput(name, kbuf, ksiz, vbuf, vsiz, dmode, lob, align);
+  } else {
+    if(vf){
+      fprintf(stderr, "%s: %s: cannot read\n", progname, val);
+    } else {
+      fprintf(stderr, "%s: out of memory\n", progname);
+    }
+    rv = 1;
+  }
+  free(kbuf);
+  free(vbuf);
+  return rv;
+}
+
+
+/* parse arguments of out command */
+int runout(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, kx, ki, lob, ksiz, rv;
+  name = NULL;
+  kx = FALSE;
+  ki = FALSE;
+  lob = FALSE;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else if(!strcmp(argv[i], "-lob")){
+        lob = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(kbuf){
+    rv = doout(name, kbuf, ksiz, lob);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of get command */
+int runget(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, opts, kx, ki, ox, lob, nb, start, max, ksiz, rv;
+  name = NULL;
+  opts = 0;
+  kx = FALSE;
+  ki = FALSE;
+  ox = FALSE;
+  lob = FALSE;
+  nb = FALSE;
+  start = 0;
+  max = -1;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        opts |= CR_ONOLCK;
+      } else if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else if(!strcmp(argv[i], "-lob")){
+        lob = TRUE;
+      } else if(!strcmp(argv[i], "-n")){
+        nb = TRUE;
+      } else if(!strcmp(argv[i], "-start")){
+        if(++i >= argc) usage();
+        start = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-max")){
+        if(++i >= argc) usage();
+        max = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key || start < 0) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(kbuf){
+    rv = doget(name, opts, kbuf, ksiz, start, max, ox, lob, nb);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+int runlist(int argc, char **argv){
+  char *name;
+  int i, opts, kb, vb, ox, rv;
+  name = NULL;
+  opts = 0;
+  kb = FALSE;
+  vb = FALSE;
+  ox = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        opts |= CR_ONOLCK;
+      } else if(!strcmp(argv[i], "-k")){
+        kb = TRUE;
+      } else if(!strcmp(argv[i], "-v")){
+        vb = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dolist(name, opts, kb, vb, ox);
+  return rv;
+}
+
+
+/* parse arguments of optimize command */
+int runoptimize(int argc, char **argv){
+  char *name;
+  int i, bnum, align, rv;
+  name = NULL;
+  bnum = -1;
+  align = ALIGNSIZ;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-bnum")){
+        if(++i >= argc) usage();
+        bnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-na")){
+        align = 0;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dooptimize(name, bnum, align);
+  return rv;
+}
+
+
+/* parse arguments of inform command */
+int runinform(int argc, char **argv){
+  char *name;
+  int i, opts, rv;
+  name = NULL;
+  opts = 0;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        opts |= CR_ONOLCK;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doinform(name, opts);
+  return rv;
+}
+
+
+/* parse arguments of remove command */
+int runremove(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doremove(name);
+  return rv;
+}
+
+
+/* parse arguments of repair command */
+int runrepair(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dorepair(name);
+  return rv;
+}
+
+
+/* parse arguments of exportdb command */
+int runexportdb(int argc, char **argv){
+  char *name, *dir;
+  int i, rv;
+  name = NULL;
+  dir = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else if(!dir){
+      dir = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !dir) usage();
+  rv = doexportdb(name, dir);
+  return rv;
+}
+
+
+/* parse arguments of importdb command */
+int runimportdb(int argc, char **argv){
+  char *name, *dir;
+  int i, bnum, dnum, rv;
+  name = NULL;
+  dir = NULL;
+  bnum = -1;
+  dnum = -1;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-bnum")){
+        if(++i >= argc) usage();
+        bnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-dnum")){
+        if(++i >= argc) usage();
+        dnum = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!dir){
+      dir = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !dir) usage();
+  rv = doimportdb(name, dir, bnum, dnum);
+  return rv;
+}
+
+
+/* parse arguments of snaffle command */
+int runsnaffle(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, kx, ki, ox, nb, start, max, ksiz, rv;
+  name = NULL;
+  kx = FALSE;
+  ki = FALSE;
+  ox = FALSE;
+  nb = FALSE;
+  start = 0;
+  max = -1;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else if(!strcmp(argv[i], "-n")){
+        nb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key || start < 0) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(kbuf){
+    rv = dosnaffle(name, kbuf, ksiz, ox, nb);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* print an object */
+void printobj(const char *obj, int size){
+  int i;
+  for(i = 0; i < size; i++){
+    putchar(obj[i]);
+  }
+}
+
+
+/* print an object as a hexadecimal string */
+void printobjhex(const char *obj, int size){
+  int i;
+  for(i = 0; i < size; i++){
+    printf("%s%02X", i > 0 ? " " : "", ((const unsigned char *)obj)[i]);
+  }
+}
+
+
+/* perform create command */
+int docreate(const char *name, int bnum, int dnum, int sparse){
+  CURIA *curia;
+  int omode;
+  omode = CR_OWRITER | CR_OCREAT | CR_OTRUNC | (sparse ? CR_OSPARSE : 0);
+  if(!(curia = cropen(name, omode, bnum, dnum))){
+    pdperror(name);
+    return 1;
+  }
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform put command */
+int doput(const char *name, const char *kbuf, int ksiz,
+          const char *vbuf, int vsiz, int dmode, int lob, int align){
+  CURIA *curia;
+  if(!(curia = cropen(name, CR_OWRITER, -1, -1))){
+    pdperror(name);
+    return 1;
+  }
+  if(align > 0 && !crsetalign(curia, ALIGNSIZ)){
+    pdperror(name);
+    crclose(curia);
+    return 1;
+  }
+  if(lob){
+    if(!crputlob(curia, kbuf, ksiz, vbuf, vsiz, dmode)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  } else {
+    if(!crput(curia, kbuf, ksiz, vbuf, vsiz, dmode)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform out command */
+int doout(const char *name, const char *kbuf, int ksiz, int lob){
+  CURIA *curia;
+  if(!(curia = cropen(name, CR_OWRITER, -1, -1))){
+    pdperror(name);
+    return 1;
+  }
+  if(lob){
+    if(!croutlob(curia, kbuf, ksiz)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  } else {
+    if(!crout(curia, kbuf, ksiz)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform get command */
+int doget(const char *name, int opts, const char *kbuf, int ksiz,
+          int start, int max, int ox, int lob, int nb){
+  CURIA *curia;
+  char *vbuf;
+  int vsiz;
+  if(!(curia = cropen(name, CR_OREADER | opts, -1, -1))){
+    pdperror(name);
+    return 1;
+  }
+  if(lob){
+    if(!(vbuf = crgetlob(curia, kbuf, ksiz, start, max, &vsiz))){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  } else {
+    if(!(vbuf = crget(curia, kbuf, ksiz, start, max, &vsiz))){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  if(ox){
+    printobjhex(vbuf, vsiz);
+  } else {
+    printobj(vbuf, vsiz);
+  }
+  free(vbuf);
+  if(!nb) putchar('\n');
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform list command */
+int dolist(const char *name, int opts, int kb, int vb, int ox){
+  CURIA *curia;
+  char *kbuf, *vbuf;
+  int ksiz, vsiz;
+  if(!(curia = cropen(name, CR_OREADER | opts, -1, -1))){
+    pdperror(name);
+    return 1;
+  }
+  criterinit(curia);
+  while((kbuf = criternext(curia, &ksiz)) != NULL){
+    if(!(vbuf = crget(curia, kbuf, ksiz, 0, -1, &vsiz))){
+      pdperror(name);
+      free(kbuf);
+      break;
+    }
+    if(ox){
+      if(!vb) printobjhex(kbuf, ksiz);
+      if(!kb && !vb) putchar('\t');
+      if(!kb) printobjhex(vbuf, vsiz);
+    } else {
+      if(!vb) printobj(kbuf, ksiz);
+      if(!kb && !vb) putchar('\t');
+      if(!kb) printobj(vbuf, vsiz);
+    }
+    putchar('\n');
+    free(vbuf);
+    free(kbuf);
+  }
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform optimize command */
+int dooptimize(const char *name, int bnum, int align){
+  CURIA *curia;
+  if(!(curia = cropen(name, CR_OWRITER, -1, -1))){
+    pdperror(name);
+    return 1;
+  }
+  if(align > 0 && !crsetalign(curia, ALIGNSIZ)){
+    pdperror(name);
+    crclose(curia);
+    return 1;
+  }
+  if(!croptimize(curia, bnum)){
+    pdperror(name);
+    crclose(curia);
+    return 1;
+  }
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform inform command */
+int doinform(const char *name, int opts){
+  CURIA *curia;
+  if(!(curia = cropen(name, CR_OREADER | opts, -1, -1))){
+    pdperror(name);
+    return 1;
+  }
+  printf("name: %s\n", crname(curia));
+  printf("file size: %.0f\n", crfsizd(curia));
+  printf("all buckets: %d\n", crbnum(curia));
+  printf("used buckets: %d\n", crbusenum(curia));
+  printf("records: %d\n", crrnum(curia));
+  printf("inode number: %d\n", crinode(curia));
+  printf("modified time: %.0f\n", (double)crmtime(curia));
+  printf("LOB records: %d\n", crrnumlob(curia));
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform remove command */
+int doremove(const char *name){
+  if(!crremove(name)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform repair command */
+int dorepair(const char *name){
+  if(!crrepair(name)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform exportdb command */
+int doexportdb(const char *name, const char *dir){
+  CURIA *curia;
+  if(!(curia = cropen(name, CR_OREADER, -1, -1))){
+    pdperror(name);
+    return 1;
+  }
+  if(!crexportdb(curia, dir)){
+    pdperror(name);
+    crclose(curia);
+    return 1;
+  }
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform importdb command */
+int doimportdb(const char *name, const char *dir, int bnum, int dnum){
+  CURIA *curia;
+  if(!(curia = cropen(name, CR_OWRITER | CR_OCREAT | CR_OTRUNC, bnum, dnum))){
+    pdperror(name);
+    return 1;
+  }
+  if(!crimportdb(curia, dir)){
+    pdperror(name);
+    crclose(curia);
+    return 1;
+  }
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform snaffle command */
+int dosnaffle(const char *name, const char *kbuf, int ksiz, int ox, int nb){
+  char *vbuf;
+  int vsiz;
+  if(!(vbuf = crsnaffle(name, kbuf, ksiz, &vsiz))){
+    pdperror(name);
+    return 1;
+  }
+  if(ox){
+    printobjhex(vbuf, vsiz);
+  } else {
+    printobj(vbuf, vsiz);
+  }
+  free(vbuf);
+  if(!nb) putchar('\n');
+  return 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/crmgr.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/crtest.c
===================================================================
--- box/trunk/qdbm/crtest.c	                        (rev 0)
+++ box/trunk/qdbm/crtest.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,873 @@
+/*************************************************************************************************
+ * Test cases of Curia
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <curia.h>
+#include <cabin.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <time.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define RECBUFSIZ      32                /* buffer for records */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+int runwrite(int argc, char **argv);
+int runread(int argc, char **argv);
+int runrcat(int argc, char **argv);
+int runcombo(int argc, char **argv);
+int runwicked(int argc, char **argv);
+int printfflush(const char *format, ...);
+void pdperror(const char *name);
+int myrand(void);
+int dowrite(const char *name, int rnum, int bnum, int dnum, int sparse, int lob);
+int doread(const char *name, int wb, int lob);
+int dorcat(const char *name, int rnum, int bnum, int dnum, int pnum,
+           int align, int fbpsiz, int cb);
+int docombo(const char *name);
+int dowicked(const char *name, int rnum, int cb);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  char *env;
+  int rv;
+  cbstdiobin();
+  if((env = getenv("QDBMDBGFD")) != NULL) dpdbgfd = atoi(env);
+  progname = argv[0];
+  if(argc < 2) usage();
+  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], "rcat")){
+    rv = runrcat(argc, argv);
+  } else if(!strcmp(argv[1], "combo")){
+    rv = runcombo(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: test cases for Curia\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write [-s] [-lob] name rnum bnum dnum\n", progname);
+  fprintf(stderr, "  %s read [-wb] [-lob] name\n", progname);
+  fprintf(stderr, "  %s rcat [-c] name rnum bnum dnum pnum align fbpsiz\n", progname);
+  fprintf(stderr, "  %s combo name\n", progname);
+  fprintf(stderr, "  %s wicked [-c] name rnum\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of write command */
+int runwrite(int argc, char **argv){
+  char *name, *rstr, *bstr, *dstr;
+  int i, rnum, bnum, dnum, sb, lob, rv;
+  name = NULL;
+  rstr = NULL;
+  bstr = NULL;
+  dstr = NULL;
+  rnum = 0;
+  bnum = 0;
+  dnum = 0;
+  sb = FALSE;
+  lob = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-s")){
+        sb = TRUE;
+      } else if(!strcmp(argv[i], "-lob")){
+        lob = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!dstr){
+      dstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr || !bstr || !dstr) usage();
+  rnum = atoi(rstr);
+  bnum = atoi(bstr);
+  dnum = atoi(dstr);
+  if(rnum < 1 || bnum < 1 || dnum < 1) usage();
+  rv = dowrite(name, rnum, bnum, dnum, sb, lob);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+int runread(int argc, char **argv){
+  char *name;
+  int i, wb, lob, rv;
+  name = NULL;
+  wb = FALSE;
+  lob = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-wb")){
+        wb = TRUE;
+      } else if(!strcmp(argv[i], "-lob")){
+        lob = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doread(name, wb, lob);
+  return rv;
+}
+
+
+/* parse arguments of rcat command */
+int runrcat(int argc, char **argv){
+  char *name, *rstr, *bstr, *dstr, *pstr, *astr, *fstr;
+  int i, rnum, bnum, dnum, pnum, align, fbpsiz, cb, rv;
+  name = NULL;
+  rstr = NULL;
+  bstr = NULL;
+  dstr = NULL;
+  pstr = NULL;
+  astr = NULL;
+  fstr = NULL;
+  rnum = 0;
+  bnum = 0;
+  pnum = 0;
+  align = 0;
+  fbpsiz = 0;
+  cb = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-c")){
+        cb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!dstr){
+      dstr = argv[i];
+    } else if(!pstr){
+      pstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr || !bstr || !dstr || !pstr || !astr || !fstr) usage();
+  rnum = atoi(rstr);
+  bnum = atoi(bstr);
+  dnum = atoi(dstr);
+  pnum = atoi(pstr);
+  align = atoi(astr);
+  fbpsiz = atoi(fstr);
+  if(rnum < 1 || bnum < 1 || dnum < 1 || pnum < 1 || fbpsiz < 0) usage();
+  rv = dorcat(name, rnum, bnum, dnum, pnum, align, fbpsiz, cb);
+  return rv;
+}
+
+
+/* parse arguments of combo command */
+int runcombo(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = docombo(name);
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+int runwicked(int argc, char **argv){
+  char *name, *rstr;
+  int i, rnum, cb, rv;
+  name = NULL;
+  rstr = NULL;
+  rnum = 0;
+  cb = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-c")){
+        cb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = dowicked(name, rnum, cb);
+  return rv;
+}
+
+
+/* print formatted string and flush the buffer */
+int printfflush(const char *format, ...){
+  va_list ap;
+  int rv;
+  va_start(ap, format);
+  rv = vprintf(format, ap);
+  if(fflush(stdout) == EOF) rv = -1;
+  va_end(ap);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* pseudo random number generator */
+int myrand(void){
+  static int cnt = 0;
+  if(cnt == 0) srand(time(NULL));
+  return (rand() * rand() + (rand() >> (sizeof(int) * 4)) + (cnt++)) & INT_MAX;
+}
+
+
+/* perform write command */
+int dowrite(const char *name, int rnum, int bnum, int dnum, int sparse, int lob){
+  CURIA *curia;
+  int i, omode, err, len;
+  char buf[RECBUFSIZ];
+  printfflush("<Writing Test>\n  name=%s  rnum=%d  bnum=%d  dnum=%d  s=%d  lob=%d\n\n",
+              name, rnum, bnum, dnum, sparse, lob);
+  /* open a database */
+  omode = CR_OWRITER | CR_OCREAT | CR_OTRUNC | (sparse ? CR_OSPARSE : 0);
+  if(!(curia = cropen(name, omode, bnum, dnum))){
+    pdperror(name);
+    return 1;
+  }
+  err = FALSE;
+  /* loop for each record */
+  for(i = 1; i <= rnum; i++){
+    /* store a record */
+    len = sprintf(buf, "%08d", i);
+    if(lob){
+      if(!crputlob(curia, buf, len, buf, len, CR_DOVER)){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+    } else {
+      if(!crput(curia, buf, len, buf, len, CR_DOVER)){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+    }
+    /* print progression */
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform read command */
+int doread(const char *name, int wb, int lob){
+  CURIA *curia;
+  int i, rnum, err, len;
+  char buf[RECBUFSIZ], vbuf[RECBUFSIZ], *val;
+  printfflush("<Reading Test>\n  name=%s  wb=%d  lob=%d\n\n", name, wb, lob);
+  /* open a database */
+  if(!(curia = cropen(name, CR_OREADER, -1, -1))){
+    pdperror(name);
+    return 1;
+  }
+  /* get the number of records */
+  if(lob){
+    rnum = crrnumlob(curia);
+  } else {
+    rnum = crrnum(curia);
+  }
+  err = FALSE;
+  /* loop for each record */
+  for(i = 1; i <= rnum; i++){
+    /* retrieve a record */
+    len = sprintf(buf, "%08d", i);
+    if(lob){
+      if(!(val = crgetlob(curia, buf, len, 0, -1, NULL))){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+      free(val);
+    } else if(wb){
+      if(crgetwb(curia, buf, len, 0, RECBUFSIZ, vbuf) == -1){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+    } else {
+      if(!(val = crget(curia, buf, len, 0, -1, NULL))){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+      free(val);
+    }
+    /* print progression */
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform rcat command */
+int dorcat(const char *name, int rnum, int bnum, int dnum, int pnum,
+           int align, int fbpsiz, int cb){
+  CURIA *curia;
+  CBMAP *map;
+  int i, err, len, ksiz, vsiz, rsiz;
+  const char *kbuf, *vbuf;
+  char buf[RECBUFSIZ], *rbuf;
+  printfflush("<Random Writing Test>\n  name=%s  rnum=%d  bnum=%d  dnum=%d  pnum=%d"
+              "  align=%d  fbpsiz=%d  c=%d\n\n", name, rnum, bnum, dnum, pnum, align, fbpsiz, cb);
+  if(!(curia = cropen(name, CR_OWRITER | CR_OCREAT | CR_OTRUNC, bnum, dnum))){
+    pdperror(name);
+    return 1;
+  }
+  if(!crsetalign(curia, align) || !crsetfbpsiz(curia, fbpsiz)){
+    pdperror(name);
+    crclose(curia);
+    return 1;
+  }
+  map = NULL;
+  if(cb) map = cbmapopen();
+  err = FALSE;
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", myrand() % pnum + 1);
+    if(!crput(curia, buf, len, buf, len, CR_DCAT)){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d: fsiz=%d rnum=%d)\n", i, crfsiz(curia), crrnum(curia));
+      }
+    }
+  }
+  if(map){
+    printfflush("Matching records ... ");
+    cbmapiterinit(map);
+    while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
+      vbuf = cbmapget(map, kbuf, ksiz, &vsiz);
+      if(!(rbuf = crget(curia, kbuf, ksiz, 0, -1, &rsiz))){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+      if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+        fprintf(stderr, "%s: %s: unmatched record\n", progname, name);
+        free(rbuf);
+        err = TRUE;
+        break;
+      }
+      free(rbuf);
+    }
+    cbmapclose(map);
+    if(!err) printfflush("ok\n");
+  }
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform combo command */
+int docombo(const char *name){
+  CURIA *curia;
+  char buf[RECBUFSIZ], wbuf[RECBUFSIZ], *vbuf;
+  int i, len, wlen, vsiz;
+  printfflush("<Combination Test>\n  name=%s\n\n", name);
+  printfflush("Creating a database with bnum 3 ... ");
+  if(!(curia = cropen(name, CR_OWRITER | CR_OCREAT | CR_OTRUNC, 3, 3))){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Setting alignment as 16 ... ");
+  if(!crsetalign(curia, 16)){
+    pdperror(name);
+    crclose(curia);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 20 records ... ");
+  for(i = 1; i <= 20; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!crput(curia, buf, len, buf, len, CR_DOVER)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking records ... ");
+  for(i = 1; i <= 20; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(vbuf = crget(curia, buf, len, 0, -1, &vsiz))){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != crvsiz(curia, buf, len)){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Overwriting top 10 records without moving rooms ... ");
+  for(i = 1; i <= 10; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!crput(curia, buf, len, buf, len, CR_DOVER)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Overwriting top 5 records with moving rooms ... ");
+  for(i = 1; i <= 5; i++){
+    len = sprintf(buf, "%08d", i);
+    wlen = sprintf(wbuf, "%024d", i);
+    if(!crput(curia, buf, len, wbuf, wlen, CR_DOVER)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Overwriting top 15 records in concatenation with moving rooms ... ");
+  for(i = 1; i <= 15; i++){
+    len = sprintf(buf, "%08d", i);
+    wlen = sprintf(wbuf, "========================");
+    if(!crput(curia, buf, len, wbuf, wlen, CR_DCAT)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking records ... ");
+  for(i = 1; i <= 20; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(vbuf = crget(curia, buf, len, 0, -1, &vsiz))){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != crvsiz(curia, buf, len)){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Deleting top 10 records ... ");
+  for(i = 1; i <= 10; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!crout(curia, buf, len)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking deleted records ... ");
+  for(i = 1; i <= 10; i++){
+    len = sprintf(buf, "%08d", i);
+    vbuf = crget(curia, buf, len, 0, -1, &vsiz);
+    free(vbuf);
+    if(vbuf || dpecode != DP_ENOITEM){
+      fprintf(stderr, "%s: %s: deleting failed\n", progname, name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Overwriting top 15 records in concatenation with moving rooms ... ");
+  for(i = 1; i <= 15; i++){
+    len = sprintf(buf, "%08d", i);
+    wlen = sprintf(wbuf, "========================");
+    if(!crput(curia, buf, len, wbuf, wlen, CR_DCAT)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking records ... ");
+  for(i = 1; i <= 20; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(vbuf = crget(curia, buf, len, 0, -1, &vsiz))){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != crvsiz(curia, buf, len)){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Optimizing the database ... ");
+  if(!croptimize(curia, -1)){
+    pdperror(name);
+    crclose(curia);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Checking records ... ");
+  for(i = 1; i <= 20; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(vbuf = crget(curia, buf, len, 0, -1, &vsiz))){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != crvsiz(curia, buf, len)){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Closing database ... ");
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Creating a database with bnum 1000000 ... ");
+  if(!(curia = cropen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, 1000000, -1))){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 1000 records ... ");
+  for(i = 1; i <= 1000; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!crput(curia, buf, len, buf, len, DP_DOVER)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Adding 64 records ... ");
+  for(i = 1; i <= 64; i++){
+    len = sprintf(buf, "%o", i);
+    if(!crput(curia, buf, len, buf, len, DP_DOVER)){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Syncing the database ... ");
+  if(!crsync(curia)){
+    pdperror(name);
+    crclose(curia);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Retrieving records directly ... ");
+  for(i = 1; i <= 64; i++){
+    len = sprintf(buf, "%o", i);
+    if(!(vbuf = crsnaffle(name, buf, len, &vsiz))){
+      pdperror(name);
+      crclose(curia);
+      return 1;
+    }
+    if(strcmp(vbuf, buf)){
+      fprintf(stderr, "%s: %s: invalid content\n", progname, name);
+      free(vbuf);
+      crclose(curia);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != crvsiz(curia, buf, len)){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      crclose(curia);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Optimizing the database ... ");
+  if(!croptimize(curia, -1)){
+    pdperror(name);
+    crclose(curia);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Closing the database ... ");
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("all ok\n\n");
+  return 0;
+}
+
+
+/* perform wicked command */
+int dowicked(const char *name, int rnum, int cb){
+  CURIA *curia;
+  CBMAP *map;
+  int i, len, err, align, mksiz, mvsiz, rsiz;
+  const char *mkbuf, *mvbuf;
+  char buf[RECBUFSIZ], vbuf[RECBUFSIZ], *val;
+  printfflush("<Wicked Writing Test>\n  name=%s  rnum=%d\n\n", name, rnum);
+  err = FALSE;
+  if(!(curia = cropen(name, CR_OWRITER | CR_OCREAT | CR_OTRUNC, rnum / 10, 5))){
+    pdperror(name);
+    return 1;
+  }
+  if(!crsetalign(curia, 16) || !crsetfbpsiz(curia, 256)){
+    pdperror(name);
+    err = TRUE;
+  }
+  map = NULL;
+  if(cb) map = cbmapopen();
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", myrand() % rnum + 1);
+    switch(myrand() % 16){
+    case 0:
+      putchar('O');
+      if(!crput(curia, buf, len, buf, len, CR_DOVER)) err = TRUE;
+      if(map) cbmapput(map, buf, len, buf, len, TRUE);
+      break;
+    case 1:
+      putchar('K');
+      if(!crput(curia, buf, len, buf, len, CR_DKEEP) && dpecode != DP_EKEEP) err = TRUE;
+      if(map) cbmapput(map, buf, len, buf, len, FALSE);
+      break;
+    case 2:
+      putchar('D');
+      if(!crout(curia, buf, len) && dpecode != DP_ENOITEM) err = TRUE;
+      if(map) cbmapout(map, buf, len);
+      break;
+    case 3:
+      putchar('G');
+      if(crgetwb(curia, buf, len, 2, RECBUFSIZ, vbuf) == -1 && dpecode != DP_ENOITEM) err = TRUE;
+      break;
+    case 4:
+      putchar('V');
+      if(crvsiz(curia, buf, len) == -1 && dpecode != DP_ENOITEM) err = TRUE;
+      break;
+    default:
+      putchar('C');
+      if(!crput(curia, buf, len, buf, len, CR_DCAT)) err = TRUE;
+      if(map) cbmapputcat(map, buf, len, buf, len);
+      break;
+    }
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+    if(!err && rnum > 100 && myrand() % (rnum / 100) == 0){
+      if(myrand() % 10 == 0){
+        align = (myrand() % 4 + 1) * -1;
+      } else {
+        align = myrand() % 32;
+      }
+      if(!crsetalign(curia, align)) err = TRUE;
+    }
+    if(err){
+      pdperror(name);
+      break;
+    }
+  }
+  if(!croptimize(curia, -1)){
+    pdperror(name);
+    err = TRUE;
+  }
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!crput(curia, buf, len, ":", -1, CR_DCAT)){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    if(map) cbmapputcat(map, buf, len, ":", -1);
+    putchar(':');
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  if(!croptimize(curia, -1)){
+    pdperror(name);
+    err = TRUE;
+  }
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(val = crget(curia, buf, len, 0, -1, NULL))){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    free(val);
+    putchar('=');
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  if(!criterinit(curia)){
+    pdperror(name);
+    err = TRUE;
+  }
+  for(i = 1; i <= rnum; i++){
+    if(!(val = criternext(curia, NULL))){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    free(val);
+    putchar('@');
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  if(map){
+    printfflush("Matching records ... ");
+    cbmapiterinit(map);
+    while((mkbuf = cbmapiternext(map, &mksiz)) != NULL){
+      mvbuf = cbmapget(map, mkbuf, mksiz, &mvsiz);
+      if(!(val = crget(curia, mkbuf, mksiz, 0, -1, &rsiz))){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+      if(rsiz != mvsiz || memcmp(val, mvbuf, rsiz)){
+        fprintf(stderr, "%s: %s: unmatched record\n", progname, name);
+        free(val);
+        err = TRUE;
+        break;
+      }
+      free(val);
+    }
+    cbmapclose(map);
+    if(!err) printfflush("ok\n");
+  }
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/crtest.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/crtsv.c
===================================================================
--- box/trunk/qdbm/crtsv.c	                        (rev 0)
+++ box/trunk/qdbm/crtsv.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,266 @@
+/*************************************************************************************************
+ * Mutual converter between a database of Curia and a TSV text
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <curia.h>
+#include <cabin.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+int runimport(int argc, char **argv);
+int runexport(int argc, char **argv);
+void pdperror(const char *name);
+char *getl(void);
+int doimport(const char *name, int bnum, int dnum, int bin);
+int doexport(const char *name, int bin);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "import")){
+    rv = runimport(argc, argv);
+  } else if(!strcmp(argv[1], "export")){
+    rv = runexport(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: mutual converter between TSV and Curia database\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s import [-bnum num] [-dnum num] [-bin] name\n", progname);
+  fprintf(stderr, "  %s export [-bin] name\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of import command */
+int runimport(int argc, char **argv){
+  char *name;
+  int i, bnum, dnum, bin, rv;
+  name = NULL;
+  bnum = -1;
+  dnum = -1;
+  bin = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-bnum")){
+        if(++i >= argc) usage();
+        bnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-dnum")){
+        if(++i >= argc) usage();
+        dnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-bin")){
+        bin = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doimport(name, bnum, dnum, bin);
+  return rv;
+}
+
+
+/* parse arguments of export command */
+int runexport(int argc, char **argv){
+  char *name;
+  int i, bin, rv;
+  name = NULL;
+  bin = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-bin")){
+        bin = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doexport(name, bin);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* read a line */
+char *getl(void){
+  char *buf;
+  int c, len, blen;
+  buf = NULL;
+  len = 0;
+  blen = 256;
+  while((c = getchar()) != EOF){
+    if(blen <= len) blen *= 2;
+    buf = cbrealloc(buf, blen + 1);
+    if(c == '\n') c = '\0';
+    buf[len++] = c;
+    if(c == '\0') break;
+  }
+  if(!buf) return NULL;
+  buf[len] = '\0';
+  return buf;
+}
+
+
+/* perform import command */
+int doimport(const char *name, int bnum, int dnum, int bin){
+  CURIA *curia;
+  char *buf, *kbuf, *vbuf, *ktmp, *vtmp;
+  int i, err, ktsiz, vtsiz;
+  /* open a database */
+  if(!(curia = cropen(name, CR_OWRITER | CR_OCREAT, bnum, dnum))){
+    pdperror(name);
+    return 1;
+  }
+  /* loop for each line */
+  err = FALSE;
+  for(i = 1; (buf = getl()) != NULL; i++){
+    kbuf = buf;
+    if((vbuf = strchr(buf, '\t')) != NULL){
+      *vbuf = '\0';
+      vbuf++;
+      /* store a record */
+      if(bin){
+        ktmp = cbbasedecode(kbuf, &ktsiz);
+        vtmp = cbbasedecode(vbuf, &vtsiz);
+        if(!crput(curia, ktmp, ktsiz, vtmp, vtsiz, CR_DOVER)){
+          pdperror(name);
+          err = TRUE;
+        }
+        free(vtmp);
+        free(ktmp);
+      } else {
+        if(!crput(curia, kbuf, -1, vbuf, -1, CR_DOVER)){
+          pdperror(name);
+          err = TRUE;
+        }
+      }
+    } else {
+      fprintf(stderr, "%s: %s: invalid format in line %d\n", progname, name, i);
+    }
+    free(buf);
+    if(err) break;
+  }
+  /* close the database */
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return err ? 1 : 0;
+}
+
+
+/* perform export command */
+int doexport(const char *name, int bin){
+  CURIA *curia;
+  char *kbuf, *vbuf, *tmp;
+  int err, ksiz, vsiz;
+  /* open a database */
+  if(!(curia = cropen(name, CR_OREADER, -1, -1))){
+    pdperror(name);
+    return 1;
+  }
+  /* initialize the iterator */
+  criterinit(curia);
+  /* loop for each key */
+  err = FALSE;
+  while((kbuf = criternext(curia, &ksiz)) != NULL){
+    /* retrieve a value with a key */
+    if(!(vbuf = crget(curia, kbuf, ksiz, 0, -1, &vsiz))){
+      pdperror(name);
+      free(kbuf);
+      err = TRUE;
+      break;
+    }
+    /* output data */
+    if(bin){
+      tmp = cbbaseencode(kbuf, ksiz);
+      printf("%s\t", tmp);
+      free(tmp);
+      tmp = cbbaseencode(vbuf, vsiz);
+      printf("%s\n", tmp);
+      free(tmp);
+    } else {
+      printf("%s\t%s\n", kbuf, vbuf);
+    }
+    /* free resources */
+    free(vbuf);
+    free(kbuf);
+  }
+  /* check whether all records were retrieved */
+  if(dpecode != DP_ENOITEM){
+    pdperror(name);
+    err = TRUE;
+  }
+  /* close the database */
+  if(!crclose(curia)){
+    pdperror(name);
+    return 1;
+  }
+  return err ? 1 : 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/crtsv.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/curia.c
===================================================================
--- box/trunk/qdbm/curia.c	                        (rev 0)
+++ box/trunk/qdbm/curia.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,1192 @@
+/*************************************************************************************************
+ * Implementation of Curia
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#define QDBM_INTERNAL  1
+
+#include "curia.h"
+#include "myconf.h"
+
+#define CR_NAMEMAX     512               /* max size of a database name */
+#define CR_DPMAX       512               /* max number of division of a database */
+#define CR_DIRMODE     00755             /* permission of a creating directory */
+#define CR_FILEMODE    00644             /* permission of a creating file */
+#define CR_PATHBUFSIZ  1024              /* size of a path buffer */
+#define CR_DEFDNUM     5                 /* default number of division of a database */
+#define CR_ATTRBNUM    16                /* bucket number of attrubute database */
+#define CR_DPNAME      "depot"           /* name of each sub database */
+#define CR_KEYDNUM     "dnum"            /* key of division number */
+#define CR_KEYLRNUM    "lrnum"           /* key of the number of large objects */
+#define CR_TMPFSUF     MYEXTSTR "crtmp"  /* suffix of a temporary directory */
+#define CR_LOBDIR      "lob"             /* name of the directory of large objects */
+#define CR_LOBDDEPTH   2                 /* depth of the directories of large objects */
+#define CR_NUMBUFSIZ   32                /* size of a buffer for a number */
+#define CR_IOBUFSIZ    8192              /* size of an I/O buffer */
+
+
+/* private function prototypes */
+static char *crstrdup(const char *str);
+static int crdpgetnum(DEPOT *depot, const char *kbuf, int ksiz);
+static char *crgetlobpath(CURIA *curia, const char *kbuf, int ksiz);
+static int crmklobdir(const char *path);
+static int crrmlobdir(const char *path);
+static int crcplobdir(CURIA *curia, const char *path);
+static int crwrite(int fd, const void *buf, int size);
+static int crread(int fd, void *buf, int size);
+
+
+
+/*************************************************************************************************
+ * public objects
+ *************************************************************************************************/
+
+
+/* Get a database handle. */
+CURIA *cropen(const char *name, int omode, int bnum, int dnum){
+  DEPOT *attr, **depots;
+  char path[CR_PATHBUFSIZ], *tname;
+  int i, j, dpomode, inode, lrnum;
+  struct stat sbuf;
+  CURIA *curia;
+  assert(name);
+  if(dnum < 1) dnum = CR_DEFDNUM;
+  if(dnum > CR_DPMAX) dnum = CR_DPMAX;
+  if(strlen(name) > CR_NAMEMAX){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return NULL;
+  }
+  dpomode = DP_OREADER;
+  if(omode & CR_OWRITER){
+    dpomode = DP_OWRITER;
+    if(omode & CR_OCREAT) dpomode |= DP_OCREAT;
+    if(omode & CR_OTRUNC) dpomode |= DP_OTRUNC;
+    if(omode & CR_OSPARSE) dpomode |= DP_OSPARSE;
+  }
+  if(omode & CR_ONOLCK) dpomode |= DP_ONOLCK;
+  if(omode & CR_OLCKNB) dpomode |= DP_OLCKNB;
+  attr = NULL;
+  lrnum = 0;
+  if((omode & CR_OWRITER) && (omode & CR_OCREAT)){
+    if(mkdir(name, CR_DIRMODE) == -1 && errno != EEXIST){
+      dpecodeset(DP_EMKDIR, __FILE__, __LINE__);
+      return NULL;
+    }
+    sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
+    if(!(attr = dpopen(path, dpomode, CR_ATTRBNUM))) return NULL;
+    if(dprnum(attr) > 0){
+      if((dnum = crdpgetnum(attr, CR_KEYDNUM, -1)) < 1 ||
+         (lrnum = crdpgetnum(attr, CR_KEYLRNUM, -1)) < 0){
+        dpclose(attr);
+        dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+        return NULL;
+      }
+    } else {
+      if(!dpput(attr, CR_KEYDNUM, -1, (char *)&dnum, sizeof(int), DP_DOVER) ||
+         !dpput(attr, CR_KEYLRNUM, -1, (char *)&lrnum, sizeof(int), DP_DOVER)){
+        dpclose(attr);
+        return NULL;
+      }
+      for(i = 0; i < dnum; i++){
+        sprintf(path, "%s%c%04d", name, MYPATHCHR, i + 1);
+        if(mkdir(path, CR_DIRMODE) == -1 && errno != EEXIST){
+          dpclose(attr);
+          dpecodeset(DP_EMKDIR, __FILE__, __LINE__);
+          return NULL;
+        }
+      }
+    }
+  }
+  if(!attr){
+    sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
+    if(!(attr = dpopen(path, dpomode, 1))) return NULL;
+    if(!(omode & CR_OTRUNC)){
+      if((dnum = crdpgetnum(attr, CR_KEYDNUM, -1)) < 1 ||
+         (lrnum = crdpgetnum(attr, CR_KEYLRNUM, -1)) < 0){
+        dpclose(attr);
+        dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+        return NULL;
+      }
+    }
+  }
+  if(omode & CR_OTRUNC){
+    for(i = 0; i < CR_DPMAX; i++){
+      sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_DPNAME);
+      if(unlink(path) == -1 && errno != ENOENT){
+        dpclose(attr);
+        dpecodeset(DP_EUNLINK, __FILE__, __LINE__);
+        return NULL;
+      }
+      sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_LOBDIR);
+      if(!crrmlobdir(path)){
+        dpclose(attr);
+        return NULL;
+      }
+      if(i >= dnum){
+        sprintf(path, "%s%c%04d", name, MYPATHCHR, i + 1);
+        if(rmdir(path) == -1 && errno != ENOENT){
+          dpclose(attr);
+          dpecodeset(DP_ERMDIR, __FILE__, __LINE__);
+          return NULL;
+        }
+      }
+    }
+    errno = 0;
+  }
+  if(lstat(name, &sbuf) == -1){
+    dpclose(attr);
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return NULL;
+  }
+  inode = sbuf.st_ino;
+  if(!(depots = malloc(dnum * sizeof(DEPOT *)))){
+    dpclose(attr);
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  for(i = 0; i < dnum; i++){
+    sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_DPNAME);
+    if(!(depots[i] = dpopen(path, dpomode, bnum))){
+      for(j = 0; j < i; j++){
+        dpclose(depots[j]);
+      }
+      free(depots);
+      dpclose(attr);
+      return NULL;
+    }
+  }
+  curia = malloc(sizeof(CURIA));
+  tname = crstrdup(name);
+  if(!curia || !tname){
+    free(curia);
+    free(tname);
+    for(i = 0; i < dnum; i++){
+      dpclose(depots[i]);
+    }
+    free(depots);
+    dpclose(attr);
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  curia->name = tname;
+  curia->wmode = (omode & CR_OWRITER);
+  curia->inode = inode;
+  curia->attr = attr;
+  curia->depots = depots;
+  curia->dnum = dnum;
+  curia->inum = 0;
+  curia->lrnum = lrnum;
+  return curia;
+}
+
+
+/* Close a database handle. */
+int crclose(CURIA *curia){
+  int i, err;
+  assert(curia);
+  err = FALSE;
+  for(i = 0; i < curia->dnum; i++){
+    if(!dpclose(curia->depots[i])) err = TRUE;
+  }
+  free(curia->depots);
+  if(curia->wmode){
+    if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER))
+      err = TRUE;
+  }
+  if(!dpclose(curia->attr)) err = TRUE;
+  free(curia->name);
+  free(curia);
+  return err ? FALSE : TRUE;
+}
+
+
+/* Store a record. */
+int crput(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){
+  int dpdmode;
+  int tnum;
+  assert(curia && kbuf && vbuf);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  switch(dmode){
+  case CR_DKEEP: dpdmode = DP_DKEEP; break;
+  case CR_DCAT: dpdmode = DP_DCAT; break;
+  default: dpdmode = DP_DOVER; break;
+  }
+  tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
+  return dpput(curia->depots[tnum], kbuf, ksiz, vbuf, vsiz, dpdmode);
+}
+
+
+/* Delete a record. */
+int crout(CURIA *curia, const char *kbuf, int ksiz){
+  int tnum;
+  assert(curia && kbuf);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
+  return dpout(curia->depots[tnum], kbuf, ksiz);
+}
+
+
+/* Retrieve a record. */
+char *crget(CURIA *curia, const char *kbuf, int ksiz, int start, int max, int *sp){
+  int tnum;
+  assert(curia && kbuf && start >= 0);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
+  return dpget(curia->depots[tnum], kbuf, ksiz, start, max, sp);
+}
+
+
+/* Retrieve a record and write the value into a buffer. */
+int crgetwb(CURIA *curia, const char *kbuf, int ksiz, int start, int max, char *vbuf){
+  int tnum;
+  assert(curia && kbuf && start >= 0 && max >= 0 && vbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
+  return dpgetwb(curia->depots[tnum], kbuf, ksiz, start, max, vbuf);
+}
+
+
+/* Get the size of the value of a record. */
+int crvsiz(CURIA *curia, const char *kbuf, int ksiz){
+  int tnum;
+  assert(curia && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
+  return dpvsiz(curia->depots[tnum], kbuf, ksiz);
+}
+
+
+/* Initialize the iterator of a database handle. */
+int criterinit(CURIA *curia){
+  int i, err;
+  assert(curia);
+  err = FALSE;
+  for(i = 0; i < curia->dnum; i++){
+    if(!dpiterinit(curia->depots[i])){
+      err = TRUE;
+      break;
+    }
+  }
+  curia->inum = 0;
+  return err ? FALSE : TRUE;
+}
+
+
+/* Get the next key of the iterator. */
+char *criternext(CURIA *curia, int *sp){
+  char *kbuf;
+  assert(curia);
+  kbuf = NULL;
+  while(curia->inum < curia->dnum && !(kbuf = dpiternext(curia->depots[curia->inum], sp))){
+    if(dpecode != DP_ENOITEM) return NULL;
+    (curia->inum)++;
+  }
+  return kbuf;
+}
+
+
+/* Set alignment of a database handle. */
+int crsetalign(CURIA *curia, int align){
+  int i, err;
+  assert(curia);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  for(i = 0; i < curia->dnum; i++){
+    if(!dpsetalign(curia->depots[i], align)){
+      err = TRUE;
+      break;
+    }
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Set the size of the free block pool of a database handle. */
+int crsetfbpsiz(CURIA *curia, int size){
+  int i, err;
+  assert(curia && size >= 0);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  for(i = 0; i < curia->dnum; i++){
+    if(!dpsetfbpsiz(curia->depots[i], size)){
+      err = TRUE;
+      break;
+    }
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Synchronize contents of updating a database with the files and the devices. */
+int crsync(CURIA *curia){
+  int i, err;
+  assert(curia);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER) ||
+     !dpsync(curia->attr)) err = TRUE;
+  for(i = 0; i < curia->dnum; i++){
+    if(!dpsync(curia->depots[i])){
+      err = TRUE;
+      break;
+    }
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Optimize a database. */
+int croptimize(CURIA *curia, int bnum){
+  int i, err;
+  assert(curia);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  for(i = 0; i < curia->dnum; i++){
+    if(!dpoptimize(curia->depots[i], bnum)){
+      err = TRUE;
+      break;
+    }
+  }
+  curia->inum = 0;
+  return err ? FALSE : TRUE;
+}
+
+
+/* Get the name of a database. */
+char *crname(CURIA *curia){
+  char *name;
+  assert(curia);
+  if(!(name = crstrdup(curia->name))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  return name;
+}
+
+
+/* Get the total size of database files. */
+int crfsiz(CURIA *curia){
+  int i, sum, rv;
+  assert(curia);
+  if((sum = dpfsiz(curia->attr)) == -1) return -1;
+  for(i = 0; i < curia->dnum; i++){
+    if((rv = dpfsiz(curia->depots[i])) == -1) return -1;
+    sum += rv;
+  }
+  return sum;
+}
+
+
+/* Get the total size of database files as double-precision value. */
+double crfsizd(CURIA *curia){
+  double sum;
+  int i, rv;
+  assert(curia);
+  sum = 0.0;
+  if((sum = dpfsiz(curia->attr)) < 0) return -1.0;
+  for(i = 0; i < curia->dnum; i++){
+    if((rv = dpfsiz(curia->depots[i])) == -1) return -1.0;
+    sum += rv;
+  }
+  return sum;
+}
+
+
+/* Get the total number of the elements of each bucket array. */
+int crbnum(CURIA *curia){
+  int i, sum, rv;
+  assert(curia);
+  sum = 0;
+  for(i = 0; i < curia->dnum; i++){
+    rv = dpbnum(curia->depots[i]);
+    if(rv == -1) return -1;
+    sum += rv;
+  }
+  return sum;
+}
+
+
+/* Get the total number of the used elements of each bucket array. */
+int crbusenum(CURIA *curia){
+  int i, sum, rv;
+  assert(curia);
+  sum = 0;
+  for(i = 0; i < curia->dnum; i++){
+    rv = dpbusenum(curia->depots[i]);
+    if(rv == -1) return -1;
+    sum += rv;
+  }
+  return sum;
+}
+
+
+/* Get the number of the records stored in a database. */
+int crrnum(CURIA *curia){
+  int i, sum, rv;
+  assert(curia);
+  sum = 0;
+  for(i = 0; i < curia->dnum; i++){
+    rv = dprnum(curia->depots[i]);
+    if(rv == -1) return -1;
+    sum += rv;
+  }
+  return sum;
+}
+
+
+/* Check whether a database handle is a writer or not. */
+int crwritable(CURIA *curia){
+  assert(curia);
+  return curia->wmode;
+}
+
+
+/* Check whether a database has a fatal error or not. */
+int crfatalerror(CURIA *curia){
+  int i;
+  assert(curia);
+  if(dpfatalerror(curia->attr)) return TRUE;
+  for(i = 0; i < curia->dnum; i++){
+    if(dpfatalerror(curia->depots[i])) return TRUE;
+  }
+  return FALSE;
+}
+
+
+/* Get the inode number of a database directory. */
+int crinode(CURIA *curia){
+  assert(curia);
+  return curia->inode;
+}
+
+
+/* Get the last modified time of a database. */
+time_t crmtime(CURIA *curia){
+  assert(curia);
+  return dpmtime(curia->attr);
+}
+
+
+/* Remove a database directory. */
+int crremove(const char *name){
+  struct stat sbuf;
+  CURIA *curia;
+  char path[CR_PATHBUFSIZ];
+  assert(name);
+  if(lstat(name, &sbuf) == -1){
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if((curia = cropen(name, CR_OWRITER | CR_OTRUNC, 1, 1)) != NULL) crclose(curia);
+  sprintf(path, "%s%c0001%c%s", name, MYPATHCHR, MYPATHCHR, CR_DPNAME);
+  dpremove(path);
+  sprintf(path, "%s%c0001", name, MYPATHCHR);
+  if(rmdir(path) == -1){
+    dpecodeset(DP_ERMDIR, __FILE__, __LINE__);
+    return FALSE;
+  }
+  sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
+  if(!dpremove(path)) return FALSE;
+  if(rmdir(name) == -1){
+    dpecodeset(DP_ERMDIR, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Repair a broken database directory. */
+int crrepair(const char *name){
+  CURIA *tcuria;
+  DEPOT *tdepot;
+  char path[CR_PATHBUFSIZ], *kbuf, *vbuf;
+  struct stat sbuf;
+  int i, j, err, flags, bnum, dnum, ksiz, vsiz;
+  assert(name);
+  err = FALSE;
+  flags = 0;
+  bnum = 0;
+  dnum = 0;
+  sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
+  if(lstat(path, &sbuf) != -1){
+    if((tdepot = dpopen(path, DP_OREADER, -1)) != NULL){
+      flags = dpgetflags(tdepot);
+      dpclose(tdepot);
+    }
+  }
+  for(i = 1; i <= CR_DPMAX; i++){
+    sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i, MYPATHCHR, CR_DPNAME);
+    if(lstat(path, &sbuf) != -1){
+      dnum++;
+      if(!dprepair(path)) err = TRUE;
+      if((tdepot = dpopen(path, DP_OREADER, -1)) != NULL){
+        bnum += dpbnum(tdepot);
+        dpclose(tdepot);
+      }
+    }
+  }
+  if(dnum < CR_DEFDNUM) dnum = CR_DEFDNUM;
+  bnum /= dnum;
+  sprintf(path, "%s%s", name, CR_TMPFSUF);
+  if((tcuria = cropen(path, CR_OWRITER | CR_OCREAT | CR_OTRUNC, bnum, dnum)) != NULL){
+    if(!crsetflags(tcuria, flags)) err = TRUE;
+    for(i = 1; i <= CR_DPMAX; i++){
+      sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i, MYPATHCHR, CR_DPNAME);
+      if(lstat(path, &sbuf) != -1){
+        if((tdepot = dpopen(path, DP_OREADER, -1)) != NULL){
+          if(!dpiterinit(tdepot)) err = TRUE;
+          while((kbuf = dpiternext(tdepot, &ksiz)) != NULL){
+            if((vbuf = dpget(tdepot, kbuf, ksiz, 0, -1, &vsiz)) != NULL){
+              if(!crput(tcuria, kbuf, ksiz, vbuf, vsiz, CR_DKEEP)) err = TRUE;
+              free(vbuf);
+            }
+            free(kbuf);
+          }
+          dpclose(tdepot);
+        } else {
+          err = TRUE;
+        }
+      }
+      for(j = 0; j <= CR_DPMAX; j++){
+        sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, j, MYPATHCHR, CR_LOBDIR);
+        if(lstat(path, &sbuf) != -1){
+          if(!crcplobdir(tcuria, path)) err = TRUE;
+        }
+      }
+    }
+    if(!crclose(tcuria)) err = TRUE;
+    if(!crremove(name)) err = TRUE;
+    sprintf(path, "%s%s", name, CR_TMPFSUF);
+    if(rename(path, name) == -1){
+      if(!err) dpecodeset(DP_EMISC, __FILE__, __LINE__);
+      err = TRUE;
+    }
+  } else {
+    err = TRUE;
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Dump all records as endian independent data. */
+int crexportdb(CURIA *curia, const char *name){
+  char path[CR_PATHBUFSIZ], *kbuf, *vbuf, *pbuf;
+  int i, err, *fds, ksiz, vsiz, psiz;
+  assert(curia && name);
+  if(!(criterinit(curia))) return FALSE;
+  if(mkdir(name, CR_DIRMODE) == -1 && errno != EEXIST){
+    dpecodeset(DP_EMKDIR, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  fds = malloc(sizeof(int) * curia->dnum);
+  for(i = 0; i < curia->dnum; i++){
+    sprintf(path, "%s%c%04d", name, MYPATHCHR, i + 1);
+    if((fds[i] = open(path, O_RDWR | O_CREAT | O_TRUNC, CR_FILEMODE)) == -1){
+      if(!err) dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+      err = TRUE;
+      break;
+    }
+  }
+  while(!err && (kbuf = criternext(curia, &ksiz)) != NULL){
+    if((vbuf = crget(curia, kbuf, ksiz, 0, -1, &vsiz)) != NULL){
+      if((pbuf = malloc(ksiz + vsiz + CR_NUMBUFSIZ * 2)) != NULL){
+        psiz = 0;
+        psiz += sprintf(pbuf + psiz, "%X\n%X\n", ksiz, vsiz);
+        memcpy(pbuf + psiz, kbuf, ksiz);
+        psiz += ksiz;
+        pbuf[psiz++] = '\n';
+        memcpy(pbuf + psiz, vbuf, vsiz);
+        psiz += vsiz;
+        pbuf[psiz++] = '\n';
+        if(!crwrite(fds[curia->inum], pbuf, psiz)){
+          dpecodeset(DP_EWRITE, __FILE__, __LINE__);
+          err = TRUE;
+        }
+        free(pbuf);
+      } else {
+        dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+        err = TRUE;
+      }
+      free(vbuf);
+    } else {
+      err = TRUE;
+    }
+    free(kbuf);
+  }
+  for(i = 0; i < curia->dnum; i++){
+    if(fds[i] != -1 && close(fds[i]) == -1){
+      if(!err) dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
+      err = TRUE;
+    }
+  }
+  free(fds);
+  return !err && !crfatalerror(curia);
+}
+
+
+/* Load all records from endian independent data. */
+int crimportdb(CURIA *curia, const char *name){
+  DEPOT *depot;
+  char ipath[CR_PATHBUFSIZ], opath[CR_PATHBUFSIZ], *kbuf, *vbuf;
+  int i, err, ksiz, vsiz;
+  struct stat sbuf;
+  assert(curia && name);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(crrnum(curia) > 0){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  for(i = 0; !err && i < CR_DPMAX; i++){
+    sprintf(ipath, "%s%c%04d", name, MYPATHCHR, i + 1);
+    if(lstat(ipath, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)) break;
+    sprintf(opath, "%s%c%04d%s", curia->name, MYPATHCHR, i + 1, CR_TMPFSUF);
+    if((depot = dpopen(opath, DP_OWRITER | DP_OCREAT | DP_OTRUNC, -1)) != NULL){
+      if(dpimportdb(depot, ipath)){
+        dpiterinit(depot);
+        while((kbuf = dpiternext(depot, &ksiz)) != NULL){
+          if((vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz)) != NULL){
+            if(!crput(curia, kbuf, ksiz, vbuf, vsiz, CR_DKEEP)) err = TRUE;
+            free(vbuf);
+          } else {
+            err = TRUE;
+          }
+          free(kbuf);
+        }
+      } else {
+        err = TRUE;
+      }
+      if(!dpclose(depot)) err = TRUE;
+      if(!dpremove(opath)) err = TRUE;
+    } else {
+      err = TRUE;
+    }
+  }
+  return !err && !crfatalerror(curia);
+}
+
+
+/* Retrieve a record directly from a database directory. */
+char *crsnaffle(const char *name, const char *kbuf, int ksiz, int *sp){
+  char path[CR_PATHBUFSIZ], *vbuf;
+  int dnum, vsiz, tnum;
+  assert(name && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
+  if(!(vbuf = dpsnaffle(path, CR_KEYDNUM, -1, &vsiz)) || vsiz != sizeof(int) ||
+     (dnum = *(int *)vbuf) < 1){
+    free(vbuf);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  free(vbuf);
+  tnum = dpouterhash(kbuf, ksiz) % dnum;
+  sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, tnum + 1, MYPATHCHR, CR_DPNAME);
+  return dpsnaffle(path, kbuf, ksiz, sp);
+}
+
+
+/* Store a large object. */
+int crputlob(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){
+  char *path;
+  int mode, fd, err, be;
+  struct stat sbuf;
+  assert(curia && kbuf && vbuf);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(vsiz < 0) vsiz = strlen(vbuf);
+  if(!(path = crgetlobpath(curia, kbuf, ksiz))) return FALSE;
+  if(!crmklobdir(path)){
+    free(path);
+    return FALSE;
+  }
+  be = lstat(path, &sbuf) != -1 && S_ISREG(sbuf.st_mode);
+  mode = O_RDWR | O_CREAT;
+  if(dmode & CR_DKEEP) mode |= O_EXCL;
+  if(dmode & CR_DCAT){
+    mode |= O_APPEND;
+  } else {
+    mode |= O_TRUNC;
+  }
+  if((fd = open(path, mode, CR_FILEMODE)) == -1){
+    free(path);
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    if(dmode == CR_DKEEP) dpecodeset(DP_EKEEP, __FILE__, __LINE__);
+    return FALSE;
+  }
+  free(path);
+  err = FALSE;
+  if(crwrite(fd, vbuf, vsiz) == -1){
+    err = TRUE;
+    dpecodeset(DP_EWRITE, __FILE__, __LINE__);
+  }
+  if(close(fd) == -1){
+    err = TRUE;
+    dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
+  }
+  if(!err && !be) (curia->lrnum)++;
+  return err ? FALSE : TRUE;
+}
+
+
+/* Delete a large object. */
+int croutlob(CURIA *curia, const char *kbuf, int ksiz){
+  char *path;
+  int err, be;
+  struct stat sbuf;
+  assert(curia && kbuf);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(!(path = crgetlobpath(curia, kbuf, ksiz))) return FALSE;
+  be = lstat(path, &sbuf) != -1 && S_ISREG(sbuf.st_mode);
+  err = FALSE;
+  if(unlink(path) == -1){
+    err = TRUE;
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+  }
+  free(path);
+  if(!err && be) (curia->lrnum)--;
+  return err ? FALSE : TRUE;
+}
+
+
+/* Retrieve a large object. */
+char *crgetlob(CURIA *curia, const char *kbuf, int ksiz, int start, int max, int *sp){
+  char *path, *buf;
+  struct stat sbuf;
+  int fd, size;
+  assert(curia && kbuf && start >= 0);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(!(path = crgetlobpath(curia, kbuf, ksiz))) return NULL;
+  if((fd = open(path, O_RDONLY, CR_FILEMODE)) == -1){
+    free(path);
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  free(path);
+  if(fstat(fd, &sbuf) == -1){
+    close(fd);
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(start > sbuf.st_size){
+    close(fd);
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(lseek(fd, start, SEEK_SET) == -1){
+    close(fd);
+    dpecodeset(DP_ESEEK, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(max < 0) max = sbuf.st_size;
+  if(!(buf = malloc(max + 1))){
+    close(fd);
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  size = crread(fd, buf, max);
+  close(fd);
+  if(size == -1){
+    free(buf);
+    dpecodeset(DP_EREAD, __FILE__, __LINE__);
+    return NULL;
+  }
+  buf[size] = '\0';
+  if(sp) *sp = size;
+  return buf;
+}
+
+
+/* Get the file descriptor of a large object. */
+int crgetlobfd(CURIA *curia, const char *kbuf, int ksiz){
+  char *path;
+  int fd;
+  assert(curia && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(!(path = crgetlobpath(curia, kbuf, ksiz))) return -1;
+  if((fd = open(path, curia->wmode ? O_RDWR: O_RDONLY, CR_FILEMODE)) == -1){
+    free(path);
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return -1;
+  }
+  free(path);
+  return fd;
+}
+
+
+/* Get the size of the value of a large object. */
+int crvsizlob(CURIA *curia, const char *kbuf, int ksiz){
+  char *path;
+  struct stat sbuf;
+  assert(curia && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(!(path = crgetlobpath(curia, kbuf, ksiz))) return -1;
+  if(lstat(path, &sbuf) == -1){
+    free(path);
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return -1;
+  }
+  free(path);
+  return sbuf.st_size;
+}
+
+
+/* Get the number of the large objects stored in a database. */
+int crrnumlob(CURIA *curia){
+  assert(curia);
+  return curia->lrnum;
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Synchronize updating contents on memory. */
+int crmemsync(CURIA *curia){
+  int i, err;
+  assert(curia);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER) ||
+     !dpmemsync(curia->attr)) err = TRUE;
+  for(i = 0; i < curia->dnum; i++){
+    if(!dpmemsync(curia->depots[i])){
+      err = TRUE;
+      break;
+    }
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Synchronize updating contents on memory, not physically. */
+int crmemflush(CURIA *curia){
+  int i, err;
+  assert(curia);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER) ||
+     !dpmemsync(curia->attr)) err = TRUE;
+  for(i = 0; i < curia->dnum; i++){
+    if(!dpmemflush(curia->depots[i])){
+      err = TRUE;
+      break;
+    }
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Get flags of a database. */
+int crgetflags(CURIA *curia){
+  assert(curia);
+  return dpgetflags(curia->attr);
+}
+
+
+/* Set flags of a database. */
+int crsetflags(CURIA *curia, int flags){
+  assert(curia);
+  if(!curia->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return dpsetflags(curia->attr, flags);
+}
+
+
+
+/*************************************************************************************************
+ * private objects
+ *************************************************************************************************/
+
+
+/* Get a copied string.
+   `str' specifies an original string.
+   The return value is a copied string whose region is allocated by `malloc'. */
+static char *crstrdup(const char *str){
+  int len;
+  char *buf;
+  assert(str);
+  len = strlen(str);
+  if(!(buf = malloc(len + 1))) return NULL;
+  memcpy(buf, str, len + 1);
+  return buf;
+}
+
+
+/* Get an integer from a database.
+   `depot' specifies an inner database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the key.
+   The return value is the integer of the corresponding record. */
+static int crdpgetnum(DEPOT *depot, const char *kbuf, int ksiz){
+  char *vbuf;
+  int vsiz, rv;
+  if(!(vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz)) || vsiz != sizeof(int)){
+    free(vbuf);
+    return INT_MIN;
+  }
+  rv = *(int *)vbuf;
+  free(vbuf);
+  return rv;
+}
+
+
+/* Get the path of a large object.
+   `curia' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the key.
+   The return value is a path string whose region is allocated by `malloc'. */
+static char *crgetlobpath(CURIA *curia, const char *kbuf, int ksiz){
+  char prefix[CR_PATHBUFSIZ], *wp, *path;
+  int i, hash;
+  assert(curia && kbuf && ksiz >= 0);
+  wp = prefix;
+  wp += sprintf(wp, "%s%c%04d%c%s%c",
+                curia->name, MYPATHCHR, dpouterhash(kbuf, ksiz) % curia->dnum + 1,
+                MYPATHCHR, CR_LOBDIR, MYPATHCHR);
+  hash = dpinnerhash(kbuf, ksiz);
+  for(i = 0; i < CR_LOBDDEPTH; i++){
+    wp += sprintf(wp, "%02X%c", hash % 0x100, MYPATHCHR);
+    hash /= 0x100;
+  }
+  if(!(path = malloc(strlen(prefix) + ksiz * 2 + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  wp = path;
+  wp += sprintf(path, "%s", prefix);
+  for(i = 0; i < ksiz; i++){
+    wp += sprintf(wp, "%02X", ((unsigned char *)kbuf)[i]);
+  }
+  return path;
+}
+
+
+/* Create directories included in a path.
+   `path' specifies a path.
+   The return value is true if successful, else, it is false. */
+static int crmklobdir(const char *path){
+  char elem[CR_PATHBUFSIZ], *wp;
+  const char *dp;
+  int err, len;
+  wp = elem;
+  err = FALSE;
+  while(*path != '\0' && (dp = strchr(path, MYPATHCHR)) != NULL){
+    len = dp - path;
+    if((wp != elem || dp == path)) wp += sprintf(wp, "%c", MYPATHCHR);
+    memcpy(wp, path, len);
+    wp[len] = '\0';
+    wp += len;
+    if(mkdir(elem, CR_DIRMODE) == -1 && errno != EEXIST) err = TRUE;
+    path = dp + 1;
+  }
+  if(err) dpecodeset(DP_EMKDIR, __FILE__, __LINE__);
+  return err ? FALSE : TRUE;
+}
+
+
+/* Remove file and directories under a directory.
+   `path' specifies a path.
+   The return value is true if successful, else, it is false. */
+static int crrmlobdir(const char *path){
+  char elem[CR_PATHBUFSIZ];
+  DIR *DD;
+  struct dirent *dp;
+  assert(path);
+  if(unlink(path) != -1){
+    return TRUE;
+  } else {
+    if(errno == ENOENT) return TRUE;
+    if(!(DD = opendir(path))){
+      dpecodeset(DP_EMISC, __FILE__, __LINE__);
+      return FALSE;
+    }
+    while((dp = readdir(DD)) != NULL){
+      if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue;
+      sprintf(elem, "%s%c%s", path, MYPATHCHR, dp->d_name);
+      if(!crrmlobdir(elem)){
+        closedir(DD);
+        return FALSE;
+      }
+    }
+  }
+  if(closedir(DD) == -1){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(rmdir(path) == -1){
+    dpecodeset(DP_ERMDIR, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Copy file and directories under a directory for repairing.
+   `path' specifies a path.
+   The return value is true if successful, else, it is false. */
+static int crcplobdir(CURIA *curia, const char *path){
+  char elem[CR_PATHBUFSIZ], numbuf[3], *rp, *kbuf, *vbuf;
+  DIR *DD;
+  struct dirent *dp;
+  struct stat sbuf;
+  int i, ksiz, vsiz, fd;
+  assert(curia && path);
+  if(lstat(path, &sbuf) == -1){
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(S_ISREG(sbuf.st_mode)){
+    rp = strrchr(path, MYPATHCHR) + 1;
+    for(i = 0; rp[i] != '\0'; i += 2){
+      numbuf[0] = rp[i];
+      numbuf[1] = rp[i+1];
+      numbuf[2] = '\0';
+      elem[i/2] = (int)strtol(numbuf, NULL, 16);
+    }
+    kbuf = elem;
+    ksiz = i / 2;
+    vsiz = sbuf.st_size;
+    if(!(vbuf = malloc(vsiz + 1))){
+      dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if((fd = open(path, O_RDONLY, CR_FILEMODE)) == -1){
+      free(vbuf);
+      dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(crread(fd, vbuf, vsiz) == -1){
+      close(fd);
+      free(vbuf);
+      dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(!crputlob(curia, kbuf, ksiz, vbuf, vsiz, DP_DOVER)){
+      close(fd);
+      free(vbuf);
+      return FALSE;
+    }
+    close(fd);
+    free(vbuf);
+    return TRUE;
+  }
+  if(!(DD = opendir(path))){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  while((dp = readdir(DD)) != NULL){
+    if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue;
+    sprintf(elem, "%s%c%s", path, MYPATHCHR, dp->d_name);
+    if(!crcplobdir(curia, elem)){
+      closedir(DD);
+      return FALSE;
+    }
+  }
+  if(closedir(DD) == -1){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Write into a file.
+   `fd' specifies a file descriptor.
+   `buf' specifies a buffer to write.
+   `size' specifies the size of the buffer.
+   The return value is the size of the written buffer, or, -1 on failure. */
+static int crwrite(int fd, const void *buf, int size){
+  char *lbuf;
+  int rv, wb;
+  assert(fd >= 0 && buf && size >= 0);
+  lbuf = (char *)buf;
+  rv = 0;
+  do {
+    wb = write(fd, lbuf, size);
+    switch(wb){
+    case -1: if(errno != EINTR) return -1;
+    case 0: break;
+    default:
+      lbuf += wb;
+      size -= wb;
+      rv += wb;
+      break;
+    }
+  } while(size > 0);
+  return rv;
+}
+
+
+/* Read from a file and store the data into a buffer.
+   `fd' specifies a file descriptor.
+   `buffer' specifies a buffer to store into.
+   `size' specifies the size to read with.
+   The return value is the size read with, or, -1 on failure. */
+static int crread(int fd, void *buf, int size){
+  char *lbuf;
+  int i, bs;
+  assert(fd >= 0 && buf && size >= 0);
+  lbuf = buf;
+  for(i = 0; i < size && (bs = read(fd, lbuf + i, size - i)) != 0; i += bs){
+    if(bs == -1 && errno != EINTR) return -1;
+  }
+  return i;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/curia.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/curia.h
===================================================================
--- box/trunk/qdbm/curia.h	                        (rev 0)
+++ box/trunk/qdbm/curia.h	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,474 @@
+/*************************************************************************************************
+ * The extended API of QDBM
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _CURIA_H                         /* duplication check */
+#define _CURIA_H
+
+#if defined(__cplusplus)                 /* export for C++ */
+extern "C" {
+#endif
+
+
+#include <depot.h>
+#include <stdlib.h>
+#include <time.h>
+
+
+#if defined(_MSC_VER) && !defined(QDBM_INTERNAL) && !defined(QDBM_STATIC)
+#define MYEXTERN extern __declspec(dllimport)
+#else
+#define MYEXTERN extern
+#endif
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of structure for the database handle */
+  char *name;                            /* name of the database directory */
+  int wmode;                             /* whether to be writable */
+  int inode;                             /* inode of the database directory */
+  DEPOT *attr;                           /* database handle for attributes */
+  DEPOT **depots;                        /* handles of the record database */
+  int dnum;                              /* number of record database handles */
+  int inum;                              /* number of the database of the using iterator */
+  int lrnum;                             /* number of large objects */
+} CURIA;
+
+enum {                                   /* enumeration for open modes */
+  CR_OREADER = 1 << 0,                   /* open as a reader */
+  CR_OWRITER = 1 << 1,                   /* open as a writer */
+  CR_OCREAT = 1 << 2,                    /* a writer creating */
+  CR_OTRUNC = 1 << 3,                    /* a writer truncating */
+  CR_ONOLCK = 1 << 4,                    /* open without locking */
+  CR_OLCKNB = 1 << 5,                    /* lock without blocking */
+  CR_OSPARSE = 1 << 6                    /* create as sparse files */
+};
+
+enum {                                   /* enumeration for write modes */
+  CR_DOVER,                              /* overwrite an existing value */
+  CR_DKEEP,                              /* keep an existing value */
+  CR_DCAT                                /* concatenate values */
+};
+
+
+/* Get a database handle.
+   `name' specifies the name of a database directory.
+   `omode' specifies the connection mode: `CR_OWRITER' as a writer, `CR_OREADER' as a reader.
+   If the mode is `CR_OWRITER', the following may be added by bitwise or: `CR_OCREAT', which
+   means it creates a new database if not exist, `CR_OTRUNC', which means it creates a new
+   database regardless if one exists.  Both of `CR_OREADER' and `CR_OWRITER' can be added to by
+   bitwise or: `CR_ONOLCK', which means it opens a database directory without file locking, or
+   `CR_OLCKNB', which means locking is performed without blocking.  `CR_OCREAT' can be added to
+   by bitwise or: `CR_OSPARSE', which means it creates database files as sparse files.
+   `bnum' specifies the number of elements of each bucket array.  If it is not more than 0,
+   the default value is specified.  The size of each bucket array is determined on creating,
+   and can not be changed except for by optimization of the database.  Suggested size of each
+   bucket array is about from 0.5 to 4 times of the number of all records to store.
+   `dnum' specifies the number of division of the database.  If it is not more than 0, the
+   default value is specified.  The number of division can not be changed from the initial value.
+   The max number of division is 512.
+   The return value is the database handle or `NULL' if it is not successful.
+   While connecting as a writer, an exclusive lock is invoked to the database directory.
+   While connecting as a reader, a shared lock is invoked to the database directory.
+   The thread blocks until the lock is achieved.  If `CR_ONOLCK' is used, the application is
+   responsible for exclusion control. */
+CURIA *cropen(const char *name, int omode, int bnum, int dnum);
+
+
+/* Close a database handle.
+   `curia' specifies a database handle.
+   If successful, the return value is true, else, it is false.
+   Because the region of a closed handle is released, it becomes impossible to use the handle.
+   Updating a database is assured to be written when the handle is closed.  If a writer opens
+   a database but does not close it appropriately, the database will be broken. */
+int crclose(CURIA *curia);
+
+
+/* Store a record.
+   `curia' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region of the value.  If it is negative, the size is
+   assigned with `strlen(vbuf)'.
+   `dmode' specifies behavior when the key overlaps, by the following values: `CR_DOVER',
+   which means the specified value overwrites the existing one, `CR_DKEEP', which means the
+   existing value is kept, `CR_DCAT', which means the specified value is concatenated at the
+   end of the existing value.
+   If successful, the return value is true, else, it is false. */
+int crput(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode);
+
+
+/* Delete a record.
+   `curia' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is true, else, it is false.  False is returned when no
+   record corresponds to the specified key. */
+int crout(CURIA *curia, const char *kbuf, int ksiz);
+
+
+/* Retrieve a record.
+   `curia' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  If it is negative, the size to read is unlimited.
+   `sp' specifies the pointer to a variable to which the size of the region of the return value
+   is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the specified key or the size of the value of the corresponding record is less than `start'.
+   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 if it
+   is no longer in use. */
+char *crget(CURIA *curia, const char *kbuf, int ksiz, int start, int max, int *sp);
+
+
+/* Retrieve a record and write the value into a buffer.
+   `curia' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  It shuld be equal to or less than the size of the
+   writing buffer.
+   `vbuf' specifies the pointer to a buffer into which the value of the corresponding record is
+   written.
+   If successful, the return value is the size of the written data, else, it is -1.  -1 is
+   returned when no record corresponds to the specified key or the size of the value of the
+   corresponding record is less than `start'.
+   Note that no additional zero code is appended at the end of the region of the writing buffer. */
+int crgetwb(CURIA *curia, const char *kbuf, int ksiz, int start, int max, char *vbuf);
+
+
+/* Get the size of the value of a record.
+   `curia' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1.
+   Because this function does not read the entity of a record, it is faster than `crget'. */
+int crvsiz(CURIA *curia, const char *kbuf, int ksiz);
+
+
+/* Initialize the iterator of a database handle.
+   `curia' specifies a database handle.
+   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. */
+int criterinit(CURIA *curia);
+
+
+/* Get the next key of the iterator.
+   `curia' specifies a database handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   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 if 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 *criternext(CURIA *curia, int *sp);
+
+
+/* Set alignment of a database handle.
+   `curia' specifies a database handle connected as a writer.
+   `align' specifies the size of alignment.
+   If successful, the return value is true, else, it is false.
+   If alignment is set to a database, the efficiency of overwriting values is improved.
+   The size of alignment is suggested to be average size of the values of the records to be
+   stored.  If alignment is positive, padding whose size is multiple number of the alignment
+   is placed.  If alignment is negative, as `vsiz' is the size of a value, the size of padding
+   is calculated with `(vsiz / pow(2, abs(align) - 1))'.  Because alignment setting is not
+   saved in a database, you should specify alignment every opening a database. */
+int crsetalign(CURIA *curia, int align);
+
+
+/* Set the size of the free block pool of a database handle.
+   `curia' specifies a database handle connected as a writer.
+   `size' specifies the size of the free block pool of a database.
+   If successful, the return value is true, else, it is false.
+   The default size of the free block pool is 16.  If the size is greater, the space efficiency
+   of overwriting values is improved with the time efficiency sacrificed. */
+int crsetfbpsiz(CURIA *curia, int size);
+
+
+/* Synchronize updating contents with the files and the devices.
+   `curia' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.
+   This function is useful when another process uses the connected database directory. */
+int crsync(CURIA *curia);
+
+
+/* Optimize a database.
+   `curia' specifies a database handle connected as a writer.
+   `bnum' specifies the number of the elements of each bucket array.  If it is not more than 0,
+   the default value is specified.
+   If successful, the return value is true, else, it is false.
+   In an alternating succession of deleting and storing with overwrite or concatenate,
+   dispensable regions accumulate.  This function is useful to do away with them. */
+int croptimize(CURIA *curia, int bnum);
+
+/* Get the name of a database.
+   `curia' specifies a database handle.
+   If successful, the return value is the pointer to the region of the name of the database,
+   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 if it is no longer in use. */
+char *crname(CURIA *curia);
+
+
+/* Get the total size of database files.
+   `curia' specifies a database handle.
+   If successful, the return value is the total size of the database files, else, it is -1.
+   If the total size is more than 2GB, the return value overflows. */
+int crfsiz(CURIA *curia);
+
+
+/* Get the total size of database files as double-precision floating-point number.
+   `curia' specifies a database handle.
+   If successful, the return value is the total size of the database files, else, it is -1.0. */
+double crfsizd(CURIA *curia);
+
+
+/* Get the total number of the elements of each bucket array.
+   `curia' specifies a database handle.
+   If successful, the return value is the total number of the elements of each bucket array,
+   else, it is -1. */
+int crbnum(CURIA *curia);
+
+
+/* Get the total number of the used elements of each bucket array.
+   `curia' specifies a database handle.
+   If successful, the return value is the total number of the used elements of each bucket
+   array, else, it is -1.
+   This function is inefficient because it accesses all elements of each bucket array. */
+int crbusenum(CURIA *curia);
+
+
+/* Get the number of the records stored in a database.
+   `curia' specifies a database handle.
+   If successful, the return value is the number of the records stored in the database, else,
+   it is -1. */
+int crrnum(CURIA *curia);
+
+
+/* Check whether a database handle is a writer or not.
+   `curia' specifies a database handle.
+   The return value is true if the handle is a writer, false if not. */
+int crwritable(CURIA *curia);
+
+
+/* Check whether a database has a fatal error or not.
+   `curia' specifies a database handle.
+   The return value is true if the database has a fatal error, false if not. */
+int crfatalerror(CURIA *curia);
+
+
+/* Get the inode number of a database directory.
+   `curia' specifies a database handle.
+   The return value is the inode number of the database directory. */
+int crinode(CURIA *curia);
+
+
+/* Get the last modified time of a database.
+   `curia' specifies a database handle.
+   The return value is the last modified time of the database. */
+time_t crmtime(CURIA *curia);
+
+
+/* Remove a database directory.
+   `name' specifies the name of a database directory.
+   If successful, the return value is true, else, it is false. */
+int crremove(const char *name);
+
+
+/* Repair a broken database directory.
+   `name' specifies the name of a database directory.
+   If successful, the return value is true, else, it is false.
+   There is no guarantee that all records in a repaired database directory correspond to the
+   original or expected state. */
+int crrepair(const char *name);
+
+
+/* Dump all records as endian independent data.
+   `curia' specifies a database handle.
+   `name' specifies the name of an output directory.
+   If successful, the return value is true, else, it is false.
+   Note that large objects are ignored. */
+int crexportdb(CURIA *curia, const char *name);
+
+
+/* Load all records from endian independent data.
+   `curia' specifies a database handle connected as a writer.  The database of the handle must
+   be empty.
+   `name' specifies the name of an input directory.
+   If successful, the return value is true, else, it is false.
+   Note that large objects are ignored. */
+int crimportdb(CURIA *curia, const char *name);
+
+
+/* Retrieve a record directly from a database directory.
+   `name' specifies the name of a database directory.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the specified key.
+   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 if it
+   is no longer in use.  Although this function can be used even while the database directory is
+   locked by another process, it is not assured that recent updated is reflected. */
+char *crsnaffle(const char *name, const char *kbuf, int ksiz, int *sp);
+
+
+/* Store a large object.
+   `curia' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region of the value.  If it is negative, the size is
+   assigned with `strlen(vbuf)'.
+   `dmode' specifies behavior when the key overlaps, by the following values: `CR_DOVER',
+   which means the specified value overwrites the existing one, `CR_DKEEP', which means the
+   existing value is kept, `CR_DCAT', which means the specified value is concatenated at the
+   end of the existing value.
+   If successful, the return value is true, else, it is false. */
+int crputlob(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode);
+
+
+/* Delete a large object.
+   `curia' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is true, else, it is false.  false is returned when no large
+   object corresponds to the specified key. */
+int croutlob(CURIA *curia, const char *kbuf, int ksiz);
+
+
+/* Retrieve a large object.
+   `curia' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  If it is negative, the size to read is unlimited.
+   `sp' specifies the pointer to a variable to which the size of the region of the return value
+   is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding large object, else, it is `NULL'.  `NULL' is returned when no large object
+   corresponds to the specified key or the size of the value of the corresponding large object
+   is less than `start'.
+   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 if it
+   is no longer in use. */
+char *crgetlob(CURIA *curia, const char *kbuf, int ksiz, int start, int max, int *sp);
+
+
+/* Get the file descriptor of a large object.
+   `curia' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is the file descriptor of the corresponding large object,
+   else, it is -1.  -1 is returned when no large object corresponds to the specified key.  The
+   returned file descriptor is opened with the `open' call.  If the database handle was opened
+   as a writer, the descriptor is writable (O_RDWR), else, it is not writable (O_RDONLY).  The
+   descriptor should be closed with the `close' call if it is no longer in use. */
+int crgetlobfd(CURIA *curia, const char *kbuf, int ksiz);
+
+
+/* Get the size of the value of a large object.
+   `curia' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is the size of the value of the corresponding large object,
+   else, it is -1.
+   Because this function does not read the entity of a large object, it is faster than
+   `crgetlob'. */
+int crvsizlob(CURIA *curia, const char *kbuf, int ksiz);
+
+
+/* Get the number of the large objects stored in a database.
+   `curia' specifies a database handle.
+   If successful, the return value is the number of the large objects stored in the database,
+   else, it is -1. */
+int crrnumlob(CURIA *curia);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Synchronize updating contents on memory.
+   `curia' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false. */
+int crmemsync(CURIA *curia);
+
+
+/* Synchronize updating contents on memory, not physically.
+   `curia' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false. */
+int crmemflush(CURIA *curia);
+
+
+/* Get flags of a database.
+   `curia' specifies a database handle.
+   The return value is the flags of a database. */
+int crgetflags(CURIA *curia);
+
+
+/* Set flags of a database.
+   `curia' specifies a database handle connected as a writer.
+   `flags' specifies flags to set.  Least ten bits are reserved for internal use.
+   If successful, the return value is true, else, it is false. */
+int crsetflags(CURIA *curia, int flags);
+
+
+
+#undef MYEXTERN
+
+#if defined(__cplusplus)                 /* export for C++ */
+}
+#endif
+
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/curia.h
___________________________________________________________________
Added: svn:mime-type
   + text/x-chdr
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/depot.c
===================================================================
--- box/trunk/qdbm/depot.c	                        (rev 0)
+++ box/trunk/qdbm/depot.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,2219 @@
+/*************************************************************************************************
+ * Implementation of Depot
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#define QDBM_INTERNAL  1
+
+#include "depot.h"
+#include "myconf.h"
+
+#define DP_FILEMODE    00644             /* permission of a creating file */
+#define DP_MAGICNUMB   "[DEPOT]\n\f"     /* magic number on environments of big endian */
+#define DP_MAGICNUML   "[depot]\n\f"     /* magic number on environments of little endian */
+#define DP_HEADSIZ     48                /* size of the reagion of the header */
+#define DP_LIBVEROFF   12                /* offset of the region for the library version */
+#define DP_FLAGSOFF    16                /* offset of the region for flags */
+#define DP_FSIZOFF     24                /* offset of the region for the file size */
+#define DP_BNUMOFF     32                /* offset of the region for the bucket number */
+#define DP_RNUMOFF     40                /* offset of the region for the record number */
+#define DP_DEFBNUM     8191              /* default bucket number */
+#define DP_FBPOOLSIZ   16                /* size of free block pool */
+#define DP_ENTBUFSIZ   128               /* size of the entity buffer */
+#define DP_STKBUFSIZ   256               /* size of the stack key buffer */
+#define DP_WRTBUFSIZ   8192              /* size of the writing buffer */
+#define DP_FSBLKSIZ    4096              /* size of a block of the file system */
+#define DP_TMPFSUF     MYEXTSTR "dptmp"  /* suffix of a temporary file */
+#define DP_OPTBLOAD    0.25              /* ratio of bucket loading at optimization */
+#define DP_OPTRUNIT    256               /* number of records in a process of optimization */
+#define DP_NUMBUFSIZ   32                /* size of a buffer for a number */
+#define DP_IOBUFSIZ    8192              /* size of an I/O buffer */
+
+/* get the first hash value */
+#define DP_FIRSTHASH(DP_res, DP_kbuf, DP_ksiz) \
+  do { \
+    const unsigned char *_DP_p; \
+    int _DP_ksiz; \
+    _DP_p = (const unsigned char *)(DP_kbuf); \
+    _DP_ksiz = DP_ksiz; \
+    if((_DP_ksiz) == sizeof(int)){ \
+      memcpy(&(DP_res), (DP_kbuf), sizeof(int)); \
+    } else { \
+      (DP_res) = 751; \
+    } \
+    while(_DP_ksiz--){ \
+      (DP_res) = (DP_res) * 31 + *(_DP_p)++; \
+    } \
+    (DP_res) = ((DP_res) * 87767623) & INT_MAX; \
+  } while(FALSE)
+
+/* get the second hash value */
+#define DP_SECONDHASH(DP_res, DP_kbuf, DP_ksiz) \
+  do { \
+    const unsigned char *_DP_p; \
+    int _DP_ksiz; \
+    _DP_p = (const unsigned char *)(DP_kbuf) + DP_ksiz - 1; \
+    _DP_ksiz = DP_ksiz; \
+    for((DP_res) = 19780211; _DP_ksiz--;){ \
+      (DP_res) = (DP_res) * 37 + *(_DP_p)--; \
+    } \
+    (DP_res) = ((DP_res) * 43321879) & INT_MAX; \
+  } while(FALSE)
+
+/* get the third hash value */
+#define DP_THIRDHASH(DP_res, DP_kbuf, DP_ksiz) \
+  do { \
+    int _DP_i; \
+    (DP_res) = 774831917; \
+    for(_DP_i = (DP_ksiz) - 1; _DP_i >= 0; _DP_i--){ \
+      (DP_res) = (DP_res) * 29 + ((const unsigned char *)(DP_kbuf))[_DP_i]; \
+    } \
+    (DP_res) = ((DP_res) * 5157883) & INT_MAX; \
+  } while(FALSE)
+
+enum {                                   /* enumeration for a record header */
+  DP_RHIFLAGS,                           /* offset of flags */
+  DP_RHIHASH,                            /* offset of value of the second hash function */
+  DP_RHIKSIZ,                            /* offset of the size of the key */
+  DP_RHIVSIZ,                            /* offset of the size of the value */
+  DP_RHIPSIZ,                            /* offset of the size of the padding bytes */
+  DP_RHILEFT,                            /* offset of the offset of the left child */
+  DP_RHIRIGHT,                           /* offset of the offset of the right child */
+  DP_RHNUM                               /* number of elements of a header */
+};
+
+enum {                                   /* enumeration for the flag of a record */
+  DP_RECFDEL = 1 << 0,                   /* deleted */
+  DP_RECFREUSE = 1 << 1                  /* reusable */
+};
+
+
+/* private function prototypes */
+static int dpbigendian(void);
+static char *dpstrdup(const char *str);
+static int dplock(int fd, int ex, int nb);
+static int dpwrite(int fd, const void *buf, int size);
+static int dpseekwrite(int fd, int off, const void *buf, int size);
+static int dpseekwritenum(int fd, int off, int num);
+static int dpread(int fd, void *buf, int size);
+static int dpseekread(int fd, int off, void *buf, int size);
+static int dpfcopy(int destfd, int destoff, int srcfd, int srcoff);
+static int dpgetprime(int num);
+static int dppadsize(DEPOT *depot, int ksiz, int vsiz);
+static int dprecsize(int *head);
+static int dprechead(DEPOT *depot, int off, int *head, char *ebuf, int *eep);
+static char *dpreckey(DEPOT *depot, int off, int *head);
+static char *dprecval(DEPOT *depot, int off, int *head, int start, int max);
+static int dprecvalwb(DEPOT *depot, int off, int *head, int start, int max, char *vbuf);
+static int dpkeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz);
+static int dprecsearch(DEPOT *depot, const char *kbuf, int ksiz, int hash, int *bip, int *offp,
+                       int *entp, int *head, char *ebuf, int *eep, int delhit);
+static int dprecrewrite(DEPOT *depot, int off, int rsiz, const char *kbuf, int ksiz,
+                        const char *vbuf, int vsiz, int hash, int left, int right);
+static int dprecappend(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                       int hash, int left, int right);
+static int dprecover(DEPOT *depot, int off, int *head, const char *vbuf, int vsiz, int cat);
+static int dprecdelete(DEPOT *depot, int off, int *head, int reusable);
+static void dpfbpoolcoal(DEPOT *depot);
+static int dpfbpoolcmp(const void *a, const void *b);
+
+
+
+/*************************************************************************************************
+ * public objects
+ *************************************************************************************************/
+
+
+/* String containing the version information. */
+const char *dpversion = _QDBM_VERSION;
+
+
+/* Get a message string corresponding to an error code. */
+const char *dperrmsg(int ecode){
+  switch(ecode){
+  case DP_ENOERR: return "no error";
+  case DP_EFATAL: return "with fatal error";
+  case DP_EMODE: return "invalid mode";
+  case DP_EBROKEN: return "broken database file";
+  case DP_EKEEP: return "existing record";
+  case DP_ENOITEM: return "no item found";
+  case DP_EALLOC: return "memory allocation error";
+  case DP_EMAP: return "memory mapping error";
+  case DP_EOPEN: return "open error";
+  case DP_ECLOSE: return "close error";
+  case DP_ETRUNC: return "trunc error";
+  case DP_ESYNC: return "sync error";
+  case DP_ESTAT: return "stat error";
+  case DP_ESEEK: return "seek error";
+  case DP_EREAD: return "read error";
+  case DP_EWRITE: return "write error";
+  case DP_ELOCK: return "lock error";
+  case DP_EUNLINK: return "unlink error";
+  case DP_EMKDIR: return "mkdir error";
+  case DP_ERMDIR: return "rmdir error";
+  case DP_EMISC: return "miscellaneous error";
+  }
+  return "(invalid ecode)";
+}
+
+
+/* Get a database handle. */
+DEPOT *dpopen(const char *name, int omode, int bnum){
+  char hbuf[DP_HEADSIZ], *map, c, *tname;
+  int i, mode, fd, inode, fsiz, rnum, msiz, *fbpool;
+  struct stat sbuf;
+  time_t mtime;
+  DEPOT *depot;
+  assert(name);
+  mode = O_RDONLY;
+  if(omode & DP_OWRITER){
+    mode = O_RDWR;
+    if(omode & DP_OCREAT) mode |= O_CREAT;
+  }
+  if((fd = open(name, mode, DP_FILEMODE)) == -1){
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!(omode & DP_ONOLCK)){
+    if(!dplock(fd, omode & DP_OWRITER, omode & DP_OLCKNB)){
+      close(fd);
+      return NULL;
+    }
+  }
+  if((omode & DP_OWRITER) && (omode & DP_OTRUNC)){
+    if(ftruncate(fd, 0) == -1){
+      close(fd);
+      dpecodeset(DP_ETRUNC, __FILE__, __LINE__);
+      return NULL;
+    }
+  }
+  if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode) ||
+     (sbuf.st_ino == 0 && lstat(name, &sbuf) == -1)){
+    close(fd);
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return NULL;
+  }
+  inode = sbuf.st_ino;
+  mtime = sbuf.st_mtime;
+  fsiz = sbuf.st_size;
+  if((omode & DP_OWRITER) && fsiz == 0){
+    memset(hbuf, 0, DP_HEADSIZ);
+    if(dpbigendian()){
+      memcpy(hbuf, DP_MAGICNUMB, strlen(DP_MAGICNUMB));
+    } else {
+      memcpy(hbuf, DP_MAGICNUML, strlen(DP_MAGICNUML));
+    }
+    sprintf(hbuf + DP_LIBVEROFF, "%d", _QDBM_LIBVER / 100);
+    bnum = bnum < 1 ? DP_DEFBNUM : bnum;
+    bnum = dpgetprime(bnum);
+    memcpy(hbuf + DP_BNUMOFF, &bnum, sizeof(int));
+    rnum = 0;
+    memcpy(hbuf + DP_RNUMOFF, &rnum, sizeof(int));
+    fsiz = DP_HEADSIZ + bnum * sizeof(int);
+    memcpy(hbuf + DP_FSIZOFF, &fsiz, sizeof(int));
+    if(!dpseekwrite(fd, 0, hbuf, DP_HEADSIZ)){
+      close(fd);
+      return NULL;
+    }
+    if(omode & DP_OSPARSE){
+      c = 0;
+      if(!dpseekwrite(fd, fsiz - 1, &c, 1)){
+        close(fd);
+        return NULL;
+      }
+    } else {
+      if(!(map = malloc(bnum * sizeof(int)))){
+        close(fd);
+        dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+        return NULL;
+      }
+      memset(map, 0, bnum * sizeof(int));
+      if(!dpseekwrite(fd, DP_HEADSIZ, map, bnum * sizeof(int))){
+        free(map);
+        close(fd);
+        return NULL;
+      }
+      free(map);
+    }
+  }
+  if(!dpseekread(fd, 0, hbuf, DP_HEADSIZ)){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!(omode & DP_ONOLCK) &&
+     ((dpbigendian() ? memcmp(hbuf, DP_MAGICNUMB, strlen(DP_MAGICNUMB)) != 0 :
+       memcmp(hbuf, DP_MAGICNUML, strlen(DP_MAGICNUML)) != 0) ||
+      *((int *)(hbuf + DP_FSIZOFF)) != fsiz)){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  bnum = *((int *)(hbuf + DP_BNUMOFF));
+  rnum = *((int *)(hbuf + DP_RNUMOFF));
+  if(bnum < 1 || rnum < 0 || fsiz < DP_HEADSIZ + bnum * sizeof(int)){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  msiz = DP_HEADSIZ + bnum * sizeof(int);
+  map = mmap(0, msiz, PROT_READ | ((mode & DP_OWRITER) ? PROT_WRITE : 0), MAP_SHARED, fd, 0);
+  if(map == MAP_FAILED){
+    close(fd);
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    return NULL;
+  }
+  tname = NULL;
+  fbpool = NULL;
+  if(!(depot = malloc(sizeof(DEPOT))) || !(tname = dpstrdup(name)) ||
+     !(fbpool = malloc(DP_FBPOOLSIZ * 2 * sizeof(int)))){
+    free(fbpool);
+    free(tname);
+    free(depot);
+    munmap(map, msiz);
+    close(fd);
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  depot->name = tname;
+  depot->wmode = (mode & DP_OWRITER);
+  depot->inode = inode;
+  depot->mtime = mtime;
+  depot->fd = fd;
+  depot->fsiz = fsiz;
+  depot->map = map;
+  depot->msiz = msiz;
+  depot->buckets = (int *)(map + DP_HEADSIZ);
+  depot->bnum = bnum;
+  depot->rnum = rnum;
+  depot->fatal = FALSE;
+  depot->ioff = 0;
+  depot->fbpool = fbpool;
+  for(i = 0; i < DP_FBPOOLSIZ * 2; i += 2){
+    depot->fbpool[i] = -1;
+    depot->fbpool[i+1] = -1;
+  }
+  depot->fbpsiz = DP_FBPOOLSIZ * 2;
+  depot->fbpinc = 0;
+  depot->align = 0;
+  return depot;
+}
+
+
+/* Close a database handle. */
+int dpclose(DEPOT *depot){
+  int fatal, err;
+  assert(depot);
+  fatal = depot->fatal;
+  err = FALSE;
+  if(depot->wmode){
+    *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
+    *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
+  }
+  if(depot->map != MAP_FAILED){
+    if(munmap(depot->map, depot->msiz) == -1){
+      err = TRUE;
+      dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    }
+  }
+  if(close(depot->fd) == -1){
+    err = TRUE;
+    dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
+  }
+  free(depot->fbpool);
+  free(depot->name);
+  free(depot);
+  if(fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Store a record. */
+int dpput(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){
+  int head[DP_RHNUM], next[DP_RHNUM];
+  int i, hash, bi, off, entoff, ee, newoff, rsiz, nsiz, fdel, mroff, mrsiz, mi, min;
+  char ebuf[DP_ENTBUFSIZ], *tval, *swap;
+  assert(depot && kbuf && vbuf);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(vsiz < 0) vsiz = strlen(vbuf);
+  newoff = -1;
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, ebuf, &ee, TRUE)){
+  case -1:
+    depot->fatal = TRUE;
+    return FALSE;
+  case 0:
+    fdel = head[DP_RHIFLAGS] & DP_RECFDEL;
+    if(dmode == DP_DKEEP && !fdel){
+      dpecodeset(DP_EKEEP, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(fdel){
+      head[DP_RHIPSIZ] += head[DP_RHIVSIZ];
+      head[DP_RHIVSIZ] = 0;
+    }
+    rsiz = dprecsize(head);
+    nsiz = DP_RHNUM * sizeof(int) + ksiz + vsiz;
+    if(dmode == DP_DCAT) nsiz += head[DP_RHIVSIZ];
+    if(off + rsiz >= depot->fsiz){
+      if(rsiz < nsiz){
+        head[DP_RHIPSIZ] += nsiz - rsiz;
+        rsiz = nsiz;
+        depot->fsiz = off + rsiz;
+      }
+    } else {
+      while(nsiz > rsiz && off + rsiz < depot->fsiz){
+        if(!dprechead(depot, off + rsiz, next, NULL, NULL)) return FALSE;
+        if(!(next[DP_RHIFLAGS] & DP_RECFREUSE)) break;
+        head[DP_RHIPSIZ] += dprecsize(next);
+        rsiz += dprecsize(next);
+      }
+      for(i = 0; i < depot->fbpsiz; i += 2){
+        if(depot->fbpool[i] >= off && depot->fbpool[i] < off + rsiz){
+          depot->fbpool[i] = -1;
+          depot->fbpool[i+1] = -1;
+        }
+      }
+    }
+    if(nsiz <= rsiz){
+      if(!dprecover(depot, off, head, vbuf, vsiz, dmode == DP_DCAT)){
+        depot->fatal = TRUE;
+        return FALSE;
+      }
+    } else {
+      tval = NULL;
+      if(dmode == DP_DCAT){
+        if(ee && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] <= DP_ENTBUFSIZ){
+          if(!(tval = malloc(head[DP_RHIVSIZ] + vsiz + 1))){
+            dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+            depot->fatal = TRUE;
+            return FALSE;
+          }
+          memcpy(tval, ebuf + (DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ]), head[DP_RHIVSIZ]);
+        } else {
+          if(!(tval = dprecval(depot, off, head, 0, -1))){
+            depot->fatal = TRUE;
+            return FALSE;
+          }
+          if(!(swap = realloc(tval, head[DP_RHIVSIZ] + vsiz + 1))){
+            free(tval);
+            dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+            depot->fatal = TRUE;
+            return FALSE;
+          }
+          tval = swap;
+        }
+        memcpy(tval + head[DP_RHIVSIZ], vbuf, vsiz);
+        vsiz += head[DP_RHIVSIZ];
+        vbuf = tval;
+      }
+      mi = -1;
+      min = -1;
+      for(i = 0; i < depot->fbpsiz; i += 2){
+        if(depot->fbpool[i+1] < nsiz) continue;
+        if(mi == -1 || depot->fbpool[i+1] < min){
+          mi = i;
+          min = depot->fbpool[i+1];
+        }
+      }
+      if(mi >= 0){
+        mroff = depot->fbpool[mi];
+        mrsiz = depot->fbpool[mi+1];
+        depot->fbpool[mi] = -1;
+        depot->fbpool[mi+1] = -1;
+      } else {
+        mroff = -1;
+        mrsiz = -1;
+      }
+      if(!dprecdelete(depot, off, head, TRUE)){
+        free(tval);
+        depot->fatal = TRUE;
+        return FALSE;
+      }
+      if(mroff > 0 && nsiz <= mrsiz){
+        if(!dprecrewrite(depot, mroff, mrsiz, kbuf, ksiz, vbuf, vsiz,
+                         hash, head[DP_RHILEFT], head[DP_RHIRIGHT])){
+          free(tval);
+          depot->fatal = TRUE;
+          return FALSE;
+        }
+        newoff = mroff;
+      } else {
+        if((newoff = dprecappend(depot, kbuf, ksiz, vbuf, vsiz,
+                                 hash, head[DP_RHILEFT], head[DP_RHIRIGHT])) == -1){
+          free(tval);
+          depot->fatal = TRUE;
+          return FALSE;
+        }
+      }
+      free(tval);
+    }
+    if(fdel) depot->rnum++;
+    break;
+  default:
+    if((newoff = dprecappend(depot, kbuf, ksiz, vbuf, vsiz, hash, 0, 0)) == -1){
+      depot->fatal = TRUE;
+      return FALSE;
+    }
+    depot->rnum++;
+    break;
+  }
+  if(newoff > 0){
+    if(entoff > 0){
+      if(!dpseekwritenum(depot->fd, entoff, newoff)){
+        depot->fatal = TRUE;
+        return FALSE;
+      }
+    } else {
+      depot->buckets[bi] = newoff;
+    }
+  }
+  return TRUE;
+}
+
+
+/* Delete a record. */
+int dpout(DEPOT *depot, const char *kbuf, int ksiz){
+  int head[DP_RHNUM], hash, bi, off, entoff, ee;
+  char ebuf[DP_ENTBUFSIZ];
+  assert(depot && kbuf);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, ebuf, &ee, FALSE)){
+  case -1:
+    depot->fatal = TRUE;
+    return FALSE;
+  case 0:
+    break;
+  default:
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!dprecdelete(depot, off, head, FALSE)){
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  depot->rnum--;
+  return TRUE;
+}
+
+
+/* Retrieve a record. */
+char *dpget(DEPOT *depot, const char *kbuf, int ksiz, int start, int max, int *sp){
+  int head[DP_RHNUM], hash, bi, off, entoff, ee, vsiz;
+  char ebuf[DP_ENTBUFSIZ], *vbuf;
+  assert(depot && kbuf && start >= 0);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, ebuf, &ee, FALSE)){
+  case -1:
+    depot->fatal = TRUE;
+    return NULL;
+  case 0:
+    break;
+  default:
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(start > head[DP_RHIVSIZ]){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(ee && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] <= DP_ENTBUFSIZ){
+    head[DP_RHIVSIZ] -= start;
+    if(max < 0){
+      vsiz = head[DP_RHIVSIZ];
+    } else {
+      vsiz = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
+    }
+    if(!(vbuf = malloc(vsiz + 1))){
+      dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+      depot->fatal = TRUE;
+      return NULL;
+    }
+    memcpy(vbuf, ebuf + (DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + start), vsiz);
+    vbuf[vsiz] = '\0';
+  } else {
+    if(!(vbuf = dprecval(depot, off, head, start, max))){
+      depot->fatal = TRUE;
+      return NULL;
+    }
+  }
+  if(sp){
+    if(max < 0){
+      *sp = head[DP_RHIVSIZ];
+    } else {
+      *sp = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
+    }
+  }
+  return vbuf;
+}
+
+
+/* Retrieve a record and write the value into a buffer. */
+int dpgetwb(DEPOT *depot, const char *kbuf, int ksiz, int start, int max, char *vbuf){
+  int head[DP_RHNUM], hash, bi, off, entoff, ee, vsiz;
+  char ebuf[DP_ENTBUFSIZ];
+  assert(depot && kbuf && start >= 0 && max >= 0 && vbuf);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, ebuf, &ee, FALSE)){
+  case -1:
+    depot->fatal = TRUE;
+    return -1;
+  case 0:
+    break;
+  default:
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return -1;
+  }
+  if(start > head[DP_RHIVSIZ]){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return -1;
+  }
+  if(ee && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] <= DP_ENTBUFSIZ){
+    head[DP_RHIVSIZ] -= start;
+    vsiz = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
+    memcpy(vbuf, ebuf + (DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + start), vsiz);
+  } else {
+    if((vsiz = dprecvalwb(depot, off, head, start, max, vbuf)) == -1){
+      depot->fatal = TRUE;
+      return -1;
+    }
+  }
+  return vsiz;
+}
+
+
+/* Get the size of the value of a record. */
+int dpvsiz(DEPOT *depot, const char *kbuf, int ksiz){
+  int head[DP_RHNUM], hash, bi, off, entoff, ee;
+  char ebuf[DP_ENTBUFSIZ];
+  assert(depot && kbuf);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  switch(dprecsearch(depot, kbuf, ksiz, hash, &bi, &off, &entoff, head, ebuf, &ee, FALSE)){
+  case -1:
+    depot->fatal = TRUE;
+    return -1;
+  case 0:
+    break;
+  default:
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return -1;
+  }
+  return head[DP_RHIVSIZ];
+}
+
+
+/* Initialize the iterator of a database handle. */
+int dpiterinit(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  depot->ioff = 0;
+  return TRUE;
+}
+
+
+/* Get the next key of the iterator. */
+char *dpiternext(DEPOT *depot, int *sp){
+  int off, head[DP_RHNUM], ee;
+  char ebuf[DP_ENTBUFSIZ], *kbuf;
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  off = DP_HEADSIZ + depot->bnum * sizeof(int);
+  off = off > depot->ioff ? off : depot->ioff;
+  while(off < depot->fsiz){
+    if(!dprechead(depot, off, head, ebuf, &ee)){
+      depot->fatal = TRUE;
+      return NULL;
+    }
+    if(head[DP_RHIFLAGS] & DP_RECFDEL){
+      off += dprecsize(head);
+    } else {
+      if(ee && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] <= DP_ENTBUFSIZ){
+        if(!(kbuf = malloc(head[DP_RHIKSIZ] + 1))){
+          dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+          depot->fatal = TRUE;
+          return NULL;
+        }
+        memcpy(kbuf, ebuf + (DP_RHNUM * sizeof(int)), head[DP_RHIKSIZ]);
+        kbuf[head[DP_RHIKSIZ]] = '\0';
+      } else {
+        if(!(kbuf = dpreckey(depot, off, head))){
+          depot->fatal = TRUE;
+          return NULL;
+        }
+      }
+      depot->ioff = off + dprecsize(head);
+      if(sp) *sp = head[DP_RHIKSIZ];
+      return kbuf;
+    }
+  }
+  dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+  return NULL;
+}
+
+
+/* Set alignment of a database handle. */
+int dpsetalign(DEPOT *depot, int align){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  depot->align = align;
+  return TRUE;
+}
+
+
+/* Set the size of the free block pool of a database handle. */
+int dpsetfbpsiz(DEPOT *depot, int size){
+  int *fbpool;
+  int i;
+  assert(depot && size >= 0);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  size *= 2;
+  if(!(fbpool = realloc(depot->fbpool, size * sizeof(int) + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  for(i = 0; i < size; i += 2){
+    fbpool[i] = -1;
+    fbpool[i+1] = -1;
+  }
+  depot->fbpool = fbpool;
+  depot->fbpsiz = size;
+  return TRUE;
+}
+
+
+
+/* Synchronize contents of updating a database with the file and the device. */
+int dpsync(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
+  *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
+  if(msync(depot->map, depot->msiz, MS_SYNC) == -1){
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  if(fsync(depot->fd) == -1){
+    dpecodeset(DP_ESYNC, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Optimize a database. */
+int dpoptimize(DEPOT *depot, int bnum){
+  DEPOT *tdepot;
+  char *name;
+  int i, err, off, head[DP_RHNUM], ee, ksizs[DP_OPTRUNIT], vsizs[DP_OPTRUNIT], unum;
+  char ebuf[DP_ENTBUFSIZ], *kbufs[DP_OPTRUNIT], *vbufs[DP_OPTRUNIT];
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(name = malloc(strlen(depot->name) + strlen(DP_TMPFSUF) + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    depot->fatal = FALSE;
+    return FALSE;
+  }
+  sprintf(name, "%s%s", depot->name, DP_TMPFSUF);
+  if(bnum < 0){
+    bnum = (int)(depot->rnum * (1.0 / DP_OPTBLOAD)) + 1;
+    if(bnum < DP_DEFBNUM / 2) bnum = DP_DEFBNUM / 2;
+  }
+  if(!(tdepot = dpopen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, bnum))){
+    free(name);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  free(name);
+  if(!dpsetflags(tdepot, dpgetflags(depot))){
+    dpclose(tdepot);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  tdepot->align = depot->align;
+  err = FALSE;
+  off = DP_HEADSIZ + depot->bnum * sizeof(int);
+  unum = 0;
+  while(off < depot->fsiz){
+    if(!dprechead(depot, off, head, ebuf, &ee)){
+      err = TRUE;
+      break;
+    }
+    if(!(head[DP_RHIFLAGS] & DP_RECFDEL)){
+      if(ee && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] <= DP_ENTBUFSIZ){
+        if(!(kbufs[unum] = malloc(head[DP_RHIKSIZ] + 1))){
+          dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+          err = TRUE;
+          break;
+        }
+        memcpy(kbufs[unum], ebuf + (DP_RHNUM * sizeof(int)), head[DP_RHIKSIZ]);
+        if(DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] <= DP_ENTBUFSIZ){
+          if(!(vbufs[unum] = malloc(head[DP_RHIVSIZ] + 1))){
+            dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+            err = TRUE;
+            break;
+          }
+          memcpy(vbufs[unum], ebuf + (DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ]),
+                 head[DP_RHIVSIZ]);
+        } else {
+          vbufs[unum] = dprecval(depot, off, head, 0, -1);
+        }
+      } else {
+        kbufs[unum] = dpreckey(depot, off, head);
+        vbufs[unum] = dprecval(depot, off, head, 0, -1);
+      }
+      ksizs[unum] = head[DP_RHIKSIZ];
+      vsizs[unum] = head[DP_RHIVSIZ];
+      unum++;
+      if(unum >= DP_OPTRUNIT){
+        for(i = 0; i < unum; i++){
+          if(kbufs[i] && vbufs[i]){
+            if(!dpput(tdepot, kbufs[i], ksizs[i], vbufs[i], vsizs[i], DP_DKEEP)) err = TRUE;
+          } else {
+            err = TRUE;
+          }
+          free(kbufs[i]);
+          free(vbufs[i]);
+          if(err) break;
+        }
+        unum = 0;
+      }
+    }
+    off += dprecsize(head);
+    if(err) break;
+  }
+  for(i = 0; i < unum; i++){
+    if(kbufs[i] && vbufs[i]){
+      if(!dpput(tdepot, kbufs[i], ksizs[i], vbufs[i], vsizs[i], DP_DKEEP)) err = TRUE;
+    } else {
+      err = TRUE;
+    }
+    free(kbufs[i]);
+    free(vbufs[i]);
+    if(err) break;
+  }
+  if(!dpsync(tdepot)) err = TRUE;
+  if(err){
+    unlink(tdepot->name);
+    dpclose(tdepot);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  if(munmap(depot->map, depot->msiz) == -1){
+    dpclose(tdepot);
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  depot->map = MAP_FAILED;
+  if(ftruncate(depot->fd, 0) == -1){
+    dpclose(tdepot);
+    unlink(tdepot->name);
+    dpecodeset(DP_ETRUNC, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  if(dpfcopy(depot->fd, 0, tdepot->fd, 0) == -1){
+    dpclose(tdepot);
+    unlink(tdepot->name);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  depot->fsiz = tdepot->fsiz;
+  depot->bnum = tdepot->bnum;
+  depot->ioff = 0;
+  for(i = 0; i < depot->fbpsiz; i += 2){
+    depot->fbpool[i] = -1;
+    depot->fbpool[i+1] = -1;
+  }
+  depot->msiz = tdepot->msiz;
+  depot->map = mmap(0, depot->msiz, PROT_READ | PROT_WRITE, MAP_SHARED, depot->fd, 0);
+  if(depot->map == MAP_FAILED){
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  depot->buckets = (int *)(depot->map + DP_HEADSIZ);
+  if(!(name = dpname(tdepot))){
+    dpclose(tdepot);
+    unlink(tdepot->name);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  if(!dpclose(tdepot)){
+    free(name);
+    unlink(tdepot->name);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  if(unlink(name) == -1){
+    free(name);
+    dpecodeset(DP_EUNLINK, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  free(name);
+  return TRUE;
+}
+
+
+/* Get the name of a database. */
+char *dpname(DEPOT *depot){
+  char *name;
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!(name = dpstrdup(depot->name))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return NULL;
+  }
+  return name;
+}
+
+
+/* Get the size of a database file. */
+int dpfsiz(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  return depot->fsiz;
+}
+
+
+/* Get the number of the elements of the bucket array. */
+int dpbnum(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  return depot->bnum;
+}
+
+
+/* Get the number of the used elements of the bucket array. */
+int dpbusenum(DEPOT *depot){
+  int i, hits;
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  hits = 0;
+  for(i = 0; i < depot->bnum; i++){
+    if(depot->buckets[i]) hits++;
+  }
+  return hits;
+}
+
+
+/* Get the number of the records stored in a database. */
+int dprnum(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  return depot->rnum;
+}
+
+
+/* Check whether a database handle is a writer or not. */
+int dpwritable(DEPOT *depot){
+  assert(depot);
+  return depot->wmode;
+}
+
+
+/* Check whether a database has a fatal error or not. */
+int dpfatalerror(DEPOT *depot){
+  assert(depot);
+  return depot->fatal;
+}
+
+
+/* Get the inode number of a database file. */
+int dpinode(DEPOT *depot){
+  assert(depot);
+  return depot->inode;
+}
+
+
+/* Get the last modified time of a database. */
+time_t dpmtime(DEPOT *depot){
+  assert(depot);
+  return depot->mtime;
+}
+
+
+/* Get the file descriptor of a database file. */
+int dpfdesc(DEPOT *depot){
+  assert(depot);
+  return depot->fd;
+}
+
+
+/* Remove a database file. */
+int dpremove(const char *name){
+  struct stat sbuf;
+  DEPOT *depot;
+  assert(name);
+  if(lstat(name, &sbuf) == -1){
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if((depot = dpopen(name, DP_OWRITER | DP_OTRUNC, -1)) != NULL) dpclose(depot);
+  if(unlink(name) == -1){
+    dpecodeset(DP_EUNLINK, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Repair a broken database file. */
+int dprepair(const char *name){
+  DEPOT *tdepot;
+  char dbhead[DP_HEADSIZ], *tname, *kbuf, *vbuf;
+  int fd, fsiz, flags, bnum, tbnum, err, head[DP_RHNUM], off, rsiz, ksiz, vsiz;
+  struct stat sbuf;
+  assert(name);
+  if(lstat(name, &sbuf) == -1){
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return FALSE;
+  }
+  fsiz = sbuf.st_size;
+  if((fd = open(name, O_RDWR, DP_FILEMODE)) == -1){
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!dpseekread(fd, 0, dbhead, DP_HEADSIZ)){
+    close(fd);
+    return FALSE;
+  }
+  flags = *(int *)(dbhead + DP_FLAGSOFF);
+  bnum = *(int *)(dbhead + DP_BNUMOFF);
+  tbnum = *(int *)(dbhead + DP_RNUMOFF) * 2;
+  if(tbnum < DP_DEFBNUM) tbnum = DP_DEFBNUM;
+  if(!(tname = malloc(strlen(name) + strlen(DP_TMPFSUF) + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  sprintf(tname, "%s%s", name, DP_TMPFSUF);
+  if(!(tdepot = dpopen(tname, DP_OWRITER | DP_OCREAT | DP_OTRUNC, tbnum))){
+    free(tname);
+    close(fd);
+    return FALSE;
+  }
+  err = FALSE;
+  off = DP_HEADSIZ + bnum * sizeof(int);
+  while(off < fsiz){
+    if(!dpseekread(fd, off, head, DP_RHNUM * sizeof(int))) break;
+    if(head[DP_RHIFLAGS] & DP_RECFDEL){
+      if((rsiz = dprecsize(head)) < 0) break;
+      off += rsiz;
+      continue;
+    }
+    ksiz = head[DP_RHIKSIZ];
+    vsiz = head[DP_RHIVSIZ];
+    if(ksiz >= 0 && vsiz >= 0){
+      kbuf = malloc(ksiz + 1);
+      vbuf = malloc(vsiz + 1);
+      if(kbuf && vbuf){
+        if(dpseekread(fd, off + DP_RHNUM * sizeof(int), kbuf, ksiz) &&
+           dpseekread(fd, off + DP_RHNUM * sizeof(int) + ksiz, vbuf, vsiz)){
+          if(!dpput(tdepot, kbuf, ksiz, vbuf, vsiz, DP_DKEEP)) err = TRUE;
+        } else {
+          err = TRUE;
+        }
+      } else {
+        if(!err) dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+        err = TRUE;
+      }
+      free(vbuf);
+      free(kbuf);
+    } else {
+      if(!err) dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      err = TRUE;
+    }
+    if((rsiz = dprecsize(head)) < 0) break;
+    off += rsiz;
+  }
+  if(!dpsetflags(tdepot, flags)) err = TRUE;
+  if(!dpsync(tdepot)) err = TRUE;
+  if(ftruncate(fd, 0) == -1){
+    if(!err) dpecodeset(DP_ETRUNC, __FILE__, __LINE__);
+    err = TRUE;
+  }
+  if(dpfcopy(fd, 0, tdepot->fd, 0) == -1) err = TRUE;
+  if(!dpclose(tdepot)) err = TRUE;
+  if(close(fd) == -1){
+    if(!err) dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
+    err = TRUE;
+  }
+  if(unlink(tname) == -1){
+    if(!err) dpecodeset(DP_EUNLINK, __FILE__, __LINE__);
+    err = TRUE;
+  }
+  free(tname);
+  return err ? FALSE : TRUE;
+}
+
+
+/* Dump all records as endian independent data. */
+int dpexportdb(DEPOT *depot, const char *name){
+  char *kbuf, *vbuf, *pbuf;
+  int fd, err, ksiz, vsiz, psiz;
+  assert(depot && name);
+  if(!(dpiterinit(depot))) return FALSE;
+  if((fd = open(name, O_RDWR | O_CREAT | O_TRUNC, DP_FILEMODE)) == -1){
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  while(!err && (kbuf = dpiternext(depot, &ksiz)) != NULL){
+    if((vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz)) != NULL){
+      if((pbuf = malloc(ksiz + vsiz + DP_NUMBUFSIZ * 2)) != NULL){
+        psiz = 0;
+        psiz += sprintf(pbuf + psiz, "%X\n%X\n", ksiz, vsiz);
+        memcpy(pbuf + psiz, kbuf, ksiz);
+        psiz += ksiz;
+        pbuf[psiz++] = '\n';
+        memcpy(pbuf + psiz, vbuf, vsiz);
+        psiz += vsiz;
+        pbuf[psiz++] = '\n';
+        if(!dpwrite(fd, pbuf, psiz)){
+          dpecodeset(DP_EWRITE, __FILE__, __LINE__);
+          err = TRUE;
+        }
+        free(pbuf);
+      } else {
+        dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+        err = TRUE;
+      }
+      free(vbuf);
+    } else {
+      err = TRUE;
+    }
+    free(kbuf);
+  }
+  if(close(fd) == -1){
+    if(!err) dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return !err && !dpfatalerror(depot);
+}
+
+
+/* Load all records from endian independent data. */
+int dpimportdb(DEPOT *depot, const char *name){
+  char mbuf[DP_IOBUFSIZ], *rbuf;
+  int i, j, fd, err, fsiz, off, msiz, ksiz, vsiz, hlen;
+  struct stat sbuf;
+  assert(depot && name);
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(dprnum(depot) > 0){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if((fd = open(name, O_RDONLY, DP_FILEMODE)) == -1){
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    close(fd);
+    return FALSE;
+  }
+  err = FALSE;
+  fsiz = sbuf.st_size;
+  off = 0;
+  while(!err && off < fsiz){
+    msiz = fsiz - off;
+    if(msiz > DP_IOBUFSIZ) msiz = DP_IOBUFSIZ;
+    if(!dpseekread(fd, off, mbuf, msiz)){
+      err = TRUE;
+      break;
+    }
+    hlen = 0;
+    ksiz = -1;
+    vsiz = -1;
+    for(i = 0; i < msiz; i++){
+      if(mbuf[i] == '\n'){
+        mbuf[i] = '\0';
+        ksiz = strtol(mbuf, NULL, 16);
+        for(j = i + 1; j < msiz; j++){
+          if(mbuf[j] == '\n'){
+            mbuf[j] = '\0';
+            vsiz = strtol(mbuf + i + 1, NULL, 16);
+            hlen = j + 1;
+            break;
+          }
+        }
+        break;
+      }
+    }
+    if(ksiz < 0 || vsiz < 0 || hlen < 4){
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      err = TRUE;
+      break;
+    }
+    if(hlen + ksiz + vsiz + 2 < DP_IOBUFSIZ){
+      if(!dpput(depot, mbuf + hlen, ksiz, mbuf + hlen + ksiz + 1, vsiz, DP_DKEEP)) err = TRUE;
+    } else {
+      if((rbuf = malloc(ksiz + vsiz + 2)) != NULL){
+        if(dpseekread(fd, off + hlen, rbuf, ksiz + vsiz + 2)){
+          if(!dpput(depot, rbuf, ksiz, rbuf + ksiz + 1, vsiz, DP_DKEEP)) err = TRUE;
+        } else {
+          err = TRUE;
+        }
+        free(rbuf);
+      } else {
+        dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+        err = TRUE;
+      }
+    }
+    off += hlen + ksiz + vsiz + 2;
+  }
+  if(close(fd) == -1){
+    if(!err) dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return !err && !dpfatalerror(depot);
+}
+
+
+/* Retrieve a record directly from a database file. */
+char *dpsnaffle(const char *name, const char* kbuf, int ksiz, int *sp){
+  char hbuf[DP_HEADSIZ], *map, *vbuf, *tkbuf;
+  int fd, fsiz, bnum, msiz, *buckets, hash, thash, head[DP_RHNUM], err, off, vsiz, tksiz, kcmp;
+  struct stat sbuf;
+  assert(name && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if((fd = open(name, O_RDONLY, DP_FILEMODE)) == -1){
+    dpecodeset(DP_EOPEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
+    close(fd);
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return NULL;
+  }
+  fsiz = sbuf.st_size;
+  if(!dpseekread(fd, 0, hbuf, DP_HEADSIZ)){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(dpbigendian() ? memcmp(hbuf, DP_MAGICNUMB, strlen(DP_MAGICNUMB)) != 0 :
+     memcmp(hbuf, DP_MAGICNUML, strlen(DP_MAGICNUML)) != 0){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  bnum = *((int *)(hbuf + DP_BNUMOFF));
+  if(bnum < 1 || fsiz < DP_HEADSIZ + bnum * sizeof(int)){
+    close(fd);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  msiz = DP_HEADSIZ + bnum * sizeof(int);
+  map = mmap(0, msiz, PROT_READ, MAP_SHARED, fd, 0);
+  if(map == MAP_FAILED){
+    close(fd);
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    return NULL;
+  }
+  buckets = (int *)(map + DP_HEADSIZ);
+  err = FALSE;
+  vbuf = NULL;
+  vsiz = 0;
+  DP_SECONDHASH(hash, kbuf, ksiz);
+  DP_FIRSTHASH(thash, kbuf, ksiz);
+  off = buckets[thash%bnum];
+  while(off != 0){
+    if(!dpseekread(fd, off, head, DP_RHNUM * sizeof(int))){
+      err = TRUE;
+      break;
+    }
+    if(head[DP_RHIKSIZ] < 0 || head[DP_RHIVSIZ] < 0 || head[DP_RHIPSIZ] < 0 ||
+       head[DP_RHILEFT] < 0 || head[DP_RHIRIGHT] < 0){
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      err = TRUE;
+      break;
+    }
+    thash = head[DP_RHIHASH];
+    if(hash > thash){
+      off = head[DP_RHILEFT];
+    } else if(hash < thash){
+      off = head[DP_RHIRIGHT];
+    } else {
+      tksiz = head[DP_RHIKSIZ];
+      if(!(tkbuf = malloc(tksiz + 1))){
+        dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+        err = TRUE;
+        break;
+      }
+      if(!dpseekread(fd, off + DP_RHNUM * sizeof(int), tkbuf, tksiz)){
+        free(tkbuf);
+        err = TRUE;
+        break;
+      }
+      tkbuf[tksiz] = '\0';
+      kcmp = dpkeycmp(kbuf, ksiz, tkbuf, tksiz);
+      free(tkbuf);
+      if(kcmp > 0){
+        off = head[DP_RHILEFT];
+      } else if(kcmp < 0){
+        off = head[DP_RHIRIGHT];
+      } else if(head[DP_RHIFLAGS] & DP_RECFDEL){
+        break;
+      } else {
+        vsiz = head[DP_RHIVSIZ];
+        if(!(vbuf = malloc(vsiz + 1))){
+          dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+          err = TRUE;
+          break;
+        }
+        if(!dpseekread(fd, off + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ], vbuf, vsiz)){
+          free(vbuf);
+          vbuf = NULL;
+          err = TRUE;
+          break;
+        }
+        vbuf[vsiz] = '\0';
+        break;
+      }
+    }
+  }
+  if(vbuf){
+    if(sp) *sp = vsiz;
+  } else if(!err){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+  }
+  munmap(map, msiz);
+  close(fd);
+  return vbuf;
+}
+
+
+/* Hash function used inside Depot. */
+int dpinnerhash(const char *kbuf, int ksiz){
+  int res;
+  assert(kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_FIRSTHASH(res, kbuf, ksiz);
+  return res;
+}
+
+
+/* Hash function which is independent from the hash functions used inside Depot. */
+int dpouterhash(const char *kbuf, int ksiz){
+  int res;
+  assert(kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  DP_THIRDHASH(res, kbuf, ksiz);
+  return res;
+}
+
+
+/* Get a natural prime number not less than a number. */
+int dpprimenum(int num){
+  assert(num > 0);
+  return dpgetprime(num);
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Name of the operating system. */
+const char *dpsysname = _QDBM_SYSNAME;
+
+
+/* File descriptor for debugging output. */
+int dpdbgfd = -1;
+
+
+/* Whether this build is reentrant. */
+const int dpisreentrant = _qdbm_ptsafe;
+
+
+/* Set the last happened error code. */
+void dpecodeset(int ecode, const char *file, int line){
+  char iobuf[DP_IOBUFSIZ];
+  assert(ecode >= DP_ENOERR && file && line >= 0);
+  dpecode = ecode;
+  if(dpdbgfd >= 0){
+    fflush(stdout);
+    fflush(stderr);
+    sprintf(iobuf, "* dpecodeset: %s:%d: [%d] %s\n", file, line, ecode, dperrmsg(ecode));
+    dpwrite(dpdbgfd, iobuf, strlen(iobuf));
+  }
+}
+
+
+/* Get the pointer of the variable of the last happened error code. */
+int *dpecodeptr(void){
+  static int defdpecode = DP_ENOERR;
+  void *ptr;
+  if(_qdbm_ptsafe){
+    if(!(ptr = _qdbm_settsd(&defdpecode, sizeof(int), &defdpecode))){
+      defdpecode = DP_EMISC;
+      return &defdpecode;
+    }
+    return (int *)ptr;
+  }
+  return &defdpecode;
+}
+
+
+/* Synchronize updating contents on memory. */
+int dpmemsync(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
+  *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
+  if(msync(depot->map, depot->msiz, MS_SYNC) == -1){
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Synchronize updating contents on memory, not physically. */
+int dpmemflush(DEPOT *depot){
+  assert(depot);
+  if(depot->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  *((int *)(depot->map + DP_FSIZOFF)) = depot->fsiz;
+  *((int *)(depot->map + DP_RNUMOFF)) = depot->rnum;
+  if(mflush(depot->map, depot->msiz, MS_SYNC) == -1){
+    dpecodeset(DP_EMAP, __FILE__, __LINE__);
+    depot->fatal = TRUE;
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Get flags of a database. */
+int dpgetflags(DEPOT *depot){
+  int flags;
+  assert(depot);
+  memcpy(&flags, depot->map + DP_FLAGSOFF, sizeof(int));
+  return flags;
+}
+
+
+/* Set flags of a database. */
+int dpsetflags(DEPOT *depot, int flags){
+  assert(depot);
+  if(!depot->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  memcpy(depot->map + DP_FLAGSOFF, &flags, sizeof(int));
+  return TRUE;
+}
+
+
+
+/*************************************************************************************************
+ * private objects
+ *************************************************************************************************/
+
+
+/* Check whether the byte order of the platform is big endian or not.
+   The return value is true if bigendian, else, it is false. */
+static int dpbigendian(void){
+  char buf[sizeof(int)];
+  *(int *)buf = 1;
+  return !buf[0];
+}
+
+
+/* Get a copied string.
+   `str' specifies an original string.
+   The return value is a copied string whose region is allocated by `malloc'. */
+static char *dpstrdup(const char *str){
+  int len;
+  char *buf;
+  assert(str);
+  len = strlen(str);
+  if(!(buf = malloc(len + 1))) return NULL;
+  memcpy(buf, str, len + 1);
+  return buf;
+}
+
+
+/* Lock a file descriptor.
+   `fd' specifies a 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. */
+static int dplock(int fd, int ex, int nb){
+  struct flock lock;
+  assert(fd >= 0);
+  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){
+      dpecodeset(DP_ELOCK, __FILE__, __LINE__);
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+
+/* Write into a file.
+   `fd' specifies a file descriptor.
+   `buf' specifies a buffer to write.
+   `size' specifies the size of the buffer.
+   The return value is the size of the written buffer, or, -1 on failure. */
+static int dpwrite(int fd, const void *buf, int size){
+  const char *lbuf;
+  int rv, wb;
+  assert(fd >= 0 && buf && size >= 0);
+  lbuf = buf;
+  rv = 0;
+  do {
+    wb = write(fd, lbuf, size);
+    switch(wb){
+    case -1: if(errno != EINTR) return -1;
+    case 0: break;
+    default:
+      lbuf += wb;
+      size -= wb;
+      rv += wb;
+      break;
+    }
+  } while(size > 0);
+  return rv;
+}
+
+
+/* Write into a file at an offset.
+   `fd' specifies a file descriptor.
+   `off' specifies an offset of the file.
+   `buf' specifies a buffer to write.
+   `size' specifies the size of the buffer.
+   The return value is true if successful, else, it is false. */
+static int dpseekwrite(int fd, int off, const void *buf, int size){
+  assert(fd >= 0 && buf && size >= 0);
+  if(size < 1) return TRUE;
+  if(off < 0){
+    if(lseek(fd, 0, SEEK_END) == -1){
+      dpecodeset(DP_ESEEK, __FILE__, __LINE__);
+      return FALSE;
+    }
+  } else {
+    if(lseek(fd, off, SEEK_SET) != off){
+      dpecodeset(DP_ESEEK, __FILE__, __LINE__);
+      return FALSE;
+    }
+  }
+  if(dpwrite(fd, buf, size) != size){
+    dpecodeset(DP_EWRITE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Write an integer into a file at an offset.
+   `fd' specifies a file descriptor.
+   `off' specifies an offset of the file.
+   `num' specifies an integer.
+   The return value is true if successful, else, it is false. */
+static int dpseekwritenum(int fd, int off, int num){
+  assert(fd >= 0);
+  return dpseekwrite(fd, off, &num, sizeof(int));
+}
+
+
+/* Read from a file and store the data into a buffer.
+   `fd' specifies a file descriptor.
+   `buffer' specifies a buffer to store into.
+   `size' specifies the size to read with.
+   The return value is the size read with, or, -1 on failure. */
+static int dpread(int fd, void *buf, int size){
+  char *lbuf;
+  int i, bs;
+  assert(fd >= 0 && buf && size >= 0);
+  lbuf = buf;
+  for(i = 0; i < size && (bs = read(fd, lbuf + i, size - i)) != 0; i += bs){
+    if(bs == -1 && errno != EINTR) return -1;
+  }
+  return i;
+}
+
+
+/* Read from a file at an offset and store the data into a buffer.
+   `fd' specifies a file descriptor.
+   `off' specifies an offset of the file.
+   `buffer' specifies a buffer to store into.
+   `size' specifies the size to read with.
+   The return value is true if successful, else, it is false. */
+static int dpseekread(int fd, int off, void *buf, int size){
+  char *lbuf;
+  assert(fd >= 0 && off >= 0 && buf && size >= 0);
+  lbuf = (char *)buf;
+  if(lseek(fd, off, SEEK_SET) != off){
+    dpecodeset(DP_ESEEK, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(dpread(fd, lbuf, size) != size){
+    dpecodeset(DP_EREAD, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Copy data between files.
+   `destfd' specifies a file descriptor of a destination file.
+   `destoff' specifies an offset of the destination file.
+   `srcfd' specifies a file descriptor of a source file.
+   `srcoff' specifies an offset of the source file.
+   The return value is the size copied with, or, -1 on failure. */
+static int dpfcopy(int destfd, int destoff, int srcfd, int srcoff){
+  char iobuf[DP_IOBUFSIZ];
+  int sum, iosiz;
+  if(lseek(srcfd, srcoff, SEEK_SET) == -1 || lseek(destfd, destoff, SEEK_SET) == -1){
+    dpecodeset(DP_ESEEK, __FILE__, __LINE__);
+    return -1;
+  }
+  sum = 0;
+  while((iosiz = dpread(srcfd, iobuf, DP_IOBUFSIZ)) > 0){
+    if(dpwrite(destfd, iobuf, iosiz) == -1){
+      dpecodeset(DP_EWRITE, __FILE__, __LINE__);
+      return -1;
+    }
+    sum += iosiz;
+  }
+  if(iosiz < 0){
+    dpecodeset(DP_EREAD, __FILE__, __LINE__);
+    return -1;
+  }
+  return sum;
+}
+
+
+/* Get a natural prime number not less than a number.
+   `num' specified a natural number.
+   The return value is a prime number not less than the specified number. */
+static int dpgetprime(int num){
+  int 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, -1
+  };
+  int i;
+  assert(num > 0);
+  for(i = 0; primes[i] > 0; i++){
+    if(num <= primes[i]) return primes[i];
+  }
+  return primes[i-1];
+}
+
+
+/* Get the padding size of a record.
+   `vsiz' specifies the size of the value of a record.
+   The return value is the padding size of a record. */
+static int dppadsize(DEPOT *depot, int ksiz, int vsiz){
+  int pad;
+  assert(depot && vsiz >= 0);
+  if(depot->align > 0){
+    return depot->align - (depot->fsiz + DP_RHNUM * sizeof(int) + ksiz + vsiz) % depot->align;
+  } else if(depot->align < 0){
+    pad = (int)(vsiz * (2.0 / (1 << -(depot->align))));
+    if(vsiz + pad >= DP_FSBLKSIZ){
+      if(vsiz <= DP_FSBLKSIZ) pad = 0;
+      if(depot->fsiz % DP_FSBLKSIZ == 0){
+        return (pad / DP_FSBLKSIZ) * DP_FSBLKSIZ + DP_FSBLKSIZ -
+          (depot->fsiz + DP_RHNUM * sizeof(int) + ksiz + vsiz) % DP_FSBLKSIZ;
+      } else {
+        return (pad / (DP_FSBLKSIZ / 2)) * (DP_FSBLKSIZ / 2) + (DP_FSBLKSIZ / 2) -
+          (depot->fsiz + DP_RHNUM * sizeof(int) + ksiz + vsiz) % (DP_FSBLKSIZ / 2);
+      }
+    } else {
+      return pad >= DP_RHNUM * sizeof(int) ? pad : DP_RHNUM * sizeof(int);
+    }
+  }
+  return 0;
+}
+
+
+/* Get the size of a record in a database file.
+   `head' specifies the header of  a record.
+   The return value is the size of a record in a database file. */
+static int dprecsize(int *head){
+  assert(head);
+  return DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] + head[DP_RHIPSIZ];
+}
+
+
+/* Read the header of a record.
+   `depot' specifies a database handle.
+   `off' specifies an offset of the database file.
+   `head' specifies a buffer for the header.
+   `ebuf' specifies the pointer to the entity buffer.
+   `eep' specifies the pointer to a variable to which whether ebuf was used is assigned.
+   The return value is true if successful, else, it is false. */
+static int dprechead(DEPOT *depot, int off, int *head, char *ebuf, int *eep){
+  assert(depot && off >= 0 && head);
+  if(off > depot->fsiz){
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ebuf){
+    *eep = FALSE;
+    if(off < depot->fsiz - DP_ENTBUFSIZ){
+      *eep = TRUE;
+      if(!dpseekread(depot->fd, off, ebuf, DP_ENTBUFSIZ)) return FALSE;
+      memcpy(head, ebuf, DP_RHNUM * sizeof(int));
+      if(head[DP_RHIKSIZ] < 0 || head[DP_RHIVSIZ] < 0 || head[DP_RHIPSIZ] < 0 ||
+         head[DP_RHILEFT] < 0 || head[DP_RHIRIGHT] < 0){
+        dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+        return FALSE;
+      }
+      return TRUE;
+    }
+  }
+  if(!dpseekread(depot->fd, off, head, DP_RHNUM * sizeof(int))) return FALSE;
+  if(head[DP_RHIKSIZ] < 0 || head[DP_RHIVSIZ] < 0 || head[DP_RHIPSIZ] < 0 ||
+     head[DP_RHILEFT] < 0 || head[DP_RHIRIGHT] < 0){
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Read the entitiy of the key of a record.
+   `depot' specifies a database handle.
+   `off' specifies an offset of the database file.
+   `head' specifies the header of a record.
+   The return value is a key data whose region is allocated by `malloc', or NULL on failure. */
+static char *dpreckey(DEPOT *depot, int off, int *head){
+  char *kbuf;
+  int ksiz;
+  assert(depot && off >= 0);
+  ksiz = head[DP_RHIKSIZ];
+  if(!(kbuf = malloc(ksiz + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!dpseekread(depot->fd, off + DP_RHNUM * sizeof(int), kbuf, ksiz)){
+    free(kbuf);
+    return NULL;
+  }
+  kbuf[ksiz] = '\0';
+  return kbuf;
+}
+
+
+/* Read the entitiy of the value of a record.
+   `depot' specifies a database handle.
+   `off' specifies an offset of the database file.
+   `head' specifies the header of a record.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  If it is negative, the size to read is unlimited.
+   The return value is a value data whose region is allocated by `malloc', or NULL on failure. */
+static char *dprecval(DEPOT *depot, int off, int *head, int start, int max){
+  char *vbuf;
+  int vsiz;
+  assert(depot && off >= 0 && start >= 0);
+  head[DP_RHIVSIZ] -= start;
+  if(max < 0){
+    vsiz = head[DP_RHIVSIZ];
+  } else {
+    vsiz = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
+  }
+  if(!(vbuf = malloc(vsiz + 1))){
+    dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!dpseekread(depot->fd, off + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + start, vbuf, vsiz)){
+    free(vbuf);
+    return NULL;
+  }
+  vbuf[vsiz] = '\0';
+  return vbuf;
+}
+
+
+/* Read the entitiy of the value of a record and write it into a given buffer.
+   `depot' specifies a database handle.
+   `off' specifies an offset of the database file.
+   `head' specifies the header of a record.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  It shuld be less than the size of the writing buffer.
+   If successful, the return value is the size of the written data, else, it is -1. */
+static int dprecvalwb(DEPOT *depot, int off, int *head, int start, int max, char *vbuf){
+  int vsiz;
+  assert(depot && off >= 0 && start >= 0 && max >= 0 && vbuf);
+  head[DP_RHIVSIZ] -= start;
+  vsiz = max < head[DP_RHIVSIZ] ? max : head[DP_RHIVSIZ];
+  if(!dpseekread(depot->fd, off + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + start, vbuf, vsiz))
+    return -1;
+  return vsiz;
+}
+
+
+/* Compare two keys.
+   `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 dpkeycmp(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);
+}
+
+
+/* Search for a record.
+   `depot' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region.
+   `hash' specifies the second hash value of the key.
+   `bip' specifies the pointer to the region to assign the index of the corresponding record.
+   `offp' specifies the pointer to the region to assign the last visited node in the hash chain,
+   or, -1 if the hash chain is empty.
+   `entp' specifies the offset of the last used joint, or, -1 if the hash chain is empty.
+   `head' specifies the pointer to the region to store the header of the last visited record in.
+   `ebuf' specifies the pointer to the entity buffer.
+   `eep' specifies the pointer to a variable to which whether ebuf was used is assigned.
+   `delhit' specifies whether a deleted record corresponds or not.
+   The return value is 0 if successful, 1 if there is no corresponding record, -1 on error. */
+static int dprecsearch(DEPOT *depot, const char *kbuf, int ksiz, int hash, int *bip, int *offp,
+                       int *entp, int *head, char *ebuf, int *eep, int delhit){
+  int off, entoff, thash, kcmp;
+  char stkey[DP_STKBUFSIZ], *tkey;
+  assert(depot && kbuf && ksiz >= 0 && hash >= 0 && bip && offp && entp && head && ebuf && eep);
+  DP_FIRSTHASH(thash, kbuf, ksiz);
+  *bip = thash % depot->bnum;
+  off = depot->buckets[*bip];
+  *offp = -1;
+  *entp = -1;
+  entoff = -1;
+  *eep = FALSE;
+  while(off != 0){
+    if(!dprechead(depot, off, head, ebuf, eep)) return -1;
+    thash = head[DP_RHIHASH];
+    if(hash > thash){
+      entoff = off + DP_RHILEFT * sizeof(int);
+      off = head[DP_RHILEFT];
+    } else if(hash < thash){
+      entoff = off + DP_RHIRIGHT * sizeof(int);
+      off = head[DP_RHIRIGHT];
+    } else {
+      if(*eep && DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] <= DP_ENTBUFSIZ){
+        kcmp = dpkeycmp(kbuf, ksiz, ebuf + (DP_RHNUM * sizeof(int)), head[DP_RHIKSIZ]);
+      } else if(head[DP_RHIKSIZ] > DP_STKBUFSIZ){
+        if(!(tkey = dpreckey(depot, off, head))) return -1;
+        kcmp = dpkeycmp(kbuf, ksiz, tkey, head[DP_RHIKSIZ]);
+        free(tkey);
+      } else {
+        if(!dpseekread(depot->fd, off + DP_RHNUM * sizeof(int), stkey, head[DP_RHIKSIZ]))
+          return -1;
+        kcmp = dpkeycmp(kbuf, ksiz, stkey, head[DP_RHIKSIZ]);
+      }
+      if(kcmp > 0){
+        entoff = off + DP_RHILEFT * sizeof(int);
+        off = head[DP_RHILEFT];
+      } else if(kcmp < 0){
+        entoff = off + DP_RHIRIGHT * sizeof(int);
+        off = head[DP_RHIRIGHT];
+      } else {
+        if(!delhit && (head[DP_RHIFLAGS] & DP_RECFDEL)){
+          entoff = off + DP_RHILEFT * sizeof(int);
+          off = head[DP_RHILEFT];
+        } else {
+          *offp = off;
+          *entp = entoff;
+          return 0;
+        }
+      }
+    }
+  }
+  *offp = off;
+  *entp = entoff;
+  return 1;
+}
+
+
+/* Overwrite a record.
+   `depot' specifies a database handle.
+   `off' specifies the offset of the database file.
+   `rsiz' specifies the size of the existing record.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region.
+   `hash' specifies the second hash value of the key.
+   `left' specifies the offset of the left child.
+   `right' specifies the offset of the right child.
+   The return value is true if successful, or, false on failure. */
+static int dprecrewrite(DEPOT *depot, int off, int rsiz, const char *kbuf, int ksiz,
+                        const char *vbuf, int vsiz, int hash, int left, int right){
+  char ebuf[DP_WRTBUFSIZ];
+  int i, head[DP_RHNUM], asiz, hoff, koff, voff, mi, min, size;
+  assert(depot && off >= 1 && rsiz > 0 && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  head[DP_RHIFLAGS] = 0;
+  head[DP_RHIHASH] = hash;
+  head[DP_RHIKSIZ] = ksiz;
+  head[DP_RHIVSIZ] = vsiz;
+  head[DP_RHIPSIZ] = rsiz - sizeof(head) - ksiz - vsiz;
+  head[DP_RHILEFT] = left;
+  head[DP_RHIRIGHT] = right;
+  asiz = sizeof(head) + ksiz + vsiz;
+  if(depot->fbpsiz > DP_FBPOOLSIZ * 4 && head[DP_RHIPSIZ] > asiz){
+    rsiz = (head[DP_RHIPSIZ] - asiz) / 2 + asiz;
+    head[DP_RHIPSIZ] -= rsiz;
+  } else {
+    rsiz = 0;
+  }
+  if(asiz <= DP_WRTBUFSIZ){
+    memcpy(ebuf, head, sizeof(head));
+    memcpy(ebuf + sizeof(head), kbuf, ksiz);
+    memcpy(ebuf + sizeof(head) + ksiz, vbuf, vsiz);
+    if(!dpseekwrite(depot->fd, off, ebuf, asiz)) return FALSE;
+  } else {
+    hoff = off;
+    koff = hoff + sizeof(head);
+    voff = koff + ksiz;
+    if(!dpseekwrite(depot->fd, hoff, head, sizeof(head)) ||
+       !dpseekwrite(depot->fd, koff, kbuf, ksiz) || !dpseekwrite(depot->fd, voff, vbuf, vsiz))
+      return FALSE;
+  }
+  if(rsiz > 0){
+    off += sizeof(head) + ksiz + vsiz + head[DP_RHIPSIZ];
+    head[DP_RHIFLAGS] = DP_RECFDEL | DP_RECFREUSE;
+    head[DP_RHIHASH] = hash;
+    head[DP_RHIKSIZ] = ksiz;
+    head[DP_RHIVSIZ] = vsiz;
+    head[DP_RHIPSIZ] = rsiz - sizeof(head) - ksiz - vsiz;
+    head[DP_RHILEFT] = 0;
+    head[DP_RHIRIGHT] = 0;
+    if(!dpseekwrite(depot->fd, off, head, sizeof(head))) return FALSE;
+    size = dprecsize(head);
+    mi = -1;
+    min = -1;
+    for(i = 0; i < depot->fbpsiz; i += 2){
+      if(depot->fbpool[i] == -1){
+        depot->fbpool[i] = off;
+        depot->fbpool[i+1] = size;
+        dpfbpoolcoal(depot);
+        mi = -1;
+        break;
+      }
+      if(mi == -1 || depot->fbpool[i+1] < min){
+        mi = i;
+        min = depot->fbpool[i+1];
+      }
+    }
+    if(mi >= 0 && size > min){
+      depot->fbpool[mi] = off;
+      depot->fbpool[mi+1] = size;
+      dpfbpoolcoal(depot);
+    }
+  }
+  return TRUE;
+}
+
+
+/* Write a record at the end of a database file.
+   `depot' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region.
+   `hash' specifies the second hash value of the key.
+   `left' specifies the offset of the left child.
+   `right' specifies the offset of the right child.
+   The return value is the offset of the record, or, -1 on failure. */
+static int dprecappend(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+                       int hash, int left, int right){
+  char ebuf[DP_WRTBUFSIZ], *hbuf;
+  int head[DP_RHNUM], asiz, psiz, off;
+  assert(depot && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  psiz = dppadsize(depot, ksiz, vsiz);
+  head[DP_RHIFLAGS] = 0;
+  head[DP_RHIHASH] = hash;
+  head[DP_RHIKSIZ] = ksiz;
+  head[DP_RHIVSIZ] = vsiz;
+  head[DP_RHIPSIZ] = psiz;
+  head[DP_RHILEFT] = left;
+  head[DP_RHIRIGHT] = right;
+  asiz = sizeof(head) + ksiz + vsiz + psiz;
+  off = depot->fsiz;
+  if(asiz <= DP_WRTBUFSIZ){
+    memcpy(ebuf, head, sizeof(head));
+    memcpy(ebuf + sizeof(head), kbuf, ksiz);
+    memcpy(ebuf + sizeof(head) + ksiz, vbuf, vsiz);
+    memset(ebuf + sizeof(head) + ksiz + vsiz, 0, psiz);
+    if(!dpseekwrite(depot->fd, off, ebuf, asiz)) return -1;
+  } else {
+    if(!(hbuf = malloc(asiz))){
+      dpecodeset(DP_EALLOC, __FILE__, __LINE__);
+      return -1;
+    }
+    memcpy(hbuf, head, sizeof(head));
+    memcpy(hbuf + sizeof(head), kbuf, ksiz);
+    memcpy(hbuf + sizeof(head) + ksiz, vbuf, vsiz);
+    memset(hbuf + sizeof(head) + ksiz + vsiz, 0, psiz);
+    if(!dpseekwrite(depot->fd, off, hbuf, asiz)){
+      free(hbuf);
+      return -1;
+    }
+    free(hbuf);
+  }
+  depot->fsiz += asiz;
+  return off;
+}
+
+
+/* Overwrite the value of a record.
+   `depot' specifies a database handle.
+   `off' specifies the offset of the database file.
+   `head' specifies the header of the record.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region.
+   `cat' specifies whether it is concatenate mode or not.
+   The return value is true if successful, or, false on failure. */
+static int dprecover(DEPOT *depot, int off, int *head, const char *vbuf, int vsiz, int cat){
+  int i, hsiz, hoff, voff;
+  assert(depot && off >= 0 && head && vbuf && vsiz >= 0);
+  for(i = 0; i < depot->fbpsiz; i += 2){
+    if(depot->fbpool[i] == off){
+      depot->fbpool[i] = -1;
+      depot->fbpool[i+1] = -1;
+      break;
+    }
+  }
+  if(cat){
+    head[DP_RHIFLAGS] = 0;
+    head[DP_RHIPSIZ] -= vsiz;
+    head[DP_RHIVSIZ] += vsiz;
+    hsiz = DP_RHNUM * sizeof(int);
+    hoff = off;
+    voff = hoff + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ] + head[DP_RHIVSIZ] - vsiz;
+  } else {
+    head[DP_RHIFLAGS] = 0;
+    head[DP_RHIPSIZ] += head[DP_RHIVSIZ] - vsiz;
+    head[DP_RHIVSIZ] = vsiz;
+    hsiz = DP_RHNUM * sizeof(int);
+    hoff = off;
+    voff = hoff + DP_RHNUM * sizeof(int) + head[DP_RHIKSIZ];
+  }
+  if(!dpseekwrite(depot->fd, hoff, head, hsiz) ||
+     !dpseekwrite(depot->fd, voff, vbuf, vsiz)) return FALSE;
+  return TRUE;
+}
+
+
+/* Delete a record.
+   `depot' specifies a database handle.
+   `off' specifies the offset of the database file.
+   `head' specifies the header of the record.
+   `reusable' specifies whether the region is reusable or not.
+   The return value is true if successful, or, false on failure. */
+static int dprecdelete(DEPOT *depot, int off, int *head, int reusable){
+  int i, mi, min, size;
+  assert(depot && off >= 0 && head);
+  if(reusable){
+    size = dprecsize(head);
+    mi = -1;
+    min = -1;
+    for(i = 0; i < depot->fbpsiz; i += 2){
+      if(depot->fbpool[i] == -1){
+        depot->fbpool[i] = off;
+        depot->fbpool[i+1] = size;
+        dpfbpoolcoal(depot);
+        mi = -1;
+        break;
+      }
+      if(mi == -1 || depot->fbpool[i+1] < min){
+        mi = i;
+        min = depot->fbpool[i+1];
+      }
+    }
+    if(mi >= 0 && size > min){
+      depot->fbpool[mi] = off;
+      depot->fbpool[mi+1] = size;
+      dpfbpoolcoal(depot);
+    }
+  }
+  return dpseekwritenum(depot->fd, off + DP_RHIFLAGS * sizeof(int),
+                        DP_RECFDEL | (reusable ? DP_RECFREUSE : 0));
+}
+
+
+/* Make contiguous records of the free block pool coalesce.
+   `depot' specifies a database handle. */
+static void dpfbpoolcoal(DEPOT *depot){
+  int i;
+  assert(depot);
+  if(depot->fbpinc++ <= depot->fbpsiz / 4) return;
+  depot->fbpinc = 0;
+  qsort(depot->fbpool, depot->fbpsiz / 2, sizeof(int) * 2, dpfbpoolcmp);
+  for(i = 2; i < depot->fbpsiz; i += 2){
+    if(depot->fbpool[i-2] > 0 &&
+       depot->fbpool[i-2] + depot->fbpool[i-1] - depot->fbpool[i] == 0){
+      depot->fbpool[i] = depot->fbpool[i-2];
+      depot->fbpool[i+1] += depot->fbpool[i-1];
+      depot->fbpool[i-2] = -1;
+      depot->fbpool[i-1] = -1;
+    }
+  }
+}
+
+
+/* Compare two records of the free block pool.
+   `a' specifies the pointer to one record.
+   `b' specifies the pointer to the other record.
+   The return value is 0 if two equals, positive if the formar is big, else, negative. */
+static int dpfbpoolcmp(const void *a, const void *b){
+  assert(a && b);
+  return *(int *)a - *(int *)b;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/depot.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/depot.h
===================================================================
--- box/trunk/qdbm/depot.h	                        (rev 0)
+++ box/trunk/qdbm/depot.h	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,492 @@
+/*************************************************************************************************
+ * The basic API of QDBM
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _DEPOT_H                         /* duplication check */
+#define _DEPOT_H
+
+#if defined(__cplusplus)                 /* export for C++ */
+extern "C" {
+#endif
+
+
+#include <stdlib.h>
+#include <time.h>
+
+
+#if defined(_MSC_VER) && !defined(QDBM_INTERNAL) && !defined(QDBM_STATIC)
+#define MYEXTERN extern __declspec(dllimport)
+#else
+#define MYEXTERN extern
+#endif
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of structure for a database handle */
+  char *name;                            /* name of the database file */
+  int wmode;                             /* whether to be writable */
+  int inode;                             /* inode of the database file */
+  time_t mtime;                          /* last modified time of the database */
+  int fd;                                /* file descriptor of the database file */
+  int fsiz;                              /* size of the database file */
+  char *map;                             /* pointer to the mapped memory */
+  int msiz;                              /* size of the mapped memory */
+  int *buckets;                          /* pointer to the bucket array */
+  int bnum;                              /* number of the bucket array */
+  int rnum;                              /* number of records */
+  int fatal;                             /* whether a fatal error occured */
+  int ioff;                              /* offset of the iterator */
+  int *fbpool;                           /* free block pool */
+  int fbpsiz;                            /* size of the free block pool */
+  int fbpinc;                            /* incrementor of update of the free block pool */
+  int align;                             /* basic size of alignment */
+} DEPOT;
+
+enum {                                   /* enumeration for error codes */
+  DP_ENOERR,                             /* no error */
+  DP_EFATAL,                             /* with fatal error */
+  DP_EMODE,                              /* invalid mode */
+  DP_EBROKEN,                            /* broken database file */
+  DP_EKEEP,                              /* existing record */
+  DP_ENOITEM,                            /* no item found */
+  DP_EALLOC,                             /* memory allocation error */
+  DP_EMAP,                               /* memory mapping error */
+  DP_EOPEN,                              /* open error */
+  DP_ECLOSE,                             /* close error */
+  DP_ETRUNC,                             /* trunc error */
+  DP_ESYNC,                              /* sync error */
+  DP_ESTAT,                              /* stat error */
+  DP_ESEEK,                              /* seek error */
+  DP_EREAD,                              /* read error */
+  DP_EWRITE,                             /* write error */
+  DP_ELOCK,                              /* lock error */
+  DP_EUNLINK,                            /* unlink error */
+  DP_EMKDIR,                             /* mkdir error */
+  DP_ERMDIR,                             /* rmdir error */
+  DP_EMISC                               /* miscellaneous error */
+};
+
+enum {                                   /* enumeration for open modes */
+  DP_OREADER = 1 << 0,                   /* open as a reader */
+  DP_OWRITER = 1 << 1,                   /* open as a writer */
+  DP_OCREAT = 1 << 2,                    /* a writer creating */
+  DP_OTRUNC = 1 << 3,                    /* a writer truncating */
+  DP_ONOLCK = 1 << 4,                    /* open without locking */
+  DP_OLCKNB = 1 << 5,                    /* lock without blocking */
+  DP_OSPARSE = 1 << 6                    /* create as a sparse file */
+};
+
+enum {                                   /* enumeration for write modes */
+  DP_DOVER,                              /* overwrite an existing value */
+  DP_DKEEP,                              /* keep an existing value */
+  DP_DCAT                                /* concatenate values */
+};
+
+
+/* String containing the version information. */
+MYEXTERN const char *dpversion;
+
+
+/* Last happened error code. */
+#define dpecode        (*dpecodeptr())
+
+
+/* Get a message string corresponding to an error code.
+   `ecode' specifies an error code.
+   The return value is the message string of the error code. The region of the return value
+   is not writable. */
+const char *dperrmsg(int ecode);
+
+
+/* Get a database handle.
+   `name' specifies the name of a database file.
+   `omode' specifies the connection mode: `DP_OWRITER' as a writer, `DP_OREADER' as a reader.
+   If the mode is `DP_OWRITER', the following may be added by bitwise or: `DP_OCREAT', which
+   means it creates a new database if not exist, `DP_OTRUNC', which means it creates a new
+   database regardless if one exists.  Both of `DP_OREADER' and `DP_OWRITER' can be added to by
+   bitwise or: `DP_ONOLCK', which means it opens a database file without file locking, or
+   `DP_OLCKNB', which means locking is performed without blocking.  `DP_OCREAT' can be added to
+   by bitwise or: `DP_OSPARSE', which means it creates a database file as a sparse file.
+   `bnum' specifies the number of elements of the bucket array.  If it is not more than 0,
+   the default value is specified.  The size of a bucket array is determined on creating,
+   and can not be changed except for by optimization of the database.  Suggested size of a
+   bucket array is about from 0.5 to 4 times of the number of all records to store.
+   The return value is the database handle or `NULL' if it is not successful.
+   While connecting as a writer, an exclusive lock is invoked to the database file.
+   While connecting as a reader, a shared lock is invoked to the database file.  The thread
+   blocks until the lock is achieved.  If `DP_ONOLCK' is used, the application is responsible
+   for exclusion control. */
+DEPOT *dpopen(const char *name, int omode, int bnum);
+
+
+/* Close a database handle.
+   `depot' specifies a database handle.
+   If successful, the return value is true, else, it is false.
+   Because the region of a closed handle is released, it becomes impossible to use the handle.
+   Updating a database is assured to be written when the handle is closed.  If a writer opens
+   a database but does not close it appropriately, the database will be broken. */
+int dpclose(DEPOT *depot);
+
+
+/* Store a record.
+   `depot' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region of the value.  If it is negative, the size is
+   assigned with `strlen(vbuf)'.
+   `dmode' specifies behavior when the key overlaps, by the following values: `DP_DOVER',
+   which means the specified value overwrites the existing one, `DP_DKEEP', which means the
+   existing value is kept, `DP_DCAT', which means the specified value is concatenated at the
+   end of the existing value.
+   If successful, the return value is true, else, it is false. */
+int dpput(DEPOT *depot, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode);
+
+
+/* Delete a record.
+   `depot' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is true, else, it is false.  False is returned when no
+   record corresponds to the specified key. */
+int dpout(DEPOT *depot, const char *kbuf, int ksiz);
+
+
+/* Retrieve a record.
+   `depot' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  If it is negative, the size to read is unlimited.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the specified key or the size of the value of the corresponding record is less than `start'.
+   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 if it
+   is no longer in use. */
+char *dpget(DEPOT *depot, const char *kbuf, int ksiz, int start, int max, int *sp);
+
+
+/* Retrieve a record and write the value into a buffer.
+   `depot' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `start' specifies the offset address of the beginning of the region of the value to be read.
+   `max' specifies the max size to be read.  It shuld be equal to or less than the size of the
+   writing buffer.
+   `vbuf' specifies the pointer to a buffer into which the value of the corresponding record is
+   written.
+   If successful, the return value is the size of the written data, else, it is -1.  -1 is
+   returned when no record corresponds to the specified key or the size of the value of the
+   corresponding record is less than `start'.
+   Note that no additional zero code is appended at the end of the region of the writing buffer. */
+int dpgetwb(DEPOT *depot, const char *kbuf, int ksiz, int start, int max, char *vbuf);
+
+
+/* Get the size of the value of a record.
+   `depot' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1.
+   Because this function does not read the entity of a record, it is faster than `dpget'. */
+int dpvsiz(DEPOT *depot, const char *kbuf, int ksiz);
+
+
+/* Initialize the iterator of a database handle.
+   `depot' specifies a database handle.
+   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. */
+int dpiterinit(DEPOT *depot);
+
+
+/* Get the next key of the iterator.
+   `depot' specifies a database handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   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 if
+   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 *dpiternext(DEPOT *depot, int *sp);
+
+
+/* Set alignment of a database handle.
+   `depot' specifies a database handle connected as a writer.
+   `align' specifies the size of alignment.
+   If successful, the return value is true, else, it is false.
+   If alignment is set to a database, the efficiency of overwriting values is improved.
+   The size of alignment is suggested to be average size of the values of the records to be
+   stored.  If alignment is positive, padding whose size is multiple number of the alignment
+   is placed.  If alignment is negative, as `vsiz' is the size of a value, the size of padding
+   is calculated with `(vsiz / pow(2, abs(align) - 1))'.  Because alignment setting is not
+   saved in a database, you should specify alignment every opening a database. */
+int dpsetalign(DEPOT *depot, int align);
+
+
+/* Set the size of the free block pool of a database handle.
+   `depot' specifies a database handle connected as a writer.
+   `size' specifies the size of the free block pool of a database.
+   If successful, the return value is true, else, it is false.
+   The default size of the free block pool is 16.  If the size is greater, the space efficiency
+   of overwriting values is improved with the time efficiency sacrificed. */
+int dpsetfbpsiz(DEPOT *depot, int size);
+
+
+/* Synchronize updating contents with the file and the device.
+   `depot' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.
+   This function is useful when another process uses the connected database file. */
+int dpsync(DEPOT *depot);
+
+
+/* Optimize a database.
+   `depot' specifies a database handle connected as a writer.
+   `bnum' specifies the number of the elements of the bucket array.  If it is not more than 0,
+   the default value is specified.
+   If successful, the return value is true, else, it is false.
+   In an alternating succession of deleting and storing with overwrite or concatenate,
+   dispensable regions accumulate.  This function is useful to do away with them. */
+int dpoptimize(DEPOT *depot, int bnum);
+
+
+/* Get the name of a database.
+   `depot' specifies a database handle.
+   If successful, the return value is the pointer to the region of the name of the database,
+   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 if it is no longer in use. */
+char *dpname(DEPOT *depot);
+
+
+/* Get the size of a database file.
+   `depot' specifies a database handle.
+   If successful, the return value is the size of the database file, else, it is -1. */
+int dpfsiz(DEPOT *depot);
+
+
+/* Get the number of the elements of the bucket array.
+   `depot' specifies a database handle.
+   If successful, the return value is the number of the elements of the bucket array, else, it
+   is -1. */
+int dpbnum(DEPOT *depot);
+
+
+/* Get the number of the used elements of the bucket array.
+   `depot' specifies a database handle.
+   If successful, the return value is the number of the used elements of the bucket array,
+   else, it is -1.
+   This function is inefficient because it accesses all elements of the bucket array. */
+int dpbusenum(DEPOT *depot);
+
+
+/* Get the number of the records stored in a database.
+   `depot' specifies a database handle.
+   If successful, the return value is the number of the records stored in the database, else,
+   it is -1. */
+int dprnum(DEPOT *depot);
+
+
+/* Check whether a database handle is a writer or not.
+   `depot' specifies a database handle.
+   The return value is true if the handle is a writer, false if not. */
+int dpwritable(DEPOT *depot);
+
+
+/* Check whether a database has a fatal error or not.
+   `depot' specifies a database handle.
+   The return value is true if the database has a fatal error, false if not. */
+int dpfatalerror(DEPOT *depot);
+
+
+/* Get the inode number of a database file.
+   `depot' specifies a database handle.
+   The return value is the inode number of the database file. */
+int dpinode(DEPOT *depot);
+
+
+/* Get the last modified time of a database.
+   `depot' specifies a database handle.
+   The return value is the last modified time of the database. */
+time_t dpmtime(DEPOT *depot);
+
+
+/* Get the file descriptor of a database file.
+   `depot' specifies a database handle.
+   The return value is the file descriptor of the database file.
+   Handling the file descriptor of a database file directly is not suggested. */
+int dpfdesc(DEPOT *depot);
+
+
+/* Remove a database file.
+   `name' specifies the name of a database file.
+   If successful, the return value is true, else, it is false. */
+int dpremove(const char *name);
+
+
+/* Repair a broken database file.
+   `name' specifies the name of a database file.
+   If successful, the return value is true, else, it is false.
+   There is no guarantee that all records in a repaired database file correspond to the original
+   or expected state. */
+int dprepair(const char *name);
+
+
+/* Dump all records as endian independent data.
+   `depot' specifies a database handle.
+   `name' specifies the name of an output file.
+   If successful, the return value is true, else, it is false. */
+int dpexportdb(DEPOT *depot, const char *name);
+
+
+/* Load all records from endian independent data.
+   `depot' specifies a database handle connected as a writer.  The database of the handle must
+   be empty.
+   `name' specifies the name of an input file.
+   If successful, the return value is true, else, it is false. */
+int dpimportdb(DEPOT *depot, const char *name);
+
+
+/* Retrieve a record directly from a database file.
+   `name' specifies the name of a database file.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the specified key.
+   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 if it
+   is no longer in use.  Although this function can be used even while the database file is
+   locked by another process, it is not assured that recent updated is reflected. */
+char *dpsnaffle(const char *name, const char *kbuf, int ksiz, int *sp);
+
+
+/* Hash function used inside Depot.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   The return value is the hash value of 31 bits length computed from the key.
+   This function is useful when an application calculates the state of the inside bucket array. */
+int dpinnerhash(const char *kbuf, int ksiz);
+
+
+/* Hash function which is independent from the hash functions used inside Depot.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   The return value is the hash value of 31 bits length computed from the key.
+   This function is useful when an application uses its own hash algorithm outside Depot. */
+int dpouterhash(const char *kbuf, int ksiz);
+
+
+/* Get a natural prime number not less than a number.
+   `num' specified a natural number.
+   The return value is a natural prime number not less than the specified number.
+   This function is useful when an application determines the size of a bucket array of its
+   own hash algorithm. */
+int dpprimenum(int num);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+#define _QDBM_VERSION  "1.8.77"
+#define _QDBM_LIBVER   1413
+
+
+/* Name of the operating system. */
+MYEXTERN const char *dpsysname;
+
+
+/* File descriptor for debugging output. */
+MYEXTERN int dpdbgfd;
+
+
+/* Whether this build is reentrant. */
+MYEXTERN const int dpisreentrant;
+
+
+/* Set the last happened error code.
+   `ecode' specifies the error code.
+   `line' specifies the number of the line where the error happened. */
+void dpecodeset(int ecode, const char *file, int line);
+
+
+/* Get the pointer of the variable of the last happened error code.
+   The return value is the pointer of the variable. */
+int *dpecodeptr(void);
+
+
+/* Synchronize updating contents on memory.
+   `depot' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false. */
+int dpmemsync(DEPOT *depot);
+
+
+/* Synchronize updating contents on memory, not physically.
+   `depot' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false. */
+int dpmemflush(DEPOT *depot);
+
+
+/* Get flags of a database.
+   `depot' specifies a database handle.
+   The return value is the flags of a database. */
+int dpgetflags(DEPOT *depot);
+
+
+/* Set flags of a database.
+   `depot' specifies a database handle connected as a writer.
+   `flags' specifies flags to set.  Least ten bits are reserved for internal use.
+   If successful, the return value is true, else, it is false. */
+int dpsetflags(DEPOT *depot, int flags);
+
+
+
+#undef MYEXTERN
+
+#if defined(__cplusplus)                 /* export for C++ */
+}
+#endif
+
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/depot.h
___________________________________________________________________
Added: svn:mime-type
   + text/x-chdr
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/dpmgr.c
===================================================================
--- box/trunk/qdbm/dpmgr.c	                        (rev 0)
+++ box/trunk/qdbm/dpmgr.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,916 @@
+/*************************************************************************************************
+ * Utility for debugging Depot and its applications
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <cabin.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define ALIGNSIZ       32                /* basic size of alignment */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+char *hextoobj(const char *str, int *sp);
+char *dectoiobj(const char *str, int *sp);
+int runcreate(int argc, char **argv);
+int runput(int argc, char **argv);
+int runout(int argc, char **argv);
+int runget(int argc, char **argv);
+int runlist(int argc, char **argv);
+int runoptimize(int argc, char **argv);
+int runinform(int argc, char **argv);
+int runremove(int argc, char **argv);
+int runrepair(int argc, char **argv);
+int runexportdb(int argc, char **argv);
+int runimportdb(int argc, char **argv);
+int runsnaffle(int argc, char **argv);
+void pdperror(const char *name);
+void printobj(const char *obj, int size);
+void printobjhex(const char *obj, int size);
+int docreate(const char *name, int bnum, int sparse);
+int doput(const char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+          int dmode, int align);
+int doout(const char *name, const char *kbuf, int ksiz);
+int doget(const char *name, int opts, const char *kbuf, int ksiz,
+          int start, int max, int ox, int nb);
+int dolist(const char *name, int opts, int kb, int vb, int ox);
+int dooptimize(const char *name, int bnum, int align);
+int doinform(const char *name, int opts);
+int doremove(const char *name);
+int dorepair(const char *name);
+int doexportdb(const char *name, const char *file);
+int doimportdb(const char *name, const char *file, int bnum);
+int dosnaffle(const char *name, const char *kbuf, int ksiz, int ox, int nb);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  char *env;
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if((env = getenv("QDBMDBGFD")) != NULL) dpdbgfd = atoi(env);
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "create")){
+    rv = runcreate(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], "inform")){
+    rv = runinform(argc, argv);
+  } else if(!strcmp(argv[1], "remove")){
+    rv = runremove(argc, argv);
+  } else if(!strcmp(argv[1], "repair")){
+    rv = runrepair(argc, argv);
+  } else if(!strcmp(argv[1], "exportdb")){
+    rv = runexportdb(argc, argv);
+  } else if(!strcmp(argv[1], "importdb")){
+    rv = runimportdb(argc, argv);
+  } else if(!strcmp(argv[1], "snaffle")){
+    rv = runsnaffle(argc, argv);
+  } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){
+    printf("Powered by QDBM version %s on %s%s\n",
+           dpversion, dpsysname, dpisreentrant ? " (reentrant)" : "");
+    printf("Copyright (c) 2000-2007 Mikio Hirabayashi\n");
+    rv = 0;
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: administration utility for Depot\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s create [-s] [-bnum num] name\n", progname);
+  fprintf(stderr, "  %s put [-kx|-ki] [-vx|-vi|-vf] [-keep|-cat] [-na] name key val\n",
+          progname);
+  fprintf(stderr, "  %s out [-kx|-ki] name key\n", progname);
+  fprintf(stderr, "  %s get [-nl] [-kx|-ki] [-start num] [-max num] [-ox] [-n] name key\n",
+          progname);
+  fprintf(stderr, "  %s list [-nl] [-k|-v] [-ox] name\n", progname);
+  fprintf(stderr, "  %s optimize [-bnum num] [-na] name\n", progname);
+  fprintf(stderr, "  %s inform [-nl] name\n", progname);
+  fprintf(stderr, "  %s remove name\n", progname);
+  fprintf(stderr, "  %s repair name\n", progname);
+  fprintf(stderr, "  %s exportdb name file\n", progname);
+  fprintf(stderr, "  %s importdb [-bnum num] name file\n", progname);
+  fprintf(stderr, "  %s snaffle [-kx|-ki] [-ox] [-n] name key\n", progname);
+  fprintf(stderr, "  %s version\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* create a binary object from a hexadecimal string */
+char *hextoobj(const char *str, int *sp){
+  char *buf, mbuf[3];
+  int len, i, j;
+  len = strlen(str);
+  if(!(buf = malloc(len + 1))) return NULL;
+  j = 0;
+  for(i = 0; i < len; i += 2){
+    while(strchr(" \n\r\t\f\v", str[i])){
+      i++;
+    }
+    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;
+}
+
+
+/* create a integer object from a decimal string */
+char *dectoiobj(const char *str, int *sp){
+  char *buf;
+  int num;
+  num = atoi(str);
+  if(!(buf = malloc(sizeof(int)))) return NULL;
+  *(int *)buf = num;
+  *sp = sizeof(int);
+  return buf;
+}
+
+
+/* parse arguments of create command */
+int runcreate(int argc, char **argv){
+  char *name;
+  int i, sb, bnum, rv;
+  name = NULL;
+  sb = FALSE;
+  bnum = -1;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-s")){
+        sb = TRUE;
+      } else if(!strcmp(argv[i], "-bnum")){
+        if(++i >= argc) usage();
+        bnum = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = docreate(name, bnum, sb);
+  return rv;
+}
+
+
+/* parse arguments of put command */
+int runput(int argc, char **argv){
+  char *name, *key, *val, *kbuf, *vbuf;
+  int i, kx, ki, vx, vi, vf, align, ksiz, vsiz, rv;
+  int dmode;
+  name = NULL;
+  kx = FALSE;
+  ki = FALSE;
+  vx = FALSE;
+  vi = FALSE;
+  vf = FALSE;
+  align = ALIGNSIZ;
+  key = NULL;
+  val = NULL;
+  dmode = DP_DOVER;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else if(!strcmp(argv[i], "-vx")){
+        vx = TRUE;
+      } else if(!strcmp(argv[i], "-vi")){
+        vi = TRUE;
+      } else if(!strcmp(argv[i], "-vf")){
+        vf = TRUE;
+      } else if(!strcmp(argv[i], "-keep")){
+        dmode = DP_DKEEP;
+      } else if(!strcmp(argv[i], "-cat")){
+        dmode = DP_DCAT;
+      } else if(!strcmp(argv[i], "-na")){
+        align = 0;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else if(!val){
+      val = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key || !val) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(vx){
+    vbuf = hextoobj(val, &vsiz);
+  } else if(vi){
+    vbuf = dectoiobj(val, &vsiz);
+  } else if(vf){
+    vbuf = cbreadfile(val, &vsiz);
+  } else {
+    vbuf = cbmemdup(val, -1);
+    vsiz = -1;
+  }
+  if(kbuf && vbuf){
+    rv = doput(name, kbuf, ksiz, vbuf, vsiz, dmode, align);
+  } else {
+    if(vf){
+      fprintf(stderr, "%s: %s: cannot read\n", progname, val);
+    } else {
+      fprintf(stderr, "%s: out of memory\n", progname);
+    }
+    rv = 1;
+  }
+  free(kbuf);
+  free(vbuf);
+  return rv;
+}
+
+
+/* parse arguments of out command */
+int runout(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, kx, ki, ksiz, rv;
+  name = NULL;
+  kx = FALSE;
+  ki = FALSE;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(kbuf){
+    rv = doout(name, kbuf, ksiz);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of get command */
+int runget(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, opts, kx, ki, ox, nb, start, max, ksiz, rv;
+  name = NULL;
+  opts = 0;
+  kx = FALSE;
+  ki = FALSE;
+  ox = FALSE;
+  nb = FALSE;
+  start = 0;
+  max = -1;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        opts |= DP_ONOLCK;
+      } else if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else if(!strcmp(argv[i], "-n")){
+        nb = TRUE;
+      } else if(!strcmp(argv[i], "-start")){
+        if(++i >= argc) usage();
+        start = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-max")){
+        if(++i >= argc) usage();
+        max = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key || start < 0) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(kbuf){
+    rv = doget(name, opts, kbuf, ksiz, start, max, ox, nb);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+int runlist(int argc, char **argv){
+  char *name;
+  int i, opts, kb, vb, ox, rv;
+  name = NULL;
+  opts = 0;
+  kb = FALSE;
+  vb = FALSE;
+  ox = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        opts |= DP_ONOLCK;
+      } else if(!strcmp(argv[i], "-k")){
+        kb = TRUE;
+      } else if(!strcmp(argv[i], "-v")){
+        vb = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dolist(name, opts, kb, vb, ox);
+  return rv;
+}
+
+
+/* parse arguments of optimize command */
+int runoptimize(int argc, char **argv){
+  char *name;
+  int i, bnum, align, rv;
+  name = NULL;
+  bnum = -1;
+  align = ALIGNSIZ;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-bnum")){
+        if(++i >= argc) usage();
+        bnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-na")){
+        align = 0;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dooptimize(name, bnum, align);
+  return rv;
+}
+
+
+/* parse arguments of inform command */
+int runinform(int argc, char **argv){
+  char *name;
+  int i, opts, rv;
+  name = NULL;
+  opts = 0;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        opts |= DP_ONOLCK;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doinform(name, opts);
+  return rv;
+}
+
+
+/* parse arguments of remove command */
+int runremove(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doremove(name);
+  return rv;
+}
+
+
+/* parse arguments of repair command */
+int runrepair(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dorepair(name);
+  return rv;
+}
+
+
+/* parse arguments of exportdb command */
+int runexportdb(int argc, char **argv){
+  char *name, *file;
+  int i, rv;
+  name = NULL;
+  file = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !file) usage();
+  rv = doexportdb(name, file);
+  return rv;
+}
+
+
+/* parse arguments of importdb command */
+int runimportdb(int argc, char **argv){
+  char *name, *file;
+  int i, bnum, rv;
+  name = NULL;
+  file = NULL;
+  bnum = -1;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-bnum")){
+        if(++i >= argc) usage();
+        bnum = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !file) usage();
+  rv = doimportdb(name, file, bnum);
+  return rv;
+}
+
+
+/* parse arguments of snaffle command */
+int runsnaffle(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, kx, ki, ox, nb, start, max, ksiz, rv;
+  name = NULL;
+  kx = FALSE;
+  ki = FALSE;
+  ox = FALSE;
+  nb = FALSE;
+  start = 0;
+  max = -1;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else if(!strcmp(argv[i], "-n")){
+        nb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key || start < 0) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(kbuf){
+    rv = dosnaffle(name, kbuf, ksiz, ox, nb);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* print an object */
+void printobj(const char *obj, int size){
+  int i;
+  for(i = 0; i < size; i++){
+    putchar(obj[i]);
+  }
+}
+
+
+/* print an object as a hexadecimal string */
+void printobjhex(const char *obj, int size){
+  int i;
+  for(i = 0; i < size; i++){
+    printf("%s%02X", i > 0 ? " " : "", ((const unsigned char *)obj)[i]);
+  }
+}
+
+
+/* perform create command */
+int docreate(const char *name, int bnum, int sparse){
+  DEPOT *depot;
+  int omode;
+  omode = DP_OWRITER | DP_OCREAT | DP_OTRUNC | (sparse ? DP_OSPARSE : 0);
+  if(!(depot = dpopen(name, omode, bnum))){
+    pdperror(name);
+    return 1;
+  }
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform put command */
+int doput(const char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+          int dmode, int align){
+  DEPOT *depot;
+  if(!(depot = dpopen(name, DP_OWRITER, -1))){
+    pdperror(name);
+    return 1;
+  }
+  if(align > 0 && !dpsetalign(depot, align)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  if(!dpput(depot, kbuf, ksiz, vbuf, vsiz, dmode)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform out command */
+int doout(const char *name, const char *kbuf, int ksiz){
+  DEPOT *depot;
+  if(!(depot = dpopen(name, DP_OWRITER, -1))){
+    pdperror(name);
+    return 1;
+  }
+  if(!dpout(depot, kbuf, ksiz)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform get command */
+int doget(const char *name, int opts, const char *kbuf, int ksiz,
+          int start, int max, int ox, int nb){
+  DEPOT *depot;
+  char *vbuf;
+  int vsiz;
+  if(!(depot = dpopen(name, DP_OREADER | opts, -1))){
+    pdperror(name);
+    return 1;
+  }
+  if(!(vbuf = dpget(depot, kbuf, ksiz, start, max, &vsiz))){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  if(ox){
+    printobjhex(vbuf, vsiz);
+  } else {
+    printobj(vbuf, vsiz);
+  }
+  free(vbuf);
+  if(!nb) putchar('\n');
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform list command */
+int dolist(const char *name, int opts, int kb, int vb, int ox){
+  DEPOT *depot;
+  char *kbuf, *vbuf;
+  int ksiz, vsiz;
+  if(!(depot = dpopen(name, DP_OREADER | opts, -1))){
+    pdperror(name);
+    return 1;
+  }
+  dpiterinit(depot);
+  while((kbuf = dpiternext(depot, &ksiz)) != NULL){
+    if(!(vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz))){
+      pdperror(name);
+      free(kbuf);
+      break;
+    }
+    if(ox){
+      if(!vb) printobjhex(kbuf, ksiz);
+      if(!kb && !vb) putchar('\t');
+      if(!kb) printobjhex(vbuf, vsiz);
+    } else {
+      if(!vb) printobj(kbuf, ksiz);
+      if(!kb && !vb) putchar('\t');
+      if(!kb) printobj(vbuf, vsiz);
+    }
+    putchar('\n');
+    free(vbuf);
+    free(kbuf);
+  }
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform optimize command */
+int dooptimize(const char *name, int bnum, int align){
+  DEPOT *depot;
+  if(!(depot = dpopen(name, DP_OWRITER, -1))){
+    pdperror(name);
+    return 1;
+  }
+  if(align > 0 && !dpsetalign(depot, align)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  if(!dpoptimize(depot, bnum)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform inform command */
+int doinform(const char *name, int opts){
+  DEPOT *depot;
+  char *tmp;
+  if(!(depot = dpopen(name, DP_OREADER | opts, -1))){
+    pdperror(name);
+    return 1;
+  }
+  tmp = dpname(depot);
+  printf("name: %s\n", tmp ? tmp : "(null)");
+  free(tmp);
+  printf("file size: %d\n", dpfsiz(depot));
+  printf("all buckets: %d\n", dpbnum(depot));
+  printf("used buckets: %d\n", dpbusenum(depot));
+  printf("records: %d\n", dprnum(depot));
+  printf("inode number: %d\n", dpinode(depot));
+  printf("modified time: %.0f\n", (double)dpmtime(depot));
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform remove command */
+int doremove(const char *name){
+  if(!dpremove(name)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform repair command */
+int dorepair(const char *name){
+  if(!dprepair(name)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform exportdb command */
+int doexportdb(const char *name, const char *file){
+  DEPOT *depot;
+  if(!(depot = dpopen(name, DP_OREADER, -1))){
+    pdperror(name);
+    return 1;
+  }
+  if(!dpexportdb(depot, file)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform importdb command */
+int doimportdb(const char *name, const char *file, int bnum){
+  DEPOT *depot;
+  if(!(depot = dpopen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, bnum))){
+    pdperror(name);
+    return 1;
+  }
+  if(!dpimportdb(depot, file)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform snaffle command */
+int dosnaffle(const char *name, const char *kbuf, int ksiz, int ox, int nb){
+  char *vbuf;
+  int vsiz;
+  if(!(vbuf = dpsnaffle(name, kbuf, ksiz, &vsiz))){
+    pdperror(name);
+    return 1;
+  }
+  if(ox){
+    printobjhex(vbuf, vsiz);
+  } else {
+    printobj(vbuf, vsiz);
+  }
+  free(vbuf);
+  if(!nb) putchar('\n');
+  return 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/dpmgr.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/dptest.c
===================================================================
--- box/trunk/qdbm/dptest.c	                        (rev 0)
+++ box/trunk/qdbm/dptest.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,836 @@
+/*************************************************************************************************
+ * Test cases of Depot
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <cabin.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <time.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define RECBUFSIZ      32                /* buffer for records */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+int runwrite(int argc, char **argv);
+int runread(int argc, char **argv);
+int runrcat(int argc, char **argv);
+int runcombo(int argc, char **argv);
+int runwicked(int argc, char **argv);
+int printfflush(const char *format, ...);
+void pdperror(const char *name);
+int myrand(void);
+int dowrite(const char *name, int rnum, int bnum, int sparse);
+int doread(const char *name, int wb);
+int dorcat(const char *name, int rnum, int bnum, int pnum, int align, int fbpsiz, int cb);
+int docombo(const char *name);
+int dowicked(const char *name, int rnum, int cb);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  char *env;
+  int rv;
+  cbstdiobin();
+  if((env = getenv("QDBMDBGFD")) != NULL) dpdbgfd = atoi(env);
+  progname = argv[0];
+  if(argc < 2) usage();
+  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], "rcat")){
+    rv = runrcat(argc, argv);
+  } else if(!strcmp(argv[1], "combo")){
+    rv = runcombo(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: test cases for Depot\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write [-s] name rnum bnum\n", progname);
+  fprintf(stderr, "  %s read [-wb] name\n", progname);
+  fprintf(stderr, "  %s rcat [-c] name rnum bnum pnum align fbpsiz\n", progname);
+  fprintf(stderr, "  %s combo name\n", progname);
+  fprintf(stderr, "  %s wicked [-c] name rnum\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of write command */
+int runwrite(int argc, char **argv){
+  char *name, *rstr, *bstr;
+  int i, rnum, bnum, sb, rv;
+  name = NULL;
+  rstr = NULL;
+  bstr = NULL;
+  rnum = 0;
+  bnum = 0;
+  sb = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-s")){
+        sb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr || !bstr) usage();
+  rnum = atoi(rstr);
+  bnum = atoi(bstr);
+  if(rnum < 1 || bnum < 1) usage();
+  rv = dowrite(name, rnum, bnum, sb);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+int runread(int argc, char **argv){
+  char *name;
+  int i, wb, rv;
+  name = NULL;
+  wb = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-wb")){
+        wb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doread(name, wb);
+  return rv;
+}
+
+
+/* parse arguments of rcat command */
+int runrcat(int argc, char **argv){
+  char *name, *rstr, *bstr, *pstr, *astr, *fstr;
+  int i, rnum, bnum, pnum, align, fbpsiz, cb, rv;
+  name = NULL;
+  rstr = NULL;
+  bstr = NULL;
+  pstr = NULL;
+  astr = NULL;
+  fstr = NULL;
+  rnum = 0;
+  bnum = 0;
+  pnum = 0;
+  align = 0;
+  fbpsiz = 0;
+  cb = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-c")){
+        cb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!bstr){
+      bstr = argv[i];
+    } else if(!pstr){
+      pstr = argv[i];
+    } else if(!astr){
+      astr = argv[i];
+    } else if(!fstr){
+      fstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr || !bstr || !pstr || !astr || !fstr) usage();
+  rnum = atoi(rstr);
+  bnum = atoi(bstr);
+  pnum = atoi(pstr);
+  align = atoi(astr);
+  fbpsiz= atoi(fstr);
+  if(rnum < 1 || bnum < 1 || pnum < 1 || fbpsiz < 0) usage();
+  rv = dorcat(name, rnum, bnum, pnum, align, fbpsiz, cb);
+  return rv;
+}
+
+
+/* parse arguments of combo command */
+int runcombo(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = docombo(name);
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+int runwicked(int argc, char **argv){
+  char *name, *rstr;
+  int i, rnum, cb, rv;
+  name = NULL;
+  rstr = NULL;
+  rnum = 0;
+  cb = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-c")){
+        cb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = dowicked(name, rnum, cb);
+  return rv;
+}
+
+
+/* print formatted string and flush the buffer */
+int printfflush(const char *format, ...){
+  va_list ap;
+  int rv;
+  va_start(ap, format);
+  rv = vprintf(format, ap);
+  if(fflush(stdout) == EOF) rv = -1;
+  va_end(ap);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* pseudo random number generator */
+int myrand(void){
+  static int cnt = 0;
+  if(cnt == 0) srand(time(NULL));
+  return (rand() * rand() + (rand() >> (sizeof(int) * 4)) + (cnt++)) & INT_MAX;
+}
+
+
+/* perform write command */
+int dowrite(const char *name, int rnum, int bnum, int sparse){
+  DEPOT *depot;
+  int i, omode, err, len;
+  char buf[RECBUFSIZ];
+  printfflush("<Writing Test>\n  name=%s  rnum=%d  bnum=%d  s=%d\n\n", name, rnum, bnum, sparse);
+  /* open a database */
+  omode = DP_OWRITER | DP_OCREAT | DP_OTRUNC | (sparse ? DP_OSPARSE : 0);
+  if(!(depot = dpopen(name, omode, bnum))){
+    pdperror(name);
+    return 1;
+  }
+  err = FALSE;
+  /* loop for each record */
+  for(i = 1; i <= rnum; i++){
+    /* store a record */
+    len = sprintf(buf, "%08d", i);
+    if(!dpput(depot, buf, len, buf, len, DP_DOVER)){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    /* print progression */
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform read command */
+int doread(const char *name, int wb){
+  DEPOT *depot;
+  int i, rnum, err, len;
+  char buf[RECBUFSIZ], vbuf[RECBUFSIZ], *val;
+  printfflush("<Reading Test>\n  name=%s  wb=%d\n\n", name, wb);
+  /* open a database */
+  if(!(depot = dpopen(name, DP_OREADER, -1))){
+    pdperror(name);
+    return 1;
+  }
+  /* get the number of records */
+  rnum = dprnum(depot);
+  err = FALSE;
+  /* loop for each record */
+  for(i = 1; i <= rnum; i++){
+    /* retrieve a record */
+    len = sprintf(buf, "%08d", i);
+    if(wb){
+      if(dpgetwb(depot, buf, len, 0, RECBUFSIZ, vbuf) == -1){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+    } else {
+      if(!(val = dpget(depot, buf, len, 0, -1, NULL))){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+      free(val);
+    }
+    /* print progression */
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform rcat command */
+int dorcat(const char *name, int rnum, int bnum, int pnum, int align, int fbpsiz, int cb){
+  DEPOT *depot;
+  CBMAP *map;
+  int i, err, len, ksiz, vsiz, rsiz;
+  const char *kbuf, *vbuf;
+  char buf[RECBUFSIZ], *rbuf;
+  printfflush("<Random Writing Test>\n  name=%s  rnum=%d  bnum=%d  pnum=%d  align=%d"
+              "  fbpsiz=%d  c=%d\n\n", name, rnum, bnum, pnum, align, fbpsiz, cb);
+  if(!(depot = dpopen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, bnum))){
+    pdperror(name);
+    return 1;
+  }
+  if(!dpsetalign(depot, align) || !dpsetfbpsiz(depot, fbpsiz)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  map = NULL;
+  if(cb) map = cbmapopen();
+  err = FALSE;
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", myrand() % pnum + 1);
+    if(!dpput(depot, buf, len, buf, len, DP_DCAT)){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    if(map) cbmapputcat(map, buf, len, buf, len);
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d: fsiz=%d rnum=%d)\n", i, dpfsiz(depot), dprnum(depot));
+      }
+    }
+  }
+  if(map){
+    printfflush("Matching records ... ");
+    cbmapiterinit(map);
+    while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
+      vbuf = cbmapget(map, kbuf, ksiz, &vsiz);
+      if(!(rbuf = dpget(depot, kbuf, ksiz, 0, -1, &rsiz))){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+      if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+        fprintf(stderr, "%s: %s: unmatched record\n", progname, name);
+        free(rbuf);
+        err = TRUE;
+        break;
+      }
+      free(rbuf);
+    }
+    cbmapclose(map);
+    if(!err) printfflush("ok\n");
+  }
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform combo command */
+int docombo(const char *name){
+  DEPOT *depot;
+  char buf[RECBUFSIZ], wbuf[RECBUFSIZ], *vbuf;
+  int i, len, wlen, vsiz;
+  printfflush("<Combination Test>\n  name=%s\n\n", name);
+  printfflush("Creating a database with bnum 3 ... ");
+  if(!(depot = dpopen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, 3))){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Setting alignment as 16 ... ");
+  if(!dpsetalign(depot, 16)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 20 records ... ");
+  for(i = 1; i <= 20; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!dpput(depot, buf, len, buf, len, DP_DOVER)){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking records ... ");
+  for(i = 1; i <= 20; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(vbuf = dpget(depot, buf, len, 0, -1, &vsiz))){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != dpvsiz(depot, buf, len)){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Overwriting top 10 records without moving rooms ... ");
+  for(i = 1; i <= 10; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!dpput(depot, buf, len, buf, len, DP_DOVER)){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Overwriting top 5 records with moving rooms ... ");
+  for(i = 1; i <= 5; i++){
+    len = sprintf(buf, "%08d", i);
+    wlen = sprintf(wbuf, "%024d", i);
+    if(!dpput(depot, buf, len, wbuf, wlen, DP_DOVER)){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Overwriting top 15 records in concatenation with moving rooms ... ");
+  for(i = 1; i <= 15; i++){
+    len = sprintf(buf, "%08d", i);
+    wlen = sprintf(wbuf, "========================");
+    if(!dpput(depot, buf, len, wbuf, wlen, DP_DCAT)){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking records ... ");
+  for(i = 1; i <= 20; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(vbuf = dpget(depot, buf, len, 0, -1, &vsiz))){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != dpvsiz(depot, buf, len)){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Deleting top 10 records ... ");
+  for(i = 1; i <= 10; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!dpout(depot, buf, len)){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking deleted records ... ");
+  for(i = 1; i <= 10; i++){
+    len = sprintf(buf, "%08d", i);
+    vbuf = dpget(depot, buf, len, 0, -1, &vsiz);
+    free(vbuf);
+    if(vbuf || dpecode != DP_ENOITEM){
+      fprintf(stderr, "%s: %s: deleting failed\n", progname, name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Overwriting top 15 records in concatenation with moving rooms ... ");
+  for(i = 1; i <= 15; i++){
+    len = sprintf(buf, "%08d", i);
+    wlen = sprintf(wbuf, "========================");
+    if(!dpput(depot, buf, len, wbuf, wlen, DP_DCAT)){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking records ... ");
+  for(i = 1; i <= 20; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(vbuf = dpget(depot, buf, len, 0, -1, &vsiz))){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != dpvsiz(depot, buf, len)){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Optimizing the database ... ");
+  if(!dpoptimize(depot, -1)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Checking records ... ");
+  for(i = 1; i <= 20; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(vbuf = dpget(depot, buf, len, 0, -1, &vsiz))){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != dpvsiz(depot, buf, len)){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Closing the database ... ");
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Creating a database with bnum 1000000 ... ");
+  if(!(depot = dpopen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, 1000000))){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 1000 records ... ");
+  for(i = 1; i <= 1000; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!dpput(depot, buf, len, buf, len, DP_DOVER)){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Adding 64 records ... ");
+  for(i = 1; i <= 1000; i++){
+    len = sprintf(buf, "%o", i);
+    if(!dpput(depot, buf, len, buf, len, DP_DOVER)){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Syncing the database ... ");
+  if(!dpsync(depot)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Retrieving records directly ... ");
+  for(i = 1; i <= 64; i++){
+    len = sprintf(buf, "%o", i);
+    if(!(vbuf = dpsnaffle(name, buf, len, &vsiz))){
+      pdperror(name);
+      dpclose(depot);
+      return 1;
+    }
+    if(strcmp(vbuf, buf)){
+      fprintf(stderr, "%s: %s: invalid content\n", progname, name);
+      free(vbuf);
+      dpclose(depot);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != dpvsiz(depot, buf, len)){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      dpclose(depot);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Optimizing the database ... ");
+  if(!dpoptimize(depot, -1)){
+    pdperror(name);
+    dpclose(depot);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Closing the database ... ");
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("all ok\n\n");
+  return 0;
+}
+
+
+/* perform wicked command */
+int dowicked(const char *name, int rnum, int cb){
+  DEPOT *depot;
+  CBMAP *map;
+  int i, len, err, align, mksiz, mvsiz, rsiz;
+  const char *mkbuf, *mvbuf;
+  char buf[RECBUFSIZ], vbuf[RECBUFSIZ], *val;
+  printfflush("<Wicked Writing Test>\n  name=%s  rnum=%d\n\n", name, rnum);
+  err = FALSE;
+  if(!(depot = dpopen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, rnum / 10))){
+    pdperror(name);
+    return 1;
+  }
+  if(!dpsetalign(depot, 16) || !dpsetfbpsiz(depot, 256)){
+    pdperror(name);
+    err = TRUE;
+  }
+  map = NULL;
+  if(cb) map = cbmapopen();
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", myrand() % rnum + 1);
+    switch(myrand() % 16){
+    case 0:
+      putchar('O');
+      if(!dpput(depot, buf, len, buf, len, DP_DOVER)) err = TRUE;
+      if(map) cbmapput(map, buf, len, buf, len, TRUE);
+      break;
+    case 1:
+      putchar('K');
+      if(!dpput(depot, buf, len, buf, len, DP_DKEEP) && dpecode != DP_EKEEP) err = TRUE;
+      if(map) cbmapput(map, buf, len, buf, len, FALSE);
+      break;
+    case 2:
+      putchar('D');
+      if(!dpout(depot, buf, len) && dpecode != DP_ENOITEM) err = TRUE;
+      if(map) cbmapout(map, buf, len);
+      break;
+    case 3:
+      putchar('G');
+      if(dpgetwb(depot, buf, len, 2, RECBUFSIZ, vbuf) == -1 && dpecode != DP_ENOITEM) err = TRUE;
+      break;
+    case 4:
+      putchar('V');
+      if(dpvsiz(depot, buf, len) == -1 && dpecode != DP_ENOITEM) err = TRUE;
+      break;
+    default:
+      putchar('C');
+      if(!dpput(depot, buf, len, buf, len, DP_DCAT)) err = TRUE;
+      if(map) cbmapputcat(map, buf, len, buf, len);
+      break;
+    }
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+    if(!err && rnum > 100 && myrand() % (rnum / 100) == 0){
+      if(myrand() % 10 == 0){
+        align = (myrand() % 4 + 1) * -1;
+      } else {
+        align = myrand() % 32;
+      }
+      if(!dpsetalign(depot, align)) err = TRUE;
+    }
+    if(err){
+      pdperror(name);
+      break;
+    }
+  }
+  if(!dpoptimize(depot, -1)){
+    pdperror(name);
+    err = TRUE;
+  }
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!dpput(depot, buf, len, ":", -1, DP_DCAT)){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    if(map) cbmapputcat(map, buf, len, ":", -1);
+    putchar(':');
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  if(!dpoptimize(depot, -1)){
+    pdperror(name);
+    err = TRUE;
+  }
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(val = dpget(depot, buf, len, 0, -1, NULL))){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    free(val);
+    putchar('=');
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  if(!dpiterinit(depot)){
+    pdperror(name);
+    err = TRUE;
+  }
+  for(i = 1; i <= rnum; i++){
+    if(!(val = dpiternext(depot, NULL))){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    free(val);
+    putchar('@');
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  if(map){
+    printfflush("Matching records ... ");
+    cbmapiterinit(map);
+    while((mkbuf = cbmapiternext(map, &mksiz)) != NULL){
+      mvbuf = cbmapget(map, mkbuf, mksiz, &mvsiz);
+      if(!(val = dpget(depot, mkbuf, mksiz, 0, -1, &rsiz))){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+      if(rsiz != mvsiz || memcmp(val, mvbuf, rsiz)){
+        fprintf(stderr, "%s: %s: unmatched record\n", progname, name);
+        free(val);
+        err = TRUE;
+        break;
+      }
+      free(val);
+    }
+    cbmapclose(map);
+    if(!err) printfflush("ok\n");
+  }
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/dptest.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/dptsv.c
===================================================================
--- box/trunk/qdbm/dptsv.c	                        (rev 0)
+++ box/trunk/qdbm/dptsv.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,261 @@
+/*************************************************************************************************
+ * Mutual converter between a database of Depot and a TSV text
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <cabin.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+int runimport(int argc, char **argv);
+int runexport(int argc, char **argv);
+void pdperror(const char *name);
+char *getl(void);
+int doimport(const char *name, int bnum, int bin);
+int doexport(const char *name, int bin);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "import")){
+    rv = runimport(argc, argv);
+  } else if(!strcmp(argv[1], "export")){
+    rv = runexport(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: mutual converter between TSV and Depot database\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s import [-bnum num] [-bin] name\n", progname);
+  fprintf(stderr, "  %s export [-bin] name\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of import command */
+int runimport(int argc, char **argv){
+  char *name;
+  int i, bnum, bin, rv;
+  name = NULL;
+  bnum = -1;
+  bin = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-bnum")){
+        if(++i >= argc) usage();
+        bnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-bin")){
+        bin = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doimport(name, bnum, bin);
+  return rv;
+}
+
+
+/* parse arguments of export command */
+int runexport(int argc, char **argv){
+  char *name;
+  int i, bin, rv;
+  name = NULL;
+  bin = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-bin")){
+        bin = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doexport(name, bin);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* read a line */
+char *getl(void){
+  char *buf;
+  int c, len, blen;
+  buf = NULL;
+  len = 0;
+  blen = 256;
+  while((c = getchar()) != EOF){
+    if(blen <= len) blen *= 2;
+    buf = cbrealloc(buf, blen + 1);
+    if(c == '\n') c = '\0';
+    buf[len++] = c;
+    if(c == '\0') break;
+  }
+  if(!buf) return NULL;
+  buf[len] = '\0';
+  return buf;
+}
+
+
+/* perform import command */
+int doimport(const char *name, int bnum, int bin){
+  DEPOT *depot;
+  char *buf, *kbuf, *vbuf, *ktmp, *vtmp;
+  int i, err, ktsiz, vtsiz;
+  /* open a database */
+  if(!(depot = dpopen(name, DP_OWRITER | DP_OCREAT, bnum))){
+    pdperror(name);
+    return 1;
+  }
+  /* loop for each line */
+  err = FALSE;
+  for(i = 1; (buf = getl()) != NULL; i++){
+    kbuf = buf;
+    if((vbuf = strchr(buf, '\t')) != NULL){
+      *vbuf = '\0';
+      vbuf++;
+      /* store a record */
+      if(bin){
+        ktmp = cbbasedecode(kbuf, &ktsiz);
+        vtmp = cbbasedecode(vbuf, &vtsiz);
+        if(!dpput(depot, ktmp, ktsiz, vtmp, vtsiz, DP_DOVER)){
+          pdperror(name);
+          err = TRUE;
+        }
+        free(vtmp);
+        free(ktmp);
+      } else {
+        if(!dpput(depot, kbuf, -1, vbuf, -1, DP_DOVER)){
+          pdperror(name);
+          err = TRUE;
+        }
+      }
+    } else {
+      fprintf(stderr, "%s: %s: invalid format in line %d\n", progname, name, i);
+    }
+    free(buf);
+    if(err) break;
+  }
+  /* close the database */
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return err ? 1 : 0;
+}
+
+
+/* perform export command */
+int doexport(const char *name, int bin){
+  DEPOT *depot;
+  char *kbuf, *vbuf, *tmp;
+  int err, ksiz, vsiz;
+  /* open a database */
+  if(!(depot = dpopen(name, DP_OREADER, -1))){
+    pdperror(name);
+    return 1;
+  }
+  /* initialize the iterator */
+  dpiterinit(depot);
+  /* loop for each key */
+  err = FALSE;
+  while((kbuf = dpiternext(depot, &ksiz)) != NULL){
+    /* retrieve a value with a key */
+    if(!(vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz))){
+      pdperror(name);
+      free(kbuf);
+      err = TRUE;
+      break;
+    }
+    /* output data */
+    if(bin){
+      tmp = cbbaseencode(kbuf, ksiz);
+      printf("%s\t", tmp);
+      free(tmp);
+      tmp = cbbaseencode(vbuf, vsiz);
+      printf("%s\n", tmp);
+      free(tmp);
+    } else {
+      printf("%s\t%s\n", kbuf, vbuf);
+    }
+    /* free resources */
+    free(vbuf);
+    free(kbuf);
+  }
+  /* check whether all records were retrieved */
+  if(dpecode != DP_ENOITEM){
+    pdperror(name);
+    err = TRUE;
+  }
+  /* close the database */
+  if(!dpclose(depot)){
+    pdperror(name);
+    return 1;
+  }
+  return err ? 1 : 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/dptsv.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/hovel.c
===================================================================
--- box/trunk/qdbm/hovel.c	                        (rev 0)
+++ box/trunk/qdbm/hovel.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,568 @@
+/*************************************************************************************************
+ * Implementation of Hovel
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#define QDBM_INTERNAL  1
+
+#include "hovel.h"
+#include "myconf.h"
+
+#define HV_INITBNUM    32749             /* initial bucket number */
+#define HV_ALIGNSIZ    16                /* size of alignment */
+
+
+/* private function prototypes */
+static int gdbm_geterrno(int ecode);
+
+
+
+/*************************************************************************************************
+ * public objects
+ *************************************************************************************************/
+
+
+/* String containing the version information. */
+char *gdbm_version = "Powered by QDBM";
+
+
+/* Get a message string corresponding to an error code. */
+char *gdbm_strerror(gdbm_error gdbmerrno){
+  switch(gdbmerrno){
+  case GDBM_NO_ERROR: return "No error";
+  case GDBM_MALLOC_ERROR: return "Malloc error";
+  case GDBM_BLOCK_SIZE_ERROR: return "Block size error";
+  case GDBM_FILE_OPEN_ERROR: return "File open error";
+  case GDBM_FILE_WRITE_ERROR: return "File write error";
+  case GDBM_FILE_SEEK_ERROR: return "File seek error";
+  case GDBM_FILE_READ_ERROR: return "File read error";
+  case GDBM_BAD_MAGIC_NUMBER: return "Bad magic number";
+  case GDBM_EMPTY_DATABASE: return "Empty database";
+  case GDBM_CANT_BE_READER: return "Can't be reader";
+  case GDBM_CANT_BE_WRITER: return "Can't be writer";
+  case GDBM_READER_CANT_DELETE: return "Reader can't delete";
+  case GDBM_READER_CANT_STORE: return "Reader can't store";
+  case GDBM_READER_CANT_REORGANIZE: return "Reader can't reorganize";
+  case GDBM_UNKNOWN_UPDATE: return "Unknown update";
+  case GDBM_ITEM_NOT_FOUND: return "Item not found";
+  case GDBM_REORGANIZE_FAILED: return "Reorganize failed";
+  case GDBM_CANNOT_REPLACE: return "Cannot replace";
+  case GDBM_ILLEGAL_DATA: return "Illegal data";
+  case GDBM_OPT_ALREADY_SET: return "Option already set";
+  case GDBM_OPT_ILLEGAL: return "Illegal option";
+  }
+  return "(invalid ecode)";
+}
+
+
+/* Get a database handle after the fashion of GDBM. */
+GDBM_FILE gdbm_open(char *name, int block_size, int read_write, int mode,
+                    void (*fatal_func)(void)){
+  GDBM_FILE dbf;
+  int dpomode;
+  DEPOT *depot;
+  int flags, fd;
+  assert(name);
+  dpomode = DP_OREADER;
+  flags = O_RDONLY;
+  if(read_write & GDBM_READER){
+    dpomode = GDBM_READER;
+    if(read_write & GDBM_NOLOCK) dpomode |= DP_ONOLCK;
+    if(read_write & GDBM_LOCKNB) dpomode |= DP_OLCKNB;
+    flags = O_RDONLY;
+  } else if(read_write & GDBM_WRITER){
+    dpomode = DP_OWRITER;
+    if(read_write & GDBM_NOLOCK) dpomode |= DP_ONOLCK;
+    if(read_write & GDBM_LOCKNB) dpomode |= DP_OLCKNB;
+    flags = O_RDWR;
+  } else if(read_write & GDBM_WRCREAT){
+    dpomode = DP_OWRITER | DP_OCREAT;
+    if(read_write & GDBM_NOLOCK) dpomode |= DP_ONOLCK;
+    if(read_write & GDBM_LOCKNB) dpomode |= DP_OLCKNB;
+    if(read_write & GDBM_SPARSE) dpomode |= DP_OSPARSE;
+    flags = O_RDWR | O_CREAT;
+  } else if(read_write & GDBM_NEWDB){
+    dpomode = DP_OWRITER | DP_OCREAT | DP_OTRUNC;
+    if(read_write & GDBM_NOLOCK) dpomode |= DP_ONOLCK;
+    if(read_write & GDBM_LOCKNB) dpomode |= DP_OLCKNB;
+    if(read_write & GDBM_SPARSE) dpomode |= DP_OSPARSE;
+    flags = O_RDWR | O_CREAT | O_TRUNC;
+  } else {
+    gdbm_errno = GDBM_ILLEGAL_DATA;
+    return NULL;
+  }
+  mode |= 00600;
+  if((fd = open(name, flags, mode)) == -1 || close(fd) == -1){
+    gdbm_errno = GDBM_FILE_OPEN_ERROR;
+    return NULL;
+  }
+  if(!(depot = dpopen(name, dpomode, HV_INITBNUM))){
+    gdbm_errno = gdbm_geterrno(dpecode);
+    if(dpecode == DP_ESTAT) gdbm_errno = GDBM_FILE_OPEN_ERROR;
+    return NULL;
+  }
+  if(dpomode & DP_OWRITER){
+    if(!dpsetalign(depot, HV_ALIGNSIZ)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      dpclose(depot);
+    }
+  }
+  if((dpomode & DP_OWRITER) && (read_write & GDBM_SYNC)){
+    if(!dpsync(depot)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      dpclose(depot);
+    }
+  }
+  if(!(dbf = malloc(sizeof(GDBM)))){
+    gdbm_errno = GDBM_MALLOC_ERROR;
+    dpclose(depot);
+    return NULL;
+  }
+  dbf->depot = depot;
+  dbf->curia = NULL;
+  dbf->syncmode = (dpomode & DP_OWRITER) && (read_write & GDBM_SYNC) ? TRUE : FALSE;
+  return dbf;
+}
+
+
+/* Get a database handle after the fashion of QDBM. */
+GDBM_FILE gdbm_open2(char *name, int read_write, int mode, int bnum, int dnum, int align){
+  GDBM_FILE dbf;
+  int dpomode, cromode, flags, fd;
+  struct stat sbuf;
+  DEPOT *depot;
+  CURIA *curia;
+  assert(name);
+  dpomode = DP_OREADER;
+  cromode = CR_OREADER;
+  flags = O_RDONLY;
+  if(read_write & GDBM_READER){
+    dpomode = DP_OREADER;
+    cromode = CR_OREADER;
+    if(read_write & GDBM_NOLOCK){
+      dpomode |= DP_ONOLCK;
+      cromode |= CR_ONOLCK;
+    }
+    if(read_write & GDBM_LOCKNB){
+      dpomode |= DP_OLCKNB;
+      cromode |= CR_OLCKNB;
+    }
+    flags = O_RDONLY;
+  } else if(read_write & GDBM_WRITER){
+    dpomode = DP_OWRITER;
+    cromode = CR_OWRITER;
+    if(read_write & GDBM_NOLOCK){
+      dpomode |= DP_ONOLCK;
+      cromode |= CR_ONOLCK;
+    }
+    if(read_write & GDBM_LOCKNB){
+      dpomode |= DP_OLCKNB;
+      cromode |= CR_OLCKNB;
+    }
+    flags = O_RDWR;
+  } else if(read_write & GDBM_WRCREAT){
+    dpomode = DP_OWRITER | DP_OCREAT;
+    cromode = CR_OWRITER | CR_OCREAT;
+    if(read_write & GDBM_NOLOCK){
+      dpomode |= DP_ONOLCK;
+      cromode |= CR_ONOLCK;
+    }
+    if(read_write & GDBM_LOCKNB){
+      dpomode |= DP_OLCKNB;
+      cromode |= CR_OLCKNB;
+    }
+    if(read_write & GDBM_SPARSE){
+      dpomode |= DP_OSPARSE;
+      cromode |= CR_OSPARSE;
+    }
+    flags = O_RDWR | O_CREAT;
+  } else if(read_write & GDBM_NEWDB){
+    dpomode = DP_OWRITER | DP_OCREAT | DP_OTRUNC;
+    cromode = CR_OWRITER | CR_OCREAT | CR_OTRUNC;
+    if(read_write & GDBM_NOLOCK){
+      dpomode |= DP_ONOLCK;
+      cromode |= CR_ONOLCK;
+    }
+    if(read_write & GDBM_LOCKNB){
+      dpomode |= DP_OLCKNB;
+      cromode |= CR_OLCKNB;
+    }
+    if(read_write & GDBM_SPARSE){
+      dpomode |= DP_OSPARSE;
+      cromode |= CR_OSPARSE;
+    }
+    flags = O_RDWR | O_CREAT | O_TRUNC;
+  } else {
+    gdbm_errno = GDBM_ILLEGAL_DATA;
+    return NULL;
+  }
+  if(lstat(name, &sbuf) != -1){
+    if(S_ISDIR(sbuf.st_mode)){
+      if(dnum < 1) dnum = 1;
+    } else {
+      dnum = 0;
+    }
+  }
+  depot = NULL;
+  curia = NULL;
+  if(dnum > 0){
+    mode |= 00700;
+    if((cromode & CR_OCREAT)){
+      if(mkdir(name, mode) == -1 && errno != EEXIST){
+        gdbm_errno = GDBM_FILE_OPEN_ERROR;
+        return NULL;
+      }
+    }
+    if(!(curia = cropen(name, cromode, bnum, dnum))){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return NULL;
+    }
+    if(cromode & CR_OWRITER) crsetalign(curia, align);
+    if((cromode & CR_OWRITER) && (read_write & GDBM_SYNC)) crsync(curia);
+  } else {
+    mode |= 00600;
+    if(dpomode & DP_OWRITER){
+      if((fd = open(name, flags, mode)) == -1 || close(fd) == -1){
+        gdbm_errno = GDBM_FILE_OPEN_ERROR;
+        return NULL;
+      }
+    }
+    if(!(depot = dpopen(name, dpomode, bnum))){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return NULL;
+    }
+    if(dpomode & DP_OWRITER) dpsetalign(depot, align);
+    if((dpomode & DP_OWRITER) && (read_write & GDBM_SYNC)) dpsync(depot);
+  }
+  if(!(dbf = malloc(sizeof(GDBM)))){
+    gdbm_errno = GDBM_MALLOC_ERROR;
+    if(depot) dpclose(depot);
+    if(curia) crclose(curia);
+    return NULL;
+  }
+  dbf->depot = depot;
+  dbf->curia = curia;
+  dbf->syncmode = (dpomode & DP_OWRITER) && (read_write & GDBM_SYNC) ? TRUE : FALSE;
+  return dbf;
+}
+
+
+/* Close a database handle. */
+void gdbm_close(GDBM_FILE dbf){
+  assert(dbf);
+  if(dbf->depot){
+    if(dbf->syncmode) dpsync(dbf->depot);
+    dpclose(dbf->depot);
+  } else {
+    if(dbf->syncmode) crsync(dbf->curia);
+    crclose(dbf->curia);
+  }
+  free(dbf);
+}
+
+
+/* Store a record. */
+int gdbm_store(GDBM_FILE dbf, datum key, datum content, int flag){
+  int dmode;
+  assert(dbf);
+  if(!key.dptr || key.dsize < 0 || !content.dptr || content.dsize < 0){
+    gdbm_errno = GDBM_ILLEGAL_DATA;
+    return -1;
+  }
+  if(dbf->depot){
+    if(!dpwritable(dbf->depot)){
+      gdbm_errno = GDBM_READER_CANT_STORE;
+      return -1;
+    }
+    dmode = (flag == GDBM_INSERT) ? DP_DKEEP : DP_DOVER;
+    if(!dpput(dbf->depot, key.dptr, key.dsize, content.dptr, content.dsize, dmode)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      if(dpecode == DP_EKEEP) return 1;
+      return -1;
+    }
+    if(dbf->syncmode && !dpsync(dbf->depot)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return -1;
+    }
+  } else {
+    if(!crwritable(dbf->curia)){
+      gdbm_errno = GDBM_READER_CANT_STORE;
+      return -1;
+    }
+    dmode = (flag == GDBM_INSERT) ? CR_DKEEP : CR_DOVER;
+    if(!crput(dbf->curia, key.dptr, key.dsize, content.dptr, content.dsize, dmode)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      if(dpecode == DP_EKEEP) return 1;
+      return -1;
+    }
+    if(dbf->syncmode && !crsync(dbf->curia)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return -1;
+    }
+  }
+  return 0;
+}
+
+
+/* Delete a record. */
+int gdbm_delete(GDBM_FILE dbf, datum key){
+  assert(dbf);
+  if(!key.dptr || key.dsize < 0){
+    gdbm_errno = GDBM_ILLEGAL_DATA;
+    return -1;
+  }
+  if(dbf->depot){
+    if(!dpwritable(dbf->depot)){
+      gdbm_errno = GDBM_READER_CANT_DELETE;
+      return -1;
+    }
+    if(!dpout(dbf->depot, key.dptr, key.dsize)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return -1;
+    }
+    if(dbf->syncmode && !dpsync(dbf->depot)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return -1;
+    }
+  } else {
+    if(!crwritable(dbf->curia)){
+      gdbm_errno = GDBM_READER_CANT_DELETE;
+      return -1;
+    }
+    if(!crout(dbf->curia, key.dptr, key.dsize)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return -1;
+    }
+    if(dbf->syncmode && !crsync(dbf->curia)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return -1;
+    }
+  }
+  return 0;
+}
+
+
+/* Retrieve a record. */
+datum gdbm_fetch(GDBM_FILE dbf, datum key){
+  datum content;
+  char *vbuf;
+  int vsiz;
+  assert(dbf);
+  if(!key.dptr || key.dsize < 0){
+    gdbm_errno = GDBM_ILLEGAL_DATA;
+    content.dptr = NULL;
+    content.dsize = 0;
+    return content;
+  }
+  if(dbf->depot){
+    if(!(vbuf = dpget(dbf->depot, key.dptr, key.dsize, 0, -1, &vsiz))){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      content.dptr = NULL;
+      content.dsize = 0;
+      return content;
+    }
+  } else {
+    if(!(vbuf = crget(dbf->curia, key.dptr, key.dsize, 0, -1, &vsiz))){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      content.dptr = NULL;
+      content.dsize = 0;
+      return content;
+    }
+  }
+  content.dptr = vbuf;
+  content.dsize = vsiz;
+  return content;
+}
+
+
+/* Check whether a record exists or not. */
+int gdbm_exists(GDBM_FILE dbf, datum key){
+  assert(dbf);
+  if(!key.dptr || key.dsize < 0){
+    gdbm_errno = GDBM_ILLEGAL_DATA;
+    return FALSE;
+  }
+  if(dbf->depot){
+    if(dpvsiz(dbf->depot, key.dptr, key.dsize) == -1){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return FALSE;
+    }
+  } else {
+    if(crvsiz(dbf->curia, key.dptr, key.dsize) == -1){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+
+/* Get the first key of a database. */
+datum gdbm_firstkey(GDBM_FILE dbf){
+  datum key;
+  assert(dbf);
+  memset(&key, 0, sizeof(datum));
+  if(dbf->depot){
+    if(dprnum(dbf->depot) < 1){
+      gdbm_errno = GDBM_EMPTY_DATABASE;
+      key.dptr = NULL;
+      key.dsize = 0;
+      return key;
+    }
+    dpiterinit(dbf->depot);
+    return gdbm_nextkey(dbf, key);
+  } else {
+    if(crrnum(dbf->curia) < 1){
+      gdbm_errno = GDBM_EMPTY_DATABASE;
+      key.dptr = NULL;
+      key.dsize = 0;
+      return key;
+    }
+    criterinit(dbf->curia);
+    return gdbm_nextkey(dbf, key);
+  }
+}
+
+
+/* Get the next key of a database. */
+datum gdbm_nextkey(GDBM_FILE dbf, datum key){
+  char *kbuf;
+  int ksiz;
+  assert(dbf);
+  if(dbf->depot){
+    if(!(kbuf = dpiternext(dbf->depot, &ksiz))){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      key.dptr = NULL;
+      key.dsize = 0;
+      return key;
+    }
+  } else {
+    if(!(kbuf = criternext(dbf->curia, &ksiz))){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      key.dptr = NULL;
+      key.dsize = 0;
+      return key;
+    }
+  }
+  key.dptr = kbuf;
+  key.dsize = ksiz;
+  return key;
+}
+
+
+/* Synchronize contents of updating with the file and the device. */
+void gdbm_sync(GDBM_FILE dbf){
+  assert(dbf);
+  if(dbf->depot){
+    if(!dpsync(dbf->depot)) gdbm_errno = gdbm_geterrno(dpecode);
+  } else {
+    if(!crsync(dbf->curia)) gdbm_errno = gdbm_geterrno(dpecode);
+  }
+}
+
+
+/* Reorganize a database. */
+int gdbm_reorganize(GDBM_FILE dbf){
+  assert(dbf);
+  if(dbf->depot){
+    if(!dpwritable(dbf->depot)){
+      gdbm_errno = GDBM_READER_CANT_REORGANIZE;
+      return -1;
+    }
+    if(!dpoptimize(dbf->depot, dprnum(dbf->depot) >= HV_INITBNUM ? -1 : HV_INITBNUM)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return -1;
+    }
+  } else {
+    if(!crwritable(dbf->curia)){
+      gdbm_errno = GDBM_READER_CANT_REORGANIZE;
+      return -1;
+    }
+    if(!croptimize(dbf->curia, crrnum(dbf->curia) >= HV_INITBNUM ? -1 : HV_INITBNUM)){
+      gdbm_errno = gdbm_geterrno(dpecode);
+      return -1;
+    }
+  }
+  return 0;
+}
+
+
+/* Get the file descriptor of a database file. */
+int gdbm_fdesc(GDBM_FILE dbf){
+  assert(dbf);
+  if(dbf->depot){
+    return dpfdesc(dbf->depot);
+  } else {
+    return -1;
+  }
+}
+
+
+/* No effect. */
+int gdbm_setopt(GDBM_FILE dbf, int option, int *value, int size){
+  assert(dbf);
+  return 0;
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Get the pointer of the last happened error code. */
+int *gdbm_errnoptr(void){
+  static int deferrno = GDBM_NO_ERROR;
+  void *ptr;
+  if(_qdbm_ptsafe){
+    if(!(ptr = _qdbm_settsd(&deferrno, sizeof(int), &deferrno))){
+      deferrno = GDBM_ILLEGAL_DATA;
+      return &deferrno;
+    }
+    return (int *)ptr;
+  }
+  return &deferrno;
+}
+
+
+
+/*************************************************************************************************
+ * private objects
+ *************************************************************************************************/
+
+
+/* Get the error code of GDBM corresponding to an error code of Depot.
+   `ecode' specifies an error code of Depot.
+   The return value is the error code of GDBM. */
+static int gdbm_geterrno(int ecode){
+  switch(ecode){
+  case DP_ENOERR: return GDBM_NO_ERROR;
+  case DP_EBROKEN: return GDBM_BAD_MAGIC_NUMBER;
+  case DP_EKEEP: return GDBM_CANNOT_REPLACE;
+  case DP_ENOITEM: return GDBM_ITEM_NOT_FOUND;
+  case DP_EALLOC: return GDBM_MALLOC_ERROR;
+  case DP_EOPEN: return GDBM_FILE_OPEN_ERROR;
+  case DP_ESEEK: return GDBM_FILE_SEEK_ERROR;
+  case DP_EREAD: return GDBM_FILE_READ_ERROR;
+  case DP_EWRITE: return GDBM_FILE_WRITE_ERROR;
+  case DP_EMKDIR: return GDBM_FILE_OPEN_ERROR;
+  default: break;
+  }
+  return GDBM_ILLEGAL_DATA;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/hovel.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/hovel.h
===================================================================
--- box/trunk/qdbm/hovel.h	                        (rev 0)
+++ box/trunk/qdbm/hovel.h	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,278 @@
+/*************************************************************************************************
+ * The GDBM-compatible API of QDBM
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _HOVEL_H                         /* duplication check */
+#define _HOVEL_H
+
+#if defined(__cplusplus)                 /* export for C++ */
+extern "C" {
+#endif
+
+
+#include <depot.h>
+#include <curia.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#if defined(_MSC_VER) && !defined(QDBM_INTERNAL) && !defined(QDBM_STATIC)
+#define MYEXTERN extern __declspec(dllimport)
+#else
+#define MYEXTERN extern
+#endif
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+enum {                                   /* enumeration for error codes */
+  GDBM_NO_ERROR,                         /* no error */
+  GDBM_MALLOC_ERROR,                     /* malloc error */
+  GDBM_BLOCK_SIZE_ERROR,                 /* block size error */
+  GDBM_FILE_OPEN_ERROR,                  /* file open error */
+  GDBM_FILE_WRITE_ERROR,                 /* file write error */
+  GDBM_FILE_SEEK_ERROR,                  /* file seek error */
+  GDBM_FILE_READ_ERROR,                  /* file read error */
+  GDBM_BAD_MAGIC_NUMBER,                 /* bad magic number */
+  GDBM_EMPTY_DATABASE,                   /* empty database */
+  GDBM_CANT_BE_READER,                   /* can't be reader */
+  GDBM_CANT_BE_WRITER,                   /* can't be writer */
+  GDBM_READER_CANT_DELETE,               /* reader can't delete */
+  GDBM_READER_CANT_STORE,                /* reader can't store */
+  GDBM_READER_CANT_REORGANIZE,           /* reader can't reorganize */
+  GDBM_UNKNOWN_UPDATE,                   /* unknown update */
+  GDBM_ITEM_NOT_FOUND,                   /* item not found */
+  GDBM_REORGANIZE_FAILED,                /* reorganize failed */
+  GDBM_CANNOT_REPLACE,                   /* cannot replace */
+  GDBM_ILLEGAL_DATA,                     /* illegal data */
+  GDBM_OPT_ALREADY_SET,                  /* option already set */
+  GDBM_OPT_ILLEGAL                       /* option illegal */
+};
+
+typedef int gdbm_error;                  /* type of error codes */
+
+typedef struct {                         /* type of structure for a database handle */
+  DEPOT *depot;                          /* internal database handle of Depot */
+  CURIA *curia;                          /* internal database handle of Curia */
+  int syncmode;                          /* whether to be besyncronous mode */
+} GDBM;
+
+typedef GDBM *GDBM_FILE;                 /* type of pointer to a database handle */
+
+typedef struct {                         /* type of structure for a key or a value */
+  char *dptr;                            /* pointer to the region */
+  size_t dsize;                          /* size of the region */
+} datum;
+
+enum {                                   /* enumeration for open modes */
+  GDBM_READER = 1 << 0,                  /* open as a reader */
+  GDBM_WRITER = 1 << 1,                  /* open as a writer */
+  GDBM_WRCREAT = 1 << 2,                 /* a writer creating */
+  GDBM_NEWDB = 1 << 3,                   /* a writer creating and truncating */
+  GDBM_SYNC = 1 << 4,                    /* syncronous mode */
+  GDBM_NOLOCK = 1 << 5,                  /* no lock mode */
+  GDBM_LOCKNB = 1 << 6,                  /* non-blocking lock mode */
+  GDBM_FAST = 1 << 7,                    /* fast mode */
+  GDBM_SPARSE = 1 << 8                   /* create as sparse file */
+};
+
+enum {                                   /* enumeration for write modes */
+  GDBM_INSERT,                           /* keep an existing value */
+  GDBM_REPLACE                           /* overwrite an existing value */
+};
+
+enum {                                   /* enumeration for options */
+  GDBM_CACHESIZE,                        /* set cache size */
+  GDBM_FASTMODE,                         /* set fast mode */
+  GDBM_SYNCMODE,                         /* set syncronous mode */
+  GDBM_CENTFREE,                         /* set free block pool */
+  GDBM_COALESCEBLKS                      /* set free block marging */
+};
+
+
+/* String containing the version information. */
+MYEXTERN char *gdbm_version;
+
+
+/* Last happened error code. */
+#define gdbm_errno     (*gdbm_errnoptr())
+
+
+/* Get a message string corresponding to an error code.
+   `gdbmerrno' specifies an error code.
+   The return value is the message string of the error code.  The region of the return value
+   is not writable. */
+char *gdbm_strerror(gdbm_error gdbmerrno);
+
+
+/* Get a database handle after the fashion of GDBM.
+   `name' specifies a name of a database.
+   `read_write' specifies the connection mode: `GDBM_READER' as a reader, `GDBM_WRITER',
+   `GDBM_WRCREAT' and `GDBM_NEWDB' as a writer.  `GDBM_WRCREAT' makes a database file or
+   directory if it does not exist.  `GDBM_NEWDB' makes a new database even if it exists.
+   You can add the following to writer modes by bitwise or: `GDBM_SYNC', `GDBM_NOLOCK',
+   `GDBM_LCKNB', `GDBM_FAST', and `GDBM_SPARSE'.  `GDBM_SYNC' means a database is synchronized
+   after every updating method.  `GDBM_NOLOCK' means a database is opened without file locking.
+   `GDBM_LOCKNB' means file locking is performed without blocking.  `GDBM_FAST' is ignored.
+   `GDBM_SPARSE' is an original mode of QDBM and makes database a sparse file.
+   `mode' specifies a mode of a database file or a database directory as the one of `open'
+   or `mkdir' call does.
+   `bnum' specifies the number of elements of each bucket array.  If it is not more than 0,
+   the default value is specified.
+   `dnum' specifies the number of division of the database.  If it is not more than 0, the
+   returning handle is created as a wrapper of Depot, else, it is as a wrapper of Curia.
+   The return value is the database handle or `NULL' if it is not successful.
+   If the database already exists, whether it is one of Depot or Curia is measured
+   automatically. */
+GDBM_FILE gdbm_open(char *name, int block_size, int read_write, int mode,
+                    void (*fatal_func)(void));
+
+
+/* Get a database handle after the fashion of QDBM.
+   `name' specifies a name of a database.
+   `read_write' specifies the connection mode: `GDBM_READER' as a reader, `GDBM_WRITER',
+   `GDBM_WRCREAT' and `GDBM_NEWDB' as a writer.  `GDBM_WRCREAT' makes a database file or
+   directory if it does not exist.  `GDBM_NEWDB' makes a new database even if it exists.
+   You can add the following to writer modes by bitwise or: `GDBM_SYNC', `GDBM_NOLOCK',
+   `GDBM_LOCKNB', `GDBM_FAST', and `GDBM_SPARSE'.  `GDBM_SYNC' means a database is synchronized
+   after every updating method.  `GDBM_NOLOCK' means a database is opened without file locking.
+   `GDBM_LOCKNB' means file locking is performed without blocking.  `GDBM_FAST' is ignored.
+   `GDBM_SPARSE' is an original mode of QDBM and makes database sparse files.
+   `mode' specifies a mode of a database file as the one of `open' or `mkdir' call does.
+   `bnum' specifies the number of elements of each bucket array.  If it is not more than 0,
+   the default value is specified.
+   `dnum' specifies the number of division of the database.  If it is not more than 0, the
+   returning handle is created as a wrapper of Depot, else, it is as a wrapper of Curia.
+   `align' specifies the basic size of alignment.
+   The return value is the database handle or `NULL' if it is not successful. */
+GDBM_FILE gdbm_open2(char *name, int read_write, int mode, int bnum, int dnum, int align);
+
+
+/* Close a database handle.
+   `dbf' specifies a database handle.
+   Because the region of the closed handle is released, it becomes impossible to use the
+   handle. */
+void gdbm_close(GDBM_FILE dbf);
+
+
+/* Store a record.
+   `dbf' specifies a database handle connected as a writer.
+   `key' specifies a structure of a key.  `content' specifies a structure of a value.
+   `flag' specifies behavior when the key overlaps, by the following values: `GDBM_REPLACE',
+   which means the specified value overwrites the existing one, `GDBM_INSERT', which means
+   the existing value is kept.
+   The return value is 0 if it is successful, 1 if it gives up because of overlaps of the key,
+   -1 if other error occurs. */
+int gdbm_store(GDBM_FILE dbf, datum key, datum content, int flag);
+
+
+/* Delete a record.
+   `dbf' specifies a database handle connected as a writer.
+   `key' specifies a structure of a key.
+   The return value is 0 if it is successful, -1 if some errors occur. */
+int gdbm_delete(GDBM_FILE dbf, datum key);
+
+
+/* Retrieve a record.
+   `dbf' specifies a database handle.
+   `key' specifies a structure of a key.
+   The return value is a structure of the result.
+   If a record corresponds, the member `dptr' of the structure is the pointer to the region
+   of the value.  If no record corresponds or some errors occur, `dptr' is `NULL'.  Because
+   the region pointed to by `dptr' is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use. */
+datum gdbm_fetch(GDBM_FILE dbf, datum key);
+
+
+/* Check whether a record exists or not.
+   `dbf' specifies a database handle.
+   `key' specifies a structure of a key.
+   The return value is true if a record corresponds and no error occurs, or false, else,
+   it is false. */
+int gdbm_exists(GDBM_FILE dbf, datum key);
+
+
+/* Get the first key of a database.
+   `dbf' specifies a database handle.
+   The return value is a structure of the result.
+   If a record corresponds, the member `dptr' of the structure is the pointer to the region
+   of the first key.  If no record corresponds or some errors occur, `dptr' is `NULL'.
+   Because the region pointed to by `dptr' is allocated with the `malloc' call, it should
+   be released with the `free' call if it is no longer in use. */
+datum gdbm_firstkey(GDBM_FILE dbf);
+
+
+/* Get the next key of a database.
+   `dbf' specifies a database handle.
+   The return value is a structure of the result.
+   If a record corresponds, the member `dptr' of the structure is the pointer to the region
+   of the next key.  If no record corresponds or some errors occur, `dptr' is `NULL'.
+   Because the region pointed to by `dptr' is allocated with the `malloc' call, it should
+   be released with the `free' call if it is no longer in use. */
+datum gdbm_nextkey(GDBM_FILE dbf, datum key);
+
+
+/* Synchronize updating contents with the file and the device.
+   `dbf' specifies a database handle connected as a writer. */
+void gdbm_sync(GDBM_FILE dbf);
+
+
+/* Reorganize a database.
+   `dbf' specifies a database handle connected as a writer.
+   If successful, the return value is 0, else -1. */
+int gdbm_reorganize(GDBM_FILE dbf);
+
+
+/* Get the file descriptor of a database file.
+   `dbf' specifies a database handle connected as a writer.
+   The return value is the file descriptor of the database file.
+   If the database is a directory the return value is -1. */
+int gdbm_fdesc(GDBM_FILE dbf);
+
+
+/* No effect.
+   `dbf' specifies a database handle.
+   `option' is ignored.  `size' is ignored.
+   The return value is 0.
+   The function is only for compatibility. */
+int gdbm_setopt(GDBM_FILE dbf, int option, int *value, int size);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Get the pointer of the last happened error code. */
+int *gdbm_errnoptr(void);
+
+
+
+#undef MYEXTERN
+
+#if defined(__cplusplus)                 /* export for C++ */
+}
+#endif
+
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/hovel.h
___________________________________________________________________
Added: svn:mime-type
   + text/x-chdr
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/hvmgr.c
===================================================================
--- box/trunk/qdbm/hvmgr.c	                        (rev 0)
+++ box/trunk/qdbm/hvmgr.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,582 @@
+/*************************************************************************************************
+ * Utility for debugging Hovel and its applications
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <hovel.h>
+#include <cabin.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define ALIGNSIZ       16                /* basic size of alignment */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+char *hextoobj(const char *str, int *sp);
+int runcreate(int argc, char **argv);
+int runstore(int argc, char **argv);
+int rundelete(int argc, char **argv);
+int runfetch(int argc, char **argv);
+int runlist(int argc, char **argv);
+int runoptimize(int argc, char **argv);
+void pgerror(const char *name);
+void printobj(const char *obj, int size);
+void printobjhex(const char *obj, int size);
+int docreate(char *name, int qdbm, int bnum, int dnum, int sparse);
+int dostore(char *name, int qdbm, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int ins);
+int dodelete(char *name, int qdbm, const char *kbuf, int ksiz);
+int dofetch(char *name, int qdbm, const char *kbuf, int ksiz, int ox, int nb);
+int dolist(char *name, int qdbm, int ox);
+int dooptimize(char *name, int qdbm);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "create")){
+    rv = runcreate(argc, argv);
+  } else if(!strcmp(argv[1], "store")){
+    rv = runstore(argc, argv);
+  } else if(!strcmp(argv[1], "delete")){
+    rv = rundelete(argc, argv);
+  } else if(!strcmp(argv[1], "fetch")){
+    rv = runfetch(argc, argv);
+  } else if(!strcmp(argv[1], "list")){
+    rv = runlist(argc, argv);
+  } else if(!strcmp(argv[1], "optimize")){
+    rv = runoptimize(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: administration utility for Hovel\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s create [-qdbm bnum dnum] [-s] name\n", progname);
+  fprintf(stderr, "  %s store [-qdbm] [-kx] [-vx|-vf] [-insert] name key val\n", progname);
+  fprintf(stderr, "  %s delete [-qdbm] [-kx] name key\n", progname);
+  fprintf(stderr, "  %s fetch [-qdbm] [-kx] [-ox] [-n] name key\n", progname);
+  fprintf(stderr, "  %s list [-qdbm] [-ox] name\n", progname);
+  fprintf(stderr, "  %s optimize [-qdbm] name\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* create a binary object from a hexadecimal string */
+char *hextoobj(const char *str, int *sp){
+  char *buf, mbuf[3];
+  int len, i, j;
+  len = strlen(str);
+  if(!(buf = malloc(len + 1))) return NULL;
+  j = 0;
+  for(i = 0; i < len; i += 2){
+    while(strchr(" \n\r\t\f\v", str[i])){
+      i++;
+    }
+    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 */
+int runcreate(int argc, char **argv){
+  char *name;
+  int i, sb, qdbm, bnum, dnum, rv;
+  name = NULL;
+  sb = FALSE;
+  qdbm = FALSE;
+  bnum = -1;
+  dnum = -1;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-qdbm")){
+        qdbm = TRUE;
+        if(++i >= argc) usage();
+        bnum = atoi(argv[i]);
+        if(++i >= argc) usage();
+        dnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-s")){
+        sb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = docreate(name, qdbm, bnum, dnum, sb);
+  return rv;
+}
+
+
+/* parse arguments of store command */
+int runstore(int argc, char **argv){
+  char *name, *key, *val, *kbuf, *vbuf;
+  int i, qdbm, kx, vx, vf, ins, ksiz, vsiz, rv;
+  name = NULL;
+  qdbm = FALSE;
+  kx = FALSE;
+  vx = FALSE;
+  vf = FALSE;
+  ins = FALSE;
+  key = NULL;
+  val = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-qdbm")){
+        qdbm = TRUE;
+      } else if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-vx")){
+        vx = TRUE;
+      } else if(!strcmp(argv[i], "-vf")){
+        vf = TRUE;
+      } else if(!strcmp(argv[i], "-insert")){
+        ins = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else if(!val){
+      val = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key || !val) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = strlen(kbuf);
+  }
+  if(vx){
+    vbuf = hextoobj(val, &vsiz);
+  } else if(vf){
+    vbuf = cbreadfile(val, &vsiz);
+  } else {
+    vbuf = cbmemdup(val, -1);
+    vsiz = strlen(vbuf);
+  }
+  if(kbuf && vbuf){
+    rv = dostore(name, qdbm, kbuf, ksiz, vbuf, vsiz, ins);
+  } else {
+    if(vf){
+      fprintf(stderr, "%s: %s: cannot read\n", progname, val);
+    } else {
+      fprintf(stderr, "%s: out of memory\n", progname);
+    }
+    rv = 1;
+  }
+  free(kbuf);
+  free(vbuf);
+  return rv;
+}
+
+
+/* parse arguments of delete command */
+int rundelete(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, qdbm, kx, ksiz, rv;
+  name = NULL;
+  qdbm = FALSE;
+  kx = FALSE;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-qdbm")){
+        qdbm = TRUE;
+      } else if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = strlen(kbuf);
+  }
+  if(kbuf){
+    rv = dodelete(name, qdbm, kbuf, ksiz);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of fetch command */
+int runfetch(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, qdbm, kx, ox, nb, ksiz, rv;
+  name = NULL;
+  qdbm = FALSE;
+  kx = FALSE;
+  ox = FALSE;
+  nb = FALSE;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-qdbm")){
+        qdbm = TRUE;
+      } else if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else if(!strcmp(argv[i], "-n")){
+        nb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = strlen(kbuf);
+  }
+  if(kbuf){
+    rv = dofetch(name, qdbm, kbuf, ksiz, ox, nb);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+int runlist(int argc, char **argv){
+  char *name;
+  int i, qdbm, ox, rv;
+  name = NULL;
+  qdbm = FALSE;
+  ox = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-qdbm")){
+        qdbm = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dolist(name, qdbm, ox);
+  return rv;
+}
+
+
+/* parse arguments of optimize command */
+int runoptimize(int argc, char **argv){
+  char *name;
+  int i, qdbm, rv;
+  name = NULL;
+  qdbm = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-qdbm")){
+        qdbm = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dooptimize(name, qdbm);
+  return rv;
+}
+
+
+/* print an error message */
+void pgerror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, gdbm_strerror(gdbm_errno));
+}
+
+
+/* print an object */
+void printobj(const char *obj, int size){
+  int i;
+  for(i = 0; i < size; i++){
+    putchar(obj[i]);
+  }
+}
+
+
+/* print an object as a hexadecimal string */
+void printobjhex(const char *obj, int size){
+  int i;
+  for(i = 0; i < size; i++){
+    printf("%s%02X", i > 0 ? " " : "", ((const unsigned char *)obj)[i]);
+  }
+}
+
+
+/* perform create command */
+int docreate(char *name, int qdbm, int bnum, int dnum, int sparse){
+  GDBM_FILE dbf;
+  int rwmode;
+  rwmode = GDBM_NEWDB | (sparse ? GDBM_SPARSE : 0);
+  if(qdbm){
+    if(!(dbf = gdbm_open2(name, rwmode, 00644, bnum, dnum, ALIGNSIZ))){
+      pgerror(name);
+      return 1;
+    }
+  } else {
+    if(!(dbf = gdbm_open(name, 0, rwmode, 00644, NULL))){
+      pgerror(name);
+      return 1;
+    }
+  }
+  gdbm_close(dbf);
+  return 0;
+}
+
+
+/* perform store command */
+int dostore(char *name, int qdbm, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int ins){
+  GDBM_FILE dbf;
+  datum key, content;
+  int rv;
+  if(qdbm){
+    if(!(dbf = gdbm_open2(name, GDBM_WRITER, 00644, -1, -1, ALIGNSIZ))){
+      pgerror(name);
+      return 1;
+    }
+  } else {
+    if(!(dbf = gdbm_open(name, 0, GDBM_WRITER, 00644, NULL))){
+      pgerror(name);
+      return 1;
+    }
+  }
+  key.dptr = (char *)kbuf;
+  key.dsize = ksiz;
+  content.dptr = (char *)vbuf;
+  content.dsize = vsiz;
+  rv = 0;
+  if(gdbm_store(dbf, key, content, ins ? GDBM_INSERT : GDBM_REPLACE) != 0){
+    pgerror(name);
+    rv = 1;
+  }
+  gdbm_close(dbf);
+  return rv;
+}
+
+
+/* perform delete command */
+int dodelete(char *name, int qdbm, const char *kbuf, int ksiz){
+  GDBM_FILE dbf;
+  datum key;
+  int rv;
+  if(qdbm){
+    if(!(dbf = gdbm_open2(name, GDBM_WRITER, 00644, -1, -1, ALIGNSIZ))){
+      pgerror(name);
+      return 1;
+    }
+  } else {
+    if(!(dbf = gdbm_open(name, 0, GDBM_WRITER, 00644, NULL))){
+      pgerror(name);
+      return 1;
+    }
+  }
+  key.dptr = (char *)kbuf;
+  key.dsize = ksiz;
+  if(gdbm_delete(dbf, key) == 0){
+    rv = 0;
+  } else {
+    pgerror(name);
+    rv = 1;
+  }
+  gdbm_close(dbf);
+  return rv;
+}
+
+
+/* perform fetch command */
+int dofetch(char *name, int qdbm, const char *kbuf, int ksiz, int ox, int nb){
+  GDBM_FILE dbf;
+  datum key, content;
+  int rv;
+  if(qdbm){
+    if(!(dbf = gdbm_open2(name, GDBM_READER, 00644, -1, -1, -1))){
+      pgerror(name);
+      return 1;
+    }
+  } else {
+    if(!(dbf = gdbm_open(name, 0, GDBM_READER, 00644, NULL))){
+      pgerror(name);
+      return 1;
+    }
+  }
+  key.dptr = (char *)kbuf;
+  key.dsize = ksiz;
+  content = gdbm_fetch(dbf, key);
+  if(content.dptr){
+    if(ox){
+      printobjhex(content.dptr, content.dsize);
+    } else {
+      printobj(content.dptr, content.dsize);
+    }
+    if(!nb) putchar('\n');
+    rv = 0;
+    free(content.dptr);
+  } else {
+    pgerror(name);
+    rv = 1;
+  }
+  gdbm_close(dbf);
+  return rv;
+}
+
+
+/* perform list command */
+int dolist(char *name, int qdbm, int ox){
+  GDBM_FILE dbf;
+  datum key, val;
+  if(qdbm){
+    if(!(dbf = gdbm_open2(name, GDBM_READER, 00644, -1, -1, -1))){
+      pgerror(name);
+      return 1;
+    }
+  } else {
+    if(!(dbf = gdbm_open(name, 0, GDBM_READER, 00644, NULL))){
+      pgerror(name);
+      return 1;
+    }
+  }
+  for(key = gdbm_firstkey(dbf); key.dptr != NULL; key = gdbm_nextkey(dbf, key)){
+    val = gdbm_fetch(dbf, key);
+    if(!val.dptr){
+      free(key.dptr);
+      break;
+    }
+    if(ox){
+      printobjhex(key.dptr, key.dsize);
+      putchar('\t');
+      printobjhex(val.dptr, val.dsize);
+    } else {
+      printobj(key.dptr, key.dsize);
+      putchar('\t');
+      printobj(val.dptr, val.dsize);
+    }
+    putchar('\n');
+    free(val.dptr);
+    free(key.dptr);
+  }
+  gdbm_close(dbf);
+  return 0;
+}
+
+
+/* perform optimize command */
+int dooptimize(char *name, int qdbm){
+  GDBM_FILE dbf;
+  int rv;
+  if(qdbm){
+    if(!(dbf = gdbm_open2(name, GDBM_WRITER, 00644, -1, -1, ALIGNSIZ))){
+      pgerror(name);
+      return 1;
+    }
+  } else {
+    if(!(dbf = gdbm_open(name, 0, GDBM_WRITER, 00644, NULL))){
+      pgerror(name);
+      return 1;
+    }
+  }
+  rv = 0;
+  if(gdbm_reorganize(dbf) != 0){
+    pgerror(name);
+    rv = 1;
+  }
+  gdbm_close(dbf);
+  return rv;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/hvmgr.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/hvtest.c
===================================================================
--- box/trunk/qdbm/hvtest.c	                        (rev 0)
+++ box/trunk/qdbm/hvtest.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,272 @@
+/*************************************************************************************************
+ * Test cases of Hovel
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <hovel.h>
+#include <cabin.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define DIVNUM         5                 /* number of division */
+#define ALIGNSIZ       16                /* basic size of alignment */
+#define RECBUFSIZ      32                /* buffer for records */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+int runwrite(int argc, char **argv);
+int runread(int argc, char **argv);
+int printfflush(const char *format, ...);
+void pgerror(const char *name);
+int dowrite(char *name, int rnum, int qdbm, int sparse);
+int doread(char *name, int rnum, int qdbm);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "write")){
+    rv = runwrite(argc, argv);
+  } else if(!strcmp(argv[1], "read")){
+    rv = runread(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: test cases for Hovel\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write [-qdbm] [-s] name rnum\n", progname);
+  fprintf(stderr, "  %s read [-qdbm] name rnum\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of write command */
+int runwrite(int argc, char **argv){
+  char *name, *rstr;
+  int i, sb, qdbm, rnum, rv;
+  name = NULL;
+  rstr = NULL;
+  rnum = 0;
+  sb = FALSE;
+  qdbm = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-qdbm")){
+        qdbm = TRUE;
+      } else if(!strcmp(argv[i], "-s")){
+        sb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = dowrite(name, rnum, qdbm, sb);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+int runread(int argc, char **argv){
+  char *name, *rstr;
+  int i, qdbm, rnum, rv;
+  name = NULL;
+  rstr = NULL;
+  rnum = 0;
+  qdbm = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-qdbm")){
+        qdbm = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = doread(name, rnum, qdbm);
+  return rv;
+}
+
+
+/* print formatted string and flush the buffer */
+int printfflush(const char *format, ...){
+  va_list ap;
+  int rv;
+  va_start(ap, format);
+  rv = vprintf(format, ap);
+  if(fflush(stdout) == EOF) rv = -1;
+  va_end(ap);
+  return rv;
+}
+
+
+/* print an error message */
+void pgerror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, gdbm_strerror(gdbm_errno));
+}
+
+
+/* perform write command */
+int dowrite(char *name, int rnum, int qdbm, int sparse){
+  GDBM_FILE dbf;
+  datum key, content;
+  int i, rwmode, err, len;
+  char buf[RECBUFSIZ];
+  printfflush("<Writing Test>\n  name=%s  rnum=%d  qdbm=%d\n\n", name, rnum, qdbm);
+  /* open a database */
+  rwmode = GDBM_NEWDB | (sparse ? GDBM_SPARSE : 0);
+  if(qdbm){
+    if(!(dbf = gdbm_open2(name, rwmode, 00644, rnum / DIVNUM, DIVNUM, ALIGNSIZ))){
+      pgerror(name);
+      return 1;
+    }
+  } else {
+    if(!(dbf = gdbm_open(name, 0, rwmode, 00644, NULL))){
+      pgerror(name);
+      return 1;
+    }
+  }
+  err = FALSE;
+  /* loop for each record */
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", i);
+    key.dptr = buf;
+    key.dsize = len;
+    content.dptr = buf;
+    content.dsize = len;
+    /* store a record */
+    if(gdbm_store(dbf, key, content, GDBM_REPLACE) != 0){
+      pgerror(name);
+      err = TRUE;
+      break;
+    }
+    /* print progression */
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  gdbm_close(dbf);
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform read command */
+int doread(char *name, int rnum, int qdbm){
+  GDBM_FILE dbf;
+  datum key, content;
+  int i, err, len;
+  char buf[RECBUFSIZ];
+  printfflush("<Reading Test>\n  name=%s  rnum=%d  qdbm=%d\n\n", name, rnum, qdbm);
+  /* open a database */
+  if(qdbm){
+    if(!(dbf = gdbm_open2(name, GDBM_READER, 00644, -1, -1, -1))){
+      pgerror(name);
+      return 1;
+    }
+  } else {
+    if(!(dbf = gdbm_open(name, 0, GDBM_READER, 00644, NULL))){
+      pgerror(name);
+      return 1;
+    }
+  }
+  err = FALSE;
+  /* loop for each record */
+  for(i = 1; i <= rnum; i++){
+    /* retrieve a record */
+    len = sprintf(buf, "%08d", i);
+    key.dptr = buf;
+    key.dsize = len;
+    content = gdbm_fetch(dbf, key);
+    if(!content.dptr){
+      pgerror(name);
+      err = TRUE;
+      break;
+    }
+    free(content.dptr);
+    /* print progression */
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  gdbm_close(dbf);
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/hvtest.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/misc/COPYING.txt
===================================================================
--- box/trunk/qdbm/misc/COPYING.txt	                        (rev 0)
+++ box/trunk/qdbm/misc/COPYING.txt	2010-01-09 16:15:40 UTC (rev 2590)
@@ -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.
+
+  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.
+
+		  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.
+
+  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.
+
+  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.
+
+  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.
+
+  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.
+
+  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.
+
+  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
+
+           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!
+
+


Property changes on: box/trunk/qdbm/misc/COPYING.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/misc/README-win32.txt
===================================================================
--- box/trunk/qdbm/misc/README-win32.txt	                        (rev 0)
+++ box/trunk/qdbm/misc/README-win32.txt	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,101 @@
+================================================================
+ QDBM: Quick Database Manager
+ Copyright (C) 2000-2007 Mikio Hirabayashi
+================================================================
+
+This is a package of Win32 binaries of QDBM.  It contains C/Java
+APIs, their utility commands, and CGI scripts.
+
+See http://qdbm.sourceforge.net/ for more information.
+
+
+The following are documents of specifications.
+
+  spex.html       : fundamental specifications
+  spex-ja.html    : fundamental specifications in Japanese
+  jspex.html      : specifications of Java API
+  jspex-ja.html   : specifications of Java API in Japanese
+  japidoc/        : documents of Java API
+
+
+The following are header files of C language.
+Include them at source codes of your applications.
+
+  depot.h
+  curia.h
+  relic.h
+  hovel.h
+  cabin.h
+  villa.h
+  vista.h
+  odeum.h
+
+
+The following are dynamic linking libraries for the API of C.
+Copy them to the system directory or a directory of your project.
+
+  qdbm.dll        : QDBM itself
+  libqdbm.dll.a   : import library for `qdbm.dll'
+  mgwz.dll        : ZLIB
+  libiconv-2.dll  : ICONV
+
+
+The following is a dynamic linking library for the API of Java.
+Copy it to the system directory or a directory of your project.
+
+  jqdbm.dll
+
+
+The following is a Java archive of the classes.
+Include it in the CLASSPATH of your environment.
+
+  qdbm.jar
+
+
+The following are utility commands for testing and debugging.
+
+  dpmgr.exe
+  dptest.exe
+  dptsv.exe
+  crmgr.exe
+  crtest.exe
+  crtsv.exe
+  rlmgr.exe
+  rltest.exe
+  hvmgr.exe
+  hvtest.exe
+  cbtest.exe
+  cbcodec.exe
+  vlmgr.exe
+  vltest.exe
+  vltsv.exe
+  odmgr.exe
+  odtest.exe
+  odidx.exe
+  qmttest.exe
+
+
+The sub directory `cgi' contains CGI scripts, their configuration
+files, and their specifications.
+
+If you want an import library or a static library for Visual C++,
+please obtain the source package and use VCmakefile in it.
+
+
+QDBM was released under the terms of the GNU Lesser General Public
+License.  See the file `COPYING.txt' for details.
+
+QDBM was written by Mikio Hirabayashi. You can contact the author
+by e-mail to `mikio at users.sourceforge.net'.  However, as for
+topics which can be shared among other users, pleae send it to
+the mailing list. To join the mailing list, refer to the following
+URL.
+
+  http://lists.sourceforge.net/lists/listinfo/qdbm-users
+
+
+Thanks.
+
+
+
+== END OF FILE ==


Property changes on: box/trunk/qdbm/misc/README-win32.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/misc/VCmakefile-old
===================================================================
--- box/trunk/qdbm/misc/VCmakefile-old	                        (rev 0)
+++ box/trunk/qdbm/misc/VCmakefile-old	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,169 @@
+# Makefile to build QDBM using Microsoft Visual C++
+
+
+
+#================================================================
+# Setting Variables
+#================================================================
+
+
+# Targets
+MYLIBS = qdbm.lib
+LIBOBJS = depot.obj curia.obj relic.obj hovel.obj \
+  cabin.obj villa.obj vista.obj odeum.obj myconf.obj
+MYBINS = dpmgr.exe dptest.exe dptsv.exe crmgr.exe crtest.exe crtsv.exe \
+  rlmgr.exe rltest.exe hvmgr.exe hvtest.exe cbtest.exe cbcodec.exe \
+  vlmgr.exe vltest.exe vltsv.exe odmgr.exe odtest.exe odidx.exe
+
+# VC++ directory
+VCPATH = C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7
+
+# Building binaries
+CLFLAGS = /I "$(VCPATH)\Include" /I "$(VCPATH)\PlatformSDK\Include" /I "." /O2 /nologo
+LIBFLAGS = /libpath:"$(VCPATH)\lib" /libpath:"$(VCPATH)\PlatformSDK\Lib" /libpath:"." /nologo
+LINKFLAGS = /libpath:"$(VCPATH)\lib" /libpath:"$(VCPATH)\PlatformSDK\Lib" /libpath:"." /nologo
+
+
+
+#================================================================
+# Suffix rules
+#================================================================
+
+
+.SUFFIXES :
+.SUFFIXES : .c .obj
+
+.c.obj :
+	cl /c $(CLFLAGS) $<
+
+
+
+#================================================================
+# Actions
+#================================================================
+
+
+all : $(MYLIBS) $(MYBINS)
+
+
+clean :
+	del *.obj *.lib *.dll *.exp *.exe
+
+
+
+#================================================================
+# Building binaries
+#================================================================
+
+
+qdbm.lib : $(LIBOBJS)
+	lib $(LIBFLAGS) /OUT:$@ $(LIBOBJS)
+
+
+dpmgr.exe : dpmgr.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ dpmgr.obj qdbm.lib
+
+
+dptest.exe : dptest.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ dptest.obj qdbm.lib
+
+
+dptsv.exe : dptsv.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ dptsv.obj qdbm.lib
+
+
+crmgr.exe : crmgr.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ crmgr.obj qdbm.lib
+
+
+crtest.exe : crtest.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ crtest.obj qdbm.lib
+
+
+crtsv.exe : crtsv.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ crtsv.obj qdbm.lib
+
+
+rlmgr.exe : rlmgr.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ rlmgr.obj qdbm.lib
+
+
+rltest.exe : rltest.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ rltest.obj qdbm.lib
+
+
+hvmgr.exe : hvmgr.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ hvmgr.obj qdbm.lib
+
+
+hvtest.exe : hvtest.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ hvtest.obj qdbm.lib
+
+
+cbtest.exe : cbtest.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ cbtest.obj qdbm.lib
+
+
+cbcodec.exe : cbcodec.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ cbcodec.obj qdbm.lib
+
+
+vlmgr.exe : vlmgr.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ vlmgr.obj qdbm.lib
+
+
+vltest.exe : vltest.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ vltest.obj qdbm.lib
+
+
+vltsv.exe : vltsv.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ vltsv.obj qdbm.lib
+
+
+odmgr.exe : odmgr.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ odmgr.obj qdbm.lib
+
+
+odtest.exe : odtest.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ odtest.obj qdbm.lib
+
+
+odidx.exe : odidx.obj qdbm.lib
+	link $(LINKFLAGS) /OUT:$@ odidx.obj qdbm.lib
+
+
+depot.obj : depot.h myconf.h
+
+curia.obj : depot.h curia.h myconf.h
+
+relic.obj : depot.h relic.h myconf.h
+
+hovel.obj : depot.h curia.h hovel.h myconf.h
+
+cabin.obj : cabin.h myconf.h
+
+villa.obj : depot.h cabin.h villa.h myconf.h
+
+vista.obj : depot.h curia.h cabin.h villa.h vista.h myconf.h
+
+odeum.obj : depot.h curia.h cabin.h villa.h myconf.h
+
+myconf.obj : myconf.h
+
+dpmgr.obj dptest.obj dptsv.obj : depot.h cabin.h
+
+crmgr.obj crtest.obj crtsv.obj : depot.h curia.h cabin.h
+
+rlmgr.obj rltest.obj : depot.h relic.h cabin.h
+
+hvmgr.obj hvtest.obj : depot.h curia.h hovel.h cabin.h
+
+cbtest.obj cbcodec.obj : cabin.h
+
+vlmgr.obj vltest.obj vltsv.obj : depot.h cabin.h villa.h
+
+odmgr.obj odtest.obj odidx.obj : depot.h curia.h cabin.h villa.h odeum.h
+
+
+
+# END OF FILE

Added: box/trunk/qdbm/misc/benchmark.pdf
===================================================================
--- box/trunk/qdbm/misc/benchmark.pdf	                        (rev 0)
+++ box/trunk/qdbm/misc/benchmark.pdf	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,588 @@
+%PDF-1.2
+%âãÏÓ
+3 0 obj
+<< 
+/Linearized 1 
+/O 5 
+/H [ 997 194 ] 
+/L 52196 
+/E 51873 
+/N 1 
+/T 52019 
+>> 
+endobj
+                                                             xref
+3 30 
+0000000016 00000 n
+0000000944 00000 n
+0000001191 00000 n
+0000001395 00000 n
+0000001539 00000 n
+0000001745 00000 n
+0000002240 00000 n
+0000002419 00000 n
+0000002440 00000 n
+0000003096 00000 n
+0000003117 00000 n
+0000003679 00000 n
+0000003700 00000 n
+0000004424 00000 n
+0000004445 00000 n
+0000004888 00000 n
+0000004909 00000 n
+0000005424 00000 n
+0000005632 00000 n
+0000005979 00000 n
+0000006000 00000 n
+0000006581 00000 n
+0000006602 00000 n
+0000007090 00000 n
+0000007111 00000 n
+0000007620 00000 n
+0000029117 00000 n
+0000051731 00000 n
+0000000997 00000 n
+0000001171 00000 n
+trailer
+<<
+/Size 33
+/Info 2 0 R 
+/Root 4 0 R 
+/Prev 52010 
+/ID[<21b37df8f836949f6a724722f5ba950d><21b37df8f836949f6a724722f5ba950d>]
+>>
+startxref
+0
+%%EOF
+     
+4 0 obj
+<< 
+/Type /Catalog 
+/Pages 1 0 R 
+>> 
+endobj
+31 0 obj
+<< /S 36 /Filter /FlateDecode /Length 32 0 R >> 
+stream
+H‰b``a``~Ê Gw1`PZ ˆ¥ ˜A”Ÿ1ñ
+ë	ÙÂmX¬€¢?L00|gØÍÔÃÀ ØÀÀ`•ÀÀð%øɆ&†' }  ¢ÔV
+endstream
+endobj
+32 0 obj
+90 
+endobj
+5 0 obj
+<< 
+/Type /Page 
+/Parent 1 0 R 
+/Resources 6 0 R 
+/Contents [ 11 0 R 13 0 R 15 0 R 17 0 R 19 0 R 23 0 R 25 0 R 27 0 R ] 
+/MediaBox [ 0 0 595 842 ] 
+/CropBox [ 0 0 595 842 ] 
+/Rotate 0 
+>> 
+endobj
+6 0 obj
+<< 
+/ProcSet [ /PDF /Text ] 
+/Font << /TT2 8 0 R /TT4 21 0 R >> 
+/ExtGState << /GS1 30 0 R >> 
+/ColorSpace << /Cs5 9 0 R >> 
+>> 
+endobj
+7 0 obj
+<< 
+/Type /FontDescriptor 
+/Ascent 985 
+/CapHeight 0 
+/Descent -216 
+/Flags 34 
+/FontBBox [ -165 -307 1246 1201 ] 
+/FontName /ADKHFG+Century 
+/ItalicAngle 0 
+/StemV 0 
+/FontFile2 28 0 R 
+>> 
+endobj
+8 0 obj
+<< 
+/Type /Font 
+/Subtype /TrueType 
+/FirstChar 32 
+/LastChar 122 
+/Widths [ 278 0 0 0 0 0 0 204 333 333 0 606 278 333 278 0 556 556 556 556 556 
+556 556 556 556 556 278 0 0 0 0 0 0 722 722 722 778 722 667 778 
+833 407 0 0 667 944 815 778 667 778 722 
 630 667 815 0 981 704 0 
+611 0 0 0 0 0 333 556 556 444 574 500 333 537 611 315 0 593 315 
+889 611 500 574 0 444 463 389 611 537 778 537 537 481 ] 
+/Encoding /WinAnsiEncoding 
+/BaseFont /ADKHFG+Century 
+/FontDescriptor 7 0 R 
+>> 
+endobj
+9 0 obj
+[ 
+/CalRGB << /WhitePoint [ 0.9505 1 1.089 ] /Gamma [ 2.22221 2.22221 2.22221 ] 
+/Matrix [ 0.4124 0.2126 0.0193 0.3576 0.71519 0.1192 0.1805 0.0722 0.9505 ] >> 
+
+]
+endobj
+10 0 obj
+578 
+endobj
+11 0 obj
+<< /Filter /FlateDecode /Length 10 0 R >> 
+stream
+H‰”TQoÚ0~÷¯¸Çä!Æg;¶óXJ†2
+¶‚§J«ú¢Ð²®T#Aýû;§$”Áh§HŽåÜ}÷ÝwŸ3¸¬SXÔ xêôÁZ/Ø`<G¸¯Â
+˜Q\“Ž´U\hHPre`S±%ÜêÞ®Ñeiŝ©SŽ†²”æv—6ôlཿd¨ª çug¨˜¬CJ
+üíwÂàÛå…EÃj½xx*7à«ºç%Œ†nž›‡jSÇþ'Khº·}$k,>ށÿW
+ľçvgRnõlŒ&í^{ŽfU½ýÕt5$ZèÞ¡ÈþëîPüuÜŸÌÉxfÚúí†Ê+ÊëVû¶üM4½˜äq’Ê”òùå¬ø拯Ó8qNf\F׳Âçà‹†(Ï¢Y~1êNB3}*¾ä0/~äñ­ÿÌrÏ~³¶DÉ`«°š ƒtÁ×°þøA¥x&wú\…Fðê8>8ÕXwOC®“¾Ý Î:h͍«Å#ŒÊ¦¼+ë
+&庼¯6€Ü‘ɏõSF„±ò»‰(<#i²$Ò
+‹ít)%Um+HBJiRº \J—µsš¾CUʽK¦ÕË1QºrçXJvÛ±$lÛ³´Üe-³0s‡ZŸ£9‡&ŠÞMÑ|{W7«fÛT§d¥Ò'ø’ñ\{+DƵîUE‚„­ã*"w‹ž0ý6¬=aa¥: <>O8ÍìÞ-ãé÷ÓPg˜)z¦ô?Þ)YØôpÒ:ßþ` 6gDé
+endstream
+endobj
+12 0 obj
+484 
+endobj
+13 0 obj
+<< /Filter /FlateDecode /Length 12 0 R >> 
+stream
+H‰””Mo›@@ïüŠ9‚*F³ß»Ç`zh¥Vu‹ÔƒÓIå6Á*F•òï;PÄN—]13ûôfv¡x¥2 9© (¥PäQ\äYRüŒ<’â¯_PI²`¼AëIAñÈ¡ÍáÏ¡|€¼lËï婁„¶ËlŸÝ/”%ÔÔgkÞÿËÞÅ­NRÃåPÆ’	’T{í1ÄF±É·ÑJkž!n–B­Ïˆ‡ú	6ÇúÔ–u;:³Äé$:yæ$ônää
+œ*5¯sfËœ–Cià̪æWõP=AžFÍ?–𺅟i”#žé7ž¦0h|[D¿#kø`F[ölIIÌà¡©¢¯PGYqqbŸÑÈå¤føuÛ‘qÁíu<×$ëüóø<ûfEzóe³$£¶go mª
+n>½ƒã=l¹ÜÆå鮪÷‡ú›}ÕÜ&K†f ;ž-55P ó£!©ékCŠ Π×k)‹tîåAóøAÐçù’ Î8äk‚ZhÊz||QÑèýê.„:7ΣŠ9dšÁ‘rî«w‚‡{E[¥'õ’šüoW¥B7>¬rz>:¯SWáû³t-Wørá—É×éæ2r¦›ÔL·0tô_ P'6ì
+endstream
+endobj
+14 0 obj
+646 
+endobj
+15 0 obj
+<< /Filter /FlateDecode /Length 14 0 R >> 
+stream
+H‰”TMoÛ0½ûWðÖfpùÛémi»Ê­‹íÐÃ[©µØr É[Ó_?JŽÓfëR,AZ|â#)AñÉBc ø¬4!iNSˆÓˆä9 h½Ó+Å65t+X¨®äZù …hù¤øáM)¡aÁø_\Çï`ôMàðÿâNCJ’CðÑp¸Oo§{€|=ÑWƒ½kæžYuCŠêfIF’p§nQ
+K.˺ej
+†k¸b:(YSö
+36Ï,îOg³ï'Àd+˧‘Ï’WÌ°%Óœ¸B`ˆ¦C‚_•0.Ì36âø”RûÅËNUš ÜpVý^qSÖÀšÆò£÷hKU¼óNçq˜šÃšo]rÖþÉšÞ%ÇvCbÝã{>]n±Nmòi`¾Óáœø{;|aG'„ìI‹^Ú<7L±–®ôžèb~=°pcÓ¯„Þ4lë2_Úª6\­:Õ2Yò}´jnÏ೐ý#„$&Q€Õ(É.¿áNï­6¼õaÁ¥}C@²«Op¾¸ó! a|=‡›÷×>`åzÁ*(âpd9ïڍ@&ÑÉ3x(KˆHDBlmïú<ýÝO|xhIJÄÐi7æãqÍw§•ÚÓšPJòx7OwR× ;(Ø4RK×ÎÑãÆ=kÑt`u×/%ˆb7(¶÷CÜqke,™ÙÔíy2+|+Y¯¹Stœ;|©º5—ð«ÆGkÌÔ¸?p½;šËÂKC2,'%)ž¦YŠ™Ä–„@oåY;H­o§xHñ† Ípí vÿ ÄÉí#€$J0Ÿ# waŽ5Ä™­ÈÞ2;pÇ8Jù0 åt‹c
+endstream
+endobj
+16 0 obj
+365 
+endobj
+17 0 obj
+<< /Filter /FlateDecode /Length 16 0 R >> 
+stream
+H‰|”»NÅ0†÷>…g¤câĹÍgCb at g`@L\Hp^»4	AÔêP©þ¿Ï­“–ç%,… ä³Çêáà°&ø|úU§*w£î2&Þ¯ÇÑ*—€”ŒzvZدǂÉx½È	Ùê-½'}´_wó<^ç
+:&àÈmL$ClM^ÏŽ'pëu:
+z%è8]—/ ×pwïàqIÚ…ÀË7™¨«°ºNËÇBÒ‡4#só?
+Šukvï=À¾*ýö‡xˍzŠ[ß{ß³†g"šÇ»(31<-Ð=3Ñ=0›ž-0<Ñ=Q¶k±<[`x&¢{dß&gy¶ÀðLDóÙ@ÑZ¯èž™èž ÇÌZ¯ž‰èžD¬9·ÀðLD÷”ŒÞšsÏD4S at o͹ºg&º'Tt¦gÏDôsá³@íi(º¿LÌ£#TÒßZªà“œŠµÉ…vI¼áÖå[€ £µð
+endstream
+endobj
+18 0 obj
+437 
+endobj
+19 0 obj
+<< /Filter /FlateDecode /Length 18 0 R >> 
+stream
+H‰t“»nÝ0†w?ço’¨µy‚ C è$Ú}ý’ŽeË©²ãüüÄ;a³jØ…A¡ÀëfÐÁ¤‡o§0€çíiûµ®ØêP	Y€KÇ&ðûçöÞOàôs1üTùˆ^¬)’÷‚½¦#3Åo´3r…ÚAjã!‚Êè×ïóãFðÂÁ_ØX…FPuwS¢™Ãÿ>þüXú0‘|fžº!yzøöpÙ)¾ Ô3Ã…]»buøˆ/Ö³JžøˆYU±=ÿPöID¢e™¢jÁº¿—uŠÂ)VF[Û©¦gqA].Y}ó/”Ö°D‚1[…„Í:Ì¿1„³±ìY¶Q.’«qk¼íUÇ¢® m%C±úW@¥¬«44_Úm_;‰ÔêÒ®œe±•t³¸AúX{'ɸ,†K»xK1¶C—õKœMÌ‹iïíìËpÛàO·x¯±ûxûü$ˆ’Mž‰S„S4i&Ná „ò°'âl7âCª>§0;›k¹„ƒÐ¸Ñ2×r	ƒˆC±¹–KD‰Î™^ bl2gz	a¬·®ÿ` åqÕ
+endstream
+endobj
+20 0 obj
+<< 
+/Type /FontDescriptor 
+/Ascent 859 
+/CapHeight 0 
+/Descent -140 
+/Flags 32 
+/FontBBox [ -121 -137 996 859 ] 
+/FontName /ADKHNA+MS-PGothic 
+/ItalicAngle 0 
+/StemV 0 
+/FontFile2 29 0 R 
+>> 
+endobj
+21 0 obj
+<< 
+/Type /Font 
+/Subtype /TrueType 
+/FirstChar 32 
+/LastChar 87 
+/Widths [ 305 0 0 0 0 0 0 0 0 0 0 0 0 500 0 0 500 500 500 500 500 500 500 500 
+500 500 0 0 0 0 0 0 0 633 637 664 648 566 0 680 0 246 0 0 0 742 
+641 0 0 707 625 602 590 0 0 742 ] 
+/Encoding /WinAnsiEncoding 
+/BaseFont /ADKHNA
 +MS-PGothic 
+/FontDescriptor 20 0 R 
+>> 
+endobj
+22 0 obj
+503 
+endobj
+23 0 obj
+<< /Filter /FlateDecode /Length 22 0 R >> 
+stream
+H‰””=oã0†wý
+vð(‰úë:(z@\" CÑ©¸E³ô†ûû¥lŦsYé}Š”)E+2,M6C¶úS‘Ë€“Á[}™ÄI,™È‰$±ð&@‚X…F›da§E°á:àØ kK$i.|äê’fa%\
+à‚ š ˆhx‡’˜AøNÖÙAYÆ<~ýÝ ³ îfÜÔ9+asfCMD"ˆ²Î&|^t@ ­#G5õ£ÒF—w• I#?ÓÀX>$ƒ&ùàÓå¤pòÿ¼)Ë\ÖåM½th¬ë
+ÓùSî­w:Ó[fXÅÞ#®SÓû	^»õÝvJÛ©ï_ËO¸´©”i` S-Õ"ÐR*×ý‡Ç¾|(Yï,sÁê2ªîiuùulÖîߨû%*Yî“´Ê8œƒª$­»Ùº’jhAüA(¹UKß
+ew{¼«H¹Y¥ÃÓØŠ0è.×»ˆiJÙet}¸‡DBª×+ò:$˜väëeüú­Þ'ÀówÙ€	àKfC=ì9¿G«å›WÅÚ‘¿Z‘'¾qõn&î‰&N5¿8t¸©'o{Þ(`;oë`{ÞºçÃCÙëòð¸oÛ@ˆ”µ|×-µ¤.ó?Fú¤5 ñ2éa;Šœß È»ú
+endstream
+endobj
+24 0 obj
+410 
+endobj
+25 0 obj
+<< /Filter /FlateDecode /Length 24 0 R >> 
+stream
+H‰|”MNÄ0…÷=…×HX±ãümá£Yp «	Xp}ìvœ6e¦ªÔÑôù}q;0$†_˜2ccNUAŠýj	«À÷ÛtžìùyrÄ ÉãYᑪ [໓DŒì •#/ ¯‰JÀ”	RB®0g@±)Fõøìº-*|ì…œáb÷1®wÌhèɘø sÕWÌ`èÝâѦ\_1ƒÁ1‘¥ÞǸÞ1£¡cr at 9¨ë+f0tL«(r€¹ê+f08FbÆxP×;f4tL‘ÃM¹¾bƒc±6ã}Œë3Œ6d`ؾÏÏú±e&ŠŒ¹W!–†¹®kœÍ¬cµ}ë\°G)mh84,4—yŸl‚<$=|ˆ•0¤ÜgÈ#JT)†Æ–ÊrÚb’ØGmZcPœŒœ#Õœ‘d·TˆI›Cëñ¶®­¬µ0ŽÝ,vqìh9-­kâ[„¦å€[uUNÏ!Íôr!;>
+rž²Ì9Œº_¡=Ë}ß\?\6¬£ ¸
+endstream
+endobj
+26 0 obj
+431 
+endobj
+27 0 obj
+<< /Filter /FlateDecode /Length 26 0 R >> 
+stream
+H‰t“ÁŽÜ †ï<ÇL¥XÆ€ãf§ªT©+µÃ­ê©ê¥Ú¹´ï/Õ	d0ÕîÌ!øÿ?lãkœ'àà¬+0ÛWãBä] yÚ»:A˜QC8‰ÀIá$r˜«¡žBVÄN‚‚îc'Q2„ ‰‡Ð‰à¼îc'‘Â\eˆŽÀëÓáœé<äûCˆè @Æ·RdÉÍšh‚"¸ÈSMPDd(¤‰&("˜€#V¾wò.4ÐE¸y"š dpÓIš0ˆâåe) ÅÃÏIwÙâᳮϺv›û0Ï÷p3[5Y®ªEùGEºB¶„R>£·õnððÿþ4ß¼¬TäÒðâP~—Õ–¤Q”®?yaòâñD¦ÉË“WzÄ%µêxùQ?›´_ýÁc±U’Ê"ËÕ¦v€åëuûr©¿
+¦ìê€3E[¯fy–wi²nÝZßØöé±-ç'«^·÷v=7KÕbN‡³õM
+ç9ßÞüºÕõéö¼#õо½\{çJ’ï¿=]é[>VÃ$Ÿ³rÙِöH¦djöÏ/¹ÿ åè·
+endstream
+endobj
+28 0 obj
+<< /Filter /FlateDecode /Length 21406 /Length1 30692 >> 
+stream
+H‰|V
+pTÕþî½ïm $²‚yË#AHB4D„ðÈn 	`Ÿ»Hénþ?1I#¨Ó†ò×¾P¦X„–ߪ(¼¤¡¢0t*C+ëÔheDÑj¦­yýÞ&a Ú¾›³÷œ{Î=÷|çœûò  $¢
+e÷ÍÎÉ­(/Gpufe]¤aBÕÑ ±­•MÆó“§îo€žVÓð`Ýåñå“ÿÐV>¸tEMϩӹýHmu¤ê”l»HÏqÏèZ.x[zžâ§<´¶®qùÙíçR(ï$-_Z_Á±íyÀb͵©‹,oˆû‰Æ³O{ã¡H]uaÚé ДÅxnk¨_Öȸù4%¸ú†‡«j§?ž$Ó§'OýôS¬¯†PÏ 
+p.vSǏœ«Ôù8_Sk‰à#|ûYJúe'ëü§ÛÔY#G‡‡;clûw[ð)ŧ¤fÒB<,¼IhÃýø!v‰¸F‹ #éÅß“˜Š{ñ0þA>„Fá×TL£ïW…‰9ø5á)œq~‹8x‘„*T£†>wãW0è#ŽáM-	èt܉r´à8®ŠR±NݏBÑÛ
+¼‰w¤’V~Ì@V£M~O>‰ž=
+K°ûÅu‘$.ieÎ0g£³É9åü§b&ê°/ãÇï9®]d‹÷•¡šœFg§ó#ÎÁ"œG«ãx%š‹<q@¼.®È9N¶«ûôNñÈäy!„QÏ3›ðVbZ±{èý$>Š1ôƒÄp‘+F‰ZÑ 6Óχr!säj¤jS¿Qç´<ýiç:ÅÓ F0¹˜€ÉD\N|ÑóZæf#†ÈŽá}Î5b‘h»ÄAqX|.óätù±¼¦ªÕj“: ë‘oÞîØÚñ§Ž+Ng®sÄá½ ÿDÆ]“Q¥ÌIÑ~Ÿ'-bvÁrâX[°
+;X™6ìÇa"z§ñG|ˆËÌG’Hwˆ{Ľb’˜'Ö
+K<ÍHN2GŸˆËRÉÞ²¿Ê1œHKe¹¬—äV¹WÚ²CPeê¨zO}©Ý®¥h†–«= ­ÒžÒjoéóõ%ú*}‹¾Ë3$né¿þðÍÈŽ„ŽšŽˆ"Í™èÌsuÖ;ç™-ðv)x8zÕ@öÏPöf6F¢€yó³§bÇlÌÅT¢–7æ!VÊ­•‹ñ1ü˜([˜×'°	›±»ˆt{t?^ÄQVÿÖð5üçØïs\ÄGøWð58¬mœH‰ÌD31Tc.Ɖ‰b¦˜+ˆ*V§^<̱Ž£Ul¿`~öŠ6ñ¢8Å,½%Ή…#5ÙW¦ÊÑr‚œ"ýr6GP6ÈuòI¹M¶Écòwòmù®</?UšJW÷¨õ3µ‡c?‡­Nª/Õ7´^Úhm¥vIëСûôÉz1³xR?¥¿¡ÿE¿à™ëyÅóϸPØIJÿÖË­†¨&Ô‹lT«—ÙYç°GL>9L\Ãí-í]™£%£D'ú¨Åfæ5ÞÙó"QÈ"‡7ø¸ªÑWy† 3n)Lޝ²Å-M¬»µ29_6É	ªãT®6C¾­Ý­.ôHÑŠéj_æ+ñïàBácoe%LÏX `lþØ1÷æÊ½û®œ‘ÙY™#†ß9,#}¨9Äg¾cPZêÀ)·÷Oî×·O’·÷m‰	½â{öˆóèš’Y³(lØa[Ë0§MËve3Â…ÈMaÛàRÑ­6¶Ž™·Zвæ¿,:-nX
+¯1ã³³Œ€iØgü¦Ñ.æ—ɯ÷›!þãgÄx-#&$Rðù¸Ã¤Ôú
+[„€]ÔTkÂ~ú‹öŠ/4«ã³³ïE¶9»ÈlˆŠ¢‰"ÆÈ¢@~T¢G"£²KLÀ.6ýn¶JDªì²ò`ÀŸêó…²³lQXiVØ0§Ø½3c&(Œc{
+í¸Ø1Æ"ZŒhÖ	«µÝ‹ŠpfB•YY´U$äž‘”iO5ýöÔG/¦dgµ‹¶9A»ga»ÀœàQ”8ÍÑâf¿?äžÖ§0¸îfóTeR®hYë{gyðf­Ïý
+…è4;«tVÐǨÍ@«á˜Œ! S‘’à Ý5f'àj3஄vOsŠYk-³X-³Vø^XRpÔy%Ú4}ö¤T3ñ§EûÁšµâPqQ|«&;+êMêÌtô¶Þ]LBâÍLõ
+]Œ‹™»£îNµp#2‹Ù"¶Qi0’ iËô1îOõX•chÆ'$˜ÑEÌ_Øòæ»…ÐÓ½¦a}6‚yé³[W"]+žtïpY·]n´õݼ™iávJ\!KËÈ&Æä{²³šìR³ÁkØ¥LÊ‚ÜÊÏaÊ}>·Ê-í¨ `7—;e©/  '3dË°«9Ñ­Ižëjš»57¶‡M¶óa¸ŸlÉvŒ½½ýûjómÑÿÿ¨«;õ¼>#ªééVY0#bµ¤f„­ÖKSÄ«hYE¦Qd…­H»Ó\a^ÓŠ––Z
+p7¤vçDKª]ЪLª=ª3vß J•¡NN¦*r¥³ÍÒòùA#`…»
+×µ2&&¹/FF÷AG€ßz3ùÿçç=¿ŠÅ~Ó#·{º–äØ.Zˆ#jêµeÎym6yžÅ"Ïð=ˆ%òY¬&õäÇc…vÚ¯¡üSÎ{ܽ´¯&] …I©Ž´‘ôÒ6Òã¤&Ú¶‘6ÓG‘ëǝÕz”ë"A?å|ÎïÆiߣEœ×êó°˜|-c	«)˜Nƒ)ÏæzAÜza|s(×S.ãÜÈ=yz<ÖPΧn4}~w¦¾éúnçºöJ÷Už;„ó휟Óà\'¿²ƒ—ñLжü‡órâºÂø¹ó^¯×»8Œ˜C¤°¥ ˆgð`%Ýo ãÆ»<TQÜȆ5´Po+Ùª6‰Ò–‚Á­#Š ……Ô8-6ª,Qd§
+nQ+ ¢©‹€R‚*ÕýÎÌŽ¡¦ý£]ëçoæÞ;wÎ=÷œswa?QÚK•—i3ž‹H­4SªîUÎÓl\ÏÒ'büMZŠqË1þE<Û€g_ÏÒvøq6úæáÙ"Ø5jˆ|•6aÎW•«øfqœt¼ÿ%wÝÎÚßà5{kbû³6=…c#ì{Ç>—`ìˆm£a»ž„h¾<ûšÄ¾$‡oâ¾U¹*À¶¬k1ö¦„1ˆžƒÍ—À•-4÷عZ=‹õm!2ðõXåµÉÈF_D{Ç¿Xš‰;I¦ôU
+hø6«ÕSÛƒµl@ìu9ñ°…ÖÂŽÙÐIÊgT$owñ®K~ùx¾bÿ mâ¢6?0°‡ðy¯_e˜c<làý‹ðÞ‹uÿ|sçJùˆß|ú‚㟛ˆC¢“ïá™åü<æ}>»mëAü=‰³GÌjü"Êî—‡³ÇéE €|w9ÎÀV°¼	!V‹0~Ç-ÇÇ(Ç	Çæ
+þÅNìò:ÖÑtìÉølîìÀóßÓ@¾øi–¼¼áØe[½¹9Æ8v<uãœã_œ¼^Ž/O<œC¯²ìŽ3Oõ³ÈÇ›4“s‘óUëªt˜fq³=Uÿ0|›ýÄ{Îyʹ2¢ž}È[ÎO@Œz:⛬ªÓ)¡•Á÷@YD1ùöp}Eù1ô%š¦$8æ±÷?AßDZ|(Ï 3¿´‘¯¸®€MFéýŠØ¦^ ^ÇßWè-é·TÇàút|6ôØgÿîÛÑþòÔÛ—§5ŽëŒ§¼oRëðÙ¬ÞÊê=Wi&×B®G\¹&)?¼#¾¯Å\'¡Y¿?åÿQ~íïÑʵ”뙏Ò~ìUvýN=@NsMàwÀ7ˆ«á?s?×}mΤu8+ðÅYÛ
+ºÁÔ„ûýگ<„½wÜœã=f_°ÍÙ|[Ã㼺¯>rⳑÇpýçñ¼¶M»£æ‘ói•“lóvÄÅyªW>¡¥Ò}ÔäwShS¸–¿Lcœz„~¹mƒu­wƱ7¡^gkºü·–ªr`áðïû	x¶1Çs>Âý!Ò´í4O
+ÁÞ»¸þ¾ë¶9µ~+-à5«Ç¨ÅÙ‹lmá½5.ÒTã%ô98Ëò0fȍ
+ÇWçY]iÕ\©Í˜ƒO5Z/Ú<?Muü•õ£ç+õW8kØW<ç#ªf_iG1æyR
+Ìm\ÂõQZ`”cüvpõöô>=çœAÅøU…3M>Žu½I¹N\7âþ<-†M–Ãap1‰1ª‚k^ûm(ÎJÎù3œˆ!-í|>þìCL< ‹QP¡‚Îż‡QeçäóŽóóʇàsÄoöÜëäÃI<ã0ÆÏu“/Õ”þH–ãՋiÍ­+p®‚‡‘K—©óž¿ýà"Ú¸uAš¢\B=1|K=!íuñ®ŸÔш^·•y²ïmÿÕˆ,\ߐäß{ʵ¤ÀtƒÄw½’BÑШÚÔ
+æ‚pû3
+í•Èÿ6P¢ÀÆÜMð×Þ%9”’^£4è = _zÍÎSk›ûÌøà‘Áëƒò[Í]}`¶—™æ/Ä^|Só=³»ÏìïW.0ÃKò¤/‘R 
+î•,ÑM¶è¶Ÿ‘~TÛg&@õ_úÌ[L³ž1ï´Al¿Ù¶§Ï¼_dÆÛù>Ç´ÖÛë¥õñ¼ úgí}fhZR nP
+¤Á=€åᄁl Š:;ã·ñ3
+LZ2_ZF¥  N~p4½’PÇ—a‰–¸†¹®Qô‚ph#­	ÐR cÔ¨{âš=œs¢¡Ï¤ÎPg¸Óê´;cU†*ÕV¥]«Ôè`è`ø uÐ>;¨QC¨!Ü`5Ø
+±ªBUá*«Ê®²‡bU±Æؐ/<d
+ÙC²•gí<;87Ô¬]ö.ÉÚcï‘R»Ò»¤Ôžôé^§ðÝó{wì–RíévÉj·Û¥T<u!O_PLÅTK•RU1%S–L2…d
+SùBÁRQ*‡}á`™(“-Ÿ¬²í³ƒq—c¾X°Qn‘÷ËãE—¶ˆ&_Sp¿HùRÁ#"íKsO‰_G°Gd|™`¿èõÙõ¯‹ß@0×ÔCþR=ì/Ó-…nûãzÌߨ'ü-z“¿žòÑÓþSz‡¿GÏøûõ^ÿu}À_Ô‚z©Vª+ÞE™V¦Çµ¸n• VÂj™R¦6*j‹Ò¢QŽ¨¹¦
+”ªá@™j*T;WcF5hQ›ûÕTàˆšœR;=j&Яö®«"*Ø©…V¬@Æå1ì•á.qãÌÊY+®|êÊo\¹ìʯ]ù¥+u®ÔºRíJ•+•®¼âJ…+“\)v¥È•ñöfèð7ðWpÜ·À'àèçÁiÐÞm |ì»À6°ÄÁ°T;ïù“ûºK®ô¹ò±=z|
+Ž‚wVÎããKw‰zÛÖÓÇô4öêzúzz‡žÞ¬§7êéuúTcŠ6&ÏEF¡1Î(0ò‘gä9†ah†bHrLdž‘£RtM¹ˆfz7StS8ópMI—Èy¥6£–”‹L~”¢kË3ó"QTÕ™¹‘hF­¯>-Ä5é{]‚ÖVw‰	|ßZœÉ_Z}uhnë¾bÖáÖ}554.òô§päJDc»Ï!êŸýP7?×Íoêh‹®ASš›ÒŸëi§©Pœ‰Ñ÷£_OL¤‘GÿÃÄÿå#–«>mPyÍÒ:Wß—ü909Q<¹¦|\¨i±cÿÂÉ…{‹»ÇÈ©Éä–”g€»f,™±„»Pä¸+ÍÁlWáÞ…“‹»Å±lWÍcà@JîL&›wâ“L&E²9Ù\Ÿ¬oF>ܺ3ÒœÜÙœ„…ù¶yg$²3’¤Œ´|k&ì×™pɲŒ¶|k—ôõlÔÔH<QñG2é4í¥Žô:Ñ%EþEuõÇ6qÝñ÷½óÝùÇ™»³}wq~Ú	v~‘8þÝ€q.ËïAÚ-!Yjp5ʏ¦(„1Ú€Ú”@
+…°t	¬k‡ÒM]†Ã¦ñ#h‚vUò6¦¶†ˆM³Zi)ÝT|ì]ZèfKß{ÏÒ½ï÷}??üÞEdÄêûäáBÈ£Þãï¡*-z+œ‚Spá x;䕇
+…¾À÷©+Øk»É „©AÄ¢’)lè»™Ö±Èð"yÒAVºV2NäÏÌüƒÞXª²U¥¼VÒ„ü>I´1äTXxÊWe…ñJ+^)­ŽD|®¨·ðڏ:‰³ô²¢²Id4"$Á©Ø¨Ýˆã9WÁm䆹1ŽáþaãÄÒ"xyÇ[2M¢Í"ÉŒ»ÐM–pˆ8˜yô`Cóë2Ï>sàоW×Ó3áÑEõý÷§ÕÒGG7ƒM×.CÇ™ñ.gv/enUÊ¤±“v¦„,a:‰N²“éÔï%÷2‡ÉÃÌ)â4yš9¥·hõY©Ý¦KàD8ßMXÁ:?A¸²Þ)ËãÒbVI–,¢`h¦0²„»°àÂÓ¯&]?Í<ÒÜxð=3öÓ깫¿S~î»#×UðMÿ‚ŸÐj«CÇt#º¿ ŠN!‚€I0pߘ$@É4PiR馄iÜô“®ÕtÌD˜.±üƒ±ÅH:2<‹©™Ç(8¿àBà¬ƒ{àžšu@Í">‡5sPÍ…¿álƒê›ð#ôg܉µŠ¿
+£1LœÜ-Úˆ€$- HXæÖ±=ì0{‡Õ
+³cìev©<<$Ø)hÕz°3Ö›úg¯¥Ò#h˜áì…¡`À]ÀÐùOÈp~ùU“I²²e5µÁot¨oVü°‡çtd½Ï[»¥sWóqƒÈ%㸄ìÉ3âæ¯Iè¢È.¿À/ OJ[ïê5Q»ÉøIü֏Íú;V¢ØÑm"ç<†*Ã:CáŽ6ŒÆ1ÆSÄ„Û„]â¨K+è«â ÇW_ï÷54ÜnôýþF|ÖxþѼ.3ÊB£Jî9D5znQâc¶ÏÍê Bת‹ë:.I4)²eŽ79LŠ‰Œ›4|Ž-!D›¦ˆF”½$ô—ɵô5}Š²—Ù-»®,°¿Ì2Š$°Gâ»7
+‹C%Ž"«‡ÿóº}Ÿ,	<*È/t|8$çÓoñûÂ!'ˆÈôþ³êýß_NŒö}0püÔ¾ýõß<C
+žü: Ž©Ã§§al˜y«1­îT÷ª_†½0FƒÅöq‹êFjúe°´Y“ )…’ÎЦg%Ð_73×a£å:ov˜[Íqóó¬™F8fs†,`ôÆæÓóü¼EcCÕbŠ`i6YZ¢3è÷…‚BÀ]
+Á(„‰‹¶œ’kiï[_ùòÎ\k¦º¿pdsbK‹·˜¼STg«	áŠâø܍0tIi9ÅNêo ?šþäù»çsô™þ?¬Aﱛ엾°<ˆM¦¹+Œ\§u+ÿ‚uWÆ@ÆñŒ¥7Ê•nУžÈÉa„‰å•$¾}mdXZ·JBdrÙ*‚¢j#щ2‰dƒ)||å°•"X¯ˆz½ƒQ"Îì`Ì1fœ™Å2×ð­Ððð)~1•J÷¦SÚÆ1и1¡Ò[zcÐ[îô…Â~ÒK8
+š<òiwE~BÅ(ø}¹€gx]Š,¶(ŵŽ‡	(ø—·^êzÇÌ=_\ýì¶[{úf¶|í]ê¿7¯ý¥ú\Z at _ûÜdóÞ#û›ìf³1cƒ¿¦·kóÛÝ[Ïoª;í^óÑ·4taf‹¸—ÍÔúÑ8ä;ï#2èFßzj}ýVßKõt«•¹ú¥ýemNgÇÕUE×E‰hTh«£²Úôfä-*ZUík<Ÿ;ׁ.	eÊjvWx |<L†·÷¯‚U±Ùê»ÕŸV“ÕçwÉòq™”·÷[À²ÉÁ*ì§,‰ð#Î&Ø+ì,{—e–Œ¤eI%ø»¸ùtwRxì*»‰gž/ɵ$£Êúær¶ÚM¸²™Üvð×{ÛÁ¹í¤§ÙÛNæPYíÈ×hLJig;@ESy;jFžrûdiäkIíÃ
+¨,!¢YW9<ö.Œˆhˍ»nO|-°Ü¾†ò1á¯Þ"f‡[7ï9|çÀÕxóSþ¨£¨tõK]oŒîëÏ÷[ryáŠ~EÙÓ×3àE<¾ª5/ÞüíÝ>âェ·œ|gëË'6¬é¨YéòK²˜ÛRvzâÝQ=m·Š2ATW½}¤5_rùBÅO­©}á™]ïtŸAع¶c­ø±zE”n*R¡X$…Ä&©SB'³„D1	5J–,ÙdY’DDÊm:›
+™—™ÚÌ,ˆ2'‰g›ÐÃö³Ÿ°X¾(°¶ŠÕ±I¢K1Ò²ˆh©•2	· ½QêÆ„O’ò@
+I‚V‡¨ˆD\Ü!&ÄqqV¤DM,v
+æE¬—t„Ÿa|-•ü|oŒŸGU‘*ü£†¸ö¨¬<X^zð•ëÚß*Š_ð‹âÿ˜Èjp
+à$Ùê…ÉQÒ–þpKÀ›~©9ð1ÕýpmÛ‡Ù‚ÜÒ²¢d:NœLÇ4îcî[1÷Ð+•
+dÈÅrCqcÙú⎲meÌ÷åÍÅD¾^AJU4ªðŽvEÛÒ$JZóòìá$Ì)åÞ	«ÕN÷#Ž3Ï3ˆ*°›<‡¢
+½re~¶Þ`(Ÿ‹K¤é¿l—}PçÇŸg÷v÷îàv÷ööÞ–ã^ààx9ïÌÖ7`•8¢ä**(hEßÐh¸¤XÄÌÀ51*ZÆ¾ ‘ 5 at lÓ©Vmì$˜É`L¦1alÒ–DglÒoíó Z¦ÓàŽûãæ÷û~¿¿Ï÷;“Ê„’bó…MÍI0©¦ÑñD=¤g“Ó&Û¾°Ýµ©ªmÛ(~cÃN˜÷È	(RbáX¿ Å<§è_¬~ì>6ƒ¹ •òeîá/3lˆaùDfñe,Èo@´x-¾
+ÑŸYæÌ
+à7"sLJ~JìèÇ„f‰õjYˆN	‰CE0ãrÿúirRæy†ÙÐë)mqé)íÊ£µÛÞr-Ùÿäe§ŒyöÖ¢Ù¹\^ohçÒÎc§[BM!Æm-éñ”™¥öÎI4h¸ä-ÝÊ+©©Êƒ>U¾ýݾå+Š2¼ÞäüQÖ¯Ç.
+z½E~›Õ›©ÓÂÔw°º·¡
+úÐÓ@8)W]án²7¹oR~`)†ÍÏcɼ¬â¬Ê¬ÁuLmÖ†`]ŽšÖæÅV+©²/=]“ܧ§ü}]‡£ÛA8Â	5ÆSÙc4 ^@Ë4 Ëèj:B«è!Ø,[½cøÊÉ:²Zר‹è¢º]?ºtŒ+9wrMaL><ʪ‰ð£œB¿xc’♚¦fŠÕÌž™¹9<Uµ)8FLSs†EKϬュ|yÿÛNBªýµu女³‹-¦wáêžÍpÖžî#;¶G÷Ѻ•·Ï|¬|û:̽
+Þ+›W5wéüYöÔdÑ&˜w)7‡2~ùöÀŁw/žÄ.8Šf˜Bµ 	¬V¸ZÖšVfB¯3’˜Á­uõÂXë’,•IÕRTê‘hiÖÈLçÝ\?÷÷£\”åHŽ³%L]~ÞH´÷‘X \3]FZ–˜’TžP]HDÉ_­yhhÛW„ÊWž7;ß·|SÙþØ—¡'ÒS-Vß=å¼2úá‰e™Kæ]ú-T½¡<s÷šÝèÛ‘Ì ø¡EnÌôÏÖéVØV8+“*Ò*¼Uþ:}½mƒs]rmZ­w¿ŸoRïÒírïJÙíoÓ´Å·é^e÷ŠûÝmþ!Ýñ‚eØÍ«u05>5å	ö)v=\G4iv²ÛÄ^ãUcIBmn…­Jç¶ •?"(}VöùYÑïgÙdüÔÕŒ½O 2>~Â%T5›:LÝ(°ýe9~VU€Ä})BªHLŒqZWU#A$9ü«§Š×:µ²–¬Ö6j#Ú¨¶GÛ¯Õ2ÚÇŒ1¦;yÂyú*¢`}™,Š €Ò ¥åóa¤º0Ä ñèNå¹'­ŒQÿE“™…åFR=íë÷~ëÁÏ”N\Ø3¼Z¹R¿¸½õ§¯5/io8ÑÕ;ÔMêç¼ßr¬éè_l;7÷ù¿u~^qàé‹kCÛVíÙ×¼®¶éëÐÁMí]]/okB>ÝŽ¶äF[rƒ xK~6?®À“—ž(ñ§W²I+<éõÌFOmzËæ`	[GÕëH“‰ä±M33ù”>
+åìãu‘Äh"‘¶ÖNÑÀ7†ÁÄ—
 MÒ:Gؤic<íDæ%«éFdÝ(ÝC÷Ó£(´ðܲ³wÿ-
+ñ¸àt¢”}ˆžgÖfÚ’B.œáNò¹yïüäàß!wñc(v>x³ólñ쬅Ãü’º7×(w£Ñ­[ºŽ­YûÉ®B³rRù½¢(]g[üä’ù>WšU°¥oÌ
+âÚûÏœºðöék8Ýza<¢z€ºŒäË)ñ÷úÈ1†óËŒ›2RÆntPI‡WY¦ê¢OTA'xT.àŒnc˜ÙsÁ¢EÁ`qqê:ÙÙ%‹UŠ‚9‹Š³³ÇŒÅAT}PýAßâá= ¨O¨;À\àšü¢dÏ°W•öJg=¨ê흉ïrpz§Ã!D ÁAlà"Ç9€Ý¾Upˆ‚à°¡Jp9Ëy½^‚€¤Ô–&¡\R:ŽwQ´C/ˆv'eu8¤BfÓÌt3*æÞd-ð.MÑCD΀õ3ɏö7¶
+“÷m­ ½ K!nº!ÄSÀ <>…­>~Élå/_fB!|Õá#t˜L\Ò5¹ØÉGè "’ ®/ ε¾Ø€ÿ¹T‰y16´(¡ª2;öuEM¼¥ü@ÄQwþõÞòÒR‘MÔàœÁÅYE¥¥¥õ䇱M8™V£¹µQW¤€,0(»KÔŒTž•š
+2ÊùrNTn-㱏©T´h¡vÒ²E”<] ÁÈ5ðb@,IYì#’â0Q²qk#\Š!%#åê§ë–qhbŠƒs½›‹Ð9QŽÙhľ.¡°W‡¿Ú7MÁ´ƒLB‡9@ƒ8@Íð˜C3Øàšº÷“-D•<£­AÖ½âä°B<¸)XM’_Ý;yêƍÊ_ûc⮼`và8-/w–¼Ú·sŽîùÓýG©«×½Ôôþ*Úpz{©Û¬:–‘;~øìƦ¼do¬7v%-­«÷ð9tÚ~FÕ õ9ÁuY®§klõÎüv}ßIaòô‡ÄCÆÃæ~DÏyŒOÑ+鍖—¨b;u„:¬;‘pÁ6f‹ßL6“$IŽZ@Àc² >n'‚,TBTèha*2Ï7i@œ
+–[Tåäôð]Y0ȆjC¿A%
+i"–Ë–¸1^rJ£ÒÒ]‰þ‡• 4L<
+\È„Ó)sª´€ÂØx¸½Oók^АœŠï<¡çÍ“Ì$¸§yÀ¤:¶¨PZ0¿f÷™‘¿ü셏ï§;”OLJ•ëÃÀŒ3ÕƒÔ3±³ÿÞñ†2¨lUΟ;ðço”(ã#0<ú9l¹4¥GÒߤ?ȯË.ç,x(ùp!«­¤;'WΪ=c9ç%‰gŸ¬KD¹†C·á?l—kl×ÇçÞ™Ù—wwfg×;ë}Ž÷ýðck0cØÇ.éìÖ,
+~4ZS³ã l"¶@©!B¨ÁMHi#hÈZAU«ŽZP+ÑF<‡”4uQ”¤˜5=wlc*Uëï;æüî9ÿó?ØJò­Œÿ²Òm‚$DQj†UùãبÓêµm‘–Ñ„ÂÁp Lk–f±&ö#Ý(ŽáÖ–vSQ6Ñ=m4ñxÌ&V\¢] E5øöxn(¶¨)ŽrA5˜ðƒ³;Ž\_Zù»ƒç
+«VÇÊ"Ç°£<QU»ûç}͉Å{2wÙñÚ•S'¿ø`¤7WáWüAæŒ/O®†ž®éxù>êþå!ȧý@åY G^¹a¿yÐzÔ|Üúªû÷—n-ÇPîFFa™dÙ
+f%»Ê½Žiuw˜:¬î¬)kͺÝÇs£Ï“ädµ[k¸~J¶ù„J!%4
+?N	Nð	X \QÚn—´ñ4U‰Sãn’}QØ•.Mó^É›ôÊÞ&o¿wÈ«¥¼<,ho7ÊI'piÏ{E´Ncìñmó¤%ØïWŸHËîA–2T&<:º¢©’BÉPWˆ
+‘£K^W›W¥¢e"¿ôÁ1‘Ÿ¨@3ˆ[MÕòÿháD4'Ô±«eÖ2XK­¨oõ0Ôü´À2Ôƒ‘¬óèÇl?sÄ]}ûÀÔÝ@°oc[_ÏîÙÒ“¨ú=·hEÉä’
+‹“ñò,;>eúû¯Ã‘¿+4Ÿ}¥ðžäG·)±s$†³Ùgó-8›(u].{’}†Ír;mƒÜ€í§ÌqV×Æ¢W¹×m÷YZÏ®„ã¡%>ÉËüÏð9p­a£ÏZiMY­$y5Vdý‹1-Š^ʇ	zÚ‡?žáïDH›Ž¦ÉøÒ^omµ‚õæbegÀ3VäöÚàù âR =и`kìÖ<_˜È?ÂI$hn%CìLâ&ºU˜8¨þÍâÇPnbÞµÕW4·ŸH}›v³+VhŸïìK.~½°èµŠHlÞ! H!]SKii᳃‡Þ|¹p­*¦(¡8óZÔOüüÞÑ PÌ$õ7yù:öûš¶SÓËîÒœ`ÏDߊ){?ú~™QaÌö²ôQªÐQ:&v•¤ŽŠ0G7ªbÉl¡OÑç@3sØ G1֝® ¥>þtXO1ÆÒ@@rd¬ÃÒŒRö»ÈR—„¥n–MÆ›|\Š'ãtSÅGqÃ#Ó•'jȲEª(NäIמ˜AV«z0U]¤H¹ž3„ÊÂæp4¤/7vS\.E	]72Åøn*1×¥(u¦z$¨PT(êD€V—“eMbêæ¶ï{u¡eÊÆßÚsmóæ«{Î]™Wóî\½0¹<Q®ï½<õÛÑK¨îÖ_°Í¯ÞyØ}èðÔí©¯§NÝ;q 4ØÐ	>Ü—íí½ŠZnßA­Lúí¥ùäp
+5ð+6‡lò|P!O
+6ñˆO#W–ùÜérŽËx2¢9£÷eôÁá”Ø%^ÿ)2¼(‰I‘s()ÛªQF£)Ï3P™é¯FMÕ¨šdàbÒ_ óµ|ž¸ ’‰ðÉS)ÈDb¦­ÐtfÂÈ5ƒ Ô2DK$V|ÌÁîܚȮúp;Þѳ$„ß,ëÄ=ہ"ÉY¿±p°SM…gŒ%Kz
+ƒY)¬gCK7±ãJ¢yj!ú`MTQì.ËŠÉ¿>Åû%`ý.Æ…¯Vñ¥ŠâtðõLYs‚Z	æò+Õ­’ÍIÂÄqÆ´I~áeD·ÞÑsä'…nÉ–>µ®Z™FëëuhÄ„“d!‘Ô›Hc™Ž÷±x R}+ñÙçVÅGjoaÍž'¢‚[遷Ž¤&Gê,¢¢Dë˜v™áÝdP¢*x75*/\güÈø™qÒÈ,75™6˜¶›ö™Ž˜~oÒjŒ²	Ó®4ˆùlChR[‚–âxsDÆ#–"!mÑñÛÌiž‘¹vx⧡ÔE,Ȩ́å.C0v
+¡[ïj´Ã–Ù mâˆý¼ý*ŒAö!ûˆý†ýžµ“¸ÝkÔ–>)¤”zâDtæ0¨æì[Æoÿ,ªüö7ÿœlðO&³K«â±£À"V6¹5¸ƒ,…q¥r>³7DtdEû4ЈQ7/¤èF¹‡»ävˆmÅضaL£‡ã
+FðÙq	ͳ±4C³ÅŽ“|:‡¨C‘vÒÅ™ß@|
+¡šíÅÃPò)C»á”1,+Âí0-ú`¿„r¢÷J‚çôÉ ÄCAÅmTœŒ;3ZjÕ:È“Á@X$¨3ÔBíLMÔÎ
+/½ ð/¬TT`øçhÍæ
+€@{‘ë¥hœM Qt}Ó»¬|Áò­…u»ÌŸŸ(|‘\ûߟ­HífÇ'»˜!%\9¹9V”˜Ä|z»}òímó4Š¢_ßG?H¬­h®~êS¹ç¼u°ëY¶×¸ÝÚã<À0î·ö;O°'ŒCÖ!§Ùâö€= ³﵁-@”“{¬¿Ø‘æEÖàOëÀÝZx^›vù“vÙÞd§íc‚ö$6œäÚD$fÚüÈŸqáì9q9´_Ž
+YÞ%¹’®&W¿kÈ5âºçú—KO¹ÈMn²<\†\´k}Nfí4·|¯ê¨7™Ã ¸ª?ä2=˜½¨Y`mcÆÆfI#RŠP!¿,“8îâÕèô®šêÌ6®ªi}°áùþµM»,UÅžŠ:`»~²oÉB¬(e=õ“Ìèƒ}Ù°bö¶ÑŸfp¾[`fø: Gy¨¹³Y³VXíÅ49ó¸ön-^c^c[ëÞIïdèvÀuƐÓ^4\°\.Ú._/9.».yþÃ~m°ó§)Ú~Z§wÞ¬ddæ
+C'™v3[oòFɘ4vÿËw•7qÝá}{XÒêص´²¤•V‡×Zk©>‘±ãÅ€œ`1E&Øø™Rè@0¥„£´@B'¡)ÇŒ›Bè'6±qiÔÀ´C˜IiÓ”ÀLM‡éÔ´
+$@‹Ö}»²¡ÉýCowÞŽfgß÷û.<iØg@ÕŠ؝{æÔÄSÁLä/ÀaØEñY²)ÑßçÌ\L£FšÑÓòØûïÉLJÁSgÎ}ö`¸ïƒu/\$þzA>rýºü£‰s`Íõ?¶‰gä3òëò:ùÃÝ z ²J)’„š•§jV yMòµ®”Nç4)ò“—rBmuIòóä¡ïµàJÔ1#]0ƒgw
+ä :ÇSå%Hßëm¹MdRAV—L¾ÉÄDÕ ]nÕd	¹‚[ãm<
+Á5">,¯q&èGÔþˆÃ€ÓýWÄXi…Odýø>À7¼ó™üÀêŠOÏ=
+Õ¾Æ/}÷é7ŽwÏ%&nž:Õ¶MÖD-vè(nüm1˜î82Ü·«*\„`ÈKr/q"îCæ Õ€“J³ð]fmö=Ë/‹½Æì¨:n§ƒCáÁØûU_ £†Óúj4ؐuÔ1ʍãQ"¦E`Ø+¤ •`(*QFÚ’‹$âxÅ‚~‹®¸¿¢ÂiAHšô’qR"	r\‘¤¬õÆP¿ËE(ÌO'~©5÷‹ºV²Ï-æ‘<g=XÜ<7Äkgï±áI[»mŸ
+Kµ)óRód^Ô²KgCçVù4KOfÂQ唩„9¡*ØfòÉ®%!jws~È¿Cºü°t±âZDp‡Ö‚¨._NJ½ ¥×ŸcË™©œJ"zœ‹èÒ_fFŒE¨š¥¬ŒÙ&•®†eò••Qÿ¦-¼×JÑr°»û›[­s:b§å£W	Øq0çÌWJ
+º.¾ºéBwU5zdë·Ÿ­š‡Š‰õ­Õ,._½"T³=L=?t❿™L¿U“WÎÊo_¿,Ÿ×¬º3øø÷ì‘ÿ0=$Ë/å»6]V%:´¤cC[ëR[™÷¢éýpސ§KÔí,Ðaètä0Q«7$ÖÅ°¬‹uéxŽ¶0åõøC© vnª@1m„¡™$ƒ1#àšä£éLÀéÛ9:miÀÅÂ?á>K+Í fGé5•.‚XèòÁ8ì’ÄñW"†ˆ´èÛűOüP¼)j“"
+Uª fBZzê^ËTF$!¦éÞIhӏ锘1¥Çº‹Ëj%„bMi#V†–6‚¢¨Ñhç`s! tù,¿]˱2& q£qEX…@>ª4ŒÿG¼"ôѧŸ?7|~ïóÂî\Öí)ÎZ`¹]·Æie©y»Û7Zì…ßO/ÙócûÁ¹“õß\?‡˜¸üã•›™@ý+ÃÛâþæuòÏÕ–ÜÚÞ³ =æ§,õõ‚?,éþí'^ÞYà*^·}zÿdh>HIâÿná?Vj¨1H¬ä\N4º:‰U†U®Äz׉K¶Kö߸þ ‹µöNÇ(tv²ìTƒ8 ‰Ø€}Ð
+:QeïÄ°3(m„/±F4µ›Òb^`x^Œæ®àø5ü~/ˆÿƒÿ?Í“>ÆWÂ8ÏkÜ‚‘§p©ébî~«®kúa	:‚70І(ïI_³dn7÷˜Ïšqóš’hvÙÛ`Ⱥúgˆ¢KýLcP5'!úªúÕÚ¡6åGLk;BÿýÞ !åŸÜ©ÒªG<^£Õi#PcZãS®@Ö¨W¯Å=
+Z$Cí­[3ÄV
+PT%²¸!s>7 á³sÄU¹œƒ
+6ÕeU=u÷ðwŽK[îO¬”ïþþÖ»½µµa7|`¬ú­Ÿ/ßüðƒ–ãIJ­é[&¯­Y3_(é:ÖsQ~x`7Z1%ß9¹¹fM]QYíª±s« ÂeАòsiAþ	þ‘[èµø"¶‰mrÄßrÞÇ5•x9[îlÀX‚°c.ÆUr
+\·—;ÂMÃVÄÍnܺTʶCã2d§(“`©$
+ÐÓ 1Á Hê1ˆ+ÕÆNÙ¢”‰0G™KBÊ2wæÌ'!÷ÐÔ‹P*§d˜é™˜Ü	dáQßW³_”:M–†o¸…ä›e‘püé%'róò÷?—ä눉ôU0ÌÿÏÊ<Xrr]øI!°«÷®2ë%Ó“j®‹ G¥Š€±„,6–˜ªÉ…ÆjÓR²ÑøuÓ›Æ7MÇÈcÆŸ˜†IZ£/&Kõu5Ægø¬Ž<PªVÔIåÀõåÓÙº8kµ1ɝäPN	m¹Ú¥:ð
+qaŸpTD ¯Ð#ô	- a]ŒDU‚.®Äc¸L*©æa˜Ià|öþOWrƒÌwç£|ñì&tnEH2ºQ‚þtWiÔ–{êõË9bµKÑX°uÅ΃òŸ%"±Ø÷`yÈó?ß6ÿùõWO¢ÇšZŠõõßhO‡îlÙ»ùåå;°Í¹x6üôuÍoáÙÄA»”?ßúj{]´“ít†:¢¯Ø7q;Cߍê
+†¹†§
+uÖÚ Gô=iL7:BAïÈô
+éU2ñêÄht5©cHRg‡B«vÆá°‚Á&ïXȤH©‡Ïõ:Row°°¨Ã¢>á½f
+ÃÑ
+†t‘¨®ÝÌfEUR
+FQa2ÀòöPsôá°¾&‚“ñv/ð~,):‚ò#°"ê½Gƒ£Û±ÁC
+I	È1°ñB9zQʦâ}q´'èx2ŽÆã_SæqmKïëH·°vøS¯Sj|v@ËO(VHØÕÁ…P@`š®Tð*‡[žýåþUd¦Æ̶™ˆFK—kgÁÁ–ñ È0+c(‚ºaB5€×Á¢¥ƒwøÅ*ˆÿåºêc›8ïðû¾w>ßù#g_ü™óÇ]b;	8¶ã'€/KB	Z‚ihHÚÊ ŽZ…Ñ6U”‘¬E¬…?í«ÛªIÚ-0T˜J7¡UK
+-cZ%@¤­:5M51¨ _ö{€ºI¾×ö½'Y~žç}~σùêXk'ùž¶!ÎáÄ}qùòj<µ|UR֏¬Ô£ŸÍè=Þt0î)‘Ê‹ïw0½G♌,;¥ûµLk¬ª)Éð‹Û»î5²Ëï½gP×Æ€ù0øÃO€ù:×Nf±­Î¾Øóν±C±c1~ÐýŠ“‚Éíq»<¼Ûí	G¡ˆKøD<Q7…ÑDm"Î{<φj¡P­+錛ñ¸‰L.·'ŽÀƒfëluÀ­õWG;ªùDµ[‡}Õ.OÈÄŠRm<³¤®
+L真ñâÿyM¢•Ú$Fúµ¯†h$G†É(17ëê4˜í¤ŠRnÕ|9߬}AñMàØ)­WGf º¥ÌtÑÜ<c›y؈¼”Jà•vÑ<åÐöC©|Èå‰ðÆÃkÙº^8ð­ÏzÓÿpWèO`Ož¯§ƒ/Ç<1ïa+¨€ã‡I÷£k~,ÿcѪ^âÝ”h©×§ðeGCkþ÷ëªú¥ëÉËêólz¥	§ô5«×­%q‘ÞƒWkååÕ8“	TlÔ›ó[ü^øìw¹—à	ýÝ5õ±d&ÃdŸÆHsK9¦¾·!ö5`؁fµ§›¹fcÆž‘6pŒ›í›¥mÆíÒ qH:À½h<h?(åŽß°“®pWmŸq_Øoqwí%,g¶k\›ýXºc'F;¶†9g±:,«MP„¡]HÐ"Làœ&™ƒŽ¨#íèvŒ9¢£ÍAôvkÛaæe°…±›M|‡p£âÇsÆx‚dµ¢6syØ<j>až2sæÓd#rÒ!ÞuÆ\”(8ˆùe)zÙ¾è²ÝÇð[6ŠÕ8€ž`Ô$VKÇÊŽ2³÷OáÆ—Oì¬Z)ß»Ätè“/´lô¬„ïNvžºœÿ¸Éò—_ü`·Ÿ]I|¤üÁœ¿wct‡Ð0bq;ôŽÛ%ù‚¸?i/DrâsëÿÚ•PëªØnï_×'	&㭐µZ˜÷Q5J¡ImÍveH!Ü#Ñã¢(œL®10 Á Ü-îéÐU9†Ìs•)Ðà[jOE{eå\%¬«œ¬¼VÉVNfÍci—{äœ<,Ê×å¯e#’5øÂÈgÈHgš2®žPIŠU51¨X4I[Ú,Ý–aÛm³ËiÒ…êç[Ñ3 ¨
+êϲB"¦}ÇNG5m¨xŒe;ýëa7÷ ³ ¡pr~PÓRâtp…›…²B
 G„aÌÑ=šŠ“¬j}¢¹«¬{tëèQœÅþ=[²é*¿>oºöΛ›6¹\eåúMýٶǾ3øÛg–fVw}·iïS¯þI9¼fGH.•Š`Uª9ÙûóU­ÛuÔ¬¨ø­²¶e»ÖfO ¨ÛæþÍ®"8ÝMM‘5Öæ±½î¬g?BÞkö‰6“Ùf*Š ™%Q”¬VŸmDBø§Z‘oâ©ñhžvOÇà™À^­IŠ4,J¬"ÕHšÔ^È ÃÒ¸4%]—¾–Ì6¸5
+¶u	 P:òŸÙ¦²½py } tZJ’éB½L¥
+1³D	56”õbE€%Äy{q)ì]¨Œ+[7|b®L™x‡%ŒoF†þhù°¯€È@û"%„3–/ô‹$.‡¼Ž:ÞÚ0Úñ‹éÿ¼³ñÐêT²Â×’^DÖ>4°mźƒ†«ùU?¦ë—?Õ?o^¿¹¾è¹زØÒLþ—ïß¼zæÉçÿþÑ9ê+Ë禙vгŠNiÀ)â]oŸ`Dw”cF®n©_Ú'HcÒ¬4'E˜æTŠ.\¢¹,#
+®ÁíxŸÇ×1Ò¶~³òæ¸Qnœ;Áç¦¸ëœq
+Wõs9ø;˜ë/i–[T¥´¿ÍPMŽó(–Y^Y/*áa	”^ä3”PÄnH—$Y)ü¡DÐÌë€bÄÆuúí÷¾Á¶‰—ßnª®ZQj[×´'Ý9¾÷-ì*Öo`rål½kiëŠ%K*ŠÍþº'ö_¾8	šÛ	šÛ
+š“Qú\K´bŸ¬•Z¼Kð•ÊY´#_®ØXš³˜¹¬ÑÄó0bÃdŸ<"Áy½äç¸aø«w)¼‡PIJÁAElITI·8&‘žÖÊ f¦aÆMÓ#…W¡à<ÔÕ< ~Õ*Ù#~	t¤Za	Ø| °¢Òÿ—UYÈd™J‡Ø2sx›„²*ìJ‘
+‡³<qú†Øwºæ÷)žá… ^0BÁdÝí1¥N{my„lÄ»R©Fý¼'õF÷øÑ×:G’-Mõ;öýpÛÎ㏯2üÅ~WÏh™sÛžÁ–O¾:Üß¹™ØÅò·O^þô^|wg=×YPßPŸÐÏ´*MÁ
+<o°±ñŽÉåDÎã|}6£!g2yï ÏñxŠÇ6óQÑêă³V,ZƒVbíR$ó`nMQ£›Iç!ìÑX )Ý>ã’’€ÙAØb‡ä ‘íÞÝØ/:{kÆ 4‡o7°Äb À]¼ª :_•JÖJ—AÏKŽcNæŸ2d6¾xawã«ú—gõ¹üé—þ/Ë÷⣡Šƒ]ÛŒÿveSï5Ÿ>mMcï¡=ÙkØuì=‰Q„˜„áKh6okò6ÿ ÿ%åu…uñ$‡±”sž)悁h€&ðZ "ëÁ‚%k6…7w«ýê>uDSgÕ9Õ(Âh˜ [þ€äŒü_²«>¶‰óŒß{çÏ|ØçøÛΝ“øÛß]ìàÄ$à˧YM‰ÉBB˜LJC£R‡$ ”vÐòGÒi°j
+ªÚ?h·¡•¦0DÕ­ÓÔIt¶ÝGþ±ŠÄ–¥ÒÐ:MØÝóžÓd,Jôæ}ïì‹ý<¿ç÷±€¼R
+GÒRäèÚ‹^SŹªs+4"èyš¤é4=KSô诫&Ÿo¥!ÑÀu€J¬Î0“™ÄPÄe¹~º`Èb
+Z¹~"d	ôƒùÅÀÉ¢¨Ùua=æ¹7EK%ú¿ÉT™Ñé,lìdᣞM¡7ß¼}!;½E5y˜px×$uy Ð'!áx|ä́î#å¡Áͱ ÀÚ*ÕçHPÄp1«ØBÝ#Zˆ$±‹øDÚ<9¹ùµñ7M·©šÝÍžæ-MRS[RJ¶©_¯zÝ8ÛD…§ØémS.—/Ú~°Æœ6“=æ%3i^ +%o\GÄ5+O‘Ó­ãꞃ>}ïh]RGºÊʔ约2,6ÉMLÅb14š:é¥ÅQ%Q—1ñCñKQ)þ´;Õ‚<·à)
+™åü1Ð
+<ÅŠò\áAÁº\ˆãW=\–­h6ƒbßÔˏ9ŒEf“*¼ªÂÑ&|S­ª…» ÅM<Z[ámêõõÉÃj-xDö÷EÚÃlܤ1Ö¸š¾_ŽNø|.WÿW7ÞÞ{ºøèγ½Ž
+ïß¹Wÿßí~ñ“Á‰‰={O²?nÙ1û“ŸÅ¦¨‰g$ÞÛÐ͆["/Øì¢ßlV=Ú;ѳõÉ–§m¶L—4T'¼Üv!`;=uâÜ™“gåF¶uúÇ»ƒI/Æ8.ö=àTø\.Ú+)ŒFÉÔq_©öÐîÏí®ªÒÐe¹BzƒË0o¸ePn ;a!hd¿jÉ•c‘цÏ%Ïå1¿êÊù°¦›¦…aN „E"„èP:4¢BÞ"†w>“/ ËÕÒ%¢ÅðÆɁ^Æé!ñ°„ð*›]¥U{í*ë¤ÑÚ4Ž#ÐàÈ5
+u2”¢ÚËPŽ£J§‚=JPä…£WVo+4o•MK󀱯Ûp
+Æ
+?ñÎОmÍ=õBº¨j–²»»¦zCbºhhŽ‹‰ÖN ÓÿÔW÷ÎD±ƒó§RÁ º9yú‰ó}ÅW#¡T*,(ö¶¶ Ÿ6 ©B}Ë	‘‘*EB‚‹¿W¹*N©Ðߤ`¥ªÊ2`.3.êf´sZ2¡í³Ÿ¡(]μX±H8ædÚ1ë ¸d5rÉòP!l"ó2|ñ2XÜâúXÇ(ïハ¿jw¶ó|Gç4A[ÝÏþ>\”O*ïvòáöŽp¸³ðYù£Ïku¦TŠ’;šãQÀÇyøü
+ˆ–zûô}ôîêAF¡×Ó”Ì0ßÓÓ&8)t,;I*L$© ™jV¯Sš2_à +X¡VTù=ã€ÉköšÊ˜2^aVèhªl¾W^.˜Lý‹’uÈtË´dZ1)Ódœy8|mR™ÈS’C$[ã`íI{f=”–ö 8†šTíšöÏøçü”Ñ•›n˜i˜k W8”†fâÂE p™|z~ofïxB©<외PÀéÿbWáVÌy"¿,ÇVˆ>ºã¿:£ƒÜªƒàªÖÑ­šV(÷8 
+ÉÒ‹ó©JÐzÁý”ÚüxüЂ(A
+õÿnôÏ츃25=E"÷—_1:Ŷ¢vgDdBÉ( ëBZ?c×™ÈînM÷­b
+—J™t:Úd™ÑS©[ñRØÌâE c» G&"&±Pd¨¹ õ"ÊiM‹Då|%™®œAƵ0¯‚è¡<`È°ñ³nBoìžáÂ?öòa§7Ö§¼ûŸ«ÕzÀF}@1±Ö¤ð~† Tw¨ëD
+m—Z|º˜Ž¼àºÐHj=UÛ»¯ª½_Q¡R§0›¨Úêë­’Äóg·[¬@&-I­?Bµ$-ÐÃ÷
+9½}ÞNÚq?Í3Ç,1+€4ÆÅô0CÌ£dHÄDŽæj8‘»Ä]á>äÔcÜIŽ¹47ËQ~/ëÏéy/ð	~ž¿ÅÊ«§ùžLðCüOñ¥ÇGæ"K‘•ˆBqEz"C‘™ˆ2¿–$¤K)aújÉu\‚KáG&’cÉÙä—I‘¤a3Ÿü4ùuRE'Óp ’¸¨ÛWµ“ˆM¼ÈØ–öÁ­þ@˜íd´É¢|e±bÂ*­«ñîqÚÂIo—É×¥ÿ8ª°¨·_øξuoí;)õš«›„bPhG¾a¸z^*ºA¬öõ>ßitÆÅb àwP—
+†í9<úüöýo…ÞšJè³gÇ;¶n-Žõ.tå•cÛFÎ_åh3>“#шÑ¼ñõЕ»Rà¬ý¢tJ*'«¡ÜÎû*µ¬/*½&,”¬,¸7ååë³P’–ëd'!À­uyÁÊã"|p­Ò•Û +Åñ¬,'t¾€Ûk/Ä×ËŽ^XužU6‡ZëPÛ°ªØmv³¤+ ß8÷jF¡dΣÊj%ÈŠ‚Zsîk]Ú *~ÙRmP•ÔdÔ&Þ}>sb;k/V	퍁æñìy!ÖV¤Û¢m[Dê²q÷ÔÈÈ·ÞxºØ…‰…Ðk§{®}¿ø6>X2Óº¤\ú
+p鄍8#	’HmC›5•	µñ ºÜ2%QcÔ,u…ú¥”(ä‚=I-]ïÓSD*{ÁNÿ++G½’ÿ)ùpdSúL*Ë0ªÒÐÄ
+ÁNW;³Â8L´°XÉÊa¤/¯–=¸¼p§ˆ,ÊdQ„ÀÖH)UÖÿq>¨p¡ÃøîµBñúØìLvòµêãâÊҏî@¦?ÿ™’¿ýÓÒíÛî}ßlKqZÑŒRÈùJò;%¥Û	"ÉiH:èSªkêNªàXe9æIúØð”ö:yÅŠì?7\òålxnk^‚­a_`)‘•Øôù®ÖØ(®+|ïÌììÌ>fvwfgfìû;³ë5žuüÊ'žàÒà°ÍckRA!Æk‹’ØjÀ!Å&­À‘ELT©)Q[¨iŠXj¢”-N_TQ+Pë´j+
+-Šb»gÆ<”FªVš=wæ®vî9çûÎ÷Eº#C‘™È­È|„ñDF#7#$à|äþÐÔ"æX°ü¨í¹ûHµ$Hƒ©
+­EJuÚeO9ÕlJ§WîÄ¥.¸”3‰¸Ì‘¹oúÚ>-ÍñŸ Òœ:€xÏ—lŸ'™"©™°%)õ¡béþ ¯@YÂ@×¾êjÛ½üZ汭Ýçz†{Ç;_«õ‡rÍ‹îæ\nõNâéžS¯÷íÉ·&/sǾí/ÎŽ|<:°uËâ‘2Ÿ²f%?jÍë~óÁµéÏÏ|dùž*ð={ Yô#·K9 ¼­\T>Pn(Sþ­0¶€?ð~àwªFj‘ˆ¤a‰¨3k:êÄNÓ•{gmö’hGÌûí.®—äF¸	î6·ÄÙy.ÊéܸqŽ»ÊÝä˜(‡90G†£›)2W™ëŘú߯F÷…"t0{¼äx›Š‘zN%Ô{¦¨oÿìì\^7]QaÎê`Ý×…K⇚éUü’Rr@
+t€ïØüTð΃4ƒ}„¶í»¯
+¿d‰L&µÓ+ðÿ"¸
+?]3V|rÛcå+k=¼©Z«Õçê,\.蹺š@™‹G®Lžjh©¯Œ¯Úø\cÿÏ{®•$ׯO‰ÍM
+fvû »_'¯ £õïh‰ÚÄzךīîauÌu2õGË8ñ.| ÿ§ŒLÆãÃNVt:Ù2½ÜO’Yv»ž}•}ƒ}‹ý)û{ƒý{—uҬijf)–
+‘¨MïÖ‹úy}ZŸÑçu;Ò=º¡/ß²n²XŽB@韗¯t”;†Å“"!*ƒòM™Ø wɽò <‹%Ù…å„”<…?1$)´×ç§}v‰&÷–Â{Å“Q¦°Ï&äsòUù:ì½-Ó¼Œå_ƒ¾õî¢6­ÍhdTkÐí
+!!’à›ÔG"æ㈇Ã1ÎàÚ¸yŽBœ‡‹q9kÙÍ
+qw‰xÅ„f_a?2S¯ÍÁ8µ¥IZ
+
+06M
+«Ï ¶"·}Kсd³è›‚BGð=ó¥•fa²ÖV
+‰rœX¶fð1!g:={‚¼ðø[/öôgŸœhlÞræàîÕ™`cÙÉ}]Ï?R1òƒG6nýÅÉãZ«E¼ãÇGºZK¢‘ÈêšvmÚûµ–ÅÉ×^*4®ÒÒrXˆ¼´õЏ¶oÂý¦–^ú”¸mÓP õáSNŒ¢HIÚ…ÜwÌÝæ¦ÜŸ³EAB'AI_!žE4raß…Q7vO;&éƒ~sFz‘àáŒ0-Ì´ „‚Ö±gç žY…B=Ðܶ$E¥7î{-F®­4IZ­ö&ªkjkp¾	ë‹®Ë7u3ô›/¿ðÊ÷{ëÚmÚâw¶/¼ýÔ3/Ÿ¸òîà.¢¿EX×'xtéSê';ÿÁÈ%NB
+J	2~ʹÙ5èv#qÐ×!8€¤Mý]qˆ"ç÷Û}ðÒ?ãx‹“öƒ€ø¦I|0nž%e„ÛÂ3á[a
+AÐ.†‡Âg`9f<áÑ0ÁzÂ8¾„ÿ„8zny®Ãá,–^¦èYÛ€ÿežöBIp;
+†ˆ ÕŽ,·xŸx%ÙFÓu‡²Éö;Xò?œéx9GË$›Zá6ÍL˜¦Z“]«Æ‡¿µ­xºùYÜZ_³jñ}qÓ;ƒíÙH¼¼ðÝý‡Þ=†»rÕd‹÷‹¥ê|#siçs›7]çj ä!ƒÁ¥Yª†<=ðOC\+n„aû1ÿ˜ÿ¢ÿ—vÆ`ðÔÒQ£ñ‹1»ä÷3v‘aì¬ Šý!+Ù’a`Ò:Ýãžqž'ÆãÚѬÀñ’&¡…ÓÌ£Ì7˜cÌc»É`Øn Րõ¾‹1>ÇOó$?E
+Wñ(>§±
+O½†€Ðº…æåAmh‘h$èùÔø¤¹BÀ‚V>ïµÐ•GJh©¹9h7°ð¼’ížY²Y2ƒ,4h!îçàµäå—ªÍ•'0 1ˆµ½Òq÷¯¶Q|xU‹°øÚÅQ1ÿy¶‰X’¤…„Âq­¾¦&ºªì‹ÉŠ²69´zi–¶CËð)ã¹g@t¥¶ªß“m¬sD±NG§Ö‘ÞíØ­
+8ŠåCå7$‡¹©êTéBæ
+çëÒ›©ÓéÓ§ªjŽ”ƒM91Ct`^òtðÑD¬#Š2eˆsº;¸ˆ²¢#bL-º §’1ø6ö°i-¦jjJëg"Ë:2éô0Ë@Ä8S©aYeYJ$“ý²‘’PdÉé`™ŒêHIð<˜8ŒèT2™NkN&“Ö(”‚ªM&R–£jŽ}‚%XE§pöÂ9«S„d„cCpr”C‚2Ù&ÐMD°QÔ‹ˆ<º„G+ïã#€rAA
+^«*À–P2(ײú}`<|Ë3)Ò*ØW‚l6¨ ÏöLÿÿëQs3c’íþBÚ%_¦Õ”9Qýpe
+–‡- at Z]PÇkÒ%Žß^TëZñ{j•Æþþ¬·j-îijQçëï~R·xÞ“yÜðK…á»mZssHñ)ßÐã0››
+O,8‰½d½" W9ð +¡K*ñ>ãDF_Cw
+±ÝÂîØúÍ016G´ûÚ…-±Îø¶Ü–
+:—«°3Œn÷	‹Çb>Ÿ/ñzKJúuFÔu¨v8V!½$&PÙœÏîehº"Çñx–w•Ð4n·YÞlÚÔ§²¢d2¥Ž¸ÝGŒ~»àoôQé4Qžoà7ðäU󼬉!b”˜!(Däƒ 	”þÊÊLé 2¢L(׊W”
+ÿe¿Zc›ªâøïœSnÛÛn½íº®Û:Ø£íF÷r
+”­{°Ž996NŽÌmÂtˆO at tfӐì¯á‰1(1HŒ	ú‰ Dcˆ5†CD·Îÿ½· 1<ŒübOïïžÿyÜÓsþÿó¹…ûþ,2PLkXú3ÆÈïŠ ùÜSYvøúÎú¥E>õZTªþs’…^êHT±Í‘ªÐü©~+¦C¡Ð_Huž>áŽ7¥:qCR'L¡Ý©ÊQJH{éâPõ„Ãªê.Ù#ï«…Rjß|fÔ=.õZÌXAaMô9Þ8`f¯Œ¶dyÙ‘úxž·Ê£¤Ïuå´Æ7µÕW½Ÿ,]K×À¯x.w9ý–ÆÆŒžïâ{æåD£“Õ’yû\±Ç“â`ѨÑ]\=ªÅðÇ4»Ä·áp—oÈÌ1æãéK‚T7à”øåt—Ëp•?é
+ít¹¶øe§ß/—˜d'X~?<¤–Ÿ„KŠz<6‡-¨;E'¤Þ1/÷¾˜•…2§lŠÝTÂ`’¸7NŠ¬œbN†í;ìíÂ~Šœ`WÔMæ}(Óò°Ø¤j]k5ê#£Ö­	+ÍÆ’T¦õD6agU	$¨Bi©‘˜¿Û”Ð?R¿Ø]õciwN22ɨǸhñ®*ƒŠòE¬ZUÁRö‘o_b³Î¥×u±]…ñëWwoŒ®©ñT9y+ZºãûÓj«É4çd§¤L_`˃óŠ¥HÄÒ=uåÓeíËC%uåù–4›Ñ|æÿ½¨ÐHG\K¹Äñ݁/ÂÍF
+mùn~„‹Q-šE—x^Œ‹ýÒ^ÛqÔz™..[SŒÂlåVòÞ»,VŠ}­0uPj°“ƒßäŽQw¨¡è|Æ­)½L²C¯L·r“ÍÆ^Ó(ƒ½×1zˆÜÏ%Ïv•	…1ro‘°W‘så –;äC²¹Gî—wÊcòUùgÙØA—OóÅV7Õ¤‚Ì؍ÉZ
+)jõxs:T«B™ŒÑ£yº—TnÄ v³<5´yU+mËçßO¿ËrÖ
+å6¸¦®ñ¶ÛíËjÜ­âhhê«íoÇ_kÙþuø[Úÿ!ŽÑ½™K8«ƒ—ήŸ[t"„K€äL0Ïä«€•¾³~
+¤îlJäþ°o瀴) ݸŽ7 Ï ¹˜]ó'²š€ìÀœ›@Î5 ÷Àû%0÷e `3P8½®Ã7út”»hùô]ð<PFû® ýTv U´§jÊMköµ+uÔý
+, =-¤uý,ž –´è¨?Dt›Þši¼UÚ²€e´Fû{I$‘DI$‘Dÿ?€SŒ®'„J±L‚„‡¡W2…ç©6ÅŽ4gº+ÃíÉœ•­ÌWPXTìó(––•WTR¢WƒºX¨
+7D›š[–¶FÛÐþhÇcËW óñ®U«»×<{øŸÿÅ€èí†BG5!Å(E–¢«ÐMèǶ™š‘‹Bø)«\‚6tÑHz1833óͽ~	^ß»ˆ‡îÉ„
+‰Rm :5AKDe©’4˜©'¾Í©¿)Aê_™ 
+D÷'h‰è‘%‘hKS³¿a}ß–­ƒÃjщ#ˆ¢…Öm&4`=ú°[1ˆa,§ÖF¢7ã)j?hæ¿ÓNÏà:BÀ,:¡‚|, vtó[ú]6€ÑˆÉ@”ÚºSc§Œ†Ý-gób*“d·™hY\0ïÏ%¸Í**>»øªïI[èÌ6i³ßªy'¨Ö§ouǏO˜o™)?‚Êmå? nõ€ý
+endstream
+endobj
+29 0 obj
+<< /Filter /FlateDecode /Length 22522 /Length1 168840 >> 
+stream
+H‰¼WIo\Ç®7ûpö•ŽÈ™)’g¸s,R%.¢$kId!R Ä"´±bÆ,%']rdžo‰}ð!‡Ä0 £àKŒ\òÄHnFùJÕ¼¯ôJ/‰J”ðuõ«î®®®®¥‡"JÑC
+Ò…ó§ç6ï¯ýœ2“Î]¿»µ}uìóÏ)ðË:Qàáõ÷ïÕéïôW{ÂøìÖöí»8õç+DÎCÿáíw~zëËÇÿü19ëD}WïÜܺñÕýàË»ÀóÛw˜áüåÂ×DÁð÷蝻÷|=¶ñ{¢P–œc¿}çÝë[ô·‘sþÿîîÖƒí·®+øÕ7<¿þ£­»7ï/jøà1ïYÛ~÷'÷XoÞúƒOe|û½›Ûzoá[rVñø5’³ý'¿¹-R‰Û2õsÛO|"ÊRŽÛå¹M²ÕˆÒ”á6J1nãÔ÷¬5ž>íÊLð
+ômÛ¡f£¾Þ©_»UïÐ[—cÕŽ³uåæZ«ã4Q`}dãÚf«à~pý`«äNÌ÷ãÂs'f„ûaF¹“ 3Æý¤0ãÜ鳏ûÂLp§f’û%a¦¸3fšûUaf¸“3ËýŒ0sÜɁ™ç~^˜…fÇ)ç[bÓÉ>nuJMRn:õÜH=¿1²qéòRµq¥Õéo’Ÿ5ЬoˆI:õ-–Yq¿n»_ƒÍzg`²Sç=ªÜ­L>zú
+÷÷4ë´ä,µ:CÍF«3ÜüÂÙ³¾öE`ˆ›à07¡7á:7‘7ѽÜÄF¸‰rÓ·›Ä7ÉqnRܤ÷s“9ÀMv’›\“›|‹›Â7ÅinJ3Ü”g¹éŸãf`ž›Ê7ƒ‹ÜTÛë|‹5Vå
+Qå ¨²$ª,‹*‡D•Ã¢ÊQ娨rLTYUŽ‹*'D•UQeMTYU6D•“¢Ê¦¨rJT9-ªœUÞUΊ*çD•ó¢ÊQå;¬
+uãáÉÓ'n‡›§þ9ÿ-3nt»»˜cHvˆ€F2ãúaŒ—AKàÅF’‘À·Ð4#‹¾ÈœâX3{†°¾4ƒ1ý^€¼¤áë^iP»>	Ý aŒÍƒ—Ârö†‘›À¼”9È_Ĺå<mè›1såLUÃKƒ@㘷ŒõY³ÿ¬±·ê\äû9Çø.øƒfŽB.5ÅØËýÆî_ä2ÝÀ~Æ$Ö¨qèê Æ3>ˆ¼)F1dæÅü`Ž˜{±›»ì…0y~cQÅyt½Ç>?xÿœ9탵úŸªõ¢IȶvJí‚îFnrÕw
+¸Aß‹†öÁ6iŒ'17‹}§@Û;PÙ§†ý3;Ш¹ÿ˜¹oKÕw“ÐÃÒ¼±Q/jc×R¥ä.i€¼˜“÷©¨ÌwØOÆ'$ÖßcŒ3Ž2«/ ‹ GŒßÉˉQcŸy¹0D^«¿/gd¤_ó‡æIuF÷(v–5ƒ³iÞ“}s÷ªc=öÄÉËSè¥~$/Ç1W}Ec"Œ}tžž#I^þÑý5æ3fžæ<]225G	
+8»Æ‡ÄÃ8¹uKd
+ëÿSàÍ£?92_ë`ÔôeLòðîùMF“qßËŒiÆûŒôÅ—–Ä'ð=…¹«à‹ïL€ÊØôÐÚšƒ’g£<~rV ó£
+=θŒ9mÈ;̘Ç<¡â1ŒŸ…,Ù{?cR¨îE^N“}OâžuLì¸ ]ÓäåU‚ÎÝ{†|=ïiØChcË€è¾ãËflö[½al¥kJ¨yCŒ˜/çìÇO1ÖP;/"Ž7¤€žÎæGÍ[oQ3c Ïb	{œÄ·Ì߀Ma—ük±…Ê­A«;A›!̉‘W¯4v5Æ,ßÖu]¯¾!/¯ê~JµþD ŸÌ+!®´Æh,fSaèêÐóo¶4hçÔœœ…¼¨Öw•ck òíNçhîÌÂÆicë4Î0tÌØ'GÞ["odøë¿æq=“¤‘§g×ûHC®Ò^õRïÓOí™l=óCïÝ}Øú*à5o3ƽW}òþ(ñF®ß–¹ÿ1¾å]UB<‰¿K›&÷Ý)±+¾ßzɾ]DÜöãL
+sæ,ì”çñè¨q£¹_ëá0ì!¹¬úÕ@ÇÀ߇¾­oàÕ1w?¾G_q}„¼¸Ó¢þa}Xã;fl5wjc<E^õûžÊÓÚ+ù=aÆäŝC^L	"<×ê§ï!û¿¢[oyÞ8yõ^ø%s?qPñU­úÎÕw×NTm¥ýn­ç½~€\Ü`QÄŸb'F9?Lj#çΣöÕ÷÷íÖF.–ú'µzòæ·~JÍ«:nÍÞ@
+؃=‚ÊÙ/aÿK°A‘¼w‰Þµú`„¼üTÁwÅŒ+?
+Z4÷4k… ?:`üIß“QÈõS‰ÁaÛ5ß1Ÿ®V¦ÆÑs5±bš'Ϗ­_ë|=Kúêù•§{ûõ[K¬ÉËÛä|µkþ–ùr'MôeÎ(|QîHbSãNkZö“8ž•;ÕqøD÷[3ö8,>̼+ØoÐF­—w@º /6ʐQÄ;Cò†äÄ”ï~ºÿå`WåÏáÎ_?`G•ÝiNucÆÆÏÖ#Gônc5òü[ßü/¢ö®5Îú}ÔÈeÌšýå5iγӹRäÓ›<_ürÓfo±Ç" ü¬¡|Íì9ô|N°z•}¾ ûØ8Ò7šØ·
+žÿ\þ¾ß°oŽÚ\ç
+þÍþ¾µv½gÏ×ÞÁ~òþ-‘›7"¯Ñ×ù¯4…:!ÿ%Z€Ô•iô³»Dì _/ìÆV¡×€×}/?cl#Ö+Œƒ&n»ß2|éo;ôÜÛ’v3îŸãŸï_ã[ÿZ¿wê÷‚oŽÚ‡¯“—»ì¼WåïG|}}oFn
+‚žV_?ýo r5Ï…À{à¸õ[ÿW´ÖûsiÐwDК0ÀÚ«~©ïé@òvÒÌË ê·IÉòÿé¹÷ã¾ºï,Ç}³êÛo'¤qfÿÛ jlÞk­`ô%ãšgäý-y]Î&o£qPy;m:nŽ^ÅqßççÇ1.Ø‹³ìÅx
+òä
+ý3°›ü¬ƒÊ;IÞ[뎛•°—¼áä}(ïy+”þ}ÿùdÿS+:7ñ=
+™Žû¿@þ+Ìb¿¾Ø3þh?ú%¬­@çAøá&Ö˼EðeŸ“ðYý¨>üŒ'ò˜–0·;‡ûW™Ê>S°uöMÀÞ#8‹Ô²³wöÖ;Ú}Ç çôž o²Îª¾7þ(öSŒâ-†
+-Cv²uÝŒo¿1œMÐÂ9X·sZX7:Lš¾è3îƒÈ¾Ï¸Å¸è¸ÿ/AÎο	[°·úÆÿ³.ZpWuèX¯Œû”þ Ɓ‚œsh‡»Y ä|܍úE	¶®‚W…Œ
+dæA	<Á	ÇõÁ!ܝøÖ‡Žëw¿€œOñý±ãúìoð-{/3~ï ÿìõ‰9§È’8ÖÜ$o–ãÀ
+pº„pFëÇÀª9®ÿÍê‹G!gëf€&°
+è[0
+¨=ú0‰û0sÔÏ5¯• w	vT¤
+Rvþ0xš'pŽÆ¥µq®6Î)±Ø‡¹5³^ö…mŒîk°õôj‚?¹ÓŽçwÇó»’ãåÊ=Ž—«'Ï3ŽÇE@ýYí¯v­`½ìÛ TÇ-ÆÛŽëCU =¬í–
+&
+J=PÁÜü¿x/—;Ž+ sðÌÜÛÝ÷ýšI#YŽ-bAÀ;CdáeÈ6›l²Î6ÿ’B磾>j'8^]]u꼟©·fÓïòÿCòùSôøjzj>Ò|¯å˜SÖ]Ú»ÅÜÇäý‡¼{¯ûfÏ&ŽÖ«¶üô[Í¿æ|Boñ¥õßæÆûÿáÍÏ­fÛMò²ŽÞr¿>ó­eÚ¿õjrî¥{òØcÊÙ|ø˜pÄyß¹}ó¿á¤«M¾Û%î}žm„·ÿæÿHØ7‰kL\ë|ßÎZL¼O[L	sËï)ùÁf§|‡k='?¼’öû<§<$äj÷ßæ{Ûry†~ÿ—øÓ/Ywk+Ý`t{ÔÙ]ÊÕèÞËÖِEþ½OÝb'×܇¤y/Ý[ã+ï£×Êmê‹xÅ'fÈ…½îÅëA´Î¢Óο¿[íWâmH:ƒlóœßßçêxó‡–§›ý8®Ñ}N¹’Æcü}àçuó)zsÎók~9ÃöÇ„g®Ýæ™íH|±g5<ÿJÙZýluöuÂ^òÿ}êðGÙû’ß?Eï…8cvy¿Ø'¿´³æSÌ=|¢ÇâF6F¶:GmRߏO=±ÏÝY¸¯ÑýÏ}Ê>æþH<²ç½mr<²²v1Ï›ð8F÷£cž!˦ì‘ϱÎIzàŸEì=EwèVØ©ÐZ-Üm„üÆø‡…{¾Äï*zmYâÇ|¸·ðuz3,Àùñκ÷›»ò¿-t¬§Ê˺Ð^:ßÜ«b¿­ÞÝ댼4–3÷p«¢Kú¢!¿Öéÿu_}eˆ¹¾ˆ
+=ÃY—Sùb'z‹•Î­ŸµÞ¬Ê¿Ï‰ë%û®DçNïLÃz¦ÖÙFkáùˆ'׳/éܼT½¹V;TŸ²—ì9	~UþM»ÚØv©±eü_•;Ë.rÁRìT}WYè‘ÛÙý‚^Ö…–mrÎu¹³ÍüEŸW˜/Ÿ-ñƝuÚîïÊÿ’ŒÌYöË5êûP`ë;ïY̐u¾¢¯ _mÿ­ ÖÒ³ôè,úËSâ<æ{ò"5˜¾oæŸÏÔ{hmµˆÇ—øÔû°½.¾®Wùÿ.>õ.ï¢×íç¤ošž•nÑç4ß·÷
+ëĵž^d_ÖƒöÐÞéß½C}»/pøÖ˜çôFá¡ït¾¼6>¥ÎžSooŠ®Ìߢ÷X®]èé"yè!ÐüÓSŽ:㟾ýÂm8ûÃYûct¿lÿô‡¢[öøþ)æµbŒÞC"¯{Ý•p]¤oû½{r13Î^tà‹zI~¡÷Z:œ’ç!û1
+ÏÒ×Fx\ߟ£Ï!ØÙáa“|ìÅ/z£OAwWÙ…ù“<}íAúÚE÷ëuÌíáüx­µà™a˜3ÈIžlóC¡\ÎSÁaë+/iƒ§¤wÍÿ¦ÇW:ÉuÍó«þ¯Âó’onÉó«<{“|]óû”çç´ÅE¶w\"Û¿‹¹o8Ùoÿ‘÷óû6úùSô‚/¼Ä|6²Þ¨ßÎCУƸ†»&쏳'>Vå¿&>׺§^]¤/ôóTød&%Ç¢×IïíØdŒ^ë¨ÅÑã£Á~=¶sSæsž¹µï«èqø’0SÒöëäËõú»<{[dy–]]GóØ«`8cOÜ ‹KôØ{L sjê½å9z=žÎÑýø,?æwSþ#Öý;^£ûþE²]t~Š¹¯¢EÞæXh°'ÊýQxLã,Zôc»óªÀ¶û×1qì²/o¡{Kø[Ì{?ä?¶~¡­?jîeaU½Ö¤÷²Ù–UOöbïXèY_¶|£ú5Ó6.òívá?Û”·Û˜ûÕ—t‚ÚWˆõ*/u{\°ãÄò/ù™}?±-‰ëù*xü½ÛÈlßs¬XæäÝÄ<§ë¸—ìmÏeUÙÑ×V´–ôÃ9¹›µ]À
+_ð¯'«ÖÿAt_b.;ýoµ÷^pžÅ襘ÑÜ{»ß±ÏÂE¢û$}áëè}
+ûzÞ¾ot×ÖSù#Ø›öÏÏ­¼½jo=ëþ¶ Knñ?¾jÛàgô†CÌgQÃ3_á»ô§òÛÙÏ7ºßè
+u•;÷¡Çè=2ùγ&0Äæ=gðvˆÞû;†ÁznÛê
+}=ÓA0Ìž¯¸[If|ʳ³2
+…ŽçV÷‚ƒÞç{I÷³mï>p'|ž!‰	ðÃCÌeÏQÿ£à†¢?Þš7ë{/æù Z—˜ëjÒzpOÍc,ëȲ sr0¸ï;ûÒ½æMÂÉþª7£îØ“<7 ŸQßQ÷ðÏۘϠ{ᆽécgxhÚ»v?ôQäYj!z™¢çkæ·kÌk~tŠ>OA£½WtKÞ!g“ӝS®Ñk¦sŠ÷¬CÊw’¬ØÓ3< §g6j1ÜþdÏ©ì‰kÛ›mu‡­ð1çÞ8ïR+n¿µ¡»£ö•'jtÕ[Õ¡õUõ\ù°žì·^ÈTë…u3Ų^¶ÂceOþ!·ú¯yß2ÂÓQpÛg;ãsù‡˜×Iò™ãØ~I.äËù¶àDç•ïAtÐSÕ•k½ë¹s)¾aÿ$<êŒØÙÇ<?Õú^FáqýsÎæÜ9
 ú«rï:l›8¿‘]Scï-·}ŒÞwzÞ¡¯pcùñSÇÕ$ëèýÁ5zŸ³Õþšz}Š>»>F÷÷bà=ç?°;Ù†··è³=׳ðâ'‡|ó¨ÿ½ðºï?þVôÎœQûôE¾ÿYøØßbîSÔMrWµù^°Ø×ù`+ltO¼Âõ
+}ŸÒ.5ŸñouŠ<زáys_?Jƒ¾Ô™c~á{÷ëÔ¾æþÓ²££š?Wœ“6‚«1líÊæû˜×ôç¼{Ž>ÔÙöœï<ëšëôU±ùþâs]TœðymëÉ}K{ó.qzŽÝæ¬3âªÁ¾Žž/ÜÓÓCx6²´ýSÌãÅütF¡–~óxs}­}8ó£gŽŠëÝGñPÞÔøsÔ¾ä=®<Ïí¿ðï=tÝ÷W[X'ž+ìçîÉÀµIý¢ç8pã7ì¿ËwÀ´Øþú?ëC®uÂ=ŠÞ.ᬏZ»klAn¸¾‰îïoÓ†ðl>Ýà ëIøà½Ö6ô¾QòìtÏ“p^ôùÜאÓ/1Ï®CÎýÕ§àv
+Þêܳ_ía=O¹pÔü¸)ø<;,õÉ«ò¯‘×í§îQè‘Vº_ż†µýC.l{Œ¹ø6BçΓ®“Ñë1ŠÏÿ.>ùø›|ßòÆ­à>&çLòÝQÿÔqbÀý¹Ð}õÕoíØ òý59á›ø<·WØ ½ù6zܬŽ8vtAïcÌýüP¾“öŽ‹%v?lyí£ž
+ðr“çºÚßWúÄ9°Ði´›­¢Ï¬ôb‘þŽy¿Šîs¶ÇÝŸÑó‹ãã=f“h¯tçûIïF­¤SŸÁ/½÷óx¥¶¸'cnEFú6ôg=Ы9O­Š|žC°ü9‡y¦@^÷€îg&Ña®Å¦î«Fѵ˜Ö”rš·¶î¢û8™É¦xÏm®»_²)¾ãü
+^ûù\7íÇònÔ~-ÝS»˜ÃVIßùw§}ýŽÑó%ü1>Hf÷Ø°#>ÆÞ3¥qX.ì€N¬SÿëôJøè.æþ7èÞDÛtêr_ë|s;¡_⃹ˆú®Z¡½OžjMµ­áExpÿÅÝ%æsÔ:á­Ãcù÷²|»S{0û¢u·)gèÆg¼?Kƒàª-ñ]–óÐAïWe¹‡aö<Ü®!{½%<ŸR¿È—îC]‡©
+Ⱥ՝óü]Ìóòcobœ~§ö¸µ×vŸ Ïuá?î£\g—úBÏeîùk=ÞèŽu-/äB–“<d<Ð gÅ·Þ»ã“Žå³pXž)æ±SûeËàYÑqo=¹÷c®öžuÙŸñ í“Þœ…9èÍký 'uŽâ‹¸—[ÒçZïÚ“[Ð	o8Ûêߺ©³.gàÙ—kŽý£ÖW÷Îo¦Í[ò¼yäß½·íÁ9øè5Ü÷âkÀ;·¹o2Ÿ•æºàs.EOö?Ï_üÛ^ìÑßXîmߪqáÜðþŸâsžœÓƚĈóóº¬af¥í™]ú_>ýñzf­ÿf¼\–7Ž(3d“ øÉîiÙ–dY+B;y§ðÎÿÿ9n„p—ű(Ô#3+Ÿ7­GìøRklcçKÛÇX©ÚÖÑ^4ÀìÆ–3±¨ëôIô퟽h³¿×}xR¯ñ3ǘsª{ÇBÞñy☊»ÎÕ`´/µä4bé»Zr<‘[#;1f9ݯb=mëÞ/uÌpݯ‚#À	ä^÷Óÿæc¼….†y­-r9½µâ0ŸÍäšxoôêÚÇ[;ÍqÇ~eúĸezÛoóÿ׏ñOéË5ú4ëíë,ßuoó:ukwésš¿Îÿ?|Œ÷úÃ7Àf÷yó¹{-~„-|î^k|=ñÿ¢õ«æÈDn¿×Rk¨Mw½×ò¾ÍçÌ÷ ^û^wïµ–Á;˜ƒk¯¢“ïÄ7Fñ¤?t=dà~8ˆ?ï„g¯{Œ[Ђôzî:Ë>q5ÖZŸ£ÞߣhŽµøÓI<L<F>@æŸkñäFæÓ¬°ÏµÆªä›)^Þk©×ÓÙm-õØu˜å¿iÏõïVK#¯œkñÇs­ó¤ûH0ø³Ïý"43rÆyÝ5‡8pØ×:“·ÑçIô]§ÜWdžß7֍üG]5Æ5ïsÐÅÿ4~±žé]S§ÖÝ Ž‰åKÜ3Ö4Fð¾q«k"û—ÿ£Ÿ½ÖÐo1†3Î4½goÎ5ãY×WlbŒ¶•Œ¶“}âXºÍ7&γÿ{lB†ô
 5듸´ïïëQ–¾±ßC¬;&OºŸ6³/¢9Ô‚cÇY-Û3©µ_}û»Ða+9þì››ù^ö±œµñc¬ÄŒÆ·öý–,ƒßË›óü	û22æZþÎû^tï$Æã¾÷9þ÷µö¯×'oå=­ž¥ËŠlƒNƵcÁ¾vžï’~ï!ŸíÌÚ'ý;v9{¯ÌøǦ–x¶-÷µŽküÕ9t_KïãxdÏyƱ…]yÿK=êÐwÒ_˜û\<NúÇ'3ǐ¯ÐzÄßO’±OÓeãÿ³è÷Ö'9Ýç[/ø›o؉O×àñ´­¯±Özn½ïT²a£]ðá­½ô¶}Â:ç ÙÒ‡ssåKÌÁÙÈ
+nÍüB_ƒ
+è¿œCÉσîÓÔSè8æ-§c‡˜»ÎoO€·ÑÏQ_çê·Z×pd#ÈìºÈšc˜âÚØŠGöÍ÷µ`ÿV½¡¯ul×ŒãŽqï\K0
+°âÛù¦»Æ ß¹öØ6]-}¤ã×ZüÛ€­Oµô­1Ñûo­ñ/{Øýµ–>ÊövßešÆ´þG°ÿ¡q;Þ;kmÐÚ1î¶dË·»ç³­.õ¨›ëLÏ{Çøbós­uq
+^ø›iõ(›eätkØ Ù}þëÌÁ‹ƒøpçÜàß×£\‡ }¬õ;m3bÊ6%Üo /½ãE4¹;ÿ^ë}ð>Š²:ï·žµvmx]ãÞ¨}tòáAƒ³Ö“ó°÷Ø';öýNrgµŽ)Çù	ý
+ҍyµøìU÷y“å½JŸÈOøX'ðékmG㟮ÖyÒ6äíÈ ¬)¾{¨µ,È;ÖÚ>Ö‹}ýZK½kñQ° :…Ÿuæ8to“ºH?±ÏÕö¥^{®7®ÿТV¡ômæwé—ó}­ýÒvKûåþ±ß™oÉ7µ|sÔ::‚Ÿó
+±i,’r%Zò´Þh8’»û¸Pëm°Ü¾cþé³®ÖþsˆõÖ[^¤{ë˸ê™^°Cæîñvæ-»yÞÅ7ãÒ}á4¾…{½gý¼ˆÆ>øž‚¿}³•WœL{N5¼‰­ÉÔϱ–ÚÎù¸„ºæ8cÍùŽœîû÷à>í:Šc¬ó½‰go]%ÿ-h‚÷РÞýûYÊët.9ˆgÜ\EÏ؇|ïzŠc-=é›dmaï¾ñ“ùÆ2ÈI/ÒiϺp<Rs©Od³¿»¿p€5a¿rŽpþÎvÖ=bÇ1p”
+àO­ÅÿÖ^æ@ÞÅۍ%<èuñùÉ×ûoµäæàWrÙIô† ŸyÄ{ôNàZdtžÇ÷±É^:ÿt…:9Gf^tnrîôžýèg2ÿÙŽîIù¶ðªóò-ö:9ĺ嵾}î,ÐÝÕ’OÈÿ¦cyxcˆ;Çú¶þvqϵÞï~‘]yÛv^w­¸Iä¦[Cùãݬ϶ñdl†$O^êѯ’vöbÎቛZgÎ?Çør&ó	k~—í“üK¸Ïmí¥þŽ1ZXèÙÈXLü0¯¼O$$Öq¾Îrn¹H?Ûy°¿­Ç7[ïÜiå^ßÛ6†í³¿.xoÅuM¼F47bàþ,‡/µäçbb;Ï©!à‰»t·•LŒŸjñ­ŸæóÓ÷ã_ãçùÿUr‚Ϧ1倿Í_zѯµÔ1p#²õµ`Îéì{-Xà*šÆ‹Î)?ÎûìMÑG·× møŒóü¦ñ*šà ð!q‹=ñ_ûŸçÏpAæs¯;'%nÿVÞsO´³¦akå‹ìÏ’ÿI<Zgû gˆóú³9ùÝ¿¤Ì~{w’s2±á3-Ü’÷2æ‰oäp®¢&{9­œ°õ/õ‡¯ý:Ûàu>3žz¨%6&ÿ~«¥n{ÜÀ]p%±@¼c¬ûÈIüҐcZõ)cãºO,·|ý¨5Û{¿I~êyå{¯µôy¯µ>œ/œë¾Ô’{κÕùäcY˜ßu‡w^DcüFæ6ÖRfó<ü	ÚY¬«ÖøÒ8COúLÖ=ôJ?bß=Ç?ú<jŸ^ošƒ ‡½Ë`c×g|üRë^Õ½Ütïç‰}ò>`nrÀf¦á»›ùÜN|^jÁ0ÐØÕç˜þv¦Ÿç¡Ã;ö¢VÞHÓÙ…lí“^4¶3=ä³¼­±/ódt
+ðü¬·ùÝžoê'ò†^{|Á»ÆñútþÓ,Ç]zà|Úƒ»Ÿ
 t–~Ùݝüû÷Z×:אN#ûÕN´§¯ó:xĽk)=vö}ÓÜÇ¿e!F]mÓϲ]Ò‡6yCðèôŸ›èOØõûúç¾Õ’gOâ…|Üc›Ÿñú^ý‚ùgŸˆN·Žûl§|bÍë¬ù
+y—¼³‹/{b-ßê¾'óIÞã»}²ß’ãoÞã=pÅ}¶éô}ŸçôFÓþ8Ï©ÓüU÷.óÿ«ÞFÍ¢¦ŒóúµÖxçK­1uf7Ó»Öâ?Ès°?;kœô=jݘŠºNï‚nNâE/Ãp¯À9baˆÿC­e2¿Qrea¬ä÷ ò"ÓI4ü¾sÐDÆŽc­õtÚø+ØáûÆ¥'Ñkg/:Ÿ}¹Z~×QgÐ/¶f=]ƒßE´™á|Ïðsú€õj ÏŒÆ}äéDœf¼ÏýN¶…6ÿÖ}ö‡öSì£Ï^|Ì›9¹‰±“îgÞîkmg÷ƈƑæé<iŸp±M,×1î8†ûZç»^¼Ï:‹_ëyî>Ç|‹§±‰w°v¿¾ëø!O“×7ñ6ö‡yo#ú{ý3ïtn3ßÝè>6eÿPŽ¥N‚³‘o/ºèf]¡· >ØG÷õèüÑñ˜ùaÐ}æ‡Øs]…ß]gÀføæ$ß{­ýÍþyh¬ç™´µ¿¬»þ'ÞېìÛ©¾§Z¿³¯µžÌ¯^)Cî·ôžzMZ-ìÏ幬™–k¯ÿ¾Á—ów͝—ñ1â)sĵ¾ýnãyëÁ½*¸Ï=¸ð^>â‘÷Ó‡œ?wq}9wCƒ¼ì5ûíyž¿I^è;wÃ7ÛŸ‡Òwvµä¥}-µ:s§k
+6ëõ¬u26ŸÎÝ4wŸg?÷°O¢ûÌ鶅ã=ók“ÿ>ïjÁi—Zò!xë:¯qg¬µÍÀÈt«%ù¬Œ\çÀä@x‚ð¯Q¼Ðõ¼Úéž}Ñ8ɵŒ`\æxõ¥'C_ö­cœ'–·:EÆ_Öñärœ§/œD÷^NCŽV‡ÿÛO÷¹c„:˜zgÎ÷¬3™£×Ó÷UçOOî8f:‡ÝçÆåÎÍØÿ¯µø~û«ìeÜc\Š‘Ÿ=ôs›÷.Ò~þËÇø¡Öx4sÑX¾—ù¾³¾¿oÌÉKƆèÄ”ž€Ü…]‚ï :¹n±üÝsŽ³LîÿEzÚÇ]ódïÐXË÷>«Û®7Çà…?Nó«lœXŽXûãeöjËQÅá÷î½{O½Çsî¹I 	QãtÀ¨$ÑGA	ø øâ“ ‚>û‰£Ýœú读Îñ>,ª»ªÖPk^Ž«¬)w{í¹^gO²)sÝ­Ê­MR·¾»Žkoþ°<öš¯êÞøÞOêÞøfòÊ¥ÊI6O÷†‰þˆC¼sÔs楝ÎOe>orÆ;\ãÈyÇ2åÔìgÓ:Ëž÷¹~1{ãV?±öܬµ?êã=Ù‚š°­:'GÝÕ•Zžx÷I6:•©þÊ”“Áïßëî.h^į×ìw.“ØÔ;ì}bçØ K®2íc™¿wj ýÈE8WÑ÷;¡Ažßí‹pð{ÞBÞwmÝëß+ºäÔ
+ôs¾œ'žû-ëÆqyÔÕ}™Ûy|Áa層‡÷ˆyør϶¥."çöû’û¼ôÛ;Á÷ ~ÖÉU|,—óú^|œƒl7t”}6ïpßw(óºâžÅ>Ê[¬CëÒvr,уñjõp-î8Wðfznç1î˜<Ý×’эc)yQ^ûð wÇv©ƒS™û.rìtnºøsØQ4ðÌ3¼‘ÚÜÓž±lCÇ殁C?	ïV|Xhð.¯™OË¬Þ~>Øž¼áÑ1æ~Ûy zž™2çk‘Óó&sÈUo݉fÎà":ÀÏÑk/›r_αögî¸OÖÎý'­ØÊ3¨mt,Ó¬€¯·aÏèÕõYÜ«¦ŸZGø-ïà.çžcsnB¦ì<·ñv닳cƒ®ëCæ»ôlaÿÏl¼³Ò>Å»8¿tö:èŒô_”y¿èþÍ}©W÷þ}ì9'º®ÿ“=£{bâç¾AS¦üÙ—¹-£m†KŽç”Õôl{î§%¾ç6û‹kMê™»2×OÎæåp
 <­ƒ½î›¯ßóÕ>èãŸÛàiº¹ŸqsоïãÞE4_Ûøvíå½áDœ£¾œ=›·5·&=øZOûjW÷Më2ÿ]¹}§g¼Ö;ø&·R#6e^—-wÖ÷ŠäçMïY÷¶7yš7àk]ã®çGbÿ»ÒíNLa›´ýŸß…ì‹JQ¡¥SûÌxg)¹r¦…ߢÌíâ†X]
+õÜë¡žágøŠ÷»ú݉ΘÇÉû଴·
+ì‡í^ênÖtb™_ÖÕ4–²!~‹ŽœW:ÁJºZI×Ô‹½þñ‰•p×A/y'¯¥ð_ÆÙNßèu]æz¤¾»†;Ÿ£ãÌgÞóÜë8Á?ñ—‹dÉz‚]õOÎ<Š½Ï&ô8Ò½ÓYçÄȾÌm¶®z{QÁoqíœY#ÏÝ«»ô—ôÜ_z&õ|ÇÝ­Î=_øÜròVÿÛ¾¦e¹ç)¯å¶ox¾ðüs(í÷ú=)sêeSnßlHû´ô‘½‚kß.ðzñN^æÿºÌ;¦ëù)é$Žõïî>Óµ}[Ç» ¿)s[¤YóÖåV>óEoîG[:4Ïm¹µ•mêý”Od_—ö¶¾Ý«ÚŸ¬û@îÙŸ2¶Òæ¾G­êü³6çì辸5K éÁÓsíHϝu™zzúÏHÔ‰“îŽwÞÕ½¯—¹S/vñïóœ•óŽa¬WÉpW¦™õ,ž—z‡zAëü9â½W¦\²Ñ]¥wŽûy÷ò¹>æ}ê.<ƽÉüºL=(z9×==Èé	Ý!—s=½·k12ϽqxÑÛZóÈVº²?Û‡œ;¼—wòù—qv½ô—ô¿<gÏúØŠ–í•1ÝòKâ"g<Ó´,™»çÿÖœ‹$îùM)ÏsàYÐ<“žýn÷ܯã¬+mÝÁ/í»®ó…g<çþäg›ç»¶ñ_Ú¹©åçëøÏ=øõúfõLš>•µÍyt§;®Kö+Çú¾ÌëLÖËìS]»ì·­zfš‡þS|ûÔ¹ñ“÷Q¸†Ä¥Î:Çeý!#¿sä£/ޏ¬Yì]…GÎvïå~ý¨wô»ÆýCœo«}æµCÐÛ~_n{oËpŒ»Ùïä>Ï\—½ZòIÝa÷¬	îe³W4=ÇŒc$ßÝŠtIÞñÌ’=cÆ_îƒçÚáqoi[QßíôÎùÙ:^Zþô•>–~^…MF½+S·nÝßweÞ'n*ÞB{#îI8Ö£k«2ù<–xGÖ¤¾Ìs/]™ûúà]îe–õü…ä[‹žý‚^Ø~ng-£ß¦î+tÒ•)¯ÎC9¦·ât÷ľßÃۏ
+ZI¿EÛk椔uWneoÕŸüwî°
+œ¿mÿçýeÕ¹s±kÀªÜöƒ¦³¯w:ýg¿’ÐÅÿ*öüïÉs÷}Ô«>î¶h<'_Z4V±·Oõ?íåY'ÝñŸz²Ìû ±,m¹â·|âžû*ògòÎÎónú=EÖ‹çè¥-[ëÿ“ÇvZ”É¿Ÿ³£g…Í4[w÷qº”²>õ6
 {®DïŽÃ®´eÜŠ÷¸®Ê|fj½¯Ó¹ëÑsºÝ”¶_[®]œçó\ù]-½·d‚vß~÷xn‚9§å›–Ár­bMÝÙ~}¹µ—Ï3öׁmþíg^Ó“qñ
+Ë¿næ'û¥çÖmÜ]–[»íb¿‹oדUœµäN¾èd,Ž5À=þëòØG0ömã¼2úÆCyœIêÞ½ÎFx¿þ_+«é\ëú ïÖ:Ü»
+ÿÏu…8gÑÿ¬ïÖÞ%øÜéžÆ½¼Q—¿oððýûªã«è=4îÞWÝÝ<Ô="+úkÉy¯ûWÑ;éÌ:³¼ôí)CK?W½-÷ôNd<Uö‡;í´Ö¾unyÏÚ?‹/óǹL}/½qÏö«îùœOöÇyh_æ9(g·­èÑ'ïÊäÇÇzç®Lý:³ºF–“h¯ÞëEã¬ÿ“Ö]™ü‚w Cä…ÿ©Lý½õÁXÝY·Ö4s¾9”yÎt_¯“ÃЫûù½¾½9Ãú̳}ß®±GÎî·Uk{eo‘¸­Þ?ç¾÷ô<€ûÒÜß=ĶæýÖwÊ0žß×õ®Ì}þîw©ÍîÝo¸Ÿw¿H¬ Ø;Ýs-ÅDzŸ"66º·.ó÷BóT÷ðÙE™×ãœIWz«{¥ƒtÁì³hØ~ûÿžýÜAw]ß·(SÌwõ|%|¾7UŸô@ís‡ü—ýà&ÎwÂY6ønâ­»ËÆ~Wne],KûMyïmð’Û±c<Ï]™ò>Þ}ã{÷ºç\H´7þ¿÷2¶ùN?=ÇŸóè:À2îwׁG¼›ö¡A3ûÒ½öú°;÷úú~lÀ½^t‰^ûèú(œNàÚî™Æ3‹ca-¾Ò½ó¢Wç[ç÷}cßµ¦­sê":ÂfðæÝ}™û#µÚg½xd=ÄNÇ2·i>%gë½¾ãœk÷Ü_ºçóÿeªîlb`-îí£Ü?gZbŒ;ä+;7â3£]<»97uÂuîÛëÞFgî:}Û_Ý"ƒß‚ŒKÑ÷LºŽkC'\÷(ìuÕn¼k<-û²ÌåöYÖ£ÄC—]¹•?sÈ"ä ³ò?u9m•µóeè…\”ušäSð|YÏ» Ç¿ýp_æ²[o+ñIþèë.Ο«ÍÖ%12Æù†ý“ÎG¸”iÞÀŽ™ÈßÌ6Ùÿž+E™÷uônà^u~”Lî÷¨Ù·‘§Ø#î\?3×_‚¶ã-ks=ƒsoqŠÜ|/ËTO­GËŽŽs¥÷¸ŠÞªÌõmf3èÑcc«…ðxÛûåq†ÄNî™ð³ìœ±#°ÿc™ò 6ãmGÙl÷ˆùñîeòêáQoÏ}³ýß3–óÔA8}ÜwÝ8WÝ»fÞ2xÖAgîçLc%ºÔLüŸ^t+:ñ ï±À#çÇcWæ¾
+ìªídKbž‡œ*xÏ}osíz[ÀGÜ<Žm÷9oÔ[Û‚¸ÆOÈe¼ÕñpОñlÛ«=ôŽOÎü띿³¡<ƃõ±­ò\Ê<f;Ñâ¾û‚Ì­Gñï˼ÿyUñ¨kÉó·¾à£~PóÄ7øÇ ÿ-¾óÙ ðZëøþð»~6ÀOêÿ§ãþßøë Vü‘×÷+¯
+ðf€ï
+ð£ŠóŠߨòü¥ÒÏ~]i¼©xŸÖu<ûv½?ÂWë;?à;üp€ÿ”©^þv€wøC}ÛÈïWü¼®àó~9À?øã ¿à_å1Ïþb€Ÿ
+çÏõûËŠóy•ó³úýEÕ%ø¯Ýx3Àv€~€oÕõ8ÀE0îmêý÷8
+ÐUøïU#ÕuD_O/¯{º§{¦ga†uØc0Ãxc`°±{0B`a”
+ÇF†àCl°ÃÂb $f‚iZÊOÄO„È">#G?–">Bb#G(È!|$çÔ=·ûÑ̐p¤wTw©[·ªnݺõê„_R2Cñ&ÄË~JòòËh|‚æ2’‘O
+ЦvB O“ö)ˆ·Fë›5Ö&}Z¥CAzr¯¼ü7JqÈ/´¿Kº7iOÒ¢éÔÆéeÝCù)ŽBŸÈïí±qÛ¶§§Þ¶!U>«“<꘳6ùÝgÓÁz´KÀ)Øt¼}à;zØ]Î@Ïِ¹|ÇÐÞƒñ ?yô$|Ôz²9w
+>½ ÿõBÇ# !cÖnÅ^ËÐ_
+{hûj¬ys?4seʹ
+v… úLÿï_ѯ³öy³£lƒOÉO,‡î°îE¬Ûb2ã°%±Ú5ÁÐ&ú3¡ÃLÌ­ÆXÅ÷!ü
+¶C'êÚew©ÇΨ„¹æ'ÚBÿÐÚ%;_Þ»QÁ,è9Ë|:ó;«óyã9›ãší°Óc½üÞJ Ïâ ÎÂcdtßØ>+_—°g˜C¬}r¢ }{qf{ѦßûCÉüž»±ßBèÍý_Ýn¾sçPì?4­;RÏ‚çEJ[¹Ÿ§´ûÇ }&77 eŒ‡Œ³±ô âün”þ`L
+DÏ؝¤Ïâv÷×ã_ƒ­_³p~˜š™³A%З±Žwä5Äï	c•à}™‡$;BºkH—
+‚EØëŒÝûBD‡¢|?‘ïJòu”î4ÊøJØä=p4ct-æz;܀”w–÷†ôÛ¢/šNÅ~uŠÒå>ý[cwŽ1Æóõ÷žw¯š2v>T¼û³/é,7o–svm—6™ìtpÂúYCúë /Ѽ‰5«@n6¬c~K݇óÍÆ¡x©pvq´ÃàëÁä³äTpþ8®5[m¿0؏önãwØd4dy€{ÖÞ€Þ§mÿ8Αg7ýVBî.Ë	µà+ at f²SÁŒíAû0Æ7 ´g‡Ñ¬ù„túÛÀ·{m4[3¦ß÷ ûm¬ýÚ'"kúÌ7	“ß'?±Ïs ¾)Ú	}çY.ËãŽÖB^<#<®7¤Ûb„¹¤Æú¤<÷µFË£ì#Ç«vað¬êžnõ»TÃlPÁö¸HÅþ#❧ñÕDš›b9ˆ1¶v½½Þ±{ZŸ†ª&Kþ,Õrª]檦™®±GT‹Mé$žæŸ“,îý0‘w,Á½à»Ðè‡V£.@»ÆÞÏ#Я±:÷ƒùz¯½#Œó<ò\sÅà=ŒŸ°š}rÄÞnù£;Rïu	Ô}¬æ»"sóä諾¯üšf½WÀ	⟦Z¥Su ë—`I¤-Ú-,’O¿YãçækŒµâˆÈÜóÒÇ×Ä3´ÇõÉÿ”ÕV«à›5ðÉ‹Ç‚ùíÄÕ!øôåÈĸ{?~^ÞÿãV›Äqgønò¾å,·l²8ÏÁÇu qä*ö³ÈAyÔWiÌónÔ€?´»îîwÂî–£IФ­­Ì»ý*Ô½I{ÑíïCæZè¹ye
+bàП*GCݸ	pw1	}Yƒ¥ƒï@ÇcX·Y9f·éÀú)
 ‰œÄº«ˆ>ã¥hºœ´üŒßéÀ¼ý«ùÆ'Ë”¹ï=ÝûÀIì³Ãd¸7ç¤jYÚ¹k<kþIØ:ê°ÙüX„Œ†òZú™9ÆSç«4r{s÷©ÖôÔË;¬‡^»Í7µVî‚LOû,çfû¡î_ÀÑX™z}ú@no²{‹ª©›¶Xý”éR`08	<´õ®¿[ðV,Ò¿ÇÖr=XÜ‚À†JÍÝbýåh¯1=“ªkÁ™ÇTÐÝŒ~C¿û	øs)ÝÛAÏÏêÿíõ&›>á]o
+¾e1éþ3\ÞO›Î¤^ÿ•ˆÓ•èw2¢Y Ôª–Ñiuxof^¼âÉÜ×ú3ªõˆ}滜—ûÌVÆ£ÃLóÁ!ӝçCl2~2²?¥5)‹…É{ÿ.âò¨Åf ž¸â´²²ºq»•·2üÂâ©Yà¾äK`¯ä…æç ûû°gôXŽEð7NûÁ÷
+ÐmºWïÚ¨A½Ã}‰áÆQ/°ïÿA“ÐTäÿ!euƱ×Jåâ‘@“ÞÆkp¾GÊù|c2ʹÌû|ûÚõ–
+‰¹·ø)åö¡Z;]4,•Ü¥ö†­2?eägOΧI»þ
+ãKŠÆÅëÇ]]½ª<WM“Ègõö.¸¸þËy¤ÏòaXUÛ¸þœß‚²~!ÚEÀÍ.§Ê1W‘áiRv„ÈŸ¼sÍÌ£ EåÓ¬x¼Íaµa…Áíµ·Ïl­þÛo´Öè~{ëÇÀFž%kŽÇUç&ésô‡óí¶‚-v&«Mž1ÁÏÜÿÆÇ*…BJ<Ë[–r~žyhÑùŽPÌqn¦òÈrí7VèÔÎ÷½çú„ΧFùŽ2šT?ŒF{
+eI¯c¸ÑüZŸÊ±ñªñT§ª!?z™a<¬ñ±Ëí[ÍG#eG\>k—¯Ú´6.ÿ~Mˆú±´—WM©?ðhäŒX;MŒØÓŸ]u‘±è|úP-7Ù›þxLàx}„Æ€@4VegV4ª£—ßRÜ'¡¸ñ4%ÿvj¬Ú®êv"ÒOVñxŸ{¾xwø¿jmt}”/j_çmü'ì)cQ
+Œ­ðƒÿ@üâ¿ÿ&	|/&«]H	ú
+„{ñUâàË8ÁàK¾·€uz3ˆÅ@·j£Zß'_¤½Žk­Öé«È¹Û¼xº=O´íõ‰®©Zÿ@ûýµBóOÉêÉnݽn!ˆà~Çï·wTj]GÈ¿þ<ƒÛh©jíý÷Y§%Êpã§bîý©ü*oýí¹ôIÌ•:¢]ðoB+Ш7€È	­†×Ý?i„¯ ø÷Æñ¿îþÁÓTêƒí¶G³ôc]ÄZ¢µŸw*ú^Í	6‚n´¶¯
+BwhmhuáÍo)çÖÕÌ봍µÑxQÖNOÇ\ŽäX[ÌÕÝ=À\Í£dË(Í×Ikó…j/”ßø×.Ê:‰õÖ“1—=mÔ^¬áX²~a­Ð" íjÏ‚û—xFr©óÃꏑ̎˜û/à¿Â£Ú¯Yý‘Ú³AãÃD©Ý¬µmÒy°âði­'ßcç>ôÿ—Æ¿ÿÓA¯¨Æ)ÿŸa„—í—ø¿soXA¾¬Ó™Ñߣeß²9‘½›äoFIßqÒs†ôþŠÆÆIÖs^_É›ªñ1ÚÏcŒÎ1ŠáÚ"Ùí’í×M©Úoœl#&ÉŽ‘Z7V<“´nªèhab¤M}ÆW²7/K€bîŸoªlœ¦ó¢/µ·ÿç»E£Îª]:¶h¬EçÉö`Í# Ãú9›öµél|\4Ë×C46D2Ú$³Hª|ÛF ÿ„xxnC•—šW¯”W›Tslxàþ¸oÚÃÔßãÛoëàÿ¼7#{AÏaìwÀÕ ãM¹ûØ|Œù«À´ÿŒ×w{ük?."_^.C7Q×Þ*ç¹|p
+:Þ®Aq9ú*Àý?Çü¿M~Ápò® W!ãºÙN=²¢Ã< ¯ÞÃï«ÜW
+ÿßÍ×uЫ(bïìÊ›¯?F]~¹á7h_6¿´ÂGÃÑæ|-æh×Kf‹Ùôèwþºd¹¯Ípï⯱öXó)æ~‰wóÖþraKpcŸ`íŸ@ÿû/^nB¯ÿ€ÒׁÚXþ-@ïœâ¤`2?µ3ª6øÚ· ÿ³—
+>ƒ®?öe«þ=yg+ç`÷ùÌôÏÙ~çÊa­õº"Ü€<îõùÿh¯¶Ø¨®+zçyçá™±ÇØ`HÁIx”ðp)`ÂÃÁBRÔ´…&(’“Äy˜gx'<í Ü´‘ÊW“¨ªøˆ”æ³ý®Ô~VªÔ~¥jÒµöYwæ2±HDºKûœ{öÙgŸ}öÞgØŽàÙ•1þ?à3Ó3‡yÔ/çýÅôÉ{×ÑÿðgóAžK½÷Wøóß¼IX¯:MÔf1·ÿi‡±˜“ó¾Äžÿ|…µþƒ³ú'Ú}xW~e²Z ÆæÝjú¬â«!R½7	æ9潌@æEÚ¤^í	ê7HNƒú’•‘¬6µíܼ—Ç=€hÄÕÂäéOÏÂ>f+>çíŠù€æ¼[sý[ˆ‹[ð]×o¬A2æÊAíZ‡ýïS¼3¿„-oã>ý¸y‚¬/Ð^
+»¦ôÕëðááßvöðŽ7øü·±ÎUÈâا^ç…ŸGàWQ€±03âê0ËOÏê¿fïC¬; ÙïCÞ Î—tøÏaÍkÛ	þίÿë]‚üNôÿ
+ÞkÀ xApÎσ~ 9[°>ÇÙžbÞà]èpÐÖgŽìµzõa˜
+Ô©ýˆw
+ô(ð*b°ôuÐÃÀQà °¶`9Œ±«@?0„õ.ƒÞÄÿ÷_£}¸&Y%ࢍg½_g€÷€z`GŽQÎ9àcìýÉ<Œ=S¯+Z—ûÞü8|¤58v8œ߀¿9í,Ú;YØ/é-6g½Ç:€5
+}o¶·…•ÚÝWííÃîixOãiÀ¯Ôõóí~	(kkÚf‚ú9ÉkP]ž7~7'£uRØO9"¿‹aÏ
+ØO6h0ýOãü]†qþË ó$ø.£}ÿésüÿ{È»
+Ò!»·
+سoc7koàãEÐ~ÈØ‚¹°Öôwa?Üû.ÌycïšÎuºÞâ3e÷iôYøõßÑÏYûz{ðÈOl…î=˜÷¬ù7eÆ°—8þ%ÑŽz¯Aÿ%è/K0¶ÿª¶g-ÊóK˜®‹,ö×ãÑjÑmf'îˇþ¾µËv~Œ§ÉÞRè¹Ôl:ã;«ùfó<þ×ÙXÙÞ@é
+öÉî#¡ð,úu¶@F7ð2ÀöDzuÙrý€^K­=9ap'qf'ѦÝGB9ôžc-Ö[½¹þs ‡Ìvîj€õ§€¦BïÄ‚Î%jçEʽr½€rßÜ[sTJ„æ¸ã ýòó»QÚ£ÇÞ#ÓaËÙ´Y4Š3Jx/a¯?6pvXÉÜ™Ë@éeН˜Çy	>Å8¡¯Œ—ðAÒeˆÒM˜CºÅ¨çmÀZÃ÷…EÙÌ·Üܧ³¥}Ãô¨QúWÜbqàhÚèŒ
+ØþêF¥ŒYÆ
+éÏEŸ5Š#ê¦G+}Ú7j1GãùqÏØ«¥‡¬þˆÝqöeýæ
+Ëc[ç
+å„^“BNf?kà›r/ôÝëÞŒàÍx;@/ƒ¾e2Ò˜³œöնLJ¼8ƒÇÛÓû	êž«+…pöÔœ¶ž»¤ ¿#¿C¯Ñ<†ä®Yox
+zß´õc8GžIÌôÛ¹Ç,'dÀW€ÌdóÞñá)ïþ÷ ÜÏa£Y³	i/ú¼[÷c­ý¶×´é÷*d¿¹¿@ûJhNÉl7ù%Ù‰}žñSÑè»ÂrY1š<ß[#,ÖÒm>Â\µ>)Ï}QÏò(ûÈñªãV ë Ö!Ýê/Š¸÷fЩ6ߝ|‹.U¶xWè?릇E96Çr}Œ5ÂÐë‹Ó(lÊzs¡ätJæ2Õx¬é–?O‡ä-æ‰ožÕÇLÇŸ’,®Í7ãRÄXœkÁv¾ÑëVgv¡µûó"ôã[nâƒùú¤Ý#ôó<ò\cEÔ>ΪAòƒývˤ	-¨ûT/
+­½i«„lÌiŠTß`Šžjîqê“õÀ&àq`5ó¸h·°A6}<4'[©«€I¡±§¥±@:r.õÉ¿>ÌØÈ{»a“óÇ‚ÙíøÕyØô¼åÈ(|ÜÝGÀËø´Ú$†˜á½Éx«³ÜÒk~^ç@cÈU¾Õlý˜w	óz-6¢à÷-Ö]|Ç-¶M€&lnuÜ­W¥îN:‰þÛhÿ2÷@Ï=È+»á/€~ qu|/àb1}Yƒ¥P³á»9Œ×#g¹sÜt`ý”@NbÝUDŸþR4]†,?ùVw:0o¿nµ#ïøD…2÷SÜŸ†°Îa“áîœ!»¶Ï˜ÐåfŸ¸Í£o™‹ÑP™K;3ÇÔÙ*…\çîÜÓª5È» ìƒ^ÇÍ6«?Af at K–s³#ÐŒÕWŽF*4Чú‘ÝÉî.ª¥ÁÝÔgõ›W¡›NàQ`xhê]¡þ
+ôá®Ø`{Jª&trÊ6Ö4…jîfëoE{·é™P˜gwPA±;ý†×ðÑç’ŠÛ±æÏëô~(Ù%¡{“±>Îû™ùdÆj]—÷S¦3i ÿvøévô»
+iÑ,2jûtXžÅ™o£xÒ÷5XµqÚlW§xsõrÉöJî0ÓßlpÞtçùGà›ôŸ´öŸÔœ¤ùÂQÈdÜŸ‚_^2ßôÀ“ŸÖCVVq³8¨Þ•q¼
+éOM×%_kÅ!Ï7;_ƒîW±ŸcÐct¸äÁ7Ýt¸œßó W§,¢¨W"ˆ—â0†zý¸Þn‰Qh2ô~HZÝ€ÿXk»rq0F÷ý‰58ï£Få|Þ1iå\æ}Þ}­ºË&DÜ]¼Z¹ý!Í](Zl–ÜÍv‡í0;¥egçOΦ	ýwýmÆ—‰7øïêꕱZš@>«·{Á½Abz4VòHÉò¡_SÛ¸~ί«¢Ÿvpc*ÿ“Ÿ«ÊhBûð‘?sMÌ£ EåÓ¬x‚=û^xÛnð~K¶ßê¿3F3FÏØ]ߎ=ò,Ys,V› ÍÑŸÈ»ÛÞ}v&3Õ&O»÷¡{áÿTå!_HŠ'myk¾å†º`œyhÖùN’Ïql‰òÈV­7UèÐÎû½…çú„Î'ª|GcT?LA{eI—@'úp£Ùµú.ÿŠ§ö²²c Ó‰g”yÓB6v¹ý€Ù¨MûˆÉf­²U‹æÆdßo£qÑà_*WK©?ðXèŒX;Ííg¤}åBÿÂã©{@­Ü|hmÚcÀÿõ!<ÑHÍ>³¢aùÍ5¾À
 uâò›€&eßý«ÝWm;ê'jx›|±¾aÿš¹áùa¾ðþ:îà¿bo¤|„DÙÛз4ü¾øâ[o„Yï‹ï«]H}ú†{±Uüà»8ÃÑ”|o{ugnÕF™ O¾P{/çZ­SªÊ¹Û¸xºžp;Ð'<§fþíÔ
+5<Î>e«'»{Ý‚Âýþ¿;ÜÚa=juÝ‚'ûçéÝAË5sï¿Ï:-^û#âîï6åï¼W½ëïÌ¥«0FTëˆV!¸Æºˆ:aœá÷&
+ñ„à¾qü¯¸w!xš€j}pÈÖh’~¬‹XKŒáž
+ßWÞ~ÐýÖjßþ;Œ6×·ºðÛÆû*y†u5ó:÷ÆÚhº(k§'#.Gò_KÄÕÝëå'&k/“5ž“<ÖækÕ^+»ñ×*Ê:‰õÖªˆËÇmÔZ¬áX²~a­Ð,Œ¥íjϳ‚{K¬‘\ê<SývÉœqï¾ÓzMê·iÍýÿžèXµ›4·E:—>©ùä[ ÿ\§Kï¿ÞV|¦¼Qý§<¼?ý/ÛÏñ½qwXA¶ÌéÌhï)Úï²ÎÐÚcdïàŒ‘¾Ó¤ç¥÷Ãú7M²ž
+ô•¼¹úß®õ´ëآ͒Ý*ÙÁ¼95ëMÓÞˆYÚG›æMÏ,Í›+:E˜jSŸé5 ìýÀN`ðLĽùæjót^´EãÿÙ¯²Ø¨®3|îؾ÷Î̽wöWìñ†0^f0;f+"dÚª4u“ÚRJi¢ªMH¤6$¡MH€6”¦…R5`Ó:!Q9$J$·âñاJ•Ú¾´ªú~ÿï0×i¤Vªò[ßýÿ³ýçßÎ?çpoÿÏßÅ RŒÕê˜e_–ñ¾ŽãuD* ±³ñ±"ľ<c£ó"C_׳¯ž2ò”™Êz› ½s$n
+¬KiU¾¯ÇXWÓ¼K_“*¿dßUàÙ>fÈ»íyÜ;ç1ÿêã<j⃸—žDÿYÜ®aýÈœW	ÀU7€9Ì»µ7€¼cß4}ïFÁM¬/ÃõñäþΗ]ÆUìuÍß/*º8¤Q¿ï5àjð5ȼ	úG´eìtÒ˜‡üÄ<ÎõpKÅocΗã ßóíºŠ¾´oÒÞ[ðéIì1~~?_Šž²÷UôÏ€ÞôõŠ 6æÅ!'¬þà×¾¼Wñ»økøóúç ÷8~{^{¾¹'ý~ûYê-ü¦¾ù×}â°Cô‰ÂQ?¢ãÖ¼íÛWŽÃ¼ª­ÃZÇ÷Y%>äx˜Á~âcÏÇSà_ÅúëÀ[¾œ˜šUwaÎ(ú"ˆ‹
+¾ì“Œ•øûô<øÎ"毁¾€¾9î£ýWöqœþû~;‰xÍûz8hG 3êϽ ^èг¾]|’öý7ççLZ=½æà»yÚ>™o@Þ5È}
+rD¿?aýß±æØûÏ࿃wå¼?/ÈÙü ;}”çK~gôï¦Ç:'u/BÈ©‹ò'_Ïv‚rl§(+BY-äe,¦¾Œs| 5Êwa™Sd-èƒËx>圷ñÌkêªKs	íKðÑ%ø¤ÜN-B2†¬AmÜGÚyäZ•zG吇žzôE৐÷[?Çrj~=
+ŸÎAÖYÄá/è“þw1þ"rî]èñ&æ¿Ž}žƒ,{I…Ô¿€ó0ëè9ÿ,ôå{˜_Ÿåßÿ²ê‡Ø÷d?	y§O¡[1_rôäŽbþè{ÿTÏBþ(ÚÅÜ3ÀiÌ=
+z
+9sè g7ö—±‡|›ªÔQàÛÐá»þþR#ƒú÷Ü£@;à_ª}8ˆ»â7@ }x8€/äù3Œ=~Âúð<úŸ΃ÿp†²~	<ãGÕρǁGS†eLäHNŸƒíߢ̇a³èõcî+vòó>
+»¢êGÜCÆŽO aÍ/0ÿ8êáàŸñý…ÿLµÚÏw¹ïÉ=@î(Š¿_#÷åû˽ÌåÛoF£ò†\=þAóþÛõ—€_ñ9<o‚õÆÂû³îßv‡¹ºí÷ß>g”ï!ò.&9{kU+Ñ}¯•>¹«­æÜ)î=Á¶øUîIYê`“J
+Ñ÷ZKÒ†æ ²œ'1aŸðMÔ_b”1*wÒ$i‚ñ•}¬€ÍRƒº2e]÷é <‘¡ï¹iêÛÌv-mIt–zÔ˜ïQv.`wåèw‚M]‚ï‡N®O°-úöÖi{KÆÂ:ÝàOì	ÄXr§&@%æî>Î8éü‡þ<AªóàÃ|.þ׳!y/÷÷ͤ’¿C´»9@„~;¬¥üN®ë'z‰1"Nˆy"Gè<—sVKè9úM¢ß ê.´1€º ¼ Ü ‚óõÙѹ×E[$žrÎí€/Š´±•ó$÷Úè­§Ô‚QÒ:RéßDãÜWÖʹÒQyÃê3¨ßlúm3*ï«4¡ßÚÏÚ‡y®—ZÔBhïöåóZOd©GÐO«è	 ó>Èsn’>’ø­f[ûTçSý$¹°Î(¿ý¶ÑV‰«¼ƒº©·®_0ÊçRjßJ£R‹šc]ÏtK•:ªë‰¶íNuBdÞC}îb{Í"HÿW€/åº"û
+SÿΙd,$_>Ë5#ŒÅÎïâÜ.Žõs~‘ºJmZ¬0*5N ¹(g|·Q>'“ôÏ(©ÄUÎÙ8}õ	£œ{‡é“ô¥^s?ù)®£ŽÈOÕËÿT/Å–R‰—Üֱݠmı•Ðgjå"L­„®ÝD;±œxHNw•š<BÈ<y»ÈYj0*õK£#€æ÷Ag }ìk§M’û›©ßnê'gCêµäØ8ul`ßùÊYε‹k¨ä—œ£½:À~Ña×kjýmŸ>W:+=_÷÷z|)ñI£òû£ý¿hg{9ùÚÒB:F{ÆW]»´ï´ouì–Ñ_íôëRÚ)sw‘~Þ(××5e#eKm׿KmäKôǧŸê臤Žª-WÔèÎ)|;§®ïVÇê}jÛS?­ŽîÙrÅP»¦¦iãñ=—‡‡TÏåø,_†Oo>K»ðéhǧЊϒf|TÏ+ªI5ª†ñÂø§7¾¢ZÔÕL¾MT+ùNÕ¡ÚÉwãÝÛE¾Oõªòýj¹ZF~P
+¨ä‹jX
+ùüå۱붭ølÞ„ÏÝølÃgý(>k×à³z>#%Ql¥Q%
+¹ÏÐUäשµj
+ù
+j½%?®6ª1ò“ên5A~‹Ú¬6‘ÿ˜Ú¦¶’ß©v¨í>gïÇÿiüW!üÙý¥ý¥’ÁR»ÔƒjúÝé&ÙØP¬»4˜)Xî‹«
+_W3*Õø³JC¥âà@.Ûh¤-³ÐÚÙ1\´L4rÙÁRq1ÔÙQ§½LÆK—¿}¶ëÙáˆíºvd_oÚóÒ½é!=Ûm·-Wðl!m®-:Õ«j¨>£"*#»ÍV‡jB!«[ÍÖX¦ŠvE¹áÂ0ö3œ\SسÏE_8êØXëBý¯ªiÕªíIÆSñÑ­5Î@aÿ_DKøe[-3“ˆ»É¬ëƼ˜m[5[>Šåã	ÛÛfôPsÜuÝTm2‡m;•ˆ$ë“É´c›f´ÖOf}‹*íë\[—¯“ËÖÙ…ÛgËTü¶ÆÌÐk¢G1¬YQÉJÄÜTÖu"–±ÏËg</¶"®¿ˆ¨fÛf$lÙÆ7s1×uÒéˆeTYN:ëá/e[.Æ윛°í˜k›–ôãY¥€®hÝ,ÚŽb™mD
+ר†Ê£q¸
+‡ª
+ÉxÊwwÉÜšËdrcc}}Ó_Jõö¦'7l¿¨z䐿¶¯3·M“;³ƒ}+ÅáR~÷˜2ÜXÚõ’^&íºU¹t¬hØ´°J5SU©dU•ãzõ­IÏuëR°(Z—›a³ªºj›‚mt9ۖ༖sµHÏtœ‹¥úHêcPðjdÊqs¦›ñj3ì&XÜÉ$cÉ+êX6ö[ÖcµŽã¦=Ó4ã]455ǝXrг¢©Z=u•ïŸÜq þ1µjÀ–ýcJ…ÄÞþlÿ¦‹Ù§ŸÅ<óAÿµG¡’;™@ò#©JÃw0Á\ÔNxN&ƒ©±Âö¿Ù­·ß8®:€ã¿ÝٝËÎmgÎÎήí¬¯/!N’í˜LSÕ)©Ò:¤Rú©\4RÕJPÄR*‘È””QT”Ë•
+iUB⥠則ŠwÄ£ùÍîØq¬š…ÇïÌ~fÏœÙÙ=çw~çÌQØï‡Q Ýpü9ÍÇl=–Y¥WµÞsÂw¢ »I ¯îj^i´u*®ž:~{à¸qä:¶Ç®ó%M­X3ÑÓcÙßÿ6;
+_²«ÆٍAê›4̺‘1~˜×¼†ë9íFÓoÇ­öQ»å:N`û¾ã½úa¶¼Òô-ôN{Ñnú­öägìV¿£-ôÛ}ß)Ûdéþ[m“Sf}5
+:Ž:
+OÔ–òáÂÐÑô¶ÎuÎõ²'³SËÖÝ»ËËo>¥ý±Fã÷¶Ž_ªO’£r|4¿Å¤¦œßkQ2N½2èíصckÚÆdjòÐÂüQ+×!Þ7ék«Ùn/ûÅd½~™ðAôžËOéœ×üÿüR¬ƒâÅ[:¹Û¾f¹¤Ý8Ø\qÜÐwíó»^¹°Ê(Ïʸÿz”;D=ÎW™¿ᥝ°G}3$i­W÷ƒ$Ò44?êÕLrx`ú7ú&I§2?ˆÒ¹¹4ô£‰“˜þbÏ2&ïhLt–ÙqDü–?ŠHËZ¯õº|üY³«c²úžH<äÌ@3ÚLŸÉgM`´[Y÷ø±ÜlÞÌÌlíΟú[¾Æ¿dùÖ8öå¸Ú
+§Ñr5Õ‚SI^¹réÒÕïl™ó¸ǧ­Ê˜“©ñ=ÕŸ=Wú““š	ÕZ}PØVËGN6\‡®gƒi×z5ß7&LM˜'¾_?´œØÉÄáNÿFž™vY¦ó³&ôý m^H’Nÿب-å|jOŸ<«µ¿OzͶvû´²´2ôNø¾¾¶º×»Ýëã>¥úéô{IÇ+¹´Ê›ƒV¹’¯.å£G¥3\ȆéôÉ“ÓùÉöÉ|ëæÍ;?ÐmçþŸêýÞîýN½a5<§ºÔc½¹¿žëkkcãÎÆF9þñö¿å=¹§÷ÏífÛÂj~ÿiZm~wÚçN¶k^7îIlLœèñBO£˜õ23Æh¡ãL†íéÙ$Ž“Y=¾Ò˲ޱù	uZZÑú¢™¥y~Ob™#ãŒãpœ{A&m­×ÜûS¿7QÛ;Ëiž
+÷d_ö¼>hB'\èÏ.çùc-Gçœ/ó<têŸyfó1ÇÑ…®ü_«·4wVzªhÇûýEnÏ‚¶³¯˜(:4ÝŽr×3™ë4ša~Èuü(ž9’†¹ëç™>ÖË,Õ„ö~EíÈ芬³Èk4›­Ðrü¸ÅiØO]¿¥‹®Ýl6·iÙ^,ãÿs–ÜÖñtdb½2ΫÖ(¯´¾QÍ}¤;oœÎOo™ÍÍq.ÕÕG£\ªþå•é£/MgXïNé™ìÎw†ÃÎÖuÝîÞº%»[                                                                                      àÙãõ‰Hí+"õ@]Uzn}_½/ÒxY¤)¡ßÓü‡ˆ½®>q•Û•¿>ÈÕ6¸ë{üEÄ[Sú»­Ù=®                  
             Àÿ‡Ô¥&åÖ«,Õb×[eŸúQ¶â{^¹1:n?,Þ(~TÜ*Þ,~\¼Uü¤¸½ïs›Å/Š_¿*ÞÕòoÔïªúßïÞß/þPü±¸3*o=rƒÙÜò’”™ÑÖÜhÈa9!§å)yZ¾(ÏÊóre{[¯–µŸ“³£Ú‹r©¬-®ìßL&ë¡mpå›ÕÝ–$²“–
+-'UÙÖÒL™½
+OkRYªÊu‰´µã²¥Ÿ¸\•ZþnU¶µüó³ç.œöìâÅË'ÖŸ~õµ—®}ýáÚásrAÎk ÎÊ¢vü²|Y¾!ßÒ/~Y¾*ßþ”ë'd]ƒôª¼¦A½&Ÿö
+ÿû'Ê<÷ÏÚ'zQvcs¸Oí³zVž¿^»-MâëºËè8~äI½awÛö't“5ý®+ziMúòqm¦ú…ºXé>¼÷b|æ_29¾ñÃ¥¯ßyßþûößd[>ÖÓþÎêòŸ í
+r
+endstream
+endobj
+30 0 obj
+<< 
+/Type /ExtGState 
+/SA false 
+/SM 0.02 
+/TR /Identity 
+>> 
+endobj
+1 0 obj
+<< 
+/Type /Pages 
+/Kids [ 5 0 R ] 
+/Count 1 
+>> 
+endobj
+2 0 obj
+<< 
+/CreationDate (D:20061105011133)
+/Producer (Acrobat Distiller 4.0 for Windows)
+/ModDate (D:20061105011215+09'00')
+>> 
+endobj
+xref
+0 3 
+0000000000 65535 f
+0000051809 00000 n
+0000051873 00000 n
+trailer
+<<
+/Size 3
+/ID[<21b37df8f836949f6a724722f5ba950d><21b37df8f836949f6a724722f5ba950d>]
+>>
+startxref
+173
+%%EOF
Added: box/trunk/qdbm/misc/icon16.png
===================================================================
(Binary files differ)


Property changes on: box/trunk/qdbm/misc/icon16.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: box/trunk/qdbm/misc/icon20.png
===================================================================
(Binary files differ)


Property changes on: box/trunk/qdbm/misc/icon20.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: box/trunk/qdbm/misc/index.html
===================================================================
--- box/trunk/qdbm/misc/index.html	                        (rev 0)
+++ box/trunk/qdbm/misc/index.html	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta http-equiv="Content-Style-Type" content="text/css" />
+<meta http-equiv="Content-Script-Type" content="text/javascript" />
+<meta name="author" content="Mikio Hirabayashi" />
+<link rel="contents" href="./" />
+<link rel="icon" href="icon16.png" />
+<link rev="made" href="mailto:mikio at users.sourceforge.net" />
+<title>QDBM: Quick Database Manager</title>
+<style type="text/css">html { margin: 0em 0em; padding: 0em 0em; background: #eeeeee none; }
+body { margin: 2em 2em; padding: 0em 0em;
+  background: #eeeeee none; color: #111111;
+  font-style: normal; font-weight: normal; }
+h1 { margin-top: 1.8em; margin-bottom: 1.3em; font-weight: bold; }
+h2 { margin-top: 1.8em; margin-bottom: 1.1em; font-weight: bold;
+  border-left: solid 0.6em #445555; border-bottom: solid 1pt #bbbbbb;
+  padding: 0.5em 0.5em; width: 60%; }
+h3 { margin-top: 1.8em; margin-bottom: 0.8em; font-weight: bold; }
+hr { margin-top: 2.5em; margin-bottom: 1.5em; height: 1pt;
+  color: #999999; background-color: #999999; border: none; }
+div.note,div.navi { text-align: right; }
+div.logo { text-align: center; margin: 3em 0em; }
+div.logo img { border: inset 2pt #ccccdd; }
+p { margin: 0.8em 0em; line-height: 140%; }
+p,dd { text-indent: 0.8em; }
+div,pre { margin-left: 1.7em; margin-right: 1.7em; }
+pre { background-color: #ddddee; padding: 0.2em; border: 1pt solid #bbbbcc; font-size: smaller; }
+kbd { color: #111111; font-style: normal; font-weight: bold; }
+a { color: #0022aa; text-decoration: none; }
+a:hover,a:focus { color: #0033ee; text-decoration: underline; }
+a.head { color: #111111; text-decoration: none; }
+table { padding: 1pt 2pt 1pt 2pt; border: none; margin-left: 1.7em; border-collapse: collapse; }
+th { padding: 1pt 4pt 1pt 4pt; border-style: none;
+  text-align: left; vertical-align: bottom; }
+td { padding: 1pt 4pt 1pt 4pt; border: 1pt solid #333333;
+  text-align: left; vertical-align: top; }
+ul,ol,dl { line-height: 140%; }
+dt { margin-left: 1.2em; }
+dd { margin-left: 2.0em; }
+ul.lines { list-style-type: none; }
+ at media print {
+  html,body { margin: 0em 0em; background-color: #ffffff; color: #000000; }
+  h1 { padding: 8em 0em 0.5em 0em; text-align: center; }
+  h2 { page-break-before: always; }
+  div.note { text-align: center; }
+  div.navi,div.logo { display: none }
+  hr { display: none; }
+  pre { margin: 0.8em 0.8em; background-color: #ffffff;
+    border: 1pt solid #aaaaaa; font-size: smaller; }
+  a,kbd { color: #000000; text-decoration: none; }
+  h1,h2,h3 { font-family: sans-serif; }
+  p,div,li,dt,dd { font-family: serif; }
+  pre,kbd { font-family: monospace; }
+  dd { font-size: smaller; }
+}
+</style>
+<script type="text/javascript">function startup(){
+  var elem = document.getElementById("headline");
+  if(elem){
+    var now = new Date();
+    if((now.getFullYear() + now.getMonth() + now.getDate() + now.getHours()) % 4 == 0){
+      var text;
+      switch((now.getMonth() + now.getDate() + now.getMinutes()) % 10){
+      default: text = "QDBM: Quicker Database Manager"; break;
+      case 1: text = "QDBM: Quantumtheoretically Dangerous Blast Machine"; break;
+      case 3: text = "QDBM: Quite Dramatic Body Metabolism"; break;
+      case 5: text = "QDBM: Quality Documentation of Black Market"; break;
+      case 7: text = "QDBM: Quake Damage Between Mountains"; break;
+      case 9: text = "QDBM: Quiet Down Before Millennium"; break;
+      }
+      elem.firstChild.nodeValue = text;
+    }
+  }
+}
+</script>
+</head>
+
+<body onload="startup();">
+
+<h1 id="headline">QDBM: Quick Database Manager</h1>
+
+<div class="note">Copyright (C) 2000-2007 Mikio Hirabayashi</div>
+<div class="note">Last Update: Thu, 26 Oct 2006 15:00:20 +0900</div>
+<div class="navi">[<span class="void">English</span>/<a href="index.ja.html" hreflang="ja">Japanese</a>]</div>
+
+<div class="logo"><img src="logo.png" alt="QDBM" width="300" height="110" /></div>
+
+<hr />
+
+<h2>Overview</h2>
+
+<p>QDBM is a library of routines for managing a database.  The database is a simple data file containing records, each is a pair of a key and a value.  Every key and value is serial bytes with variable length.  Both binary data and character string can be used as a key and a value.  There is neither concept of data tables nor data types.  Records are organized in hash table or B+ tree.</p>
+
+<p>As for database of hash table, each key must be unique within a database, so it is impossible to store two or more records with a key overlaps.  The following access methods are provided to the database: storing a record with a key and a value, deleting a record by a key, retrieving a record by a key.  Moreover, traversal access to every key are provided, although the order is arbitrary.  These access methods are similar to ones of DBM (or its followers: NDBM and GDBM) library defined in the UNIX standard.  QDBM is an alternative for DBM because of its higher performance.</p>
+
+<p>As for database of B+ tree, records whose keys are duplicated can be stored.  Access methods of storing, deleting, and retrieving are provided as with the database of hash table.  Records are stored in order by a comparing function assigned by a user.  It is possible to access each record with the cursor in ascending or descending order.  According to this mechanism, forward matching search for strings and range search for integers are realized.  Moreover, transaction is available in database of B+ tree.</p>
+
+<p>QDBM is written in C, and provided as APIs of C, C++, Java, Perl, and Ruby.  QDBM is available on platforms which have API conforming to POSIX.  QDBM is a free software licensed under the GNU Lesser General Public License.</p>
+
+<hr />
+
+<h2>Documents</h2>
+
+<p>The following are documents of QDBM.  They are contained also in the source package.</p>
+
+<ul>
+<li><a href="spex.html">Fundamental Specifications</a></li>
+<li><a href="xspex.html">Specifications of C++ API</a></li>
+<li><a href="jspex.html">Specifications of Java API</a></li>
+<li><a href="plspex.html">Specifications of Perl API</a></li>
+<li><a href="rbspex.html">Specifications of Ruby API</a></li>
+<li><a href="cgispex.html">Specifications of CGI scripts</a></li>
+</ul>
+
+<hr />
+
+<h2>Packages</h2>
+
+<p>The following are packages of QDBM.  The Linux binary package contains APIs for C, C++, and Java.  It contains CGI scripts also.  The Windows binary package contains APIs for C and Java.  It contains CGI scripts also.</p>
+
+<ul>
+<li><a href="qdbm-1.8.77.tar.gz">Latest Source Package (version 1.8.77)</a></li>
+</ul>
+
+<ul>
+<li><a href="past/">Past Versions</a></li>
+<li><a href="rpm/">RPM Binary Packages for Linux</a></li>
+<li><a href="win/">Binary Packages for Windows</a></li>
+<li><a href="misc/">Related Packages</a></li>
+</ul>
+
+<ul>
+<li><a href="http://www.freshports.org/databases/qdbm/">Binary Packages for FreeBSD</a></li>
+<li><a href="http://www.blastwave.org/packages.php/qdbm">Binary Packages for Solaris</a></li>
+<li><a href="http://hpux.cs.utah.edu/hppd/cgi-bin/search?package=on&description=on&term=qdbm&Search=Search">Binary Packages for HP-UX</a></li>
+<li><a href="http://www.sbellon.de/sw-ports.html">Binary Packages for RISC OS</a></li>
+</ul>
+
+<hr />
+
+<h2>Applications</h2>
+
+<p>The following are links to applications of QDBM.  If you run or find a project using QDBM, please tell it, and it will added to this list.</p>
+
+<ul>
+<li><a href="http://hyperestraier.sourceforge.net/">Hyper Estraier</a> : Full-text Search System for Communities written by Mikio Hirabayashi</li>
+<li><a href="http://estraier.sourceforge.net/">Estraier</a> : Personal Full-text Search System written by Mikio Hirabayashi</li>
+<li><a href="http://diqt.sourceforge.net/">Diqt</a> : Multilingual Dictionary Searcher written by Mikio Hirabayashi</li>
+<li><a href="http://rbbs.sourceforge.jp/">RBBS</a> : WWW-based Bulletin Board System written by Mikio Hirabayashi</li>
+<li><a href="http://harvest.sourceforge.net/">Harvest</a> : Distributed Search System written by Kang-Jin Lee et al</li>
+<li><a href="http://bogofilter.sourceforge.net/">Bogofilter</a> : Bayesian Spam Mail Filter written by Eric S. Raymond et al</li>
+<li><a href="http://www.foo.be/mqs/">MQS</a> : Minimalist Queue Services written by Alexandre Dulaunoy</li>
+<li><a href="http://www.zedshaw.com/projects/ruby_odeum/">Ruby/Odeum</a> : Ruby Binding to the Inverted API written by Zed A. Shaw</li>
+<li><a href="http://www.surfulater.com/">Surfulater</a> : Utility to Save and Organize Web Pages written by Soft As it Gets Pty Ltd</li>
+<li><a href="http://mutt-ng.berlios.de/">Mutt-NG</a> : Text Based Mail Client written by Andreas Krennmair et al</li>
+<li><a href="http://smfs.sourceforge.net/">SMFS</a> : Smart Sendmail Filters written by Eugene Kurmanin</li>
+<li><a href="http://dixit.sourceforge.net/">Dixit</a> : Romanian Dictionary Searcher written by Octavian Procopiuc</li>
+</ul>
+
+<hr />
+
+<h2>Brothers</h2>
+
+<p>There are many followers of UNIX DBM.  Select the best suited one for your products.  NDBM is ancient and you should not use it.  SDBM is maintained by Apache Project, and GDBM is maintained by GNU Project.  They are most popular and time-tested.  TDB is maintained by Samba Team.  It allows multiple simultaneous writers.  While CDB does not support updating at a runtime, it is the fastest.  Berkeley DB is very multifunctional and ACID compliant.  It is used in many commercial products.  Finally, QDBM is balanced of performance, functionality, portability, and usability.</p>
+
+<ul>
+<li><a href="bros/ndbm-bsd-5.1.m1.tar.gz">NDBM</a> : New DBM written by BSD</li>
+<li><a href="bros/sdbm-1.0.2.tar.gz">SDBM</a> : Substitute DBM written by Ozan S. Yigit</li>
+<li><a href="bros/gdbm-1.8.3.tar.gz">GDBM</a> : GNU Database Manager written by Philip A. Nelson et al</li>
+<li><a href="bros/tdb-1.0.6.tar.gz">TDB</a> : Trivial Database written by Andrew Tridgell et al</li>
+<li><a href="bros/tinycdb-0.75.tar.gz">TinyCDB</a> : Constant Database written by Michael Tokarev</li>
+<li><a href="bros/db-4.4.20.tar.gz">Berkeley DB</a> : Berkeley DB written by Sleepycat Software</li>
+</ul>
+
+<ul>
+<li><a href="benchmark.pdf">Report of Benchmark Test</a></li>
+<li><a href="http://tokyocabinet.sourceforge.net/">Tokyo Cabinet</a> : the successor of QDBM</li>
+</ul>
+
+<hr />
+
+<h2>Information</h2>
+
+<p>QDBM was written by <a href="http://qdbm.sourceforge.net/mikio/">Mikio Hirabayashi</a>.  You can contact the author by e-mail to `mikio at users.sourceforge.net'.  However, as for topics which can be shared among other users, pleae send it to the mailing list.  To join the mailing list, refer to `<a href="http://lists.sourceforge.net/lists/listinfo/qdbm-users">http://lists.sourceforge.net/lists/listinfo/qdbm-users</a>'.</p>
+
+<p>The project page on SourceForge.net is `<a href="http://sourceforge.net/projects/qdbm/">http://sourceforge.net/projects/qdbm/</a>'.</p>
+
+<p>Update of this project is announced on Freshmeat.net at `<a href="http://freshmeat.net/projects/qdbm/">http://freshmeat.net/projects/qdbm/</a>'.</p>
+
+<hr />
+
+</body>
+
+</html>
+
+<!-- END OF FILE -->

Added: box/trunk/qdbm/misc/index.ja.html
===================================================================
--- box/trunk/qdbm/misc/index.ja.html	                        (rev 0)
+++ box/trunk/qdbm/misc/index.ja.html	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta http-equiv="Content-Style-Type" content="text/css" />
+<meta http-equiv="Content-Script-Type" content="text/javascript" />
+<meta name="author" content="Mikio Hirabayashi" />
+<link rel="contents" href="./" />
+<link rel="icon" href="icon16.png" />
+<link rev="made" href="mailto:mikio at users.sourceforge.net" />
+<title>データベースライブラリ QDBM</title>
+<style type="text/css">html { margin: 0em 0em; padding: 0em 0em; background: #eeeeee none; }
+body { margin: 2em 2em; padding: 0em 0em;
+  background: #eeeeee none; color: #111111;
+  font-style: normal; font-weight: normal; }
+h1 { margin-top: 1.8em; margin-bottom: 1.3em; font-weight: bold; }
+h2 { margin-top: 1.8em; margin-bottom: 1.1em; font-weight: bold;
+  border-left: solid 0.6em #445555; border-bottom: solid 1pt #bbbbbb;
+  padding: 0.5em 0.5em; width: 60%; }
+h3 { margin-top: 1.8em; margin-bottom: 0.8em; font-weight: bold; }
+hr { margin-top: 2.5em; margin-bottom: 1.5em; height: 1pt;
+  color: #999999; background-color: #999999; border: none; }
+div.note,div.navi { text-align: right; }
+div.logo { text-align: center; margin: 3em 0em; }
+div.logo img { border: inset 2pt #ccccdd; }
+p { margin: 0.8em 0em; line-height: 140%; }
+p,dd { text-indent: 0.8em; }
+div,pre { margin-left: 1.7em; margin-right: 1.7em; }
+pre { background-color: #ddddee; padding: 0.2em; border: 1pt solid #bbbbcc; font-size: smaller; }
+kbd { color: #111111; font-style: normal; font-weight: bold; }
+a { color: #0022aa; text-decoration: none; }
+a:hover,a:focus { color: #0033ee; text-decoration: underline; }
+a.head { color: #111111; text-decoration: none; }
+table { padding: 1pt 2pt 1pt 2pt; border: none; margin-left: 1.7em; border-collapse: collapse; }
+th { padding: 1pt 4pt 1pt 4pt; border-style: none;
+  text-align: left; vertical-align: bottom; }
+td { padding: 1pt 4pt 1pt 4pt; border: 1pt solid #333333;
+  text-align: left; vertical-align: top; }
+ul,ol,dl { line-height: 140%; }
+dt { margin-left: 1.2em; }
+dd { margin-left: 2.0em; }
+ul.lines { list-style-type: none; }
+ at media print {
+  html,body { margin: 0em 0em; background-color: #ffffff; color: #000000; }
+  h1 { padding: 8em 0em 0.5em 0em; text-align: center; }
+  h2 { page-break-before: always; }
+  div.note { text-align: center; }
+  div.navi,div.logo { display: none }
+  hr { display: none; }
+  pre { margin: 0.8em 0.8em; background-color: #ffffff;
+    border: 1pt solid #aaaaaa; font-size: smaller; }
+  a,kbd { color: #000000; text-decoration: none; }
+  h1,h2,h3 { font-family: sans-serif; }
+  p,div,li,dt,dd { font-family: serif; }
+  pre,kbd { font-family: monospace; }
+  dd { font-size: smaller; }
+}
+</style>
+<script type="text/javascript">function startup(){
+  var elem = document.getElementById("headline");
+  if(elem){
+    var now = new Date();
+    if((now.getFullYear() + now.getMonth() + now.getDate() + now.getHours()) % 4 == 0){
+      var text;
+      switch((now.getMonth() + now.getDate() + now.getMinutes()) % 24){
+      default: text = "QDBM: 高速資料基盤管理器"; break;
+      case 1: text = "QDBM: NDBMとは違うのだよ、NDBMとは!"; break;
+      case 3: text = "QDBM: QDBMは伊達じゃないっ!"; break;
+      case 5: text = "QDBM: 敢えて言おう、高速であると!"; break;
+      case 7: text = "QDBM: しかし、私もDBMのはずだ!"; break;
+      case 9: text = "QDBM: 今の私はQDBMだ。それ以上でもそれ以下でもない。"; break;
+      case 11: text = "QDBM: 私はBerkeleyとは関係ない。私はいつも一人のDBMだった。"; break;
+      case 13: text = "QDBM: 見せてもらおうか!Berkeleyのデータベースの性能とやらを!"; break;
+      case 15: text = "QDBM: 悲しいけどこれ、DBMなのよね。"; break;
+      case 17: text = "QDBM: 新しい時代を創るのはSQLではない!!"; break;
+      case 19: text = "QDBM: えぇぃ、マイナーチェンジのくせにっ!"; break;
+      case 21: text = "QDBM: よくもこんなくたびれたDBMが現役でいられるものだ。"; break;
+      case 22: text = "QDBM: 見える!私にもレコードが見える"; break;
+      case 23: text = "QDBM: そんなRDBみたいな口の利き方、おやめなさい!"; break;
+      }
+      elem.firstChild.nodeValue = text;
+    }
+  }
+}
+</script>
+</head>
+
+<body onload="startup();">
+
+<h1 id="headline">QDBM: Quick Database Manager</h1>
+
+<div class="note">Copyright (C) 2000-2007 Mikio Hirabayashi</div>
+<div class="note">Last Update: Thu, 26 Oct 2006 15:00:20 +0900</div>
+<div class="navi">[<a href="index.html" hreflang="en">English</a>/<span class="void">Japanese</span>]</div>
+
+<div class="logo"><img src="logo.png" alt="QDBM" width="300" height="110" /></div>
+
+<hr />
+
+<h2><a name="overview" id="overview" class="head">概要</a></h2>
+
+<p>QDBMはデータベースを扱うルーチン群のライブラリです。データベースといっても単純なもので、キーと値のペアからなるレコード群を格納したデータファイルです。キーと値は任意の長さを持つ一連のバイト列であり、文字列でもバイナリでも扱うことができます。テーブルやデータ型の概念はありません。レコードはハッシュ表またはB+木で編成されます。</p>
+
+<p>ハッシュ表のデータベースでは、キーはデータベース内で一意であり、キーが重複する複数のレコードを格納することはできません。このデータベースに対しては、キーと値を指定してレコードを格納したり、キーを指定して対応するレコードを削除したり、キーを指定して対応するレコードを検索したりすることができます。また、データベースに格納してある全てのキーを順不同に一つずつ取り出すこともできます。このような操作は、UNIX標準で定義されているDBMライブラリおよびその追従であるNDBMやGDBMに類するものです。QDBMはDBMのより良い代替として利用することができます。</p>
+
+<p>B+木のデータベースでは、キーが重複する複数のレコードを格納することができます。このデータベースに対しては、ハッシュ表のデータベースと同様に、キーを指定してレコードを格納したり取り出したり削除したりすることができます。レコードはユーザが指示した比較関数に基づいて整列されて格納されます。カーソルを用いて各レコードを昇順または降順で参照することができます。この機構によって、文字列の前方一致検索や数値の範囲検索が可能になります。また、B+木のデータベースではトランザクションが利用できます。</p>
+
+<p>QDBMはCで記述され、C、C++、Java、PerlおよびRubyのAPIとして提供されます。QDBMはPOSIX準拠のAPIを備えるプラットフォームで利用できます。QDBMはGNU Lesser General Public Licenseに基づくフリーソフトウェアです。</p>
+
+<hr />
+
+<h2>文書</h2>
+
+<p>以下の文書をお読みください。ソースパッケージにも同じものが含まれています。</p>
+
+<ul>
+<li><a href="spex-ja.html">基本仕様書</a></li>
+<li><a href="xspex-ja.html">C++用API仕様書</a></li>
+<li><a href="jspex-ja.html">Java用API仕様書</a></li>
+<li><a href="plspex-ja.html">Perl用API仕様書</a></li>
+<li><a href="rbspex-ja.html">Ruby用API仕様書</a></li>
+<li><a href="cgispex-ja.html">CGIスクリプト仕様書</a></li>
+<li><a href="tutorial-ja.html">チュートリアル</a></li>
+</ul>
+
+<hr />
+
+<h2>ダウンロード</h2>
+
+<p>以下のパッケージをダウンロードしてください。Linux用バイナリパッケージは、CとC++とJavaのAPIと、CGIスクリプトを納めています。Windows用バイナリパッケージは、CとJavaのAPIと、CGIスクリプトを納めています。</p>
+
+<ul>
+<li><a href="qdbm-1.8.77.tar.gz">最新のソースパッケージ(バージョン1.8.77)</a></li>
+</ul>
+
+<ul>
+<li><a href="past/">過去のバージョン</a></li>
+<li><a href="rpm/">Linux用バイナリパッケージ(RPM)</a></li>
+<li><a href="win/">Windows用バイナリパッケージ</a></li>
+<li><a href="misc/">関連するパッケージ</a></li>
+</ul>
+
+<ul>
+<li><a href="http://www.freshports.org/databases/qdbm/">FreeBSD用パッケージ</a></li>
+<li><a href="http://www.blastwave.org/packages.php/qdbm">Solaris用パッケージ</a></li>
+<li><a href="http://hpux.cs.utah.edu/hppd/cgi-bin/search?package=on&description=on&term=qdbm&Search=Search">HP-UX用パッケージ</a></li>
+<li><a href="http://www.sbellon.de/sw-ports.html">RISC OS用パッケージ</a></li>
+</ul>
+
+<hr />
+
+<h2>アプリケーション</h2>
+
+<p>QDBMのアプリケーションには以下のものがあります。もしあなたのプロジェクトがQDBMを使っているなら、連絡をいただければこのリストに追加いたします。</p>
+
+<ul>
+<li><a href="http://hyperestraier.sourceforge.net/">Hyper Estraier</a> : 平林幹雄による共同体的全文検索システム</li>
+<li><a href="http://estraier.sourceforge.net/">Estraier</a> : 平林幹雄による個人用全文検索システム</li>
+<li><a href="http://diqt.sourceforge.net/">Diqt</a> : 平林幹雄による多言語辞書検索システム</li>
+<li><a href="http://rbbs.sourceforge.jp/">RBBS</a> : 平林幹雄によるWeb掲示板システム</li>
+<li><a href="http://harvest.sourceforge.net/">Harvest</a> : Kang-Jin Leeらによる分散情報検索システム</li>
+<li><a href="http://bogofilter.sourceforge.net/">Bogofilter</a> : Eric S. Raymondらによるベイズ式スパムメールフィルタ</li>
+<li><a href="http://www.foo.be/mqs/">MQS</a> : Alexandre Dulaunoyによるキュー管理サービス</li>
+<li><a href="http://www.zedshaw.com/projects/ruby_odeum/">Ruby/Odeum</a> : Zed A. Shawによる転置APIのRuby用インターフェイス</li>
+<li><a href="http://www.surfulater.com/">Surfulater</a> : Soft As it Gets Pty社によるWebページスクラップブック</li>
+<li><a href="http://mutt-ng.berlios.de/">Mutt-NG</a> : Andreas Krennmairらによるテキスト型メールクライアント</li>
+<li><a href="http://smfs.sourceforge.net/">SMFS</a> : Eugene KurmaninによるSendmail用フィルタシステム</li>
+<li><a href="http://dixit.sourceforge.net/">Dixit</a> : Octavian Procopiucによるルーマニア語辞書検索システム</li>
+</ul>
+
+<hr />
+
+<h2>兄弟</h2>
+
+<p>UNIX標準のDBMに追従したライブラリはたくさんあります。それぞれ特徴がありますので、あなたの製品にとって都合がいいものを選んでください。NDBMは古いので、もはや使う理由はないでしょう。SDBMはApache Projectで管理され、GDBMはGNU Projectで管理されています。それらは最も人気があり、また数多くテストされているものでしょう。TDBはSamba Teamで管理されており、複数同時の書き込みが可能です。TinyCDBは随時の更新はできませんが、最も高速に動作します。Berkeley DBは非常に多機能で、ACID準拠で、多くの商用製品で使われています。最後に、QDBMは性能と機能と移植性と利便性のバランスがとれた製品です。</p>
+
+<ul>
+<li><a href="bros/ndbm-bsd-5.1.m1.tar.gz">NDBM</a> : BSDによるNew DBM</li>
+<li><a href="bros/sdbm-1.0.2.tar.gz">SDBM</a> : Ozan S. YigitによるSubstitute DBM</li>
+<li><a href="bros/gdbm-1.8.3.tar.gz">GDBM</a> : Philip A. NelsonらによるGNU Database Manager</li>
+<li><a href="bros/tdb-1.0.6.tar.gz">TDB</a> : Andrew TridgellらによるTrivial Database</li>
+<li><a href="bros/tinycdb-0.75.tar.gz">TinyCDB</a> :  Michael TokarevによるConstant Database</li>
+<li><a href="bros/db-4.4.20.tar.gz">Berkeley DB</a> : Sleepycat Software社によるBerkeley DB</li>
+</ul>
+
+<ul>
+<li><a href="benchmark.pdf">ベンチマークテストのレポート</a></li>
+<li><a href="http://tokyocabinet.sourceforge.net/">Tokyo Cabinet</a> : QDBMの後継製品</li>
+</ul>
+
+<hr />
+
+<h2>その他の情報</h2>
+
+<p>QDBMは<a href="http://qdbm.sourceforge.net/mikio/">平林幹雄</a>が作成しました。作者と連絡をとるには、`mikio at users.sourceforge.net' 宛に電子メールを送ってください。ただし、質問やバグレポートなど、他のユーザと共有できる話題はメーリングリストに送ってください。メーリングリストの参加方法については、`<a href="http://lists.sourceforge.net/lists/listinfo/qdbm-users">http://lists.sourceforge.net/lists/listinfo/qdbm-users</a>' を参照してください。</p>
+
+<p>SourceForge.netにおけるプロジェクトページは `<a href="http://sourceforge.net/projects/qdbm/">http://sourceforge.net/projects/qdbm/</a>' にあります。Freshmeatにおける更新情報は `<a href="http://freshmeat.net/projects/qdbm/">http://freshmeat.net/projects/qdbm/</a>' から参照できます。</p>
+
+<hr />
+
+</body>
+
+</html>
+
+<!-- END OF FILE -->

Added: box/trunk/qdbm/misc/logo.png
===================================================================
(Binary files differ)


Property changes on: box/trunk/qdbm/misc/logo.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Added: box/trunk/qdbm/misc/makevcdef
===================================================================
--- box/trunk/qdbm/misc/makevcdef	                        (rev 0)
+++ box/trunk/qdbm/misc/makevcdef	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,48 @@
+#! /bin/sh
+
+#================================================================
+# makevcdef
+# Generator of module definition file for Visual C++
+#================================================================
+
+
+# check arguments
+file="$1"
+if [ -f "$file" ]
+then
+  true
+else
+  printf 'usage: makevcdef library\n' 1>&2
+  exit 1
+fi
+
+
+# show headers
+name=`echo $file | sed -e 's/^lib//' -e 's/\..*//'`
+printf 'EXPORTS\r\n'
+
+
+# show variables
+nm -g "$file" | grep ' [BDR] '  | sed 's/.* [BDR] //' | grep -v '^_' | sort | uniq |
+while read name
+do
+  num=$((num + 1))
+  printf '  %s = %s DATA\r\n' "$name" "$name"
+done
+
+
+# show functions
+nm -g "$file" | grep ' T '  | sed 's/.* T //' | grep -v '^_' | sort | uniq |
+while read name
+do
+  num=$((num + 1))
+  printf '  %s = %s\r\n' "$name" "$name"
+done
+
+
+# exit normally
+exit 0
+
+
+
+# END OF FILE


Property changes on: box/trunk/qdbm/misc/makevcdef
___________________________________________________________________
Added: svn:executable
   + *

Added: box/trunk/qdbm/misc/mymemo-ja.html
===================================================================
--- box/trunk/qdbm/misc/mymemo-ja.html	                        (rev 0)
+++ box/trunk/qdbm/misc/mymemo-ja.html	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="author" content="Mikio Hirabayashi" />
+<link rel="contents" href="./" />
+<title>My Private Memo for QDBM</title>
+</head>
+
+<body>
+
+<h1>QDBMのための私的メモ</h1>
+
+<hr />
+
+<h2>cygwin+mingwでのビルド環境の設定方法</h2>
+
+<ul>
+<li>Cygwinのセットアップ時に、mingwのgccとmingwのzlibも選択する。</li>
+<li>mingwのlibiconvはmingwのサイトから手に入れる。そして、*.h は /usr/include/mingw の下に、*.dll.a は /lib/mingw の下に、*.dll は /binの下に移動させる。</li>
+<li>QDBMのビルド環境の設定は ./configure --enable-zlib --enable-iconv で行い、パッケージの作成は make win32pkg で行う。</li>
+</ul>
+
+<hr />
+
+</body>
+
+</html>
+
+<!-- END OF FILE -->

Added: box/trunk/qdbm/misc/tutorial-ja.html
===================================================================
--- box/trunk/qdbm/misc/tutorial-ja.html	                        (rev 0)
+++ box/trunk/qdbm/misc/tutorial-ja.html	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,622 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta http-equiv="Content-Style-Type" content="text/css" />
+<meta name="author" content="Mikio Hirabayashi" />
+<meta name="keywords" content="QDBM, tutrial" />
+<meta name="description" content="tutorial of QDBM" />
+<link rel="contents" href="./" />
+<link rev="made" href="mailto:mikio at users.sourceforge.net" />
+<title>Tutorial of QDBM Version 1 (Japanese)</title>
+<style type="text/css">html { margin: 0em 0em; padding: 0em 0em; background: #eeeeee none; }
+body { margin: 2em 2em; padding: 0em 0em;
+  background: #eeeeee none; color: #111111;
+  font-style: normal; font-weight: normal; }
+h1 { margin-top: 1.8em; margin-bottom: 1.3em; font-weight: bold; }
+h2 { margin-top: 1.8em; margin-bottom: 1.1em; font-weight: bold;
+  border-left: solid 0.6em #445555; border-bottom: solid 1pt #bbbbbb;
+  padding: 0.5em 0.5em; width: 60%; }
+h3 { margin-top: 1.8em; margin-bottom: 0.8em; font-weight: bold; }
+hr { margin-top: 2.5em; margin-bottom: 1.5em; height: 1pt;
+  color: #999999; background-color: #999999; border: none; }
+div.note,div.navi { text-align: right; }
+div.logo { text-align: center; margin: 3em 0em; }
+div.logo img { border: inset 2pt #ccccdd; }
+p { margin: 0.8em 0em; line-height: 140%; }
+p,dd { text-indent: 0.8em; }
+div,pre { margin-left: 1.7em; margin-right: 1.7em; }
+pre { background-color: #ddddee; padding: 0.2em; border: 1pt solid #bbbbcc; font-size: smaller; }
+kbd { color: #111111; font-style: normal; font-weight: bold; }
+a { color: #0022aa; text-decoration: none; }
+a:hover,a:focus { color: #0033ee; text-decoration: underline; }
+a.head { color: #111111; text-decoration: none; }
+table { padding: 1pt 2pt 1pt 2pt; border: none; margin-left: 1.7em; border-collapse: collapse; }
+th { padding: 1pt 4pt 1pt 4pt; border-style: none;
+  text-align: left; vertical-align: bottom; }
+td { padding: 1pt 4pt 1pt 4pt; border: 1pt solid #333333;
+  text-align: left; vertical-align: top; }
+ul,ol,dl { line-height: 140%; }
+dt { margin-left: 1.2em; }
+dd { margin-left: 2.0em; }
+ul.lines { list-style-type: none; }
+ at media print {
+  html,body { margin: 0em 0em; background-color: #ffffff; color: #000000; }
+  h1 { padding: 8em 0em 0.5em 0em; text-align: center; }
+  h2 { page-break-before: always; }
+  div.note { text-align: center; }
+  div.navi,div.logo { display: none }
+  hr { display: none; }
+  pre { margin: 0.8em 0.8em; background-color: #ffffff;
+    border: 1pt solid #aaaaaa; font-size: smaller; }
+  a,kbd { color: #000000; text-decoration: none; }
+  h1,h2,h3 { font-family: sans-serif; }
+  p,div,li,dt,dd { font-family: serif; }
+  pre,kbd { font-family: monospace; }
+  dd { font-size: smaller; }
+}
+</style>
+</head>
+
+<body>
+
+<h1>QDBMのチュートリアル</h1>
+
+<div class="note">Copyright (C) 2000-2007 Mikio Hirabayashi</div>
+<div class="note">Last Update: Thu, 26 Oct 2006 15:00:20 +0900</div>
+<div class="navi">[<a href="http://qdbm.sourceforge.net/">Home</a>]</div>
+
+<hr />
+
+<h2>目次</h2>
+
+<ol>
+<li><a href="#introduction">イントロダクション</a></li>
+<li><a href="#depot">Depot: 基本API</a></li>
+<li><a href="#curia">Curia: æ‹¡å¼µAPI</a></li>
+<li><a href="#relic">Relic: NDBM互換API</a></li>
+<li><a href="#hovel">Hovel: GDBM互換API</a></li>
+<li><a href="#cabin">Cabin: ユーティリティAPI</a></li>
+<li><a href="#villa">Villa: 上級API</a></li>
+<li><a href="#odeum">Odeum: 転置API</a></li>
+</ol>
+
+<hr />
+
+<h2><a name="introduction" id="introduction" class="head">イントロダクション</a></h2>
+
+<p>QDBMは、シンプルながら便利なデータベースライブラリです。データベースというとSQLやリレーショナルデータベースを思い浮かべる人が多いと思いますが、QDBMはそんな高機能なものではありません。「キー」と「値」の組からなるレコードをファイルに保存したり、保存しておいたレコードの中から特定のキーを持つものを取り出す機能を提供するだけです。そのような機能をここでは「ハッシュデータベース」と呼ぶことにします。ハッシュデータベースの特長は、使い方が簡単で、パフォーマンスが高いことです。</p>
+
+<p>QDBMはC言語のライブラリです(他の言語のAPIもありますが)。QDBMにはハッシュデータベースの機能だけでなく、私(QDBMの作者)がプログラミングをする時によく使う機能が詰め込まれています。Cで書いたプログラムは高速に動作するのが利点ですが、C++、Java、Perl、Rubyといった比較的高級な言語では標準的にサポートされるデータ構造やアルゴリズムを自分で実装しなければなりません。そういった作業は面倒ですし、バグを生みやすいものです。そこで、QDBMの登場です。QDBMを再利用すれば、CでのプログラミングがPerlを使っているかのように手軽になります。しかも、UNIXでもWindowsでもMac OS Xでも利用できるので、移植性のあるプログラムが書きやすくなります。</p>
+
+<p>シンプルといいながら、QDBMの機能はなかなか豊富です。データベースとしては、ハッシュ表とB+木が利用できます。メモリ上で扱うユーティリティとしては、リストやマップなどがあります。MIMEやCSVやXMLの解析もできます。しまいには全文検索までできたりするので驚きです。</p>
+
+<p>このチュートリアルではQDBMの使い方を簡単に説明するとともに、基本仕様書の補足を述べます。QDBMの詳細については基本仕様書を御覧ください。なお、ここではハッシュ表やB+木といったデータ構造の説明はしませんので、不慣れな方は適当な本やWebサイトで調べておいてください。</p>
+
+<hr />
+
+<h2><a name="depot" id="depot" class="head">Depot: 基本API</a></h2>
+
+<p>データベースアプリケーションの典型的な例として、「社員番号を入力すると、その内線番号がわかる」というプログラムを考えてみましょう。社員番号をキーとして、それに対応する値である内線番号を検索するということです。</p>
+
+<p>まずはQDBMを使わないでやってみます。社員番号と内線番号の対応表は、CSVテキストでファイルに保存することにします。書式の例を以下に示します。</p>
+
+<pre>00001,8-902-1234
+00002,7-938-834
+00008,4-214-491
+</pre>
+
+<p>レコードを加える関数 `putphonenumber' と、レコードを検索する関数 `getphonenumber' を実装します。</p>
+
+<pre>#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PHONEFILE    "phone"
+#define LINEBUFSIZ   256
+
+int putphonenumber(const char *id, const char *phone){
+  FILE *OUT;
+  /* ファイルを追記モードで開く */
+  if(!(OUT = fopen(PHONEFILE, "a"))) return -1;
+  /* レコードを書き込む */
+  fprintf(OUT, "%s,%s\n", id, phone);
+  /* ファイルを閉じる */
+  if(fclose(OUT) != 0) return -1;
+  return 0;
+}
+
+char *getphonenumber(const char *id){
+  FILE *IN;
+  char line[LINEBUFSIZ], *pivot, *phone;
+  int len;
+  /* ファイルを読み込みモードで開く */
+  if(!(IN = fopen(PHONEFILE, "r"))) return NULL;
+  /* 各行を読み込む */
+  while(fscanf(IN, "%s", line) == 1){
+    /* 区切り文字を処理する */
+    if(!(pivot = strchr(line, ','))) continue;
+    *pivot = '\0';
+    pivot++;
+    /* キーの一致判定 */
+    if(strcmp(line, id) == 0){
+      /* ファイルを閉じる */
+      if(fclose(IN) != 0) return NULL;
+      /* メモリを確保して戻り値を生成する */
+      len = strlen(pivot);
+      if(!(phone = malloc(len + 1))) return NULL;
+      memcpy(phone, pivot, len + 1);
+      return phone;
+    }
+  }
+  /* ファイルを閉じる */
+  fclose(IN);
+  return NULL;
+}
+</pre>
+
+<p>`fscanf' を使っている時点でかなり貧弱ですが、きちんと書こうとすると非常に長くなるので妥協しました(ちなみに、255文字を越える行があったら暴走します)。とにかく、この程度の処理でやたら長いコードを書かねばならないのでは悲しくなります。さらに重大な欠点は、検索の処理が遅いということです。ファイルの最初から最後まで(平均的には半分まで)読まなければならないからです。既存のレコードを修正する時にもかなり面倒なことをしなければなりません。</p>
+
+<p>QDBMを使えばもっとエレガントなコードが書けます。上記と同じ機能の関数を実装してみます。</p>
+
+<pre>#include <depot.h>
+#include <stdlib.h>
+
+#define PHONEFILE    "phone"
+
+int putphonenumber(const char *id, const char *phone){
+  DEPOT *depot;
+  /* データベースを追記モードで開く */
+  if(!(depot = dpopen(PHONEFILE, DP_OWRITER | DP_OCREAT, -1))) return -1;
+  /* レコードを書き込む */
+  dpput(depot, id, -1, phone, -1, DP_DOVER);
+  /* データベースを閉じる */
+  if(!dpclose(depot)) return -1;
+  return 0;
+}
+
+char *getphonenumber(const char *id){
+  DEPOT *depot;
+  char *phone;
+  /* データベースを読み込みモードで開く */
+  if(!(depot = dpopen(PHONEFILE, DP_OREADER, -1))) return NULL;
+  /* レコードを探索して戻り値を生成する */
+  phone = dpget(depot, id, -1, 0, -1, NULL);
+  /* データベースを閉じる */
+  dpclose(depot);
+  return phone;
+}
+</pre>
+
+<p>もはやファイル形式はCSVファイルではなく、区切り文字が何であるか気にする必要はありません。プログラマはファイル形式がどうであるかなど考えなくてもよいのです。メモリの確保などもQDBMの内部でやってくれるので、バッファのサイズを気にする必要はありません(解放は必要です)。処理速度を気にする必要もありません。データベースがどんなに大きくても、レコードの追加や削除や検索が一瞬でできます。このように、プログラマをデータ管理の苦悩から解放するのがQDBMの役割です。</p>
+
+<p>上記の例ではQDBMの基本APIであるDepotを利用しています。まず、`DEPOT' という型が登場しています。これは標準ライブラリの `FILE' と同様に、操作対象のファイルの情報を格納している構造体の型です。この型へのポインタをハンドルとして各種の関数に渡すことになります。関数としては、`dpopen'、`dpclose'、`dpput' および `dpget' が登場しています。この四つの使い方を覚えればQDBMの半分は理解したようなものです。</p>
+
+<div><kbd>DEPOT *dpopen(const char *<var>name</var>, int <var>omode</var>, int <var>bnum</var>);</kbd></div>
+
+<p>`dpopen' はその名の通り、Depotのデータベースを開く関数です。その結果として `DEPOT' 型の構造体へのポインタが返されます。第1引数にはデータを格納するファイル名を指定します。これは相対パスで指定しても絶対パスで指定しても構いません。第2引数は接続モードを指定します。読み込みと書き込みの両方をするなら `DP_OWRITER' を指定します。ただし、データベースファイルが存在しない場合に新規作成するならば同時に `DP_OCREAT' をビットORとして加える必要があります。この二つを指定すると、`fopen' の `a+' モードとほぼ同じ意味になります。他に `DP_OTRUNC' というフラグがあるのですが、それはファイルを切り詰めることを指示します。それも加えて三つを指定すると `fopen' の `w+' モードとほぼ同じ意味になります。読み込みã
  ã‘をする場合、`DP_OREADER' を指定します。これは `fopen' の `r' モードとほぼ同じ意味です。第3引数はハッシュ表のバケット数を指定します。とりあえずデフォルト値を意味する `-1' を指定しておけばいいでしょう。</p>
+
+<p>複数のプロセスが同じファイルを読み書きする場合、「レースコンディション」という問題が起こります。同時にファイルに書き込むと、内容が混ざって変になってしまう可能性があるのです。QDBMではそれに対処するために「ファイルロック」をかけます。あるプロセスがデータベースを書き込みモードで開いている場合は、他のプロセスがデータベースを開こうとしてもブロックされるのです。処理が失敗するわけではなく、既にデータベースを開いているプロセスがデータベースを閉じるまで待ってくれるのです。なお、読み込みモード同士であればレースコンディションは起こらないので同時にアクセスすることができます。</p>
+
+<div><kbd>int dpclose(DEPOT *<var>depot</var>);</kbd></div>
+
+<p>`dpclose' はデータベースを閉じる関数です。第1引数には `dpopen' で開いたハンドルを渡します。開いたデータベースは必ず閉じてください。そうしないとデータベースが壊れます(読み込みモードの場合は壊れませんが、メモリリークになります)。</p>
+
+<p>ところで、QDBMを使わない例では、書き込みの際に呼び出す `fprintf' の戻り値をチェックしていません。`fprintf' が失敗した場合は `fclose' もエラーを返すと規定されているからです。同様に、QDBMでもデータベースに一度でも致命的なエラーが起きた場合は `dpclose' がエラーを返すので、エラーチェックを簡略化することができるのです。</p>
+
+<div><kbd>int dpput(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>dmode</var>);</kbd></div>
+
+<p>`dpput' はデータベースにレコードを追加する関数です。第1引数には `dpopen' で開いたハンドルを渡します。第2引数には、書き込むレコードのキーの内容を保持する領域へのポインタを指定します。第3引数にはその領域のサイズを指定します。それが負数の場合は、第2引数を文字列として扱って、`strlen' の値をサイズとして判定します(文字列とバイナリの両方を簡単に扱えるようにするためです)。第4引数と第5引数は、レコードの値に関して同様にポインタとサイズを指定します。第6引数は書き込みのモードです。データベース内には同じキーを持つ複数のレコードを格納することができないので、既存のレコードのキーと同一のキーを持つレコードを格納しようとした際にどうするかを指示する必要があります。`DP_DOVER' としã
 Ÿå ´åˆã¯ã€æ—¢å­˜ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’新しいレコードで上書きします。`DP_DKEEP' とした場合は、既存のレコードを優先し、エラーが返されます(`DP_EKEEP' というエラーコードが外部変数 `dpecode' に設定されます)。</p>
+
+<p>`dpput' の書き込みモードには、`DP_DCAT' による「連結モード」もあります。これを利用することはあまりないかもしれませんが、他のDBMにはない特徴なので説明します。連結モードは、レコードの値として配列を入れる際に便利なのです。例えば、[10,11,12] という三つの数値を要素に持つ配列を格納していて、それを [10,11,12,13] にしたい場合を考えてみます。他のDBMでは、まずそのレコードを検索して、[10,11,12] を獲得してから、それを [10,11,12,13] に加工して値を生成し、元のレコードに上書きで書き込むといったことをしなければなりません。QDBMの場合は、連結モードで [13] を既存のレコードに書き込むだけで同じことができます。配列の要素数が大きい場合にはこの違いはパフォーマンスに大きな影響を及ぼします。</p>
+
+<div><kbd>char *dpget(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, int *<var>sp</var>);</kbd></div>
+
+<p>`dpget' はデータベースを検索してレコードを取り出す関数です。第1引数は他と一緒ですね。第2引数と第3引数は `dpput' と同様に検索キーのポインタとサイズを指定します。第4引数と第5引数はとりあえず `0' と '-1' にするものだと思っていただいて結構です。一応説明すると、取り出す領域の開始オフセットと最大サイズを指定します。`-1' は無制限という意味です。例えばレコードの値が `abcdef' の場合に開始オフセットを `1'、サイズを `3' にした場合、`bcd' が取り出されます。戻り値は取り出した値の内容を記録した領域へのポインタです。その領域は `malloc' でヒープに確保されているので、アプリケーションの責任で `free' に渡して解放する必要があります。戻り値の領域は終端にゼロコードが付加されていることが保証されてã
 „るので、文字列として利用できます。ただし、バイナリを扱う場合には明示的にサイズが知りたいでしょうから、その為に第6引数にサイズを受け取る変数へのポインタを指定することができます(このサイズには終端のゼロコードは勘定されません)。該当のレコードがない場合には `NULL' が返されます(`DP_ENOITEM' というエラーコードが外部変数 `dpecode' に設定されます)。</p>
+
+<p>Depotの関数で他によく使うものとして、`dpout'、`dpiterinit'、`dpiternext' があります。これらについても説明しておきます。</p>
+
+<div><kbd>int dpout(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></div>
+
+<p>`dpout' はデータベースからレコードを削除する関数です。三つの引数の扱いは `dpget' のものと同じです。該当のレコードがない場合はエラーを返します(`DP_ENOITEM' というエラーコードが外部変数 `dpecode' に設定されます)。</p>
+
+<p>`dpiterinit' と `dpiternext' はデータベースの中のレコードを一つ一つ見ていく場合に使います。先程の内線番号データベースの全レコードを表示する関数の例を以下に示します。</p>
+
+<pre>#include <depot.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define PHONEFILE    "phone"
+
+int printphonenumbers(void){
+  DEPOT *depot;
+  char *kbuf, *vbuf;
+  /* データベースを読み込みモードで開く */
+  if(!(depot = dpopen(PHONEFILE, DP_OREADER, -1))) return -1;
+  /* イテレータを初期化する */
+  dpiterinit(depot);
+  /* 各レコードのキーを取り出す */
+  while((kbuf = dpiternext(depot, NULL)) != NULL){
+    /* 各レコードの値を取り出す */
+    if((vbuf = dpget(depot, kbuf, -1, 0, -1, NULL)) != NULL){
+      printf("%s: %s\n", kbuf, vbuf);
+      /* 値の領域を解放する */
+      free(vbuf);
+    }
+    /* キーの領域を解放する */
+    free(kbuf);
+  }
+  /* データベースを閉じる */
+  return dpclose(depot) ? 0 : -1;
+}
+</pre>
+
+<p>全てのレコードを横断的に見ていくことを「トラバーサルアクセス」と呼ぶことにします。トラバーサルアクセスの際には、「イテレータ」を利用します。イテレータは集合に含まれる個々の要素を処理する際に、その要素を一つずつ取り出す機能です。トラバーサルアクセスをはじめる前にはイテレータを初期化します。そして、イテレータが「打ち止め」の合図を返すまで繰り返して呼び出します。</p>
+
+<div><kbd>int dpiterinit(DEPOT *<var>depot</var>);</kbd></div>
+
+<p>`dpiterinit' はイテレータを初期化する関数です。特に説明は必要ありませんね。</p>
+
+<div><kbd>char *dpiternext(DEPOT *<var>depot</var>, int *<var>sp</var>);</kbd></div>
+
+<p>`dpiternext' はイテレータから次のレコードのキーを取り出す関数です。第1引数は他と同じです。例によって戻り値はゼロコードが付加された領域へのポインタです。領域は `malloc' で確保されているので、アプリケーションの責任で `free' してください。明示的にサイズを知りたい場合は第2引数にそれを受け取る変数へのポインタを指定します。もう取り出すレコードがなくなったら `NULL' が返されます(`DP_ENOITEM' というエラーコードが外部変数 `dpecode' に設定されます)。</p>
+
+<p>トラバーサルアクセスで各レコードを辿る順番については規定されていませんので、レコードの順序に依存したプログラミングをしてはいけません(そのような場合はB+木データベースを使いましょう)。また、イテレータを繰り返している途中でレコードを上書きした場合、既に読んだはずのレコードがまた取り出される可能性があります。途中でレコードを削除することに関しては問題ありません。</p>
+
+<p>ここまででDepotの基本的な使い方は説明し終えました。QDBMのそれ以外のAPIもDepotに似たような使い方をしますので、基本仕様書のサンプルコードを見れば理解してもらえると思います。</p>
+
+<hr />
+
+<h2><a name="curia" id="curia" class="head">Curia: æ‹¡å¼µAPI</a></h2>
+
+<p>Curiaは、Depotとほとんど同じ機能とインタフェースを備えますが、ファイルでなくディレクトリとしてデータベースを扱うAPIです。やたらと大量のデータを扱わなければならない場合にはCuriaがお薦めです。データをディレクトリの中の複数のファイルに分散して格納するので、Depotよりも大きな(2GB以上の)データベースを扱うことができます。Depotではハンドルに `DEPOT' へのポインタを使いましたが、Curiaでは `CURIA' へのポインタを使います。そして、Depotでは `dp' で始まっていた関数名が、Curiaでは `cr' で始まります。名前が違うだけで、使い方は全く一緒です。ただし、データベースを開く `cropen' という関数は、データベースの分割数を指定する引数が増えています。</p>
+
+<div><kbd>CURIA *cropen(const char *<var>name</var>, int <var>omode</var>, int <var>bnum</var>, int <var>dnum</var>);</kbd></div>
+
+<p>第1、第2、第3引数は `dpopen' のものと全く一緒です。第4引数は、データベースの分割数を指定します。ディレクトリの中に、指定した数だけのファイルが作られます。なお、第3引数で指定した値はその各々のデータベースファイルが持つバケット数になります。戻り値はCuriaのデータベースハンドルとなります。</p>
+
+<p>Curiaには「ラージオブジェクト」を扱う機能もあります。ラージオブジェクトとは、各レコードをファイルとして独立させて保存する仕組みです。ラージオブジェクトにすると通常のレコードより処理速度が落ちますが、データベースのサイズを無限に(ディスクが許す限り)大きくすることができます。なお、ラージオブジェクトのトラバーサルアクセスはサポートされません。</p>
+
+<hr />
+
+<h2><a name="relic" id="relic" class="head">Relic: NDBM互換API</a></h2>
+
+<p>Relicは、NDBMのアプリケーションをすべからくQDBMに乗り換えさせるという野望の下に作られたAPIです。パフォーマンスはオリジナルのNDBMの数倍は出ます。NDBMのアプリケーションはあまり見ないですが、Perl等のスクリプト言語がそのインタフェースを備えています。つまりRelicによって各種のスクリプト言語でQDBMが使えることが保証されるわけです。</p>
+
+<p>あなたのアプリケーションのソースコード中で `ndbm.h' をインクルードしている部分を `relic.h' に書き換え、リンク対象を `ndbm' から `qdbm' に換えて再コンパイルしください。それだけであなたのアプリケーションはQDBMに乗り換えることができます。なお、新たにアプリケーションを書く際には、RelicでなくDepotを利用することをお薦めします。</p>
+
+<hr />
+
+<h2><a name="hovel" id="hovel" class="head">Hovel: GDBM互換API</a></h2>
+
+<p>Hovelは、GDBMのアプリケーションをすべからくQDBMに乗り換えさせるという野望の下に作られたAPIです。パフォーマンスはオリジナルのGDBMの数倍は出ます。GDBMのアプリケーションは市場に多く見られますが、それらをお使いの場合は、ぜひともQDBMに移植してあげてください。パフォーマンスが目に見えてに改善されることうけあいです。</p>
+
+<p>あなたのアプリケーションのソースコード中で `gdbm.h' をインクルードしている部分を `hovel.h' に書き換え、リンク対象を `gdbm' から `qdbm' に換えて再コンパイルしください。それだけであなたのアプリケーションはQDBMに乗り換えることができます。なお、新たにアプリケーションを書く際には、HovelでなくDepotを利用することをお薦めします。</p>
+
+<p>通常、Hovelが生成するデータベースファイルはDepotのものと全く同じものです。しかし、ちょっと細工するとそれをCuriaによるデータベースディレクトリに変更することができます。データベースハンドルを取得する関数 `gdbm_open' を、`gdbm_open2' に書き換えればよいのです。単一のファイルにデータベースを格納している場合は、ファイルサイズが2GBまでという制限にひっかかってしまいますが、`gdbm_open2' を使えばそれを乗り越えることができます。`gdbm_open' を呼び出しているところ以外は全く変更する必要がないというのが嬉しいところです。</p>
+
+<div><kbd>GDBM_FILE gdbm_open2(char *<var>name</var>, int <var>read_write</var>, int <var>mode</var>, int <var>bnum</var>, int <var>dnum</var>, int <var>align</var>);</kbd></div>
+
+<p>第1引数は生成するファイルかディレクトリの名前です。第2引数と第3引数は `gdbm_open' の第3引数と第4引数として渡すものと一緒です。第4引数はバケットの要素数です。第5引数はデータベースファイルの分割数です。第6引数は各レコードのアラインメントです。戻り値は `gdbm_open' と同じくデータベースハンドルです。</p>
+
+<hr />
+
+<h2><a name="cabin" id="cabin" class="head">Cabin: ユーティリティAPI</a></h2>
+
+<p>Cabinは、データの操作を簡単に行うためのユーティリティを集めたAPIです。密かにQDBMのAPIの中で最も充実しています。特にリストとマップに関連する関数が重宝します。他にも、ファイルやディレクトリを読んだり、文字列を分割したり、CSVやXMLを解析したり、各種の符号化と復号もできます。ここではリストとマップとXMLについて詳しく説明します。</p>
+
+<p>リストとは、順序を持った集合のことです。Cabinが扱うリストには任意の文字列やバイナリを要素として加えることができます。リストの先頭に対して要素の追加と削除ができるとともに、リストの末尾に対して要素の追加と削除をすることもできます(つまりデクです)。また、配列を使って実装されているので、任意の順番の要素の値を高速に参照することができます。以下の例では、`first'、`second'、`third' という文字列を順に末尾から追加した上で、先頭から末尾まで要素の内容を表示しています。</p>
+
+<pre>#include <cabin.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+void listtest(void){
+  CBLIST *list;
+  int i;
+  /* リストを開く */
+  list = cblistopen();
+  /* 要素を末尾から追加する */
+  cblistpush(list, "first", -1);
+  cblistpush(list, "second", -1);
+  cblistpush(list, "third", -1);
+  /* 先頭から要素の内容を表示する */
+  for(i = 0; i < cblistnum(list); i++){
+    printf("%s\n", cblistval(list, i, NULL));
+  }
+  /* リストを閉じる */
+  cblistclose(list);
+}
+</pre>
+
+<p>`CBLIST' 型へのポインタをリストのハンドルとして用います。実際のハンドルは `cblistopen' を呼び出して獲得します。ハンドルを閉じてメモリを解放するには、`cblistclose' を呼び出します。`cblistpush' は末尾に要素を追加します。`cblistnum' はリストの要素数を返します。`cblistval' はリスト内の特定の番号(ゼロからはじまる)の要素を返します。リスト操作の関数はその他にもいくつかあります。</p>
+
+<p>マップとは、キーと値からなるレコードの集合です。Cabinが扱うマップには任意の文字列やバイナリをキーや値に持つレコードを格納することができます。キーが完全に一致するレコードを検索して値を取り出すことができます(実装はハッシュ表です)。マップ内のレコードを先頭から一つずつ取り出すこともできます。なお、各要素は格納した順番で並んでいることが保証されています。以下の例では、キー `one' と値 `first'、キー `two' と値 `second'、キー `three' と値 `third' のレコードを順に格納した上で、その各々を検索して表示しています。</p>
+
+<pre>#include <cabin.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+void maptest(void){
+  CBMAP *map;
+  /* マップを開く */
+  map = cbmapopen();
+  /* レコードを追加する */
+  cbmapput(map, "one", -1, "first", -1, 1);
+  cbmapput(map, "two", -1, "second", -1, 1);
+  cbmapput(map, "three", -1, "third", -1, 1);
+  /* レコードを検索して内容を表示する */
+  printf("one: %s\n", cbmapget(map, "one", -1, NULL));
+  printf("two: %s\n", cbmapget(map, "two", -1, NULL));
+  printf("three: %s\n", cbmapget(map, "three", -1, NULL));
+  /* マップを閉じる */
+  cbmapclose(map);
+}
+</pre>
+
+<p>`CBMAP' 型へのポインタをマップのハンドルとして用います。実際のハンドルは `cbmapopen' を呼び出して獲得します。ハンドルを閉じてメモリを解放するには、`cbmapclose' を呼び出します。`cbmapput' でレコードを追加します。`cbmapget' でレコードを検索します。マップ操作の関数はその他にもいくつかあります。</p>
+
+<p>XMLを簡単に処理するために、簡易的なパーザが用意されています。このパーザは妥当性検証をせず、書式の検査も厳密でないのが特徴です。したがって、一般的なHTMLやSGMLの解析にも用いることができます。単純な構造のXML文書を処理する際には、DOMやSAXといったAPIを使うよりも便利です。以下の例では、XML文書の中から `id' 属性の値で要素を指定して、そのテキストを取り出して表示します。</p>
+
+<pre>#include <cabin.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+void showtextbyid(const char *xml, const char *id){
+  CBLIST *elems;
+  CBMAP *attrs;
+  const char *elem, *attr;
+  char *orig;
+  int i;
+  /* タグとテキストのリストを取得する */
+  elems = cbxmlbreak(xml, 1);
+  /* リストの各要素をたどる */
+  for(i = 0; i < cblistnum(elems); i++){
+    /* 要素を取り出す */
+    elem = cblistval(elems, i, NULL);
+    /* タグでない場合は読み飛ばす */
+    if(elem[0] != '<' || elem[1] == '?' || elem[1] == '!' || elem[1] == '/') continue;
+    /* 属性のマップを取得する */
+    attrs = cbxmlattrs(elem);
+    /* ID要素の値を取り出し、一致を検査する */
+    attr = cbmapget(attrs, "id", -1, NULL);
+    if(attr && !strcmp(attr, id)){
+      /* 次の要素を取り出す */
+      elem = cblistval(elems, i + 1, NULL);
+      if(elem){
+        /* 実体参照を復元して表示する */
+        orig = cbxmlunescape(elem);
+        printf("%s\n", orig);
+        free(orig);
+      }
+    }
+    /* 属性マップを閉じる */
+    cbmapclose(attrs);
+  }
+  /* 要素リストを閉じる */
+  cblistclose(elems);
+}
+</pre>
+
+<p>処理対象のXML文書のテキストを `cbxmlbreak' で分解します。例えば `<body><p id="nuts">NUTS&amp;MILK</p></body>' を分解すると、`<body>'、`<p id="nuts">'、`NUTS&amp;MILK'、`</p>'、`</body>' が得られます。そして、各要素を巡回します。1文字目が '<' であればタグか各種の宣言であり、かつ2文字目が '?'、`!'、`/' のいずれでもなければ開始タグまたは空タグであると判断できます。タグに対して `cbxmlattrs' を呼ぶことで属性のマップが得られます。このマップは属性名をキーにして値を取り出すことができます。属性値やテキストセクションの文字列は文書内に出現したままの形式になっています。実体参照を含んだ文字列を復元するには `cbxmlunescape' を用います。</p>
+
+<p>GTK+に付属するGLibやApacheに付属するAPRなどの便利なライブラリが世の中にはありますので、単体でCabinを利用する価値はあまりありません。正直言って、GLibやAPRの方が高機能で、ユーザ数も多く、参考になる情報も多いです。とはいえ、Cabinの方が手軽に使えるので私は好きです。</p>
+
+<hr />
+
+<h2><a name="villa" id="villa" class="head">Villa: 上級API</a></h2>
+
+<p>Villaは、B+木のデータベースを扱うAPIです。B+木データベースにはユーザが指定した順序でレコードが並べられて格納されます。DepotやCuriaはキーの完全一致による検索しかできませんが、Villaを用いると範囲を指定してレコードを検索することができます。また、同じキーを持つ複数のレコードを格納することもできます。例えば、キーを文字列の辞書順(ABC順とかアイウエオ順と同じほぼ意味です)で並べるように指定した場合は、文字列の前方一致検索ができるのです。</p>
+
+<p>とはいえ、基本的な使い方はDepotと一緒です。Depotの説明で挙げた関数をVillaを使って実装しなおしてみます。</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+
+#define PHONEFILE    "phone"
+
+int putphonenumber(const char *id, const char *phone){
+  VILLA *villa;
+  /* データベースを追記モードで開く */
+  if(!(villa = vlopen(PHONEFILE, VL_OWRITER | VL_OCREAT, VL_CMPLEX))) return -1;
+  /* レコードを書き込む */
+  vlput(villa, id, -1, phone, -1, VL_DOVER);
+  /* データベースを閉じる */
+  if(!vlclose(villa)) return -1;
+  return 0;
+}
+
+char *getphonenumber(const char *id){
+  VILLA *villa;
+  char *phone;
+  /* データベースを読み込みモードで開く */
+  if(!(villa = vlopen(PHONEFILE, VL_OREADER, VL_CMPLEX))) return NULL;
+  /* レコードを検索して戻り値を生成する */
+  phone = vlget(villa, id, -1, NULL);
+  /* データベースを閉じる */
+  vlclose(villa);
+  return phone;
+}
+</pre>
+
+<p>`VILLA' へのポインタは例によってデータベースハンドルです。`vlopen' でそのハンドルを獲得します。その第3引数の `VL_CMPLEX' は辞書順の比較を行う関数です。開いたハンドルは `vlclose' で閉じます。`vlput' はレコードを格納する関数で、`vlget' はレコードを検索する関数です。</p>
+
+<p>上記の例ではハッシュデータベースと全く同じ使い方をしましたが、順番に基づいてレコードにアクセスする機能がB+木データベースの特徴です。辞書順を例にとって説明します。キーがそれぞれ `one'、`two'、`three'、`four' というレコードを格納したとすれば、それは `four'、`one'、`three'、`two' という順番で並べられて保存されます。検索にはハッシュデータベースと同様に完全一致条件も使えます。さらに、「カーソル」という機能を使って範囲を指定した検索ができます。カーソルはレコードの位置を指し示します。例えば、`one' の場所にカーソルを飛ばすといった指定ができます。`one' がない場合は、`one' の直後の `three' の位置にカーソルが飛ぶことになります(`one' が複数あった場合は、その最初のレコードに飛びます)。カã
 ƒ¼ã‚½ãƒ«ã¯ã€ç¾åœ¨ä½ç½®ã‹ã‚‰å‰ã«é€²ã‚ãŸã‚Šå¾Œã‚ã«æˆ»ã—たりすることもできます。そして、カーソルの位置のレコードの内容を読み出せば、範囲を指定した検索ができるというわけです。以下の例は、`one' から `three' までのレコードのキーと値を表示する関数です。</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define WORDFILE     "word"
+#define TOPWORD      "one"
+#define BOTTOMWORD   "three"
+
+void printwords(void){
+  VILLA *villa;
+  char *kbuf, *vbuf;
+  int ksiz;
+  /* データベースを読み込みモードで開く */
+  if(!(villa = vlopen(WORDFILE, VL_OREADER, VL_CMPLEX))) return;
+  /* カーソルを候補の先頭に飛ばす */
+  vlcurjump(villa, TOPWORD, -1, VL_JFORWARD);
+  /* 各候補を処理する */
+  do {
+    /* レコードのキーを取り出す */
+    kbuf = vlcurkey(villa, &ksiz);
+    /* 候補が範囲外であれば抜ける */
+    if(!kbuf || VL_CMPLEX(kbuf, ksiz, BOTTOMWORD, sizeof(BOTTOMWORD) - 1) > 0){
+      free(kbuf);
+      break;
+    }
+    /* レコードの値を取り出して表示する */
+    vbuf = vlcurval(villa, NULL);
+    if(kbuf && vbuf) printf("%s: %s\n", kbuf, vbuf);
+    /* キーと値の領域を解放する */
+    free(vbuf);
+    free(kbuf);
+    /* カーソルを次に進める */
+  } while(vlcurnext(villa));
+  /* データベースを閉じる */
+  vlclose(villa);
+}
+</pre>
+
+<p>`vlcurjump' でカーソルを候補の先頭に飛ばしています。`VL_JFORWARD' はこれからカーソルを前に進めていく場合に指定します(候補の末尾に飛ばしてから後ろに戻して行く場合は `VL_JBACKWARD' を指定します)。do-whileループの条件部で `vlcurnext' を呼んでいますが、これがカーソルを前に進めています(データベースの最後まで来たら偽を返すのでループから抜けます)。`vlcurkey' と `vlcurval' はそれぞれカーソルのキーと値を取り出します。`VL_CMPLEX' を明示的に呼んでいる場所がありますが、ここで候補の末尾に来たかどうか判定しています。比較関数は、二つのキーのポインタとサイズを渡して、前者が大きい(つまり後ろに位置すべき)なら正の値、前者が小さい(つまり前に位置すべき)なら負の値、両者が同じならゼロを返すと規定
 されています。したがって、正の値が返された場合、今取り出したキーは候補の末尾よりも後ろだということになります。</p>
+
+<p>辞書順以外の比較関数も使えるところがVillaのミソです。int型の数値を比較する `VL_CMPINT' や、10進数の文字列を比較する `VL_CMPDEC' といった関数が最初から用意されています。さらに、あなたが自分で定義した関数も比較関数として使うことができます。使い方が面倒だという欠点を除けば、B+木データベースはハッシュデータベースよりも多くのシーンで活用できると思います。ファイルがハッシュデータベースよりも小さかったり、トランザクションが使えるといった特徴も、人によっては嬉しいかもしれません。</p>
+
+<p>Villaは、キュー(FIFO)を永続化する目的でも利用できます。`VL_CMPINT' を比較関数にしてデータベースを開きます。各レコードのキーはint型とし、値には任意のオブジェクトを入れることにします。キューに要素を追加するには、末尾のキーの数値(なければゼロ)をインクリメントしてキーを生成して格納します。キューから要素を取り出すには、先頭のレコードを取り出してから削除すればよいのです。そのような機能を持つラッパ関数を、`qopen'、`qclose'、`qappend'、`qconsume' といった名前で作っておくと小粋ですね。</p>
+
+<hr />
+
+<h2><a name="odeum" id="odeum" class="head">Odeum: 転置API</a></h2>
+
+<p>Odeumは、全文検索用の転置インデックスを扱うAPIです。テキストファイル(またはテキストを含むHTMLやMS-Wordの文書など)は単語の集合とみなせますが、ある単語がどのファイルに含まれるかという情報をデータベースにしたものを転置インデックスと呼びます。本の巻末にある索引は、ある単語がどのページに含まれるかという情報を持っていますが、それと似たようなものです。私がQDBMを開発する契機となったのは、とある全文検索システムの開発で使っていたGDBMのパフォーマンスに限界を感じたことです。その経緯から、QDBM(特にCuria)には転置インデックスの実現に都合のよい特徴がいくつか備わっています。</p>
+
+<p>転置インデックスの核となるのは、ある単語をキーとし、その単語を含むファイルのIDの配列を値とするレコードからなるデータベースです。単語は完全一致で検索できればよいので、データ構造にはハッシュ表を採用しています。転置インデックスのイメージを例示します。ファイル `penguin.txt' には「flightless marine birds」というテキストが格納されていて、ファイル `ostrich.txt' には、「flightless birds in africa」というテキストが格納されているとします。各ファイルには、読み込んだ順番でIDをつけることにします。これらを対象として転置インデックスを作成すると、以下のようになります。IDとファイル名の対応づけは別の「文書データベース」に保存しておきます。</p>
+
+<table summary="index example">
+<tr><td>flightless</td><td>1,2</td></tr>
+<tr><td>marine</td><td>1</td></tr>
+<tr><td>birds</td><td>1,2</td></tr>
+<tr><td>in</td><td>2</td></tr>
+<tr><td>africa</td><td>2</td></tr>
+</table>
+
+<p>その次に読み込んだ文書に「birds」という単語が含まれていた場合には、`birds' の値を [1,2,3] に変更することになります。このように、既存のレコードの値の末尾にデータを追加するという操作が頻繁に発生します。この処理を効率的に行うために、DepotやCuriaの書き込み操作には「連結モード」があるのです。</p>
+
+<p>検索時には、検索語をキーにして値の配列を取り出し、個々のIDに対応するファイル名を文書データベースから取り出して提示することになります。ただし該当の文書が多い場合にその全てを提示してもユーザは困惑するので、先頭の何件かに絞って取り出します。DepotやCuriaの読み込み操作ではレコードの値から特定の部分のみを取り出すことができるので、この処理を効率良く行うことができます。実際は、ファイルにおける単語のスコア(重要度)も配列要素の一部として格納しておいて、それを基準にソートしておくことによって、先頭の要素がユーザにとって意味のあるものにしています。</p>
+
+<p>ファイルには名前以外にも、タイトルや作者名や更新日時といった属性をつけたい場合があります。それらの情報は文書データベースに保存します。Odeumで扱うファイル(以後は文書と呼びます)は、ファイル名の代わりにURIをつけるものとしています。URI以外の属性はユーザがラベルをつけて任意のものを格納できます。</p>
+
+<p>Odeumは、文書のテキストや属性を元のデータから抽出する機能については提供しません。それらはドメインに強く依存するので、共通化することが難しいからです。したがって、アプリケーションがそれを実装する必要があります。Odeumのサンプルアプリケーションは、ローカルのファイルシステムにあるプレーンテキストとHTMLからテキストと属性を抽出できます。あなたのアプリケーションでは、PDFã‚„MS-Wordの文書に対応するのもよいでしょう。Webから文書を取得してもよいでしょう。同じ理由から、Odeumはテキストから単語を抽出する機能についても提供しません。英語と日本語では全く異なる手法でテキストの解析をしなければなりませんが、そういったこともアプリケーションに任されます。Odeumのサンプルアプリケーションã
 §ã¯ã€å˜ã«ç©ºç™½ã§èªžã‚’区切るという手法をとっています。あなたのアプリケーションでは、日本語の形態素解析を行うのもよいでしょう。英単語のステミング処理を行ってもよいでしょう。</p>
+
+<p>多くの言語では、同じ単語に対して異体や活用が存在します。例えば「使う/使っ(た)」「go/went」「child/children」などです。そこで、Odeumは各単語を「正規形」および「出現形」の組として扱います。テキストから出現形の単語を切り出したり、それらの正規形を生成する処理はアプリケーションに任されます。転置インデックスでのレコードのキーには正規形が使われます。「child」で検索すれば、「children」を含む文書も該当させられるということです。検索語に対しても正規化の処理を行えば、「children」で検索して「child」を該当にできます。なお、出現形もデータベースに記録されますが、それは検索結果として文書の要約を提示するなどの用途で利用されます(不要な場合は出現形を全て空文字列にして記憶領域を節約で
 きます)。</p>
+
+<p>全文検索システムの善し悪しを評価する際には、スコアリング(ランキング)が重要な要素になります。検索結果が多い場合に、その中から絞り込んでユーザに提示する文書をどうやって選択するかということです。いくら検索速度が速くても、満足のいく検索結果が提示されずに何度も検索したり、無駄な文書を閲覧しなければならないのでは、結局は時間がかかることになってしまいます。Odeumでは、文書を登録する際に、そこに含まれる各単語について以下の式でスコアを算出して、文書IDとともに転置インデックスに格納しています。</p>
+
+<div><kbd>スコア = (該当語の出現数 * 10000) / log(文書内の総語数) ^ 3</kbd></div>
+
+<p>ある単語がたくさん出現するということは、その文書でその単語のことについて詳しく説明されている可能性が高いと判断できます。ただし、大きな文書は小さい文書よりも単語の出現数が多くなりますので、その調整をする必要があります。そこで、該当語の出現数を、総語数の自然対数の三乗で割っているのです。なお、テキストの先頭10%以内に出て来る単語は「トピックセンテンス」を構成するとみなして、初出時に限り10000でなく15000を加算しています。</p>
+
+<p>全文検索は通常、複数の検索語を使って行われます。その際には、各検索語のスコアを調整しないと、特定の検索語のスコアの影響が強すぎるという事態が発生します。例えば「the beatles」で検索した場合、「the」のスコアが「beatles」のスコアを圧倒して、「beatles」について知りたいのに、関係ない文書ばかりが提示されることになってしまいます。Odeumは転置インデックスの内容を提示するだけで、それ以後の処理はアプリケーションに任されます。Odeumに付属するサンプルアプリケーションでは以下の式で調整を行っています。</p>
+
+<div><kbd>調整済みスコア = 登録されたスコア / log(その語を含む文書数) ^ 2</kbd></div>
+
+<p>ありふれた単語はスコアを下げ、特徴的な単語はスコアを維持すべきです。そこで、登録されたスコアを、その語を含む文書数の自然対数の二乗で割っているのです(TF-IDF法を強化したものです)。各検索語の調整済みスコアを足したものを文書のスコアとし、その降順で検索結果を提示します。他にもドメインに依存した様々なスコアリング手法があると思いますが、アプリケーションがそれを実装できるのが嬉しいところです。</p>
+
+<p>Odeumのサンプルアプリケーションでは、関連文書検索(類似文書検索と呼んだ方が適切かもしれません)の機能も実装しています。ある文書(種文書と呼びます)に関連した文書の一覧を提示するものです。単語の出現傾向が似通った文書は互いに関連しているという考え方(ベクトル空間モデル)に基づいています。文書をデータベースに登録する際には、各文書に含まれる全単語に対して調整済みスコアを計算し、その上位32語(キーワードと呼びます)の情報を文書と対応づけて登録しておきます。検索時には、種文書のキーワードとスコアを取り出し、それを32次元のベクトルとして表現します。関連度を判定する対象の各文書からもキーワードとスコアを取り出し、種文書のベクトル空間に対応したベクトルを生成しますã€
 ‚そうしてできた二つのベクトルのなす角が小さいものは関連度が高いと判定します。実際には、なす角の余弦(0から1の範囲で、完全一致する場合は1になる)が大きいものから提示されることになります。なお、登録された全ての文書を対象として類似度の判定を行うとあまりに時間がかかるので、キーワードでOR検索を行った結果の上位の文書のみを関連度算出の対象としています。</p>
+
+<p>前置きはここまでにして、Odeumの使用方法についての説明に入ります。URIが `http://tako.ika/uni.txt' で、そこから取り出したテキストが「The sun is driven by the Grateful Dead.」で、切り出した単語(正規形/出現形)が「the/The」「sun/sun」「be/is」「drive/driven」「by/by」「the/the」「grateful/Grateful」「die/dead」「(正規形なし)/.」だとしましょう(この処理はアプリケーションが独自に実装してください)(いわゆるストップワードは正規形を空文字列にして表現します)。それを `index' という名前のデータベースに登録する例を示します。</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <odeum.h>
+#include <stdlib.h>
+
+int docregistertest(void){
+  ODEUM *odeum;
+  ODDOC *doc;
+  /* データベースを開く */
+  if(!(odeum = odopen("index", OD_OWRITER | OD_OCREAT))) return -1;
+  /* 文書を表現する */
+  doc = oddocopen("http://tako.ika/uni.txt");
+  oddocaddword(doc, "the", "the");
+  oddocaddword(doc, "sun", "sun");
+  oddocaddword(doc, "be", "is");
+  oddocaddword(doc, "drive", "driven");
+  oddocaddword(doc, "by", "by");
+  oddocaddword(doc, "the", "the");
+  oddocaddword(doc, "grateful", "Grateful");
+  oddocaddword(doc, "die", "Dead");
+  oddocaddword(doc, "", ".");
+  /* 文書を登録する */
+  odput(odeum, doc, -1, 1);
+  /* 文書の領域を解放する */
+  oddocclose(doc);
+  /* データベースを閉じる */
+  if(!odclose(odeum)) return -1;
+  return 0;
+}
+</pre>
+
+<p>`ODEUM' へのポインタは例によってデータベースハンドルです。`odopen' でそのハンドルを獲得します。開いたハンドルは `odclose' で閉じます。`ODDOC' は文書ハンドルです。各文書の内容は文書ハンドルによって表現されます。`oddocopen' はハンドルを開く関数です。その第1引数で文書のURIを指定します。文書ハンドルは不要になったら `oddocclose' で解放します。テキストの各単語を文書に登録するには、`oddocaddword' を用います。その第2引数は単語の正規形で、第3引数は出現形です。文書を表現したら、それを `odput' でデータベースに登録します。第3引数は、文書データベースに登録する語数を指定します。`-1' にすると全部の語が登録されます。第4引数は、同じURIの既存の文書がある場合に、それを上書きするか否か指定します。</p>
+
+<p>`odput' の第3引数の指定が少し難しいので補足します。ある文書が適切に検索されるために、転置インデックスにおいて、その文書に含まれる全ての単語のレコードの値にその文書のIDが無条件で追加されます。ところで、多くの全文検索システムでは、検索結果の画面で該当の文書の要約を表示します。そのためには、文書データベースの中に、各文書と関連づけて含まれる単語を順番に記録しておく必要があります。検索語の周辺の文を切り出して表示する場合を考えると、検索語が文書中のどこに現れるかは予想できないので、全ての単語を文書データベースに記録しておかなければなりません。あるいは、冒頭の何語かだけ表示する場合には、その語数分の語を登録しておけばよいことになります。そういった決定をアプリケ
 ーションに任せるために、文書データベースに登録する語数を指定できるようになっているのです。</p>
+
+<p>以下の例では、`grateful' という単語を含む文書を検索して、そのURIとスコアを表示します。まず転置インデックスを検索して結果の配列を受け取ります。その配列の各要素は文書のIDとスコアの組です。文書の内容を取得するには、文書IDを使って文書データベースに問い合わせます。</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <odeum.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+void docsearchtest(void){
+  ODEUM *odeum;
+  ODPAIR *pairs;
+  ODDOC *doc;
+  int i, pnum;
+  /* データベースを読み込みモードで開く */
+  if(!(odeum = odopen("index", OD_OREADER))) return;
+  /* 転置インデックスを検索する */
+  pairs = odsearch(odeum, "grateful", -1, &pnum);
+  if(pairs && pnum > 0){
+    /* 該当の各文書を処理する */
+    for(i = 0; i < pnum; i++){
+      /* 文書データベースから文書を取り出す */
+      if(!(doc = odgetbyid(odeum, pairs[i].id))) continue;
+      /* 文書の内容を表示する */
+      printf("URI: %s\n", oddocuri(doc));
+      printf("SCORE: %d\n", pairs[i].score);
+      /* 文書の領域を解放する */
+      oddocclose(doc);
+    }
+  }
+  /* 検索結果の領域を解放する */
+  free(pairs);
+  /* データベースを閉じる */
+  odclose(odeum);
+}
+</pre>
+
+<p>転置インデックスを検索するのが `odsearch' という関数です。第2引数には正規形の検索語を指定します。第3引数には、検索する文書の最大数を指定しますが、全部取り出す場合は `-1' とします。結果の配列はスコアの降順でソートされていることが規定されています。第4引数には、結果の配列の要素数を格納する変数のポインタを指定します。次に、配列の各要素を処理していきます。`odgetbyid' は文書IDを用いて文書の内容を問い合わせる関数です。転置インデックスの中には既に削除されたり上書きされてIDが変更された文書の情報も入っているので(最適化すれば不要な情報はなくなりますが)、`odgetbyid' は失敗する可能性があります。そういう時は単に無視して次のループに進んでください。文書が取得できたら、あとはそれã
 ‚’表示します。`oddocuri' は文書のURIを返す関数です。他にも文書の情報を取得する関数がいくつか用意されています。</p>
+
+<p>Odeumでは、複数の検索語を用いて、AND条件(検索語の全てを含む)やOR条件(検索語のいずれかを含む)やNOTAND条件(検索語の前者を含むが後者は含まない)といった集合演算を処理するための関数が用意されているほか、全文検索システムの実装に便利なユーティリティ関数が多数提供されます。全文検索システムを実装する際には性能と精度のバランスを考えなければなりませんが、OdeumのAPIはアプリケーションがそれを任意に決められるように設計されています。大規模なインデックスを扱う際には、まず精度を落した検索を行って、その結果がユーザの要求を満たさなければ精度を高めたパラメータで再検索する手法が有効でしょう。</p>
+
+<hr />
+
+</body>
+
+</html>
+
+<!-- END OF FILE -->

Added: box/trunk/qdbm/misc/win32check.bat
===================================================================
--- box/trunk/qdbm/misc/win32check.bat	                        (rev 0)
+++ box/trunk/qdbm/misc/win32check.bat	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,111 @@
+dptest write casket 50000 5000
+if errorlevel 1 goto error
+dptest read casket
+if errorlevel 1 goto error
+dptest read -wb casket
+if errorlevel 1 goto error
+dptest rcat casket 50000 50 500 32 32
+if errorlevel 1 goto error
+dptest combo casket
+if errorlevel 1 goto error
+dptest wicked casket 5000
+if errorlevel 1 goto error
+del /Q casket
+
+crtest write casket 50000 500 10
+if errorlevel 1 goto error
+crtest read casket
+if errorlevel 1 goto error
+crtest read -wb casket
+if errorlevel 1 goto error
+crtest rcat casket 50000 5 10 500 32 32
+if errorlevel 1 goto error
+crtest combo casket
+if errorlevel 1 goto error
+crtest wicked casket 5000
+if errorlevel 1 goto error
+rd /S /Q casket
+
+crtest write -lob casket 1000 50 10
+if errorlevel 1 goto error
+crtest read -lob casket
+if errorlevel 1 goto error
+rd /S /Q casket
+
+rltest write casket 50000
+if errorlevel 1 goto error
+rltest read casket 50000
+if errorlevel 1 goto error
+del /Q casket*
+
+hvtest write casket 50000
+if errorlevel 1 goto error
+hvtest read casket 50000
+if errorlevel 1 goto error
+del /Q casket
+
+hvtest write -qdbm casket 50000
+if errorlevel 1 goto error
+hvtest read -qdbm casket 50000
+if errorlevel 1 goto error
+rd /S /Q casket
+
+cbtest sort 5000
+if errorlevel 1 goto error
+cbtest strstr 500
+if errorlevel 1 goto error
+cbtest list 50000
+if errorlevel 1 goto error
+cbtest map 50000
+if errorlevel 1 goto error
+cbtest wicked 5000
+if errorlevel 1 goto error
+cbtest misc
+if errorlevel 1 goto error
+
+vltest write -tune 25 64 32 32 casket 50000
+if errorlevel 1 goto error
+vltest read casket
+if errorlevel 1 goto error
+vltest rdup -tune 25 64 256 256 casket 50000 50000
+if errorlevel 1 goto error
+vltest combo casket
+if errorlevel 1 goto error
+vltest wicked casket 5000
+if errorlevel 1 goto error
+del /Q casket
+
+vltest write -int -cz -tune 25 64 32 32 casket 50000
+if errorlevel 1 goto error
+vltest read -int casket
+if errorlevel 1 goto error
+vltest rdup -int -cz -tune 25 64 256 256 casket 50000 50000
+if errorlevel 1 goto error
+vltest combo -cz casket
+if errorlevel 1 goto error
+vltest wicked -cz casket 5000
+if errorlevel 1 goto error
+del /Q casket
+
+odtest write casket 500 50 5000
+if errorlevel 1 goto error
+odtest read casket
+if errorlevel 1 goto error
+odtest combo casket
+if errorlevel 1 goto error
+odtest wicked casket 500
+if errorlevel 1 goto error
+rd /S /Q casket
+
+ at echo off
+echo #================================
+echo # SUCCESS
+echo #================================
+goto :EOF
+
+:error
+ at echo off
+echo #================================
+echo # ERROR
+echo #================================
+goto :EOF

Added: box/trunk/qdbm/myconf.c
===================================================================
--- box/trunk/qdbm/myconf.c	                        (rev 0)
+++ box/trunk/qdbm/myconf.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,1113 @@
+/*************************************************************************************************
+ * Emulation of system calls
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "myconf.h"
+
+
+
+/*************************************************************************************************
+ * for dosish filesystems
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) || defined(_SYS_CYGWIN_)
+
+
+#define DOSPATHBUFSIZ  8192
+
+
+int _qdbm_win32_lstat(const char *pathname, struct stat *buf){
+  char pbuf[DOSPATHBUFSIZ], *p;
+  int inode;
+  if(stat(pathname, buf) == -1) return -1;
+  if(GetFullPathName(pathname, DOSPATHBUFSIZ, pbuf, &p) != 0){
+    inode = 11003;
+    for(p = pbuf; *p != '\0'; p++){
+      inode = inode * 31 + *(unsigned char *)p;
+    }
+    buf->st_ino = (inode * 911) & 0x7FFF;
+  }
+  return 0;
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for POSIX thread
+ *************************************************************************************************/
+
+
+#if defined(MYPTHREAD)
+
+
+#include <pthread.h>
+
+
+#define PTKEYMAX       8
+
+
+struct { void *ptr; pthread_key_t key; } _qdbm_ptkeys[PTKEYMAX];
+int _qdbm_ptknum = 0;
+
+
+static void *_qdbm_gettsd(void *ptr, int size, const void *initval);
+
+
+void *_qdbm_settsd(void *ptr, int size, const void *initval){
+  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+  char *val;
+  if((val = _qdbm_gettsd(ptr, size, initval)) != NULL) return val;
+  if(pthread_mutex_lock(&mutex) != 0) return NULL;
+  if((val = _qdbm_gettsd(ptr, size, initval)) != NULL){
+    pthread_mutex_unlock(&mutex);
+    return val;
+  }
+  if(_qdbm_ptknum >= PTKEYMAX){
+    pthread_mutex_unlock(&mutex);
+    return NULL;
+  }
+  _qdbm_ptkeys[_qdbm_ptknum].ptr = ptr;
+  if(pthread_key_create(&(_qdbm_ptkeys[_qdbm_ptknum].key), free) != 0){
+    pthread_mutex_unlock(&mutex);
+    return NULL;
+  }
+  if(!(val = malloc(size))){
+    pthread_key_delete(_qdbm_ptkeys[_qdbm_ptknum].key);
+    pthread_mutex_unlock(&mutex);
+    return NULL;
+  }
+  memcpy(val, initval, size);
+  if(pthread_setspecific(_qdbm_ptkeys[_qdbm_ptknum].key, val) != 0){
+    free(val);
+    pthread_key_delete(_qdbm_ptkeys[_qdbm_ptknum].key);
+    pthread_mutex_unlock(&mutex);
+    return NULL;
+  }
+  _qdbm_ptknum++;
+  pthread_mutex_unlock(&mutex);
+  return val;
+}
+
+
+static void *_qdbm_gettsd(void *ptr, int size, const void *initval){
+  char *val;
+  int i;
+  for(i = 0; i < _qdbm_ptknum; i++){
+    if(_qdbm_ptkeys[i].ptr == ptr){
+      if(!(val = pthread_getspecific(_qdbm_ptkeys[i].key))){
+        if(!(val = malloc(size))) return NULL;
+        memcpy(val, initval, size);
+        if(pthread_setspecific(_qdbm_ptkeys[i].key, val) != 0){
+          free(val);
+          return NULL;
+        }
+      }
+      return val;
+    }
+  }
+  return NULL;
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for systems without mmap
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+
+#define MMFDESCMAX     2048
+
+
+struct { void *start; HANDLE handle; } mmhandles[MMFDESCMAX];
+int mmhnum = 0;
+CRITICAL_SECTION mmcsec;
+
+
+static void _qdbm_delete_mmap_env(void);
+
+
+void *_qdbm_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset){
+  static volatile long first = TRUE;
+  static volatile long ready = FALSE;
+  HANDLE handle;
+  int i;
+  if(InterlockedExchange((void *)&first, FALSE)){
+    InitializeCriticalSection(&mmcsec);
+    atexit(_qdbm_delete_mmap_env);
+    InterlockedExchange((void *)&ready, TRUE);
+  }
+  while(!InterlockedCompareExchange((void *)&ready, TRUE, TRUE)){
+    Sleep(1);
+  }
+  if(fd < 0 || flags & MAP_FIXED) return MAP_FAILED;
+  if(!(handle = CreateFileMapping((HANDLE)_get_osfhandle(fd), NULL,
+                                  (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY,
+                                  0, length, NULL))) return MAP_FAILED;
+  if(!(start = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_WRITE : FILE_MAP_READ,
+                             0, 0, length))){
+    CloseHandle(handle);
+    return MAP_FAILED;
+  }
+  EnterCriticalSection(&mmcsec);
+  if(mmhnum >= MMFDESCMAX - 1){
+    UnmapViewOfFile(start);
+    CloseHandle(handle);
+    LeaveCriticalSection(&mmcsec);
+    return MAP_FAILED;
+  }
+  for(i = 0; i < MMFDESCMAX; i++){
+    if(!mmhandles[i].start){
+      mmhandles[i].start = start;
+      mmhandles[i].handle = handle;
+      break;
+    }
+  }
+  mmhnum++;
+  LeaveCriticalSection(&mmcsec);
+  return start;
+}
+
+
+int _qdbm_munmap(void *start, size_t length){
+  HANDLE handle;
+  int i;
+  EnterCriticalSection(&mmcsec);
+  handle = NULL;
+  for(i = 0; i < MMFDESCMAX; i++){
+    if(mmhandles[i].start == start){
+      handle = mmhandles[i].handle;
+      mmhandles[i].start = NULL;
+      mmhandles[i].handle = NULL;
+      break;
+    }
+  }
+  if(!handle){
+    LeaveCriticalSection(&mmcsec);
+    return -1;
+  }
+  mmhnum--;
+  LeaveCriticalSection(&mmcsec);
+  if(!UnmapViewOfFile(start)){
+    CloseHandle(handle);
+    return -1;
+  }
+  if(!CloseHandle(handle)) return -1;
+  return 0;
+}
+
+
+int _qdbm_msync(const void *start, size_t length, int flags){
+  if(!FlushViewOfFile(start, length)) return -1;
+  return 0;
+}
+
+
+static void _qdbm_delete_mmap_env(void){
+  DeleteCriticalSection(&mmcsec);
+}
+
+
+#elif defined(_SYS_FREEBSD_) || defined(_SYS_NETBSD_) || defined(_SYS_OPENBSD_) || \
+  defined(_SYS_AIX_) || defined(_SYS_RISCOS_) || defined(MYNOMMAP)
+
+
+void *_qdbm_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset){
+  char *buf, *wp;
+  int rv, rlen;
+  if(flags & MAP_FIXED) return MAP_FAILED;
+  if(lseek(fd, SEEK_SET, offset) == -1) return MAP_FAILED;
+  if(!(buf = malloc(sizeof(int) * 3 + length))) return MAP_FAILED;
+  wp = buf;
+  *(int *)wp = fd;
+  wp += sizeof(int);
+  *(int *)wp = offset;
+  wp += sizeof(int);
+  *(int *)wp = prot;
+  wp += sizeof(int);
+  rlen = 0;
+  while((rv = read(fd, wp + rlen, length - rlen)) > 0){
+    rlen += rv;
+  }
+  if(rv == -1 || rlen != length){
+    free(buf);
+    return MAP_FAILED;
+  }
+  return wp;
+}
+
+
+int _qdbm_munmap(void *start, size_t length){
+  char *buf, *rp;
+  int fd, offset, prot, rv, wlen;
+  buf = (char *)start - sizeof(int) * 3;
+  rp = buf;
+  fd = *(int *)rp;
+  rp += sizeof(int);
+  offset = *(int *)rp;
+  rp += sizeof(int);
+  prot = *(int *)rp;
+  rp += sizeof(int);
+  if(prot & PROT_WRITE){
+    if(lseek(fd, offset, SEEK_SET) == -1){
+      free(buf);
+      return -1;
+    }
+    wlen = 0;
+    while(wlen < (int)length){
+      rv = write(fd, rp + wlen, length - wlen);
+      if(rv == -1){
+        if(errno == EINTR) continue;
+        free(buf);
+        return -1;
+      }
+      wlen += rv;
+    }
+  }
+  free(buf);
+  return 0;
+}
+
+
+int _qdbm_msync(const void *start, size_t length, int flags){
+  char *buf, *rp;
+  int fd, offset, prot, rv, wlen;
+  buf = (char *)start - sizeof(int) * 3;
+  rp = buf;
+  fd = *(int *)rp;
+  rp += sizeof(int);
+  offset = *(int *)rp;
+  rp += sizeof(int);
+  prot = *(int *)rp;
+  rp += sizeof(int);
+  if(prot & PROT_WRITE){
+    if(lseek(fd, offset, SEEK_SET) == -1) return -1;
+    wlen = 0;
+    while(wlen < (int)length){
+      rv = write(fd, rp + wlen, length - wlen);
+      if(rv == -1){
+        if(errno == EINTR) continue;
+        return -1;
+      }
+      wlen += rv;
+    }
+  }
+  return 0;
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for reentrant time routines
+ *************************************************************************************************/
+
+
+#if defined(_SYS_LINUX_) || defined(_SYS_FREEBSD_) || defined(_SYS_OPENBSD_) || \
+  defined(_SYS_NETBSD_) || defined(_SYS_SUNOS_) || defined(_SYS_HPUX_) || \
+  defined(_SYS_MACOSX_) || defined(_SYS_CYGWIN_)
+
+
+struct tm *_qdbm_gmtime(const time_t *timep, struct tm *result){
+  return gmtime_r(timep, result);
+}
+
+
+struct tm *_qdbm_localtime(const time_t *timep, struct tm *result){
+  return localtime_r(timep, result);
+}
+
+
+# else
+
+
+struct tm *_qdbm_gmtime(const time_t *timep, struct tm *result){
+  return gmtime(timep);
+}
+
+
+struct tm *_qdbm_localtime(const time_t *timep, struct tm *result){
+  return localtime(timep);
+}
+
+
+# endif
+
+
+
+/*************************************************************************************************
+ * for systems without times
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+
+clock_t _qdbm_times(struct tms *buf){
+  buf->tms_utime = clock();
+  buf->tms_stime = 0;
+  buf->tms_cutime = 0;
+  buf->tms_cstime = 0;
+  return 0;
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for Win32
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+
+#define WINLOCKWAIT    100
+
+
+int _qdbm_win32_fcntl(int fd, int cmd, struct flock *lock){
+  HANDLE fh;
+  DWORD opt;
+  OVERLAPPED ol;
+  fh = (HANDLE)_get_osfhandle(fd);
+  opt = (cmd == F_SETLK) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
+  if(lock->l_type == F_WRLCK) opt |= LOCKFILE_EXCLUSIVE_LOCK;
+  memset(&ol, 0, sizeof(OVERLAPPED));
+  ol.Offset = INT_MAX;
+  ol.OffsetHigh = 0;
+  ol.hEvent = 0;
+  if(!LockFileEx(fh, opt, 0, 1, 0, &ol)){
+    if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED){
+      while(TRUE){
+        if(LockFile(fh, 0, 0, 1, 0)) return 0;
+        Sleep(WINLOCKWAIT);
+      }
+    }
+    return -1;
+  }
+  return 0;
+}
+
+
+#endif
+
+
+#if defined(_SYS_MSVC_)
+
+
+DIR *_qdbm_win32_opendir(const char *name){
+  char expr[8192];
+  int len;
+  DIR *dir;
+  HANDLE fh;
+  WIN32_FIND_DATA data;
+  len = strlen(name);
+  if(len > 0 && name[len-1] == MYPATHCHR){
+    sprintf(expr, "%s*", name);
+  } else {
+    sprintf(expr, "%s%c*", name, MYPATHCHR);
+  }
+  if((fh = FindFirstFile(expr, &data)) == INVALID_HANDLE_VALUE) return NULL;
+  if(!(dir = malloc(sizeof(DIR)))){
+    FindClose(fh);
+    return NULL;
+  }
+  dir->fh = fh;
+  dir->data = data;
+  dir->first = TRUE;
+  return dir;
+}
+
+
+int _qdbm_win32_closedir(DIR *dir){
+  if(!FindClose(dir->fh)){
+    free(dir);
+    return -1;
+  }
+  free(dir);
+  return 0;
+}
+
+
+struct dirent *_qdbm_win32_readdir(DIR *dir){
+  if(dir->first){
+    sprintf(dir->de.d_name, "%s", dir->data.cFileName);
+    dir->first = FALSE;
+    return &(dir->de);
+  }
+  if(!FindNextFile(dir->fh, &(dir->data))) return NULL;
+  sprintf(dir->de.d_name, "%s", dir->data.cFileName);
+  return &(dir->de);
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for checking information of the system
+ *************************************************************************************************/
+
+
+#if defined(_SYS_LINUX_)
+
+
+int _qdbm_vmemavail(size_t size){
+  char buf[4096], *rp;
+  int fd, rv, bsiz;
+  double avail;
+  if((fd = open("/proc/meminfo", O_RDONLY, 00644)) == -1) return TRUE;
+  rv = TRUE;
+  if((bsiz = read(fd, buf, sizeof(buf) - 1)) > 0){
+    buf[bsiz] = '\0';
+    avail = -1;
+    if((rp = strstr(buf, "MemFree:")) != NULL){
+      rp = strchr(rp, ':') + 1;
+      avail = strtod(rp, NULL) * 1024.0;
+      if((rp = strstr(buf, "SwapFree:")) != NULL){
+        rp = strchr(rp, ':') + 1;
+        avail += strtod(rp, NULL) * 1024.0;
+      }
+      if(size >= avail) rv = FALSE;
+    }
+  }
+  close(fd);
+  return rv;
+}
+
+
+#elif defined(_SYS_MSVC_) || defined(_SYS_MINGW_) || defined(_SYS_CYGWIN_)
+
+
+int _qdbm_vmemavail(size_t size){
+  MEMORYSTATUS sbuf;
+  sbuf.dwLength = sizeof(MEMORYSTATUS);
+  GlobalMemoryStatus(&sbuf);
+  return size < sbuf.dwAvailVirtual;
+}
+
+
+#else
+
+
+int _qdbm_vmemavail(size_t size){
+  return TRUE;
+}
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for ZLIB
+ *************************************************************************************************/
+
+
+#if defined(MYZLIB)
+
+
+#include <zlib.h>
+
+#define ZLIBBUFSIZ     8192
+
+
+static char *_qdbm_deflate_impl(const char *ptr, int size, int *sp, int mode);
+static char *_qdbm_inflate_impl(const char *ptr, int size, int *sp, int mode);
+static unsigned int _qdbm_getcrc_impl(const char *ptr, int size);
+
+
+char *(*_qdbm_deflate)(const char *, int, int *, int) = _qdbm_deflate_impl;
+char *(*_qdbm_inflate)(const char *, int, int *, int) = _qdbm_inflate_impl;
+unsigned int (*_qdbm_getcrc)(const char *, int) = _qdbm_getcrc_impl;
+
+
+static char *_qdbm_deflate_impl(const char *ptr, int size, int *sp, int mode){
+  z_stream zs;
+  char *buf, *swap;
+  unsigned char obuf[ZLIBBUFSIZ];
+  int rv, asiz, bsiz, osiz;
+  if(size < 0) size = strlen(ptr);
+  zs.zalloc = Z_NULL;
+  zs.zfree = Z_NULL;
+  zs.opaque = Z_NULL;
+  switch(mode){
+  case _QDBM_ZMRAW:
+    if(deflateInit2(&zs, 5, Z_DEFLATED, -15, 7, Z_DEFAULT_STRATEGY) != Z_OK)
+      return NULL;
+    break;
+  case _QDBM_ZMGZIP:
+    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 == _QDBM_ZMRAW) bsiz++;
+  *sp = bsiz;
+  deflateEnd(&zs);
+  return buf;
+}
+
+
+static char *_qdbm_inflate_impl(const char *ptr, int size, int *sp, int mode){
+  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 _QDBM_ZMRAW:
+    if(inflateInit2(&zs, -15) != Z_OK) return NULL;
+    break;
+  case _QDBM_ZMGZIP:
+    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';
+  if(sp) *sp = bsiz;
+  inflateEnd(&zs);
+  return buf;
+}
+
+
+static unsigned int _qdbm_getcrc_impl(const char *ptr, int size){
+  int crc;
+  if(size < 0) size = strlen(ptr);
+  crc = crc32(0, Z_NULL, 0);
+  return crc32(crc, (unsigned char *)ptr, size);
+}
+
+
+#else
+
+
+char *(*_qdbm_deflate)(const char *, int, int *, int) = NULL;
+char *(*_qdbm_inflate)(const char *, int, int *, int) = NULL;
+unsigned int (*_qdbm_getcrc)(const char *, int) = NULL;
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for LZO
+ *************************************************************************************************/
+
+
+#if defined(MYLZO)
+
+
+#include <lzo/lzo1x.h>
+
+
+static char *_qdbm_lzoencode_impl(const char *ptr, int size, int *sp);
+static char *_qdbm_lzodecode_impl(const char *ptr, int size, int *sp);
+
+
+int _qdbm_lzo_init = FALSE;
+char *(*_qdbm_lzoencode)(const char *, int, int *) = _qdbm_lzoencode_impl;
+char *(*_qdbm_lzodecode)(const char *, int, int *) = _qdbm_lzodecode_impl;
+
+
+static char *_qdbm_lzoencode_impl(const char *ptr, int size, int *sp){
+  char wrkmem[LZO1X_1_MEM_COMPRESS];
+  lzo_bytep buf;
+  lzo_uint bsiz;
+  if(!_qdbm_lzo_init){
+    if(lzo_init() != LZO_E_OK) return NULL;
+    _qdbm_lzo_init = TRUE;
+  }
+  if(size < 0) size = strlen(ptr);
+  if(!(buf = malloc(size + size / 16 + 80))) return NULL;
+  if(lzo1x_1_compress((lzo_bytep)ptr, size, buf, &bsiz, wrkmem) != LZO_E_OK){
+    free(buf);
+    return NULL;
+  }
+  buf[bsiz] = '\0';
+  *sp = bsiz;
+  return (char *)buf;
+}
+
+
+static char *_qdbm_lzodecode_impl(const char *ptr, int size, int *sp){
+  lzo_bytep buf;
+  lzo_uint bsiz;
+  int rat, rv;
+  if(!_qdbm_lzo_init){
+    if(lzo_init() != LZO_E_OK) return NULL;
+    _qdbm_lzo_init = TRUE;
+  }
+  rat = 6;
+  while(TRUE){
+    bsiz = (size + 256) * rat + 3;
+    if(!(buf = malloc(bsiz + 1))) return NULL;
+    rv = lzo1x_decompress_safe((lzo_bytep)(ptr), size, buf, &bsiz, NULL);
+    if(rv == LZO_E_OK){
+      break;
+    } else if(rv == LZO_E_OUTPUT_OVERRUN){
+      free(buf);
+      rat *= 2;
+    } else {
+      free(buf);
+      return NULL;
+    }
+  }
+  buf[bsiz] = '\0';
+  if(sp) *sp = bsiz;
+  return (char *)buf;
+}
+
+
+#else
+
+
+char *(*_qdbm_lzoencode)(const char *, int, int *) = NULL;
+char *(*_qdbm_lzodecode)(const char *, int, int *) = NULL;
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for BZIP2
+ *************************************************************************************************/
+
+
+#if defined(MYBZIP)
+
+
+#include <bzlib.h>
+
+#define BZIPBUFSIZ     8192
+
+
+static char *_qdbm_bzencode_impl(const char *ptr, int size, int *sp);
+static char *_qdbm_bzdecode_impl(const char *ptr, int size, int *sp);
+
+
+char *(*_qdbm_bzencode)(const char *, int, int *) = _qdbm_bzencode_impl;
+char *(*_qdbm_bzdecode)(const char *, int, int *) = _qdbm_bzdecode_impl;
+
+
+static char *_qdbm_bzencode_impl(const char *ptr, int size, int *sp){
+  bz_stream zs;
+  char *buf, *swap, obuf[BZIPBUFSIZ];
+  int rv, asiz, bsiz, osiz;
+  if(size < 0) size = strlen(ptr);
+  zs.bzalloc = NULL;
+  zs.bzfree = NULL;
+  zs.opaque = NULL;
+  if(BZ2_bzCompressInit(&zs, 9, 0, 30) != BZ_OK) return NULL;
+  asiz = size + 16;
+  if(asiz < BZIPBUFSIZ) asiz = BZIPBUFSIZ;
+  if(!(buf = malloc(asiz))){
+    BZ2_bzCompressEnd(&zs);
+    return NULL;
+  }
+  bsiz = 0;
+  zs.next_in = (char *)ptr;
+  zs.avail_in = size;
+  zs.next_out = obuf;
+  zs.avail_out = BZIPBUFSIZ;
+  while((rv = BZ2_bzCompress(&zs, BZ_FINISH)) == BZ_FINISH_OK){
+    osiz = BZIPBUFSIZ - zs.avail_out;
+    if(bsiz + osiz > asiz){
+      asiz = asiz * 2 + osiz;
+      if(!(swap = realloc(buf, asiz))){
+        free(buf);
+        BZ2_bzCompressEnd(&zs);
+        return NULL;
+      }
+      buf = swap;
+    }
+    memcpy(buf + bsiz, obuf, osiz);
+    bsiz += osiz;
+    zs.next_out = obuf;
+    zs.avail_out = BZIPBUFSIZ;
+  }
+  if(rv != BZ_STREAM_END){
+    free(buf);
+    BZ2_bzCompressEnd(&zs);
+    return NULL;
+  }
+  osiz = BZIPBUFSIZ - zs.avail_out;
+  if(bsiz + osiz + 1 > asiz){
+    asiz = asiz * 2 + osiz;
+    if(!(swap = realloc(buf, asiz))){
+      free(buf);
+      BZ2_bzCompressEnd(&zs);
+      return NULL;
+    }
+    buf = swap;
+  }
+  memcpy(buf + bsiz, obuf, osiz);
+  bsiz += osiz;
+  buf[bsiz] = '\0';
+  *sp = bsiz;
+  BZ2_bzCompressEnd(&zs);
+  return buf;
+}
+
+
+static char *_qdbm_bzdecode_impl(const char *ptr, int size, int *sp){
+  bz_stream zs;
+  char *buf, *swap, obuf[BZIPBUFSIZ];
+  int rv, asiz, bsiz, osiz;
+  zs.bzalloc = NULL;
+  zs.bzfree = NULL;
+  zs.opaque = NULL;
+  if(BZ2_bzDecompressInit(&zs, 0, 0) != BZ_OK) return NULL;
+  asiz = size * 2 + 16;
+  if(asiz < BZIPBUFSIZ) asiz = BZIPBUFSIZ;
+  if(!(buf = malloc(asiz))){
+    BZ2_bzDecompressEnd(&zs);
+    return NULL;
+  }
+  bsiz = 0;
+  zs.next_in = (char *)ptr;
+  zs.avail_in = size;
+  zs.next_out = obuf;
+  zs.avail_out = BZIPBUFSIZ;
+  while((rv = BZ2_bzDecompress(&zs)) == BZ_OK){
+    osiz = BZIPBUFSIZ - zs.avail_out;
+    if(bsiz + osiz >= asiz){
+      asiz = asiz * 2 + osiz;
+      if(!(swap = realloc(buf, asiz))){
+        free(buf);
+        BZ2_bzDecompressEnd(&zs);
+        return NULL;
+      }
+      buf = swap;
+    }
+    memcpy(buf + bsiz, obuf, osiz);
+    bsiz += osiz;
+    zs.next_out = obuf;
+    zs.avail_out = BZIPBUFSIZ;
+  }
+  if(rv != BZ_STREAM_END){
+    free(buf);
+    BZ2_bzDecompressEnd(&zs);
+    return NULL;
+  }
+  osiz = BZIPBUFSIZ - zs.avail_out;
+  if(bsiz + osiz >= asiz){
+    asiz = asiz * 2 + osiz;
+    if(!(swap = realloc(buf, asiz))){
+      free(buf);
+      BZ2_bzDecompressEnd(&zs);
+      return NULL;
+    }
+    buf = swap;
+  }
+  memcpy(buf + bsiz, obuf, osiz);
+  bsiz += osiz;
+  buf[bsiz] = '\0';
+  if(sp) *sp = bsiz;
+  BZ2_bzDecompressEnd(&zs);
+  return buf;
+}
+
+
+#else
+
+
+char *(*_qdbm_bzencode)(const char *, int, int *) = NULL;
+char *(*_qdbm_bzdecode)(const char *, int, int *) = NULL;
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for ICONV
+ *************************************************************************************************/
+
+
+#if defined(MYICONV)
+
+
+#include <iconv.h>
+
+#define ICONVCHECKSIZ  32768
+#define ICONVMISSMAX   256
+#define ICONVALLWRAT   0.001
+
+
+static char *_qdbm_iconv_impl(const char *ptr, int size,
+                              const char *icode, const char *ocode, int *sp, int *mp);
+static const char *_qdbm_encname_impl(const char *ptr, int size);
+static int _qdbm_encmiss(const char *ptr, int size, const char *icode, const char *ocode);
+
+
+char *(*_qdbm_iconv)(const char *, int, const char *, const char *,
+                     int *, int *) = _qdbm_iconv_impl;
+const char *(*_qdbm_encname)(const char *, int) = _qdbm_encname_impl;
+
+
+static char *_qdbm_iconv_impl(const char *ptr, int size,
+                              const char *icode, const char *ocode, int *sp, int *mp){
+  iconv_t ic;
+  char *obuf, *wp, *rp;
+  size_t isiz, osiz;
+  int miss;
+  if(size < 0) size = strlen(ptr);
+  isiz = size;
+  if((ic = iconv_open(ocode, icode)) == (iconv_t)-1) return NULL;
+  osiz = isiz * 5;
+  if(!(obuf = malloc(osiz + 1))){
+    iconv_close(ic);
+    return NULL;
+  }
+  wp = obuf;
+  rp = (char *)ptr;
+  miss = 0;
+  while(isiz > 0){
+    if(iconv(ic, (void *)&rp, &isiz, &wp, &osiz) == -1){
+      if(errno == EILSEQ && (*rp == 0x5c || *rp == 0x7e)){
+        *wp = *rp;
+        wp++;
+        rp++;
+        isiz--;
+      } else if(errno == EILSEQ || errno == EINVAL){
+        rp++;
+        isiz--;
+        miss++;
+      } else {
+        break;
+      }
+    }
+  }
+  *wp = '\0';
+  if(iconv_close(ic) == -1){
+    free(obuf);
+    return NULL;
+  }
+  if(sp) *sp = wp - obuf;
+  if(mp) *mp = miss;
+  return obuf;
+}
+
+
+static const char *_qdbm_encname_impl(const char *ptr, int size){
+  const char *hypo;
+  int i, miss, cr;
+  if(size < 0) size = strlen(ptr);
+  if(size > ICONVCHECKSIZ) size = ICONVCHECKSIZ;
+  if(size >= 2 && (!memcmp(ptr, "\xfe\xff", 2) || !memcmp(ptr, "\xff\xfe", 2))) return "UTF-16";
+  for(i = 0; i < size - 1; i += 2){
+    if(ptr[i] == 0 && ptr[i+1] != 0) return "UTF-16BE";
+    if(ptr[i+1] == 0 && ptr[i] != 0) return "UTF-16LE";
+  }
+  for(i = 0; i < size - 3; i++){
+    if(ptr[i] == 0x1b){
+      i++;
+      if(ptr[i] == '(' && strchr("BJHI", ptr[i+1])) return "ISO-2022-JP";
+      if(ptr[i] == '$' && strchr("@B(", ptr[i+1])) return "ISO-2022-JP";
+    }
+  }
+  if(_qdbm_encmiss(ptr, size, "US-ASCII", "UTF-16BE") < 1) return "US-ASCII";
+  if(_qdbm_encmiss(ptr, size, "UTF-8", "UTF-16BE") < 1) return "UTF-8";
+  hypo = NULL;
+  cr = FALSE;
+  for(i = 0; i < size; i++){
+    if(ptr[i] == 0xd){
+      cr = TRUE;
+      break;
+    }
+  }
+  if(cr){
+    if((miss = _qdbm_encmiss(ptr, size, "Shift_JIS", "EUC-JP")) < 1) return "Shift_JIS";
+    if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "Shift_JIS";
+    if((miss = _qdbm_encmiss(ptr, size, "EUC-JP", "UTF-16BE")) < 1) return "EUC-JP";
+    if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "EUC-JP";
+  } else {
+    if((miss = _qdbm_encmiss(ptr, size, "EUC-JP", "UTF-16BE")) < 1) return "EUC-JP";
+    if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "EUC-JP";
+    if((miss = _qdbm_encmiss(ptr, size, "Shift_JIS", "EUC-JP")) < 1) return "Shift_JIS";
+    if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "Shift_JIS";
+  }
+  if((miss = _qdbm_encmiss(ptr, size, "UTF-8", "UTF-16BE")) < 1) return "UTF-8";
+  if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "UTF-8";
+  if((miss = _qdbm_encmiss(ptr, size, "CP932", "UTF-16BE")) < 1) return "CP932";
+  if(!hypo && miss / (double)size <= ICONVALLWRAT) hypo = "CP932";
+  return hypo ? hypo : "ISO-8859-1";
+}
+
+
+static int _qdbm_encmiss(const char *ptr, int size, const char *icode, const char *ocode){
+  iconv_t ic;
+  char obuf[ICONVCHECKSIZ], *wp, *rp;
+  size_t isiz, osiz;
+  int miss;
+  isiz = size;
+  if((ic = iconv_open(ocode, icode)) == (iconv_t)-1) return ICONVMISSMAX;
+  miss = 0;
+  rp = (char *)ptr;
+  while(isiz > 0){
+    osiz = ICONVCHECKSIZ;
+    wp = obuf;
+    if(iconv(ic, (void *)&rp, &isiz, &wp, &osiz) == -1){
+      if(errno == EILSEQ || errno == EINVAL){
+        rp++;
+        isiz--;
+        miss++;
+        if(miss >= ICONVMISSMAX) break;
+      } else {
+        break;
+      }
+    }
+  }
+  if(iconv_close(ic) == -1) return ICONVMISSMAX;
+  return miss;
+}
+
+
+#else
+
+
+char *(*_qdbm_iconv)(const char *, int, const char *, const char *, int *, int *) = NULL;
+const char *(*_qdbm_encname)(const char *, int) = NULL;
+
+
+#endif
+
+
+
+/*************************************************************************************************
+ * common settings
+ *************************************************************************************************/
+
+
+int _qdbm_dummyfunc(void){
+  return 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/myconf.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/myconf.h
===================================================================
--- box/trunk/qdbm/myconf.h	                        (rev 0)
+++ box/trunk/qdbm/myconf.h	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,593 @@
+/*************************************************************************************************
+ * System configurations for QDBM
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; 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
+
+#if defined(__cplusplus)                 /* export for C++ */
+extern "C" {
+#endif
+
+
+
+/*************************************************************************************************
+ * system discrimination
+ *************************************************************************************************/
+
+
+#if defined(__linux__)
+
+#define _SYS_LINUX_
+#define _QDBM_SYSNAME  "Linux"
+
+#elif defined(__FreeBSD__)
+
+#define _SYS_FREEBSD_
+#define _QDBM_SYSNAME  "FreeBSD"
+
+#elif defined(__NetBSD__)
+
+#define _SYS_NETBSD_
+#define _QDBM_SYSNAME  "NetBSD"
+
+#elif defined(__OpenBSD__)
+
+#define _SYS_OPENBSD_
+#define _QDBM_SYSNAME  "OpenBSD"
+
+#elif defined(__sun__)
+
+#define _SYS_SUNOS_
+#define _QDBM_SYSNAME  "SunOS"
+
+#elif defined(__hpux)
+
+#define _SYS_HPUX_
+#define _QDBM_SYSNAME  "HP-UX"
+
+#elif defined(__osf)
+
+#define _SYS_TRU64_
+#define _QDBM_SYSNAME  "Tru64"
+
+#elif defined(_AIX)
+
+#define _SYS_AIX_
+#define _QDBM_SYSNAME  "AIX"
+
+#elif defined(__APPLE__) && defined(__MACH__)
+
+#define _SYS_MACOSX_
+#define _QDBM_SYSNAME  "Mac OS X"
+
+#elif defined(_MSC_VER)
+
+#define _SYS_MSVC_
+#define _QDBM_SYSNAME  "Windows (VC++)"
+
+#elif defined(_WIN32)
+
+#define _SYS_MINGW_
+#define _QDBM_SYSNAME  "Windows (MinGW)"
+
+#elif defined(__CYGWIN__)
+
+#define _SYS_CYGWIN_
+#define _QDBM_SYSNAME  "Windows (Cygwin)"
+
+#elif defined(__riscos__) || defined(__riscos)
+
+#define _SYS_RISCOS_
+#define _QDBM_SYSNAME  "RISC OS"
+
+#else
+
+#define _SYS_GENERIC_
+#define _QDBM_SYSNAME  "Generic"
+
+#endif
+
+
+
+/*************************************************************************************************
+ * general headers
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <direct.h>
+#include <windows.h>
+#include <io.h>
+
+#elif defined(_SYS_MINGW_)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <windows.h>
+#include <io.h>
+
+#elif defined(_SYS_CYGWIN_)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/times.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <windows.h>
+#include <io.h>
+
+#else
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/times.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#endif
+
+
+
+/*************************************************************************************************
+ * notation of filesystems
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+#define MYPATHCHR       '\\'
+#define MYPATHSTR       "\\"
+#define MYEXTCHR        '.'
+#define MYEXTSTR        "."
+#define MYCDIRSTR       "."
+#define MYPDIRSTR       ".."
+
+#elif defined(_SYS_RISCOS_)
+
+#define MYPATHCHR       '.'
+#define MYPATHSTR       "."
+#define MYEXTCHR        '/'
+#define MYEXTSTR        "/"
+#define MYCDIRSTR       "@"
+#define MYPDIRSTR       "^"
+
+#else
+
+#define MYPATHCHR       '/'
+#define MYPATHSTR       "/"
+#define MYEXTCHR        '.'
+#define MYEXTSTR        "."
+#define MYCDIRSTR       "."
+#define MYPDIRSTR       ".."
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for dosish filesystems
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) || defined(_SYS_CYGWIN_)
+
+#undef UNICODE
+#undef open
+
+#define \
+  open(pathname, flags, mode) \
+  open(pathname, flags | O_BINARY, mode)
+
+#define \
+  lstat(pathname, buf) \
+  _qdbm_win32_lstat(pathname, buf)
+
+int _qdbm_win32_lstat(const char *pathname, struct stat *buf);
+
+#else
+
+#undef O_BINARY
+#undef O_TEXT
+#undef setmode
+
+#define O_BINARY           0
+#define O_TEXT             1
+
+#define \
+  setmode(fd, mode) \
+  (O_BINARY)
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for POSIX thread
+ *************************************************************************************************/
+
+
+#if defined(MYPTHREAD)
+
+#define _qdbm_ptsafe       TRUE
+
+void *_qdbm_settsd(void *ptr, int size, const void *initval);
+
+#else
+
+#define _qdbm_ptsafe       FALSE
+
+#define \
+  _qdbm_settsd(ptr, size, initval) \
+  (NULL)
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for systems without file locking
+ *************************************************************************************************/
+
+
+#if defined(_SYS_RISCOS_) || defined(MYNOLOCK)
+
+#undef fcntl
+
+#define \
+  fcntl(fd, cmd, lock) \
+  (0)
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for systems without mmap
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) || \
+  defined(_SYS_FREEBSD_) || defined(_SYS_NETBSD_) || defined(_SYS_OPENBSD_) || \
+  defined(_SYS_AIX_) || defined(_SYS_RISCOS_) || defined(MYNOMMAP)
+
+#undef PROT_EXEC
+#undef PROT_READ
+#undef PROT_WRITE
+#undef PROT_NONE
+#undef MAP_FIXED
+#undef MAP_SHARED
+#undef MAP_PRIVATE
+#undef MAP_FAILED
+#undef MS_ASYNC
+#undef MS_SYNC
+#undef MS_INVALIDATE
+#undef mmap
+#undef munmap
+#undef msync
+#undef mflush
+
+#define PROT_EXEC      (1 << 0)
+#define PROT_READ      (1 << 1)
+#define PROT_WRITE     (1 << 2)
+#define PROT_NONE      (1 << 3)
+#define MAP_FIXED      1
+#define MAP_SHARED     2
+#define MAP_PRIVATE    3
+#define MAP_FAILED     ((void *)-1)
+#define MS_ASYNC       (1 << 0)
+#define MS_SYNC        (1 << 1)
+#define MS_INVALIDATE  (1 << 2)
+
+#define \
+  mmap(start, length, prot, flags, fd, offset) \
+  _qdbm_mmap(start, length, prot, flags, fd, offset)
+
+#define \
+  munmap(start, length) \
+  _qdbm_munmap(start, length)
+
+#define \
+  msync(start, length, flags) \
+  _qdbm_msync(start, length, flags)
+
+#define \
+  mflush(start, length, flags) \
+  _qdbm_msync(start, length, flags)
+
+void *_qdbm_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+int _qdbm_munmap(void *start, size_t length);
+int _qdbm_msync(const void *start, size_t length, int flags);
+
+#else
+
+#undef mflush
+#define \
+  mflush(start, length, flags) \
+  (0)
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for reentrant time routines
+ *************************************************************************************************/
+
+
+struct tm *_qdbm_gmtime(const time_t *timep, struct tm *result);
+struct tm *_qdbm_localtime(const time_t *timep, struct tm *result);
+
+
+
+/*************************************************************************************************
+ * for systems without times
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+#undef times
+#undef sysconf
+
+struct tms {
+  clock_t tms_utime;
+  clock_t tms_stime;
+  clock_t tms_cutime;
+  clock_t tms_cstime;
+};
+
+#define \
+  times(buf) \
+  _qdbm_times(buf)
+
+#define \
+  sysconf(name) \
+  (CLOCKS_PER_SEC)
+
+clock_t _qdbm_times(struct tms *buf);
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for Win32
+ *************************************************************************************************/
+
+
+#if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
+
+#undef F_WRLCK
+#undef F_RDLCK
+#undef F_SETLK
+#undef F_SETLKW
+#undef fcntl
+#undef ftruncate
+#undef fsync
+#undef mkdir
+#undef rename
+
+#define F_WRLCK        0
+#define F_RDLCK        1
+#define F_SETLK        0
+#define F_SETLKW       1
+
+struct flock {
+  int l_type;
+  int l_whence;
+  int l_start;
+  int l_len;
+  int l_pid;
+};
+
+#define \
+  fcntl(fd, cmd, lock) \
+  _qdbm_win32_fcntl(fd, cmd, lock)
+
+#define \
+  ftruncate(fd, length) \
+  _chsize(fd, length)
+
+#define \
+  fsync(fd) \
+  (0)
+
+#define \
+  mkdir(pathname, mode) \
+  mkdir(pathname)
+
+#define \
+  rename(oldpath, newpath) \
+  (unlink(newpath), rename(oldpath, newpath))
+
+int _qdbm_win32_fcntl(int fd, int cmd, struct flock *lock);
+
+#endif
+
+
+#if defined(_SYS_MSVC_)
+
+#undef S_ISDIR
+#undef S_ISREG
+#undef opendir
+#undef closedir
+#undef readdir
+
+#define S_ISDIR(x)     (x & _S_IFDIR)
+#define S_ISREG(x)     (x & _S_IFREG)
+
+struct dirent {
+  char d_name[1024];
+};
+
+typedef struct {
+  HANDLE fh;
+  WIN32_FIND_DATA data;
+  struct dirent de;
+  int first;
+} DIR;
+
+#define \
+  opendir(name) \
+  _qdbm_win32_opendir(name)
+
+#define \
+  closedir(dir) \
+  _qdbm_win32_closedir(dir)
+
+#define \
+  readdir(dir) \
+  _qdbm_win32_readdir(dir)
+
+DIR *_qdbm_win32_opendir(const char *name);
+
+int _qdbm_win32_closedir(DIR *dir);
+
+struct dirent *_qdbm_win32_readdir(DIR *dir);
+
+#endif
+
+
+
+/*************************************************************************************************
+ * for checking information of the system
+ *************************************************************************************************/
+
+
+int _qdbm_vmemavail(size_t size);
+
+
+
+/*************************************************************************************************
+ * for ZLIB
+ *************************************************************************************************/
+
+
+enum {
+  _QDBM_ZMZLIB,
+  _QDBM_ZMRAW,
+  _QDBM_ZMGZIP
+};
+
+
+extern char *(*_qdbm_deflate)(const char *, int, int *, int);
+
+extern char *(*_qdbm_inflate)(const char *, int, int *, int);
+
+extern unsigned int (*_qdbm_getcrc)(const char *, int);
+
+
+
+/*************************************************************************************************
+ * for LZO
+ *************************************************************************************************/
+
+
+extern char *(*_qdbm_lzoencode)(const char *, int, int *);
+
+extern char *(*_qdbm_lzodecode)(const char *, int, int *);
+
+
+
+/*************************************************************************************************
+ * for BZIP2
+ *************************************************************************************************/
+
+
+extern char *(*_qdbm_bzencode)(const char *, int, int *);
+
+extern char *(*_qdbm_bzdecode)(const char *, int, int *);
+
+
+
+/*************************************************************************************************
+ * for ICONV
+ *************************************************************************************************/
+
+
+extern char *(*_qdbm_iconv)(const char *, int, const char *, const char *, int *, int *);
+
+extern const char *(*_qdbm_encname)(const char *, int);
+
+
+
+/*************************************************************************************************
+ * common settings
+ *************************************************************************************************/
+
+
+#undef TRUE
+#define TRUE           1
+#undef FALSE
+#define FALSE          0
+
+#define sizeof(a)      ((int)sizeof(a))
+
+int _qdbm_dummyfunc(void);
+
+
+
+#if defined(__cplusplus)                 /* export for C++ */
+}
+#endif
+
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/myconf.h
___________________________________________________________________
Added: svn:mime-type
   + text/x-chdr
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/odeum.c
===================================================================
--- box/trunk/qdbm/odeum.c	                        (rev 0)
+++ box/trunk/qdbm/odeum.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,2090 @@
+/*************************************************************************************************
+ * Implementation of Odeum
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#define QDBM_INTERNAL  1
+
+#include "odeum.h"
+#include "myconf.h"
+
+#define OD_NAMEMAX     256               /* max size of a database name */
+#define OD_DIRMODE     00755             /* permission of a creating directory */
+#define OD_PATHBUFSIZ  1024              /* size of a path buffer */
+#define OD_NUMBUFSIZ   32                /* size of a buffer for a number */
+#define OD_MAPPBNUM    127               /* bucket size of a petit map handle */
+#define OD_DOCSNAME    "docs"            /* name of the database for documents */
+#define OD_INDEXNAME   "index"           /* name of the database for inverted index */
+#define OD_RDOCSNAME   "rdocs"           /* name of the database for reverse dictionary */
+#define OD_DOCSBNUM    2039              /* initial bucket number of document database */
+#define OD_DOCSDNUM    17                /* division number of document database */
+#define OD_DOCSALIGN   -4                /* alignment of document database */
+#define OD_DOCSFBP     32                /* size of free block pool of document database */
+#define OD_INDEXBNUM   32749             /* initial bucket number of inverted index */
+#define OD_INDEXDNUM   7                 /* division number of inverted index */
+#define OD_INDEXALIGN  -2                /* alignment of inverted index */
+#define OD_INDEXFBP    32                /* size of free block pool of inverted index */
+#define OD_RDOCSLRM    81                /* records in a leaf node of reverse dictionary */
+#define OD_RDOCSNIM    192               /* records in a non-leaf node of reverse dictionary */
+#define OD_RDOCSLCN    128               /* number of leaf cache of reverse dictionary */
+#define OD_RDOCSNCN    32                /* number of non-leaf cache of reverse dictionary */
+#define OD_CACHEBNUM   262139            /* number of buckets for dirty buffers */
+#define OD_CACHESIZ    8388608           /* max bytes to use memory for dirty buffers */
+#define OD_CFLIVERAT   0.8               /* ratio of usable cache region */
+#define OD_CFBEGSIZ    2048              /* beginning size of flushing frequent words */
+#define OD_CFENDSIZ    64                /* lower limit of flushing frequent words */
+#define OD_CFRFRAT     0.2               /* ratio of flushing rare words a time */
+#define OD_OTCBBUFSIZ  1024              /* size of a buffer for call back functions */
+#define OD_OTPERWORDS  10000             /* frequency of call back in merging index */
+#define OD_OTPERDOCS   1000              /* frequency of call back in merging docs */
+#define OD_MDBRATIO    2.5               /* ratio of bucket number and document number */
+#define OD_MIBRATIO    1.5               /* ratio of bucket number and word number */
+#define OD_MIARATIO    0.75              /* ratio of alignment to the first words */
+#define OD_MIWUNIT     32                /* writing unit of merging inverted index */
+#define OD_DMAXEXPR    "dmax"            /* key of max number of the document ID */
+#define OD_DNUMEXPR    "dnum"            /* key of number of the documents */
+#define OD_URIEXPR     "1"               /* map key of URI */
+#define OD_ATTRSEXPR   "2"               /* map key of attributes */
+#define OD_NWORDSEXPR  "3"               /* map key of normal words */
+#define OD_AWORDSEXPR  "4"               /* map key of as-is words */
+#define OD_WTOPRATE    0.1               /* ratio of top words */
+#define OD_WTOPBONUS   5000              /* bonus points of top words */
+#define OD_KEYCRATIO   1.75              /* ratio of number to max of keyword candidates */
+#define OD_WOCCRPOINT  10000             /* points per occurence */
+#define OD_SPACECHARS  "\t\n\v\f\r "     /* space characters */
+#define OD_DELIMCHARS  "!\"#$%&'()*/<=>?[\\]^`{|}~"  /* delimiter characters */
+#define OD_GLUECHARS   "+,-.:;@"         /* glueing characters */
+#define OD_MAXWORDLEN  48                /* max length of a word */
+
+typedef struct {                         /* type of structure for word counting */
+  const char *word;                      /* pointer to the word */
+  int num;                               /* frequency of the word */
+} ODWORD;
+
+enum {                                   /* enumeration for events binded to each character */
+  OD_EVWORD,                             /* word */
+  OD_EVSPACE,                            /* space */
+  OD_EVDELIM,                            /* delimiter */
+  OD_EVGLUE                              /* glue */
+};
+
+
+/* private global variables */
+int odindexbnum = OD_INDEXBNUM;
+int odindexdnum = OD_INDEXDNUM;
+int odcachebnum = OD_CACHEBNUM;
+int odcachesiz = OD_CACHESIZ;
+void (*odotcb)(const char *, ODEUM *, const char *) = NULL;
+
+
+/* private function prototypes */
+static ODEUM *odopendb(const char *name, int omode, int docsbnum, int indexbnum,
+                       const char *fname);
+static int odcacheflush(ODEUM *odeum, const char *fname);
+static int odcacheflushfreq(ODEUM *odeum, const char *fname, int min);
+static int odcacheflushrare(ODEUM *odeum, const char *fname, double ratio);
+static int odsortindex(ODEUM *odeum, const char *fname);
+static int odsortcompare(const void *a, const void *b);
+static int odpurgeindex(ODEUM *odeum, const char *fname);
+static CBMAP *odpairsmap(const ODPAIR *pairs, int num);
+static int odwordcompare(const void *a, const void *b);
+static int odmatchoperator(ODEUM *odeum, CBLIST *tokens);
+static ODPAIR *odparsesubexpr(ODEUM *odeum, CBLIST *tokens, CBLIST *nwords, int *np,
+                              CBLIST *errors);
+static ODPAIR *odparseexpr(ODEUM *odeum, CBLIST *tokens, CBLIST *nwords, int *np,
+                           CBLIST *errors);
+static void odfixtokens(ODEUM *odeum, CBLIST *tokens);
+static void odcleannormalized(ODEUM *odeum, CBLIST *nwords);
+
+
+
+/*************************************************************************************************
+ * public objects
+ *************************************************************************************************/
+
+
+/* Get a database handle. */
+ODEUM *odopen(const char *name, int omode){
+  assert(name);
+  return odopendb(name, omode, OD_DOCSBNUM, odindexbnum, "odopen");
+}
+
+
+/* Close a database handle. */
+int odclose(ODEUM *odeum){
+  char numbuf[OD_NUMBUFSIZ];
+  int err;
+  assert(odeum);
+  err = FALSE;
+  if(odotcb) odotcb("odclose", odeum, "closing the connection");
+  if(odeum->wmode){
+    if(odotcb) odotcb("odclose", odeum, "writing meta information");
+    sprintf(numbuf, "%d", odeum->dmax);
+    if(!vlput(odeum->rdocsdb, OD_DMAXEXPR, sizeof(OD_DMAXEXPR), numbuf, -1, VL_DOVER)) err = TRUE;
+    sprintf(numbuf, "%d", odeum->dnum);
+    if(!vlput(odeum->rdocsdb, OD_DNUMEXPR, sizeof(OD_DNUMEXPR), numbuf, -1, VL_DOVER)) err = TRUE;
+    if(!odcacheflushfreq(odeum, "odclose", OD_CFENDSIZ)) err = TRUE;
+    if(!odcacheflushrare(odeum, "odclose", OD_CFRFRAT)) err = TRUE;
+    if(!odcacheflush(odeum, "odclose")) err = TRUE;
+    if(!odsortindex(odeum, "odclose")) err = TRUE;
+    cbmapclose(odeum->cachemap);
+    cbmapclose(odeum->sortmap);
+  }
+  if(!vlclose(odeum->rdocsdb)) err = TRUE;
+  if(!crclose(odeum->indexdb)) err = TRUE;
+  if(!crclose(odeum->docsdb)) err = TRUE;
+  free(odeum->name);
+  free(odeum);
+  return err ? FALSE : TRUE;
+}
+
+
+/* Store a document. */
+int odput(ODEUM *odeum, ODDOC *doc, int wmax, int over){
+  char *tmp, *zbuf;
+  const char *word, *ctmp;
+  int i, docid, tsiz, wsiz, wnum, tmax, num, zsiz;
+  double ival;
+  ODPAIR pair;
+  CBMAP *map;
+  CBLIST *tlist;
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!odeum->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if((tmp = vlget(odeum->rdocsdb, doc->uri, -1, &tsiz)) != NULL){
+    if(!over){
+      free(tmp);
+      dpecodeset(DP_EKEEP, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(tsiz != sizeof(int) || !odoutbyid(odeum, *(int *)tmp)){
+      free(tmp);
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      odeum->fatal = TRUE;
+      return FALSE;
+    }
+    free(tmp);
+  }
+  odeum->dmax++;
+  odeum->dnum++;
+  docid = odeum->dmax;
+  map = cbmapopen();
+  cbmapput(map, OD_URIEXPR, sizeof(OD_URIEXPR), doc->uri, -1, TRUE);
+  tmp = cbmapdump(doc->attrs, &tsiz);
+  cbmapput(map, OD_ATTRSEXPR, sizeof(OD_ATTRSEXPR), tmp, tsiz, TRUE);
+  free(tmp);
+  if(wmax < 0 || wmax > cblistnum(doc->nwords)) wmax = cblistnum(doc->nwords);
+  tlist = cblistopen();
+  for(i = 0; i < wmax; i++){
+    ctmp = cblistval(doc->nwords, i, &wsiz);
+    cblistpush(tlist, ctmp, wsiz);
+  }
+  tmp = cblistdump(tlist, &tsiz);
+  cbmapput(map, OD_NWORDSEXPR, sizeof(OD_NWORDSEXPR), tmp, tsiz, TRUE);
+  free(tmp);
+  cblistclose(tlist);
+  tlist = cblistopen();
+  for(i = 0; i < wmax; i++){
+    ctmp = cblistval(doc->awords, i, &wsiz);
+    if(strcmp(ctmp, cblistval(doc->nwords, i, NULL))){
+      cblistpush(tlist, ctmp, wsiz);
+    } else {
+      cblistpush(tlist, "\0", 1);
+    }
+  }
+  tmp = cblistdump(tlist, &tsiz);
+  cbmapput(map, OD_AWORDSEXPR, sizeof(OD_AWORDSEXPR), tmp, tsiz, TRUE);
+  free(tmp);
+  cblistclose(tlist);
+  tmp = cbmapdump(map, &tsiz);
+  cbmapclose(map);
+  if(_qdbm_deflate){
+    if(!(zbuf = _qdbm_deflate(tmp, tsiz, &zsiz, _QDBM_ZMRAW))){
+      free(tmp);
+      dpecodeset(DP_EMISC, __FILE__, __LINE__);
+      odeum->fatal = TRUE;
+      return FALSE;
+    }
+    free(tmp);
+    tmp = zbuf;
+    tsiz = zsiz;
+  }
+  if(!crput(odeum->docsdb, (char *)&docid, sizeof(int), tmp, tsiz, CR_DKEEP)){
+    free(tmp);
+    if(dpecode == DP_EKEEP) dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  free(tmp);
+  if(!vlput(odeum->rdocsdb, doc->uri, -1, (char *)&docid, sizeof(int), VL_DOVER)){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  map = cbmapopen();
+  wnum = cblistnum(doc->nwords);
+  tmax = (int)(wnum * OD_WTOPRATE);
+  for(i = 0; i < wnum; i++){
+    word = cblistval(doc->nwords, i, &wsiz);
+    if(wsiz < 1) continue;
+    if((ctmp = cbmapget(map, word, wsiz, NULL)) != NULL){
+      num = *(int *)ctmp + OD_WOCCRPOINT;
+    } else {
+      num = i <= tmax ? OD_WTOPBONUS + OD_WOCCRPOINT : OD_WOCCRPOINT;
+    }
+    cbmapput(map, word, wsiz, (char *)&num, sizeof(int), TRUE);
+  }
+  ival = odlogarithm(wnum);
+  ival = (ival * ival * ival) / 8.0;
+  if(ival < 8.0) ival = 8.0;
+  cbmapiterinit(map);
+  while((word = cbmapiternext(map, &wsiz)) != NULL){
+    pair.id = docid;
+    pair.score = (int)(*(int *)cbmapget(map, word, wsiz, NULL) / ival);
+    cbmapputcat(odeum->cachemap, word, wsiz, (char *)&pair, sizeof(pair));
+    cbmapmove(odeum->cachemap, word, wsiz, FALSE);
+    odeum->cacheasiz += sizeof(pair);
+    cbmapput(odeum->sortmap, word, wsiz, "", 0, FALSE);
+  }
+  cbmapclose(map);
+  if(odeum->cacheasiz > odcachesiz){
+    for(i = OD_CFBEGSIZ; odeum->cacheasiz > odcachesiz * OD_CFLIVERAT && i >= OD_CFENDSIZ;
+        i /= 2){
+      if(!odcacheflushfreq(odeum, "odput", i)) return FALSE;
+    }
+    while(odeum->cacheasiz > odcachesiz * OD_CFLIVERAT){
+      if(!odcacheflushrare(odeum, "odput", OD_CFRFRAT)) return FALSE;
+    }
+  }
+  doc->id = docid;
+  odeum->ldid = docid;
+  return TRUE;
+}
+
+
+/* Delete a document by a URL. */
+int odout(ODEUM *odeum, const char *uri){
+  char *tmp;
+  int tsiz, docid;
+  assert(odeum && uri);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!odeum->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(tmp = vlget(odeum->rdocsdb, uri, -1, &tsiz))){
+    if(dpecode != DP_ENOITEM) odeum->fatal = TRUE;
+    return FALSE;
+  }
+  if(tsiz != sizeof(int)){
+    free(tmp);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  docid = *(int *)tmp;
+  free(tmp);
+  return odoutbyid(odeum, docid);
+}
+
+
+/* Delete a document specified by an ID number. */
+int odoutbyid(ODEUM *odeum, int id){
+  char *tmp, *zbuf;
+  const char *uritmp;
+  int tsiz, uritsiz, zsiz;
+  CBMAP *map;
+  assert(odeum && id > 0);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!odeum->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(tmp = crget(odeum->docsdb, (char *)&id, sizeof(int), 0, -1, &tsiz))){
+    if(dpecode != DP_ENOITEM) odeum->fatal = TRUE;
+    return FALSE;
+  }
+  if(_qdbm_inflate){
+    if(!(zbuf = _qdbm_inflate(tmp, tsiz, &zsiz, _QDBM_ZMRAW))){
+      free(tmp);
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      odeum->fatal = TRUE;
+      return FALSE;
+    }
+    free(tmp);
+    tmp = zbuf;
+    tsiz = zsiz;
+  }
+  map = cbmapload(tmp, tsiz);
+  free(tmp);
+  uritmp = cbmapget(map, OD_URIEXPR, sizeof(OD_URIEXPR), &uritsiz);
+  if(!uritmp || !vlout(odeum->rdocsdb, uritmp, uritsiz)){
+    cbmapclose(map);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  cbmapclose(map);
+  if(!crout(odeum->docsdb, (char *)&id, sizeof(int))){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  odeum->dnum--;
+  return TRUE;
+}
+
+
+/* Retrieve a document by a URI. */
+ODDOC *odget(ODEUM *odeum, const char *uri){
+  char *tmp;
+  int tsiz, docid;
+  assert(odeum && uri);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!(tmp = vlget(odeum->rdocsdb, uri, -1, &tsiz))){
+    if(dpecode != DP_ENOITEM) odeum->fatal = TRUE;
+    return NULL;
+  }
+  if(tsiz != sizeof(int)){
+    free(tmp);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    odeum->fatal = TRUE;
+    return NULL;
+  }
+  docid = *(int *)tmp;
+  free(tmp);
+  return odgetbyid(odeum, docid);
+}
+
+
+/* Retrieve a document by an ID number. */
+ODDOC *odgetbyid(ODEUM *odeum, int id){
+  char *tmp, *zbuf;
+  const char *uritmp, *attrstmp, *nwordstmp, *awordstmp, *asis, *normal;
+  int i, tsiz, uritsiz, attrstsiz, nwordstsiz, awordstsiz, zsiz, asiz, nsiz;
+  ODDOC *doc;
+  CBMAP *map;
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(id < 1){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!(tmp = crget(odeum->docsdb, (char *)&id, sizeof(int), 0, -1, &tsiz))){
+    if(dpecode != DP_ENOITEM) odeum->fatal = TRUE;
+    return NULL;
+  }
+  if(_qdbm_inflate){
+    if(!(zbuf = _qdbm_inflate(tmp, tsiz, &zsiz, _QDBM_ZMRAW))){
+      free(tmp);
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      odeum->fatal = TRUE;
+      return NULL;
+    }
+    free(tmp);
+    tmp = zbuf;
+    tsiz = zsiz;
+  }
+  map = cbmapload(tmp, tsiz);
+  free(tmp);
+  uritmp = cbmapget(map, OD_URIEXPR, sizeof(OD_URIEXPR), &uritsiz);
+  attrstmp = cbmapget(map, OD_ATTRSEXPR, sizeof(OD_ATTRSEXPR), &attrstsiz);
+  nwordstmp = cbmapget(map, OD_NWORDSEXPR, sizeof(OD_NWORDSEXPR), &nwordstsiz);
+  awordstmp = cbmapget(map, OD_AWORDSEXPR, sizeof(OD_AWORDSEXPR), &awordstsiz);
+  if(!uritmp || !attrstmp || !nwordstmp || !awordstmp){
+    cbmapclose(map);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    odeum->fatal = TRUE;
+    return NULL;
+  }
+  doc = cbmalloc(sizeof(ODDOC));
+  doc->id = id;
+  doc->uri = cbmemdup(uritmp, uritsiz);
+  doc->attrs = cbmapload(attrstmp, attrstsiz);
+  doc->nwords = cblistload(nwordstmp, nwordstsiz);
+  doc->awords = cblistload(awordstmp, awordstsiz);
+  cbmapclose(map);
+  for(i = 0; i < cblistnum(doc->awords); i++){
+    asis = cblistval(doc->awords, i, &asiz);
+    if(asiz == 1 && asis[0] == '\0'){
+      normal = cblistval(doc->nwords, i, &nsiz);
+      cblistover(doc->awords, i, normal, nsiz);
+    }
+  }
+  return doc;
+}
+
+
+/* Retrieve the ID of the document specified by a URI. */
+int odgetidbyuri(ODEUM *odeum, const char *uri){
+  char *tmp;
+  int tsiz, docid;
+  assert(odeum && uri);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  if(!(tmp = vlget(odeum->rdocsdb, uri, -1, &tsiz))){
+    if(dpecode != DP_ENOITEM) odeum->fatal = TRUE;
+    return -1;
+  }
+  if(tsiz != sizeof(int)){
+    free(tmp);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    odeum->fatal = TRUE;
+    return -1;
+  }
+  docid = *(int *)tmp;
+  free(tmp);
+  return docid;
+}
+
+
+/* Check whether the document specified by an ID number exists. */
+int odcheck(ODEUM *odeum, int id){
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(id < 1){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return crvsiz(odeum->docsdb, (char *)&id, sizeof(int)) != -1;
+}
+
+
+/* Search the inverted index for documents including a word. */
+ODPAIR *odsearch(ODEUM *odeum, const char *word, int max, int *np){
+  char *tmp;
+  int tsiz;
+  assert(odeum && word && np);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(odeum->wmode && cbmaprnum(odeum->sortmap) > 0 &&
+     (!odcacheflush(odeum, "odsearch") || !odsortindex(odeum, "odsearch"))){
+    odeum->fatal = TRUE;
+    return NULL;
+  }
+  max = max < 0 ? -1 : max * sizeof(ODPAIR);
+  if(!(tmp = crget(odeum->indexdb, word, -1, 0, max, &tsiz))){
+    if(dpecode != DP_ENOITEM){
+      odeum->fatal = TRUE;
+      return NULL;
+    }
+    *np = 0;
+    return cbmalloc(1);
+  }
+  *np = tsiz / sizeof(ODPAIR);
+  return (ODPAIR *)tmp;
+}
+
+
+/* Get the number of documents including a word. */
+int odsearchdnum(ODEUM *odeum, const char *word){
+  int rv;
+  assert(odeum && word);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  rv = crvsiz(odeum->indexdb, word, -1);
+  return rv < 0 ? -1 : rv / sizeof(ODPAIR);
+}
+
+
+/* Initialize the iterator of a database handle. */
+int oditerinit(ODEUM *odeum){
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return criterinit(odeum->docsdb);
+}
+
+
+/* Get the next key of the iterator. */
+ODDOC *oditernext(ODEUM *odeum){
+  char *tmp;
+  int tsiz, docsid;
+  ODDOC *doc;
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  doc = NULL;
+  while(TRUE){
+    if(!(tmp = criternext(odeum->docsdb, &tsiz))){
+      if(dpecode != DP_ENOITEM) odeum->fatal = TRUE;
+      return NULL;
+    }
+    if(tsiz != sizeof(int)){
+      free(tmp);
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      odeum->fatal = TRUE;
+      return NULL;
+    }
+    docsid = *(int *)tmp;
+    free(tmp);
+    if((doc = odgetbyid(odeum, docsid)) != NULL) break;
+    if(dpecode != DP_ENOITEM){
+      odeum->fatal = TRUE;
+      return NULL;
+    }
+  }
+  return doc;
+}
+
+
+/* Synchronize updating contents with the files and the devices. */
+int odsync(ODEUM *odeum){
+  char numbuf[OD_NUMBUFSIZ];
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!odeum->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(odotcb) odotcb("odsync", odeum, "writing meta information");
+  sprintf(numbuf, "%d", odeum->dmax);
+  if(!vlput(odeum->rdocsdb, OD_DMAXEXPR, sizeof(OD_DMAXEXPR), numbuf, -1, VL_DOVER)){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  sprintf(numbuf, "%d", odeum->dnum);
+  if(!vlput(odeum->rdocsdb, OD_DNUMEXPR, sizeof(OD_DNUMEXPR), numbuf, -1, VL_DOVER)){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  if(!odcacheflush(odeum, "odsync")){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  if(!odsortindex(odeum, "odsync")){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  if(odotcb) odotcb("odsync", odeum, "synchronizing the document database");
+  if(!crsync(odeum->docsdb)){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  if(odotcb) odotcb("odsync", odeum, "synchronizing the inverted index");
+  if(!crsync(odeum->indexdb)){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  if(odotcb) odotcb("odsync", odeum, "synchronizing the reverse dictionary");
+  if(!vlsync(odeum->rdocsdb)){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Optimize a database. */
+int odoptimize(ODEUM *odeum){
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!odeum->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!odcacheflush(odeum, "odoptimize")){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  if(odeum->ldid < 1 || odeum->ldid != odeum->dnum){
+    if(!odpurgeindex(odeum, "odoptimize")){
+      odeum->fatal = TRUE;
+      return FALSE;
+    }
+  }
+  if(odeum->ldid > 0){
+    if(!odsortindex(odeum, "odoptimize")){
+      odeum->fatal = TRUE;
+      return FALSE;
+    }
+  }
+  if(odotcb) odotcb("odoptimize", odeum, "optimizing the document database");
+  if(!croptimize(odeum->docsdb, -1)){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  if(odotcb) odotcb("odoptimize", odeum, "optimizing the inverted index");
+  if(!croptimize(odeum->indexdb, -1)){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  if(odotcb) odotcb("odoptimize", odeum, "optimizing the reverse dictionary");
+  if(!vloptimize(odeum->rdocsdb)){
+    odeum->fatal = TRUE;
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Get the name of a database. */
+char *odname(ODEUM *odeum){
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return NULL;
+  }
+  return cbmemdup(odeum->name, -1);
+}
+
+
+/* Get the total size of database files. */
+double odfsiz(ODEUM *odeum){
+  double fsiz, rv;
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  fsiz = 0;
+  if((rv = crfsizd(odeum->docsdb)) < 0) return -1.0;
+  fsiz += rv;
+  if((rv = crfsizd(odeum->indexdb)) < 0) return -1.0;
+  fsiz += rv;
+  if((rv = vlfsiz(odeum->rdocsdb)) == -1) return -1.0;
+  fsiz += rv;
+  return fsiz;
+}
+
+
+/* Get the total number of the elements of the bucket arrays for the inverted index. */
+int odbnum(ODEUM *odeum){
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  return crbnum(odeum->indexdb);
+}
+
+
+/* Get the total number of the used elements of the bucket arrays in the inverted index. */
+int odbusenum(ODEUM *odeum){
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  return crbusenum(odeum->indexdb);
+}
+
+
+/* Get the number of the documents stored in a database. */
+int oddnum(ODEUM *odeum){
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  return odeum->dnum;
+}
+
+
+/* Get the number of the words stored in a database. */
+int odwnum(ODEUM *odeum){
+  assert(odeum);
+  if(odeum->fatal){
+    dpecodeset(DP_EFATAL, __FILE__, __LINE__);
+    return -1;
+  }
+  return crrnum(odeum->indexdb);
+}
+
+
+/* Check whether a database handle is a writer or not. */
+int odwritable(ODEUM *odeum){
+  assert(odeum);
+  return odeum->wmode;
+}
+
+
+/* Check whether a database has a fatal error or not. */
+int odfatalerror(ODEUM *odeum){
+  assert(odeum);
+  return odeum->fatal;
+}
+
+
+/* Get the inode number of a database directory. */
+int odinode(ODEUM *odeum){
+  assert(odeum);
+  return odeum->inode;
+}
+
+
+/* Get the last modified time of a database. */
+time_t odmtime(ODEUM *odeum){
+  assert(odeum);
+  return crmtime(odeum->indexdb);
+}
+
+
+/* Merge plural database directories. */
+int odmerge(const char *name, const CBLIST *elemnames){
+  ODEUM *odeum, **elems;
+  CURIA *curia, *ecuria;
+  VILLA *villa, *evilla;
+  ODPAIR *pairs;
+  char *word, *kbuf, *vbuf, *dbuf, otmsg[OD_OTCBBUFSIZ];
+  char *wpunit[OD_MIWUNIT], *vpunit[OD_MIWUNIT];
+  int i, j, k, num, dnum, wnum, dbnum, ibnum, tnum, wsunit[OD_MIWUNIT], vsunit[OD_MIWUNIT];
+  int err, *bases, sum, max, wsiz, ksiz, vsiz, uend, unum, pnum, align, id, nid, dsiz;
+  assert(name && elemnames);
+  num = cblistnum(elemnames);
+  elems = cbmalloc(num * sizeof(ODEUM *) + 1);
+  dnum = 0;
+  wnum = 0;
+  for(i = 0; i < num; i++){
+    if(!(elems[i] = odopen(cblistval(elemnames, i, NULL), OD_OREADER))){
+      for(i -= 1; i >= 0; i--){
+        odclose(elems[i]);
+      }
+      free(elems);
+      return FALSE;
+    }
+    dnum += oddnum(elems[i]);
+    wnum += odwnum(elems[i]);
+  }
+  dbnum = (int)(dnum * OD_MDBRATIO / OD_DOCSDNUM);
+  ibnum = (int)(wnum * OD_MIBRATIO / odindexdnum);
+  if(!(odeum = odopendb(name, OD_OWRITER | OD_OCREAT | OD_OTRUNC, dbnum, ibnum, "odmerge"))){
+    for(i = 0; i < num; i++){
+      odclose(elems[i]);
+    }
+    free(elems);
+    return FALSE;
+  }
+  err = FALSE;
+  if(odotcb) odotcb("odmerge", odeum, "calculating the base ID numbers");
+  bases = cbmalloc(num * sizeof(int) + 1);
+  sum = 0;
+  for(i = 0; i < num; i++){
+    ecuria = elems[i]->docsdb;
+    max = 0;
+    if(!criterinit(ecuria) && dpecode != DP_ENOITEM) err = TRUE;
+    while((kbuf = criternext(ecuria, &ksiz)) != NULL){
+      if(ksiz == sizeof(int)){
+        if(*(int *)kbuf > max) max = *(int *)kbuf;
+      }
+      free(kbuf);
+    }
+    bases[i] = sum;
+    sum += max;
+  }
+  curia = odeum->indexdb;
+  for(i = 0; i < num; i++){
+    if(odotcb){
+      sprintf(otmsg, "merging the inverted index (%d/%d)", i + 1, num);
+      odotcb("odmerge", odeum, otmsg);
+    }
+    ecuria = elems[i]->indexdb;
+    tnum = 0;
+    uend = FALSE;
+    if(!criterinit(ecuria) && dpecode != DP_ENOITEM) err = TRUE;
+    while(!uend){
+      for(unum = 0; unum < OD_MIWUNIT; unum++){
+        if(!(word = criternext(ecuria, &wsiz))){
+          uend = TRUE;
+          break;
+        }
+        if(!(vbuf = crget(ecuria, word, wsiz, 0, -1, &vsiz))){
+          err = TRUE;
+          free(word);
+          break;
+        }
+        wpunit[unum] = word;
+        wsunit[unum] = wsiz;
+        vpunit[unum] = vbuf;
+        vsunit[unum] = vsiz;
+      }
+      for(j = 0; j < unum; j++){
+        word = wpunit[j];
+        wsiz = wsunit[j];
+        vbuf = vpunit[j];
+        vsiz = vsunit[j];
+        pairs = (ODPAIR *)vbuf;
+        pnum = vsiz / sizeof(ODPAIR);
+        for(k = 0; k < pnum; k++){
+          pairs[k].id += bases[i];
+        }
+        align = (int)(i < num - 1 ? vsiz * (num - i) * OD_MIARATIO : OD_INDEXALIGN);
+        if(!crsetalign(curia, align)) err = TRUE;
+        if(!crput(curia, word, wsiz, vbuf, vsiz, CR_DCAT)) err = TRUE;
+        free(vbuf);
+        free(word);
+        if(odotcb && (tnum + 1) % OD_OTPERWORDS == 0){
+          sprintf(otmsg, "... (%d/%d)", tnum + 1, crrnum(ecuria));
+          odotcb("odmerge", odeum, otmsg);
+        }
+        tnum++;
+      }
+    }
+  }
+  if(odotcb) odotcb("odmerge", odeum, "sorting the inverted index");
+  tnum = 0;
+  if(!criterinit(curia) && dpecode != DP_ENOITEM) err = TRUE;
+  while((word = criternext(curia, &wsiz)) != NULL){
+    if((vbuf = crget(curia, word, wsiz, 0, -1, &vsiz)) != NULL){
+      if(vsiz > sizeof(ODPAIR)){
+        pairs = (ODPAIR *)vbuf;
+        pnum = vsiz / sizeof(ODPAIR);
+        qsort(pairs, pnum, sizeof(ODPAIR), odsortcompare);
+        if(!crput(curia, word, wsiz, vbuf, vsiz, CR_DOVER)) err = TRUE;
+      }
+      free(vbuf);
+    }
+    free(word);
+    if(odotcb && (tnum + 1) % OD_OTPERWORDS == 0){
+      sprintf(otmsg, "... (%d/%d)", tnum + 1, crrnum(curia));
+      odotcb("odmerge", odeum, otmsg);
+    }
+    tnum++;
+  }
+  if(odotcb) odotcb("odmerge", odeum, "synchronizing the inverted index");
+  if(!crsync(curia)) err = TRUE;
+  dnum = 0;
+  curia = odeum->docsdb;
+  villa = odeum->rdocsdb;
+  for(i = 0; i < num; i++){
+    if(odotcb){
+      sprintf(otmsg, "merging the document database (%d/%d)", i + 1, num);
+      odotcb("odmerge", odeum, otmsg);
+    }
+    evilla = elems[i]->rdocsdb;
+    ecuria = elems[i]->docsdb;
+    tnum = 0;
+    if(!vlcurfirst(evilla) && dpecode != DP_ENOITEM) err = TRUE;
+    while(TRUE){
+      if(!(kbuf = vlcurkey(evilla, &ksiz))) break;
+      if((ksiz == sizeof(OD_DMAXEXPR) && !memcmp(kbuf, OD_DMAXEXPR, ksiz)) ||
+         (ksiz == sizeof(OD_DNUMEXPR) && !memcmp(kbuf, OD_DNUMEXPR, ksiz))){
+        free(kbuf);
+        if(!vlcurnext(evilla)) break;
+        continue;
+      }
+      if(!(vbuf = vlcurval(evilla, &vsiz))){
+        free(kbuf);
+        if(!vlcurnext(evilla)) break;
+        continue;
+      }
+      if(vsiz != sizeof(int)){
+        free(vbuf);
+        free(kbuf);
+        if(!vlcurnext(evilla)) break;
+        continue;
+      }
+      id =  *(int *)vbuf;
+      nid = id + bases[i];
+      if(vlput(villa, kbuf, ksiz, (char *)&nid, sizeof(int), VL_DKEEP)){
+        if((dbuf = crget(ecuria, (char *)&id, sizeof(int), 0, -1, &dsiz)) != NULL){
+          if(crput(curia, (char *)&nid, sizeof(int), dbuf, dsiz, CR_DKEEP)){
+            dnum++;
+          } else {
+            err = TRUE;
+          }
+          free(dbuf);
+        } else {
+          err = TRUE;
+        }
+      } else if(dpecode != DP_EKEEP){
+        err = TRUE;
+      }
+      free(vbuf);
+      free(kbuf);
+      odeum->dnum++;
+      if(odotcb && (tnum + 1) % OD_OTPERDOCS == 0){
+        sprintf(otmsg, "... (%d/%d)", tnum + 1, crrnum(ecuria));
+        odotcb("odmerge", odeum, otmsg);
+      }
+      tnum++;
+      if(!vlcurnext(evilla)) break;
+    }
+  }
+  odeum->dnum = dnum;
+  odeum->dmax = dnum;
+  free(bases);
+  if(odotcb) odotcb("odmerge", odeum, "synchronizing the document index");
+  if(!crsync(curia)) err = TRUE;
+  if(!odclose(odeum)) err = TRUE;
+  for(i = 0; i < num; i++){
+    if(!odclose(elems[i])) err = TRUE;
+  }
+  free(elems);
+  return err ? FALSE : TRUE;
+}
+
+
+/* Remove a database directory. */
+int odremove(const char *name){
+  char docsname[OD_PATHBUFSIZ], indexname[OD_PATHBUFSIZ], rdocsname[OD_PATHBUFSIZ];
+  char path[OD_PATHBUFSIZ];
+  const char *file;
+  struct stat sbuf;
+  CBLIST *list;
+  int i;
+  assert(name);
+  sprintf(docsname, "%s%c%s", name, MYPATHCHR, OD_DOCSNAME);
+  sprintf(indexname, "%s%c%s", name, MYPATHCHR, OD_INDEXNAME);
+  sprintf(rdocsname, "%s%c%s", name, MYPATHCHR, OD_RDOCSNAME);
+  if(lstat(name, &sbuf) == -1){
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(lstat(docsname, &sbuf) != -1 && !crremove(docsname)) return FALSE;
+  if(lstat(indexname, &sbuf) != -1 && !crremove(indexname)) return FALSE;
+  if(lstat(rdocsname, &sbuf) != -1 && !vlremove(rdocsname)) return FALSE;
+  if((list = cbdirlist(name)) != NULL){
+    for(i = 0; i < cblistnum(list); i++){
+      file = cblistval(list, i, NULL);
+      if(!strcmp(file, MYCDIRSTR) || !strcmp(file, MYPDIRSTR)) continue;
+      sprintf(path, "%s%c%s", name, MYPATHCHR, file);
+      if(lstat(path, &sbuf) == -1) continue;
+      if(S_ISDIR(sbuf.st_mode)){
+        if(!crremove(path)) return FALSE;
+      } else {
+        if(!dpremove(path)) return FALSE;
+      }
+    }
+    cblistclose(list);
+  }
+  if(rmdir(name) == -1){
+    dpecodeset(DP_ERMDIR, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Get a document handle. */
+ODDOC *oddocopen(const char *uri){
+  ODDOC *doc;
+  assert(uri);
+  doc = cbmalloc(sizeof(ODDOC));
+  doc->id = -1;
+  doc->uri = cbmemdup(uri, -1);
+  doc->attrs = cbmapopenex(OD_MAPPBNUM);
+  doc->nwords = cblistopen();
+  doc->awords = cblistopen();
+  return doc;
+}
+
+
+/* Close a document handle. */
+void oddocclose(ODDOC *doc){
+  assert(doc);
+  cblistclose(doc->awords);
+  cblistclose(doc->nwords);
+  cbmapclose(doc->attrs);
+  free(doc->uri);
+  free(doc);
+}
+
+
+/* Add an attribute to a document. */
+void oddocaddattr(ODDOC *doc, const char *name, const char *value){
+  assert(doc && name && value);
+  cbmapput(doc->attrs, name, -1, value, -1, TRUE);
+}
+
+
+/* Add a word to a document. */
+void oddocaddword(ODDOC *doc, const char *normal, const char *asis){
+  assert(doc && normal && asis);
+  cblistpush(doc->nwords, normal, -1);
+  cblistpush(doc->awords, asis, -1);
+}
+
+
+/* Get the ID number of a document. */
+int oddocid(const ODDOC *doc){
+  assert(doc);
+  return doc->id;
+}
+
+
+/* Get the URI of a document. */
+const char *oddocuri(const ODDOC *doc){
+  assert(doc);
+  return doc->uri;
+}
+
+
+/* Get the value of an attribute of a document. */
+const char *oddocgetattr(const ODDOC *doc, const char *name){
+  assert(doc && name);
+  return cbmapget(doc->attrs, name, -1, NULL);
+}
+
+
+/* Get the list handle contains words in normalized form of a document. */
+const CBLIST *oddocnwords(const ODDOC *doc){
+  assert(doc);
+  return doc->nwords;
+}
+
+
+/* Get the list handle contains words in appearance form of a document. */
+const CBLIST *oddocawords(const ODDOC *doc){
+  assert(doc);
+  return doc->awords;
+}
+
+
+/* Get the map handle contains keywords in normalized form and their scores. */
+CBMAP *oddocscores(const ODDOC *doc, int max, ODEUM *odeum){
+  const CBLIST *nwords;
+  CBMAP *map, *kwmap;
+  const char *word, *ctmp;
+  char numbuf[OD_NUMBUFSIZ];
+  ODWORD *owords;
+  int i, wsiz, wnum, hnum, mnum, nbsiz;
+  double ival;
+  assert(doc && max >= 0);
+  map = cbmapopen();
+  nwords = oddocnwords(doc);
+  for(i = 0; i < cblistnum(nwords); i++){
+    word = cblistval(nwords, i, &wsiz);
+    if(wsiz < 1) continue;
+    if((ctmp = cbmapget(map, word, wsiz, NULL)) != NULL){
+      wnum = *(int *)ctmp + OD_WOCCRPOINT;
+    } else {
+      wnum = OD_WOCCRPOINT;
+    }
+    cbmapput(map, word, wsiz, (char *)&wnum, sizeof(int), TRUE);
+  }
+  mnum = cbmaprnum(map);
+  owords = cbmalloc(mnum * sizeof(ODWORD) + 1);
+  cbmapiterinit(map);
+  for(i = 0; (word = cbmapiternext(map, &wsiz)) != NULL; i++){
+    owords[i].word = word;
+    owords[i].num = *(int *)cbmapget(map, word, wsiz, NULL);
+  }
+  qsort(owords, mnum, sizeof(ODWORD), odwordcompare);
+  if(odeum){
+    if(mnum > max * OD_KEYCRATIO) mnum = (int)(max * OD_KEYCRATIO);
+    for(i = 0; i < mnum; i++){
+      if((hnum = odsearchdnum(odeum, owords[i].word)) < 0) hnum = 0;
+      ival = odlogarithm(hnum);
+      ival = (ival * ival * ival) / 8.0;
+      if(ival < 8.0) ival = 8.0;
+      owords[i].num = (int)(owords[i].num / ival);
+    }
+    qsort(owords, mnum, sizeof(ODWORD), odwordcompare);
+  }
+  if(mnum > max) mnum = max;
+  kwmap = cbmapopenex(OD_MAPPBNUM);
+  for(i = 0; i < mnum; i++){
+    nbsiz = sprintf(numbuf, "%d", owords[i].num);
+    cbmapput(kwmap, owords[i].word, -1, numbuf, nbsiz, TRUE);
+  }
+  free(owords);
+  cbmapclose(map);
+  return kwmap;
+}
+
+
+/* Break a text into words in appearance form. */
+CBLIST *odbreaktext(const char *text){
+  const char *word;
+  CBLIST *elems, *words;
+  int i, j, dif, wsiz, pv, delim;
+  assert(text);
+  words = cblistopen();
+  elems = cbsplit(text, -1, OD_SPACECHARS);
+  for(i = 0; i < cblistnum(elems); i++){
+    word = cblistval(elems, i, &wsiz);
+    delim = FALSE;
+    j = 0;
+    pv = 0;
+    while(TRUE){
+      dif = j - pv;
+      if(j >= wsiz){
+        if(dif > 0 && dif <= OD_MAXWORDLEN) cblistpush(words, word + pv, j - pv);
+        break;
+      }
+      if(delim){
+        if(!strchr(OD_DELIMCHARS, word[j])){
+          if(dif > 0 && dif <= OD_MAXWORDLEN) cblistpush(words, word + pv, j - pv);
+          pv = j;
+          delim = FALSE;
+        }
+      } else {
+        if(strchr(OD_DELIMCHARS, word[j])){
+          if(dif > 0 && dif <= OD_MAXWORDLEN) cblistpush(words, word + pv, j - pv);
+          pv = j;
+          delim = TRUE;
+        }
+      }
+      j++;
+    }
+  }
+  cblistclose(elems);
+  return words;
+}
+
+
+/* Make the normalized form of a word. */
+char *odnormalizeword(const char *asis){
+  char *nword;
+  int i;
+  assert(asis);
+  for(i = 0; asis[i] != '\0'; i++){
+    if(!strchr(OD_DELIMCHARS, asis[i])) break;
+  }
+  if(asis[i] == '\0') return cbmemdup("", 0);
+  nword = cbmemdup(asis, -1);
+  for(i = 0; nword[i] != '\0'; i++){
+    if(nword[i] >= 'A' && nword[i] <= 'Z') nword[i] += 'a' - 'A';
+  }
+  while(i >= 0){
+    if(strchr(OD_GLUECHARS, nword[i])){
+      nword[i] = '\0';
+    } else {
+      break;
+    }
+    i--;
+  }
+  return nword;
+}
+
+
+/* Get the common elements of two sets of documents. */
+ODPAIR *odpairsand(ODPAIR *apairs, int anum, ODPAIR *bpairs, int bnum, int *np){
+  CBMAP *map;
+  ODPAIR *result;
+  const char *tmp;
+  int i, rnum;
+  assert(apairs && anum >= 0 && bpairs && bnum >= 0);
+  map = odpairsmap(bpairs, bnum);
+  result = cbmalloc(sizeof(ODPAIR) * anum + 1);
+  rnum = 0;
+  for(i = 0; i < anum; i++){
+    if(!(tmp = cbmapget(map, (char *)&(apairs[i].id), sizeof(int), NULL))) continue;
+    result[rnum].id = apairs[i].id;
+    result[rnum].score = apairs[i].score + *(int *)tmp;
+    rnum++;
+  }
+  cbmapclose(map);
+  qsort(result, rnum, sizeof(ODPAIR), odsortcompare);
+  *np = rnum;
+  return result;
+}
+
+
+/* Get the sum of elements of two sets of documents. */
+ODPAIR *odpairsor(ODPAIR *apairs, int anum, ODPAIR *bpairs, int bnum, int *np){
+  CBMAP *map;
+  ODPAIR *result;
+  const char *tmp;
+  int i, score, rnum;
+  assert(apairs && anum >= 0 && bpairs && bnum >= 0);
+  map = odpairsmap(bpairs, bnum);
+  for(i = 0; i < anum; i++){
+    score = 0;
+    if((tmp = cbmapget(map, (char *)&(apairs[i].id), sizeof(int), NULL)) != NULL)
+      score = *(int *)tmp;
+    score += apairs[i].score;
+    cbmapput(map, (char *)&(apairs[i].id), sizeof(int),
+             (char *)&score, sizeof(int), TRUE);
+  }
+  rnum = cbmaprnum(map);
+  result = cbmalloc(rnum * sizeof(ODPAIR) + 1);
+  cbmapiterinit(map);
+  for(i = 0; (tmp = cbmapiternext(map, NULL)) != NULL; i++){
+    result[i].id = *(int *)tmp;
+    result[i].score = *(int *)cbmapget(map, tmp, sizeof(int), NULL);
+  }
+  cbmapclose(map);
+  qsort(result, rnum, sizeof(ODPAIR), odsortcompare);
+  *np = rnum;
+  return result;
+}
+
+
+/* Get the difference set of documents. */
+ODPAIR *odpairsnotand(ODPAIR *apairs, int anum, ODPAIR *bpairs, int bnum, int *np){
+  CBMAP *map;
+  ODPAIR *result;
+  const char *tmp;
+  int i, rnum;
+  assert(apairs && anum >= 0 && bpairs && bnum >= 0);
+  map = odpairsmap(bpairs, bnum);
+  result = cbmalloc(sizeof(ODPAIR) * anum + 1);
+  rnum = 0;
+  for(i = 0; i < anum; i++){
+    if((tmp = cbmapget(map, (char *)&(apairs[i].id), sizeof(int), NULL)) != NULL) continue;
+    result[rnum].id = apairs[i].id;
+    result[rnum].score = apairs[i].score;
+    rnum++;
+  }
+  cbmapclose(map);
+  qsort(result, rnum, sizeof(ODPAIR), odsortcompare);
+  *np = rnum;
+  return result;
+}
+
+
+/* Sort a set of documents in descending order of scores. */
+void odpairssort(ODPAIR *pairs, int pnum){
+  assert(pairs && pnum >= 0);
+  qsort(pairs, pnum, sizeof(ODPAIR), odsortcompare);
+}
+
+
+/* Get the natural logarithm of a number. */
+double odlogarithm(double x){
+  int i;
+  if(x <= 1.0) return 0.0;
+  x = x * x * x * x * x * x * x * x * x * x;
+  for(i = 0; x > 1.0; i++){
+    x /= 2.718281828459;
+  }
+  return (double)i / 10.0;
+}
+
+
+/* Get the cosine of the angle of two vectors. */
+double odvectorcosine(const int *avec, const int *bvec, int vnum){
+  double rv;
+  assert(avec && bvec && vnum >= 0);
+  rv = odvecinnerproduct(avec, bvec, vnum) /
+    ((odvecabsolute(avec, vnum) * odvecabsolute(bvec, vnum)));
+  return rv > 0.0 ? rv : 0.0;
+}
+
+
+/* Set the global tuning parameters. */
+void odsettuning(int ibnum, int idnum, int cbnum, int csiz){
+  if(ibnum > 0) odindexbnum = ibnum;
+  if(idnum > 0) odindexdnum = idnum;
+  if(cbnum > 0) odcachebnum = dpprimenum(cbnum);
+  if(csiz > 0) odcachesiz = csiz;
+}
+
+
+/* Break a text into words and store appearance forms and normalized form into lists. */
+void odanalyzetext(ODEUM *odeum, const char *text, CBLIST *awords, CBLIST *nwords){
+  char aword[OD_MAXWORDLEN+1], *wp;
+  int lev, wsiz;
+  assert(odeum && text && awords);
+  lev = OD_EVSPACE;
+  wsiz = 0;
+  for(; *text != '\0'; text++){
+    switch(odeum->statechars[*(unsigned char *)text]){
+    case OD_EVWORD:
+      if(wsiz > 0 && lev == OD_EVDELIM){
+        cblistpush(awords, aword, wsiz);
+        if(nwords) cblistpush(nwords, "", 0);
+        wsiz = 0;
+      }
+      if(wsiz <= OD_MAXWORDLEN){
+        aword[wsiz++] = *text;
+      }
+      lev = OD_EVWORD;
+      break;
+    case OD_EVGLUE:
+      if(wsiz > 0 && lev == OD_EVDELIM){
+        cblistpush(awords, aword, wsiz);
+        if(nwords) cblistpush(nwords, "", 0);
+        wsiz = 0;
+      }
+      if(wsiz <= OD_MAXWORDLEN){
+        aword[wsiz++] = *text;
+      }
+      lev = OD_EVGLUE;
+      break;
+    case OD_EVDELIM:
+      if(wsiz > 0 && lev != OD_EVDELIM){
+        cblistpush(awords, aword, wsiz);
+        if(nwords){
+          wp = aword;
+          aword[wsiz] = '\0';
+          while(*wp != '\0'){
+            if(*wp >= 'A' && *wp <= 'Z') *wp += 'a' - 'A';
+            wp++;
+          }
+          wp--;
+          while(wp >= aword && odeum->statechars[*(unsigned char *)wp] == OD_EVGLUE){
+            wsiz--;
+            wp--;
+          }
+          cblistpush(nwords, aword, wsiz);
+        }
+        wsiz = 0;
+      }
+      if(wsiz <= OD_MAXWORDLEN){
+        aword[wsiz++] = *text;
+      }
+      lev = OD_EVDELIM;
+      break;
+    default:
+      if(wsiz > 0){
+        cblistpush(awords, aword, wsiz);
+        if(nwords){
+          if(lev == OD_EVDELIM){
+            cblistpush(nwords, "", 0);
+          } else {
+            wp = aword;
+            aword[wsiz] = '\0';
+            while(*wp != '\0'){
+              if(*wp >= 'A' && *wp <= 'Z') *wp += 'a' - 'A';
+              wp++;
+            }
+            wp--;
+            while(wp >= aword && odeum->statechars[*(unsigned char *)wp] == OD_EVGLUE){
+              wsiz--;
+              wp--;
+            }
+            cblistpush(nwords, aword, wsiz);
+          }
+        }
+        wsiz = 0;
+      }
+      lev = OD_EVSPACE;
+      break;
+    }
+  }
+  if(wsiz > 0){
+    cblistpush(awords, aword, wsiz);
+    if(nwords){
+      if(lev == OD_EVDELIM){
+        cblistpush(nwords, "", 0);
+      } else {
+        wp = aword;
+        aword[wsiz] = '\0';
+        while(*wp != '\0'){
+          if(*wp >= 'A' && *wp <= 'Z') *wp += 'a' - 'A';
+          wp++;
+        }
+        wp--;
+        while(wp >= aword && odeum->statechars[*(unsigned char *)wp] == OD_EVGLUE){
+          wsiz--;
+          wp--;
+        }
+        cblistpush(nwords, aword, wsiz);
+      }
+    }
+    wsiz = 0;
+  }
+}
+
+
+/* Set the classes of characters used by `odanalyzetext'. */
+void odsetcharclass(ODEUM *odeum, const char *spacechars, const char *delimchars,
+                    const char *gluechars){
+  assert(odeum && spacechars && delimchars && gluechars);
+  memset(odeum->statechars, OD_EVWORD, sizeof(odeum->statechars));
+  for(; *spacechars != '\0'; spacechars++){
+    odeum->statechars[*(unsigned char *)spacechars] = OD_EVSPACE;
+  }
+  for(; *delimchars != '\0'; delimchars++){
+    odeum->statechars[*(unsigned char *)delimchars] = OD_EVDELIM;
+  }
+  for(; *gluechars != '\0'; gluechars++){
+    odeum->statechars[*(unsigned char *)gluechars] = OD_EVGLUE;
+  }
+}
+
+
+/* Query a database using a small boolean query language. */
+ODPAIR *odquery(ODEUM *odeum, const char *query, int *np, CBLIST *errors){
+  CBLIST *tokens = cblistopen();
+  CBLIST *nwords = cblistopen();
+  ODPAIR *results = NULL;
+  assert(odeum && query && np);
+  odanalyzetext(odeum, query, tokens, nwords);
+  odcleannormalized(odeum, nwords);
+  odfixtokens(odeum, tokens);
+  results = odparseexpr(odeum, tokens, nwords, np, errors);
+  cblistclose(tokens);
+  cblistclose(nwords);
+  return results;
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Get the internal database handle for documents. */
+CURIA *odidbdocs(ODEUM *odeum){
+  assert(odeum);
+  return odeum->docsdb;
+}
+
+
+/* Get the internal database handle for the inverted index. */
+CURIA *odidbindex(ODEUM *odeum){
+  assert(odeum);
+  return odeum->indexdb;
+}
+
+
+/* Get the internal database handle for the reverse dictionary. */
+VILLA *odidbrdocs(ODEUM *odeum){
+  assert(odeum);
+  return odeum->rdocsdb;
+}
+
+
+/* Set the call back function called in merging. */
+void odsetotcb(void (*otcb)(const char *, ODEUM *, const char *)){
+  odotcb = otcb;
+}
+
+
+/* Get the positive one of square roots of a number. */
+double odsquareroot(double x){
+  double c, rv;
+  if(x <= 0.0) return 0.0;
+  c = x > 1.0 ? x : 1;
+  do {
+    rv = c;
+    c = (x / c + c) * 0.5;
+  } while(c < rv);
+  return rv;
+}
+
+
+/* Get the absolute of a vector. */
+double odvecabsolute(const int *vec, int vnum){
+  double rv;
+  int i;
+  assert(vec && vnum >= 0);
+  rv = 0;
+  for(i = 0; i < vnum; i++){
+    rv += (double)vec[i] * (double)vec[i];
+  }
+  return odsquareroot(rv);
+}
+
+
+/* Get the inner product of two vectors. */
+double odvecinnerproduct(const int *avec, const int *bvec, int vnum){
+  double rv;
+  int i;
+  assert(avec && bvec && vnum >= 0);
+  rv = 0;
+  for(i = 0; i < vnum; i++){
+    rv += (double)avec[i] * (double)bvec[i];
+  }
+  return rv;
+}
+
+
+
+/*************************************************************************************************
+ * private objects
+ *************************************************************************************************/
+
+
+/* Get a database handle.
+   `name' specifies the name of a database directory.
+   `omode' specifies the connection mode.
+   `docsbnum` specifies the number of buckets of the document database.
+   `indexbnum` specifies the number of buckets of the index database.
+   `fname' specifies the name of caller function.
+   The return value is the database handle or `NULL' if it is not successful. */
+static ODEUM *odopendb(const char *name, int omode, int docsbnum, int indexbnum,
+                       const char *fname){
+  int cromode, vlomode, inode, dmax, dnum;
+  char docsname[OD_PATHBUFSIZ], indexname[OD_PATHBUFSIZ], rdocsname[OD_PATHBUFSIZ], *tmp;
+  struct stat sbuf;
+  CURIA *docsdb, *indexdb;
+  VILLA *rdocsdb;
+  CBMAP *cachemap;
+  CBMAP *sortmap;
+  ODEUM *odeum;
+  assert(name);
+  if(strlen(name) > OD_NAMEMAX){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return NULL;
+  }
+  cromode = CR_OREADER;
+  vlomode = VL_OREADER;
+  if(omode & OD_OWRITER){
+    cromode = CR_OWRITER;
+    vlomode = VL_OWRITER | VL_OZCOMP | VL_OYCOMP;
+    if(omode & OD_OCREAT){
+      cromode |= CR_OCREAT;
+      vlomode |= VL_OCREAT;
+    }
+    if(omode & OD_OTRUNC){
+      cromode |= CR_OTRUNC;
+      vlomode |= VL_OTRUNC;
+    }
+  }
+  if(omode & OD_ONOLCK){
+    cromode |= CR_ONOLCK;
+    vlomode |= VL_ONOLCK;
+  }
+  if(omode & OD_OLCKNB){
+    cromode |= CR_OLCKNB;
+    vlomode |= VL_OLCKNB;
+  }
+  sprintf(docsname, "%s%c%s", name, MYPATHCHR, OD_DOCSNAME);
+  sprintf(indexname, "%s%c%s", name, MYPATHCHR, OD_INDEXNAME);
+  sprintf(rdocsname, "%s%c%s", name, MYPATHCHR, OD_RDOCSNAME);
+  docsdb = NULL;
+  indexdb = NULL;
+  rdocsdb = NULL;
+  if((omode & OD_OWRITER) && (omode & OD_OCREAT)){
+    if(mkdir(name, OD_DIRMODE) == -1 && errno != EEXIST){
+      dpecodeset(DP_EMKDIR, __FILE__, __LINE__);
+      return NULL;
+    }
+  }
+  if(lstat(name, &sbuf) == -1){
+    dpecodeset(DP_ESTAT, __FILE__, __LINE__);
+    return NULL;
+  }
+  inode = sbuf.st_ino;
+  if(!(docsdb = cropen(docsname, cromode, docsbnum, OD_DOCSDNUM))) return NULL;
+  if(!(indexdb = cropen(indexname, cromode, indexbnum, odindexdnum))){
+    crclose(docsdb);
+    return NULL;
+  }
+  if(omode & OD_OWRITER){
+    if(!crsetalign(docsdb, OD_DOCSALIGN) || !crsetfbpsiz(docsdb, OD_DOCSFBP) ||
+       !crsetalign(indexdb, OD_INDEXALIGN) || !crsetfbpsiz(indexdb, OD_INDEXFBP)){
+      crclose(indexdb);
+      crclose(docsdb);
+      return NULL;
+    }
+  }
+  if(!(rdocsdb = vlopen(rdocsname, vlomode, VL_CMPLEX))){
+    crclose(indexdb);
+    crclose(docsdb);
+    return NULL;
+  }
+  vlsettuning(rdocsdb, OD_RDOCSLRM, OD_RDOCSNIM, OD_RDOCSLCN, OD_RDOCSNCN);
+  if(omode & OD_OWRITER){
+    cachemap = cbmapopenex(odcachebnum);
+    sortmap = cbmapopenex(odcachebnum);
+  } else {
+    cachemap = NULL;
+    sortmap = NULL;
+  }
+  if(vlrnum(rdocsdb) > 0){
+    dmax = -1;
+    dnum = -1;
+    if((tmp = vlget(rdocsdb, OD_DMAXEXPR, sizeof(OD_DMAXEXPR), NULL)) != NULL){
+      dmax = atoi(tmp);
+      free(tmp);
+    }
+    if((tmp = vlget(rdocsdb, OD_DNUMEXPR, sizeof(OD_DNUMEXPR), NULL)) != NULL){
+      dnum = atoi(tmp);
+      free(tmp);
+    }
+    if(dmax < 0 || dnum < 0){
+      if(sortmap) cbmapclose(sortmap);
+      if(cachemap) cbmapclose(cachemap);
+      vlclose(rdocsdb);
+      crclose(indexdb);
+      crclose(docsdb);
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      return NULL;
+    }
+  } else {
+    dmax = 0;
+    dnum = 0;
+  }
+  odeum = cbmalloc(sizeof(ODEUM));
+  odeum->name = cbmemdup(name, -1);
+  odeum->wmode = omode & OD_OWRITER;
+  odeum->fatal = FALSE;
+  odeum->inode = inode;
+  odeum->docsdb = docsdb;
+  odeum->indexdb = indexdb;
+  odeum->rdocsdb = rdocsdb;
+  odeum->cachemap = cachemap;
+  odeum->cacheasiz = 0;
+  odeum->sortmap = sortmap;
+  odeum->dmax = dmax;
+  odeum->dnum = dnum;
+  odeum->ldid = -1;
+  odsetcharclass(odeum, OD_SPACECHARS, OD_DELIMCHARS, OD_GLUECHARS);
+  if(odotcb) odotcb(fname, odeum, "the connection was established");
+  return odeum;
+}
+
+
+/* Flush the cache for dirty buffer of words.
+   `odeum' specifies a database handle.
+   `fname' specifies the name of caller function.
+   If successful, the return value is true, else, it is false. */
+static int odcacheflush(ODEUM *odeum, const char *fname){
+  const char *kbuf, *vbuf;
+  char otmsg[OD_OTCBBUFSIZ];
+  int i, rnum, ksiz, vsiz;
+  assert(odeum);
+  if((rnum = cbmaprnum(odeum->cachemap)) < 1) return TRUE;
+  if(odotcb) odotcb(fname, odeum, "flushing caches");
+  cbmapiterinit(odeum->cachemap);
+  for(i = 0; (kbuf = cbmapiternext(odeum->cachemap, &ksiz)) != NULL; i++){
+    vbuf = cbmapget(odeum->cachemap, kbuf, ksiz, &vsiz);
+    if(!crput(odeum->indexdb, kbuf, ksiz, vbuf, vsiz, CR_DCAT)){
+      odeum->fatal = TRUE;
+      return FALSE;
+    }
+    if(odotcb && (i + 1) % OD_OTPERWORDS == 0){
+      sprintf(otmsg, "... (%d/%d)", i + 1, rnum);
+      odotcb(fname, odeum, otmsg);
+    }
+  }
+  cbmapclose(odeum->cachemap);
+  odeum->cachemap = cbmapopenex(odcachebnum);
+  odeum->cacheasiz = 0;
+  return TRUE;
+}
+
+
+/* Flush all frequent words in the cache for dirty buffer of words.
+   `odeum' specifies a database handle.
+   `fname' specifies the name of caller function.
+   `min' specifies the minimum size of frequent words.
+   If successful, the return value is true, else, it is false. */
+static int odcacheflushfreq(ODEUM *odeum, const char *fname, int min){
+  const char *kbuf, *vbuf;
+  char otmsg[OD_OTCBBUFSIZ];
+  int rnum, ksiz, vsiz;
+  assert(odeum);
+  if((rnum = cbmaprnum(odeum->cachemap)) < 1) return TRUE;
+  if(odotcb){
+    sprintf(otmsg, "flushing frequent words: min=%d asiz=%d rnum=%d)",
+            min, odeum->cacheasiz, rnum);
+    odotcb(fname, odeum, otmsg);
+  }
+  cbmapiterinit(odeum->cachemap);
+  while((kbuf = cbmapiternext(odeum->cachemap, &ksiz)) != NULL){
+    vbuf = cbmapget(odeum->cachemap, kbuf, ksiz, &vsiz);
+    if(vsiz >= sizeof(ODPAIR) * min){
+      if(!crput(odeum->indexdb, kbuf, ksiz, vbuf, vsiz, CR_DCAT)){
+        odeum->fatal = TRUE;
+        return FALSE;
+      }
+      cbmapout(odeum->cachemap, kbuf, ksiz);
+      odeum->cacheasiz -= vsiz;
+    }
+  }
+  if(odotcb){
+    sprintf(otmsg, "... (done): min=%d asiz=%d rnum=%d)",
+            min, odeum->cacheasiz, cbmaprnum(odeum->cachemap));
+    odotcb(fname, odeum, otmsg);
+  }
+  return TRUE;
+}
+
+
+/* Flush the half of rare words in the cache for dirty buffer of words.
+   `odeum' specifies a database handle.
+   `fname' specifies the name of caller function.
+   `ratio' specifies the ratio of rare words.
+   If successful, the return value is true, else, it is false. */
+static int odcacheflushrare(ODEUM *odeum, const char *fname, double ratio){
+  const char *kbuf, *vbuf;
+  char otmsg[OD_OTCBBUFSIZ];
+  int i, rnum, limit, ksiz, vsiz;
+  assert(odeum);
+  if((rnum = cbmaprnum(odeum->cachemap)) < 1) return TRUE;
+  if(odotcb){
+    sprintf(otmsg, "flushing rare words: ratio=%.2f asiz=%d rnum=%d)",
+            ratio, odeum->cacheasiz, rnum);
+    odotcb(fname, odeum, otmsg);
+  }
+  cbmapiterinit(odeum->cachemap);
+  limit = (int)(rnum * ratio);
+  for(i = 0; i < limit && (kbuf = cbmapiternext(odeum->cachemap, &ksiz)) != NULL; i++){
+    vbuf = cbmapget(odeum->cachemap, kbuf, ksiz, &vsiz);
+    if(!crput(odeum->indexdb, kbuf, ksiz, vbuf, vsiz, CR_DCAT)){
+      odeum->fatal = TRUE;
+      return FALSE;
+    }
+    cbmapout(odeum->cachemap, kbuf, ksiz);
+    odeum->cacheasiz -= vsiz;
+  }
+  if(odotcb){
+    sprintf(otmsg, "... (done): ratio=%.2f asiz=%d rnum=%d)",
+            ratio, odeum->cacheasiz, cbmaprnum(odeum->cachemap));
+    odotcb(fname, odeum, otmsg);
+  }
+  return TRUE;
+}
+
+
+/* Sort the records of inverted index.
+   `odeum' specifies a database handle.
+   `fname' specifies the name of caller function.
+   If successful, the return value is true, else, it is false. */
+static int odsortindex(ODEUM *odeum, const char *fname){
+  const char *word;
+  char *tmp, otmsg[OD_OTCBBUFSIZ];
+  int i, rnum, wsiz, tsiz;
+  ODPAIR *pairs;
+  assert(odeum);
+  if((rnum = cbmaprnum(odeum->sortmap)) < 1) return TRUE;
+  if(odotcb) odotcb(fname, odeum, "sorting the inverted index");
+  cbmapiterinit(odeum->sortmap);
+  for(i = 0; (word = cbmapiternext(odeum->sortmap, &wsiz)) != NULL; i++){
+    if((tmp = crget(odeum->indexdb, word, wsiz, 0, -1, &tsiz)) != NULL){
+      if(tsiz > sizeof(ODPAIR)){
+        pairs = (ODPAIR *)tmp;
+        qsort(pairs, tsiz / sizeof(ODPAIR), sizeof(ODPAIR), odsortcompare);
+        if(!crput(odeum->indexdb, word, wsiz, tmp, tsiz, CR_DOVER)){
+          free(tmp);
+          return FALSE;
+        }
+      }
+      free(tmp);
+    } else if(dpecode != DP_ENOITEM){
+      return FALSE;
+    }
+    if(odotcb && (i + 1) % OD_OTPERWORDS == 0){
+      sprintf(otmsg, "... (%d/%d)", i + 1, rnum);
+      odotcb(fname, odeum, otmsg);
+    }
+  }
+  cbmapclose(odeum->sortmap);
+  odeum->sortmap = cbmapopenex(odcachebnum);
+  return TRUE;
+}
+
+
+/* Compare two pairs of structures of a search result.
+   `a' specifies the pointer to the region of one pair.
+   `b' specifies the pointer to the region of the other pair.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+static int odsortcompare(const void *a, const void *b){
+  ODPAIR *ap, *bp;
+  int rv;
+  assert(a && b);
+  ap = (ODPAIR *)a;
+  bp = (ODPAIR *)b;
+  rv = bp->score - ap->score;
+  if(rv != 0) return rv;
+  return ap->id - bp->id;
+}
+
+
+/* Purge the elements of the deleted documents from the inverted index.
+   `odeum' specifies a database handle.
+   `fname' specifies the name of caller function.
+   If successful, the return value is true, else, it is false. */
+static int odpurgeindex(ODEUM *odeum, const char *fname){
+  ODPAIR *pairs;
+  char *kbuf, *vbuf, otmsg[OD_OTCBBUFSIZ];
+  int i, rnum, tnum, ksiz, vsiz, pnum, wi;
+  assert(odeum);
+  if((rnum = crrnum(odeum->indexdb)) < 1) return TRUE;
+  if(odotcb) odotcb(fname, odeum, "purging dispensable regions");
+  if(!criterinit(odeum->indexdb)) return FALSE;
+  tnum = 0;
+  while(TRUE){
+    if(!(kbuf = criternext(odeum->indexdb, &ksiz))){
+      if(dpecode != DP_ENOITEM) return FALSE;
+      break;
+    }
+    if(!(vbuf = crget(odeum->indexdb, kbuf, ksiz, 0, -1, &vsiz))){
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      free(kbuf);
+      return FALSE;
+    }
+    pairs = (ODPAIR *)vbuf;
+    pnum = vsiz / sizeof(ODPAIR);
+    wi = 0;
+    for(i = 0; i < pnum; i++){
+      if(crvsiz(odeum->docsdb, (char *)&(pairs[i].id), sizeof(int)) != -1){
+        pairs[wi++] = pairs[i];
+      }
+    }
+    if(wi > 0){
+      if(!crput(odeum->indexdb, kbuf, ksiz, vbuf, wi * sizeof(ODPAIR), CR_DOVER)){
+        free(vbuf);
+        free(kbuf);
+        return FALSE;
+      }
+    } else {
+      if(!crout(odeum->indexdb, kbuf, ksiz)){
+        free(vbuf);
+        free(kbuf);
+        return FALSE;
+      }
+    }
+    free(vbuf);
+    free(kbuf);
+    if(odotcb && (tnum + 1) % OD_OTPERWORDS == 0){
+      sprintf(otmsg, "... (%d/%d)", tnum + 1, rnum);
+      odotcb(fname, odeum, otmsg);
+    }
+    tnum++;
+  }
+  return TRUE;
+}
+
+
+/* Create a map of a document array.
+   `pairs' specifies the pointer to a document array.
+   `num' specifies the number of elements of the array.
+   The return value is a map of the document array. */
+static CBMAP *odpairsmap(const ODPAIR *pairs, int num){
+  CBMAP *map;
+  int i;
+  assert(pairs && num >= 0);
+  map = cbmapopen();
+  for(i = 0; i < num; i++){
+    cbmapput(map, (char *)&(pairs[i].id), sizeof(int),
+             (char *)&(pairs[i].score), sizeof(int), TRUE);
+  }
+  return map;
+}
+
+
+/* compare two pairs of structures of words in a document.
+   `a' specifies the pointer to the region of one word.
+   `b' specifies the pointer to the region of the other word.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+static int odwordcompare(const void *a, const void *b){
+  ODWORD *ap, *bp;
+  int rv;
+  assert(a && b);
+  ap = (ODWORD *)a;
+  bp = (ODWORD *)b;
+  if((rv = bp->num - ap->num) != 0) return rv;
+  if((rv = strlen(bp->word) - strlen(ap->word)) != 0) return rv;
+  return strcmp(ap->word, bp->word);
+}
+
+
+/* Match an operator without taking it off the token list.
+   `odeum' specifies a database handle.
+   `tokens' specifies a list handle of tokens.
+   The return value is whether the next token is an operator. */
+static int odmatchoperator(ODEUM *odeum, CBLIST *tokens){
+  const char *tk = NULL;
+  int tk_len = 0;
+  tk = cblistval(tokens, 0, &tk_len);
+  if(tk && (tk[0] == '&' || tk[0] == '|' || tk[0] == '!')) return 1;
+  return 0;
+}
+
+
+/* Implements the subexpr part of the grammar.
+   `odeum' specifies a database handle.
+   `tokens' specifies a list handle of tokens.
+   `nwords' specifies a list handle of normalized words.
+   `np' specifies the pointer to a variable to which the number of the elements of the return
+   value is assigned.
+   `errors' specifies a list handle into which error messages are stored.
+   The return value is the pointer to an array of document IDs. */
+static ODPAIR *odparsesubexpr(ODEUM *odeum, CBLIST *tokens, CBLIST *nwords, int *np,
+                              CBLIST *errors){
+  char *tk = NULL;
+  int tk_len = 0;
+  char *nword = NULL;  /* used to do the actual search, should match with tokens */
+  ODPAIR *result = NULL;
+  int result_num = 0;
+  int i;
+  double ival;
+  if((tk = cblistshift(tokens, &tk_len)) != NULL){
+    assert(tk != NULL);
+    if(tk[0] == '('){
+      free(tk);
+      /* recurse into expr */
+      result = odparseexpr(odeum, tokens, nwords, &result_num, errors);
+      /* match right token RPAREN */
+      tk = cblistshift(tokens, &tk_len);
+      /* print an error if either we didn't get anything or we didn't get a ) */
+      if(tk == NULL){
+        if(errors) cblistpush(errors, "Expression ended without closing ')'", -1);
+      } else if(tk[0] != ')'){
+        if(errors) cblistpush(errors, "Un-balanced parenthesis.", -1);
+      }
+    } else if(odeum->statechars[*(unsigned char *)tk] == 0){
+      /* Perform odsearch with the next norm word that isn't an operator. */
+      nword = cblistshift(nwords, NULL);
+      assert(nword != NULL);
+      if((result = odsearch(odeum, nword, -1, &result_num)) != NULL){
+        /* TF-IDF tuning */
+        ival = odlogarithm(result_num);
+        ival = (ival * ival) / 4.0;
+        if(ival < 4.0) ival = 4.0;
+        for(i = 0; i < result_num; i++){
+          result[i].score = (int)(result[i].score / ival);
+        }
+      }
+      free(nword);
+    } else {
+      if(errors) cblistpush(errors, "Invalid sub-expression.  Expected '(' or WORD.", -1);
+      result = cbmalloc(1);
+      result_num = 0;
+    }
+    /* done with the token */
+    free(tk);
+  }
+  *np = result_num;
+  return result;
+}
+
+
+/* Implements the actual recursive decent parser for the mini query language.
+   `odeum' specifies a database handle.
+   `tokens' specifies a list handle of tokens.
+   `nwords' specifies a list handle of normalized words.
+   `np' specifies the pointer to a variable to which the number of the elements of the return
+   value is assigned.
+   `errors' specifies a list handle into which error messages are stored.
+   The return value is the pointer to an array of document IDs.
+   It simply parses an initial subexpr, and then loops over as many (operator subexpr)
+   sequences as it can find.  The odmatchoperator function handles injecting a default &
+   between consecutive words. */
+static ODPAIR *odparseexpr(ODEUM *odeum, CBLIST *tokens, CBLIST *nwords, int *np,
+                           CBLIST *errors){
+  ODPAIR *left = NULL;
+  ODPAIR *right = NULL;
+  ODPAIR *temp = NULL;
+  int left_num = 0;
+  int right_num = 0;
+  int temp_num = 0;
+  char *op = NULL;
+  int op_len = 0;
+  if(!(left = odparsesubexpr(odeum, tokens, nwords, &left_num, errors))) return NULL;
+  /* expr ::= subexpr ( op subexpr )* */
+  while(odmatchoperator(odeum, tokens)){
+    op = cblistshift(tokens, &op_len);
+    if(!(right = odparsesubexpr(odeum, tokens, nwords, &right_num, errors))){
+      free(op);
+      free(left);
+      return NULL;
+    }
+    switch(op[0]){
+    case '&':
+      temp = odpairsand(left, left_num, right, right_num, &temp_num);
+      break;
+    case '|':
+      temp = odpairsor(left, left_num, right, right_num, &temp_num);
+      break;
+    case '!':
+      temp = odpairsnotand(left, left_num, right, right_num, &temp_num);
+      break;
+    default:
+      if(errors) cblistpush(errors, "Invalid operator.  Expected '&', '|', or '!'.", -1);
+      break;
+    }
+    if(temp){
+      /* an operator was done so we must swap it with the left */
+      free(left); left = NULL;
+      left = temp;
+      left_num = temp_num;
+    }
+    free(op);
+    if(right) free(right);
+  }
+  *np = left_num;
+  return left;
+}
+
+
+/* Processes the tokens in order to break them up further.
+   `odeum' specifies a database handle.
+   `tokens' specifies a list handle of tokens. */
+static void odfixtokens(ODEUM *odeum, CBLIST *tokens){
+  const char *tk = NULL;
+  int tk_len = 0;
+  int i = 0;
+  int lastword = 0;
+  for(i = 0; i < cblistnum(tokens); i++){
+    tk = cblistval(tokens, i, &tk_len);
+    assert(tk);
+    if(tk[0] == '&' || tk[0] == '|' || tk[0] == '!' || tk[0] == '(' || tk[0] == ')'){
+      lastword = 0;
+      if(tk_len > 1){
+        /* need to break it up for the next loop around */
+        tk = cblistremove(tokens, i, &tk_len);
+        cblistinsert(tokens, i, tk, 1);
+        cblistinsert(tokens, i+1, tk+1, tk_len-1);
+        free((char *)tk);
+      }
+    } else if(odeum->statechars[*(unsigned char *)tk] == 0){
+      /* if the last one was a word and this is a word then we need a default & between them */
+      if(lastword){
+        cblistinsert(tokens, i, "&", 1);
+        i++;
+      }
+      lastword = 1;
+    }
+  }
+}
+
+
+/* Cleans out the parts of the normalized word list that are not considered words.
+   `odeum' specifies a database handle.
+   `tokens' specifies a list handle of tokens. */
+static void odcleannormalized(ODEUM *odeum, CBLIST *nwords){
+  char *tk = NULL;
+  int tk_len = 0;
+  int i = 0;
+  for(i = 0; i < cblistnum(nwords); i++){
+    tk = (char *)cblistval(nwords, i, &tk_len);
+    if(tk_len == 0 || (!odeum->statechars[*(unsigned char *)tk] == 0)){
+      /* not a word so delete it */
+      tk = cblistremove(nwords, i, &tk_len);
+      free(tk);
+      i--;
+    }
+  }
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/odeum.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/odeum.h
===================================================================
--- box/trunk/qdbm/odeum.h	                        (rev 0)
+++ box/trunk/qdbm/odeum.h	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,590 @@
+/*************************************************************************************************
+ * The inverted API of QDBM
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _ODEUM_H                         /* duplication check */
+#define _ODEUM_H
+
+#if defined(__cplusplus)                 /* export for C++ */
+extern "C" {
+#endif
+
+
+#include <depot.h>
+#include <curia.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+#include <time.h>
+
+
+#if defined(_MSC_VER) && !defined(QDBM_INTERNAL) && !defined(QDBM_STATIC)
+#define MYEXTERN extern __declspec(dllimport)
+#else
+#define MYEXTERN extern
+#endif
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of structure for a database handle */
+  char *name;                            /* name of the database directory */
+  int wmode;                             /* whether to be writable */
+  int fatal;                             /* whether a fatal error occured */
+  int inode;                             /* inode of the database directory */
+  CURIA *docsdb;                         /* database handle for documents */
+  CURIA *indexdb;                        /* database handle for the inverted index */
+  VILLA *rdocsdb;                        /* database handle for the reverse dictionary */
+  CBMAP *cachemap;                       /* cache for dirty buffers of words */
+  int cacheasiz;                         /* total allocated size of dirty buffers */
+  CBMAP *sortmap;                        /* map handle for candidates of sorting */
+  int dmax;                              /* max number of the document ID */
+  int dnum;                              /* number of the documents */
+  int ldid;                              /* ID number of the last registered document */
+  char statechars[256];                  /* state of single byte characters */
+} ODEUM;
+
+typedef struct {                         /* type of structure for a document handle */
+  int id;                                /* ID number */
+  char *uri;                             /* uniform resource identifier */
+  CBMAP *attrs;                          /* map handle for attrubutes */
+  CBLIST *nwords;                        /* list handle for words in normalized form */
+  CBLIST *awords;                        /* list handle for words in appearance form */
+} ODDOC;
+
+typedef struct {                         /* type of structure for an element of search result */
+  int id;                                /* ID number of the document */
+  int score;                             /* score of the document */
+} ODPAIR;
+
+enum {                                   /* enumeration for open modes */
+  OD_OREADER = 1 << 0,                   /* open as a reader */
+  OD_OWRITER = 1 << 1,                   /* open as a writer */
+  OD_OCREAT = 1 << 2,                    /* a writer creating */
+  OD_OTRUNC = 1 << 3,                    /* a writer truncating */
+  OD_ONOLCK = 1 << 4,                    /* open without locking */
+  OD_OLCKNB = 1 << 5                     /* lock without blocking */
+};
+
+
+/* Get a database handle.
+   `name' specifies the name of a database directory.
+   `omode' specifies the connection mode: `OD_OWRITER' as a writer, `OD_OREADER' as a reader.
+   If the mode is `OD_OWRITER', the following may be added by bitwise or: `OD_OCREAT', which
+   means it creates a new database if not exist, `OD_OTRUNC', which means it creates a new
+   database regardless if one exists.  Both of `OD_OREADER' and `OD_OWRITER' can be added to by
+   bitwise or: `OD_ONOLCK', which means it opens a database directory without file locking, or
+   `OD_OLCKNB', which means locking is performed without blocking.
+   The return value is the database handle or `NULL' if it is not successful.
+   While connecting as a writer, an exclusive lock is invoked to the database directory.
+   While connecting as a reader, a shared lock is invoked to the database directory.
+   The thread blocks until the lock is achieved.  If `OD_ONOLCK' is used, the application is
+   responsible for exclusion control. */
+ODEUM *odopen(const char *name, int omode);
+
+
+/* Close a database handle.
+   `odeum' specifies a database handle.
+   If successful, the return value is true, else, it is false.
+   Because the region of a closed handle is released, it becomes impossible to use the handle.
+   Updating a database is assured to be written when the handle is closed.  If a writer opens
+   a database but does not close it appropriately, the database will be broken. */
+int odclose(ODEUM *odeum);
+
+
+/* Store a document.
+   `odeum' specifies a database handle connected as a writer.
+   `doc' specifies a document handle.
+   `wmax' specifies the max number of words to be stored in the document database.  If it is
+   negative, the number is unlimited.
+   `over' specifies whether the data of the duplicated document is overwritten or not.  If it
+   is false and the URI of the document is duplicated, the function returns as an error.
+   If successful, the return value is true, else, it is false. */
+int odput(ODEUM *odeum, ODDOC *doc, int wmax, int over);
+
+
+/* Delete a document specified by a URI.
+   `odeum' specifies a database handle connected as a writer.
+   `uri' specifies the string of the URI of a document.
+   If successful, the return value is true, else, it is false.  False is returned when no
+   document corresponds to the specified URI. */
+int odout(ODEUM *odeum, const char *uri);
+
+
+/* Delete a document specified by an ID number.
+   `odeum' specifies a database handle connected as a writer.
+   `id' specifies the ID number of a document.
+   If successful, the return value is true, else, it is false.  False is returned when no
+   document corresponds to the specified ID number. */
+int odoutbyid(ODEUM *odeum, int id);
+
+
+/* Retrieve a document specified by a URI.
+   `odeum' specifies a database handle.
+   `uri' specifies the string the URI of a document.
+   If successful, the return value is the handle of the corresponding document, else, it is
+   `NULL'.  `NULL' is returned when no document corresponds to the specified URI.
+   Because the handle of the return value is opened with the function `oddocopen', it should
+   be closed with the function `oddocclose'. */
+ODDOC *odget(ODEUM *odeum, const char *uri);
+
+
+/* Retrieve a document by an ID number.
+   `odeum' specifies a database handle.
+   `id' specifies the ID number of a document.
+   If successful, the return value is the handle of the corresponding document, else, it is
+   `NULL'.  `NULL' is returned when no document corresponds to the specified ID number.
+   Because the handle of the return value is opened with the function `oddocopen', it should
+   be closed with the function `oddocclose'. */
+ODDOC *odgetbyid(ODEUM *odeum, int id);
+
+
+/* Retrieve the ID of the document specified by a URI.
+   `odeum' specifies a database handle.
+   `uri' specifies the string the URI of a document.
+   If successful, the return value is the ID number of the document, else, it is -1.  -1 is
+   returned when no document corresponds to the specified URI. */
+int odgetidbyuri(ODEUM *odeum, const char *uri);
+
+
+/* Check whether the document specified by an ID number exists.
+   `odeum' specifies a database handle.
+   `id' specifies the ID number of a document.
+   The return value is true if the document exists, else, it is false. */
+int odcheck(ODEUM *odeum, int id);
+
+
+/* Search the inverted index for documents including a particular word.
+   `odeum' specifies a database handle.
+   `word' specifies a searching word.
+   `max' specifies the max number of documents to be retrieve.
+   `np' specifies the pointer to a variable to which the number of the elements of the return
+   value is assigned.
+   If successful, the return value is the pointer to an array, else, it is `NULL'.  Each
+   element of the array is a pair of the ID number and the score of a document, and sorted in
+   descending order of their scores.  Even if no document corresponds to the specified word,
+   it is not error but returns an dummy array.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use.  Note that each element of the array
+   of the return value can be data of a deleted document. */
+ODPAIR *odsearch(ODEUM *odeum, const char *word, int max, int *np);
+
+
+/* Get the number of documents including a word.
+   `odeum' specifies a database handle.
+   `word' specifies a searching word.
+   If successful, the return value is the number of documents including the word, else, it is -1.
+   Because this function does not read the entity of the inverted index, it is faster than
+   `odsearch'. */
+int odsearchdnum(ODEUM *odeum, const char *word);
+
+
+/* Initialize the iterator of a database handle.
+   `odeum' specifies a database handle.
+   If successful, the return value is true, else, it is false.
+   The iterator is used in order to access every document stored in a database. */
+int oditerinit(ODEUM *odeum);
+
+
+/* Get the next key of the iterator.
+   `odeum' specifies a database handle.
+   If successful, the return value is the handle of the next document, else, it is `NULL'.
+   `NULL' is returned when no document is to be get out of the iterator.
+   It is possible to access every document 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
+   string matches the one of the traversal access.  Because the handle of the return value is
+   opened with the function `oddocopen', it should be closed with the function `oddocclose'. */
+ODDOC *oditernext(ODEUM *odeum);
+
+
+/* Synchronize updating contents with the files and the devices.
+   `odeum' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.
+   This function is useful when another process uses the connected database directory. */
+int odsync(ODEUM *odeum);
+
+
+/* Optimize a database.
+   `odeum' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.
+   Elements of the deleted documents in the inverted index are purged. */
+int odoptimize(ODEUM *odeum);
+
+
+/* Get the name of a database.
+   `odeum' specifies a database handle.
+   If successful, the return value is the pointer to the region of the name of the database,
+   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 if it is no longer in use. */
+char *odname(ODEUM *odeum);
+
+
+/* Get the total size of database files.
+   `odeum' specifies a database handle.
+   If successful, the return value is the total size of the database files, else, it is -1.0. */
+double odfsiz(ODEUM *odeum);
+
+
+/* Get the total number of the elements of the bucket arrays in the inverted index.
+   `odeum' specifies a database handle.
+   If successful, the return value is the total number of the elements of the bucket arrays,
+   else, it is -1. */
+int odbnum(ODEUM *odeum);
+
+
+/* Get the total number of the used elements of the bucket arrays in the inverted index.
+   `odeum' specifies a database handle.
+   If successful, the return value is the total number of the used elements of the bucket
+   arrays, else, it is -1. */
+int odbusenum(ODEUM *odeum);
+
+
+/* Get the number of the documents stored in a database.
+   `odeum' specifies a database handle.
+   If successful, the return value is the number of the documents stored in the database, else,
+   it is -1. */
+int oddnum(ODEUM *odeum);
+
+
+/* Get the number of the words stored in a database.
+   `odeum' specifies a database handle.
+   If successful, the return value is the number of the words stored in the database, else,
+   it is -1.
+   Because of the I/O buffer, the return value may be less than the hard number. */
+int odwnum(ODEUM *odeum);
+
+
+/* Check whether a database handle is a writer or not.
+   `odeum' specifies a database handle.
+   The return value is true if the handle is a writer, false if not. */
+int odwritable(ODEUM *odeum);
+
+
+/* Check whether a database has a fatal error or not.
+   `odeum' specifies a database handle.
+   The return value is true if the database has a fatal error, false if not. */
+int odfatalerror(ODEUM *odeum);
+
+
+/* Get the inode number of a database directory.
+   `odeum' specifies a database handle.
+   The return value is the inode number of the database directory. */
+int odinode(ODEUM *odeum);
+
+
+/* Get the last modified time of a database.
+   `odeum' specifies a database handle.
+   The return value is the last modified time of the database. */
+time_t odmtime(ODEUM *odeum);
+
+
+/* Merge plural database directories.
+   `name' specifies the name of a database directory to create.
+   `elemnames' specifies a list of names of element databases.
+   If successful, the return value is true, else, it is false.
+   If two or more documents which have the same URL come in, the first one is adopted and the
+   others are ignored. */
+int odmerge(const char *name, const CBLIST *elemnames);
+
+
+/* Remove a database directory.
+   `name' specifies the name of a database directory.
+   If successful, the return value is true, else, it is false.
+   A database directory can contain databases of other APIs of QDBM, they are also removed by
+   this function. */
+int odremove(const char *name);
+
+
+/* Get a document handle.
+   `uri' specifies the URI of a document.
+   The return value is a document handle.
+   The ID number of a new document is not defined.  It is defined when the document is stored
+   in a database. */
+ODDOC *oddocopen(const char *uri);
+
+
+/* Close a document handle.
+   `doc' specifies a document handle.
+   Because the region of a closed handle is released, it becomes impossible to use the handle. */
+void oddocclose(ODDOC *doc);
+
+
+/* Add an attribute to a document.
+   `doc' specifies a document handle.
+   `name' specifies the string of the name of an attribute.
+   `value' specifies the string of the value of the attribute. */
+void oddocaddattr(ODDOC *doc, const char *name, const char *value);
+
+
+/* Add a word to a document.
+   `doc' specifies a document handle.
+   `normal' specifies the string of the normalized form of a word.  Normalized forms are
+   treated as keys of the inverted index.  If the normalized form of a word is an empty
+   string, the word is not reflected in the inverted index.
+   `asis' specifies the string of the appearance form of the word.  Appearance forms are used
+   after the document is retrieved by an application. */
+void oddocaddword(ODDOC *doc, const char *normal, const char *asis);
+
+
+/* Get the ID number of a document.
+   `doc' specifies a document handle.
+   The return value is the ID number of a document. */
+int oddocid(const ODDOC *doc);
+
+
+/* Get the URI of a document.
+   `doc' specifies a document handle.
+   The return value is the string of the URI of a document. */
+const char *oddocuri(const ODDOC *doc);
+
+
+/* Get the value of an attribute of a document.
+   `doc' specifies a document handle.
+   `name' specifies the string of the name of an attribute.
+   The return value is the string of the value of the attribute, or `NULL' if no attribute
+   corresponds. */
+const char *oddocgetattr(const ODDOC *doc, const char *name);
+
+
+/* Get the list handle contains words in normalized form of a document.
+   `doc' specifies a document handle.
+   The return value is the list handle contains words in normalized form. */
+const CBLIST *oddocnwords(const ODDOC *doc);
+
+
+/* Get the list handle contains words in appearance form of a document.
+   `doc' specifies a document handle.
+   The return value is the list handle contains words in appearance form. */
+const CBLIST *oddocawords(const ODDOC *doc);
+
+
+/* Get the map handle contains keywords in normalized form and their scores.
+   `doc' specifies a document handle.
+   `max' specifies the max number of keywords to get.
+   `odeum' specifies a database handle with which the IDF for weighting is calculate.
+   If it is `NULL', it is not used.
+   The return value is the map handle contains keywords and their scores.  Scores are expressed
+   as decimal strings.
+   Because the handle of the return value is opened with the function `cbmapopen', it should
+   be closed with the function `cbmapclose' if it is no longer in use. */
+CBMAP *oddocscores(const ODDOC *doc, int max, ODEUM *odeum);
+
+
+/* Break a text into words in appearance form.
+   `text' specifies the string of a text.
+   The return value is the list handle contains words in appearance form.
+   Words are separated with space characters and such delimiters as period, comma and so on.
+   Because the handle of the return value is opened with the function `cblistopen', it should
+   be closed with the function `cblistclose' if it is no longer in use. */
+CBLIST *odbreaktext(const char *text);
+
+
+/* Make the normalized form of a word.
+   `asis' specifies the string of the appearance form of a word.
+   The return value is is the string of the normalized form of the word.
+   Alphabets of the ASCII code are unified into lower cases.  Words composed of only delimiters
+   are treated as empty strings.  Because the region of the return value is allocated with the
+   `malloc' call, it should be released with the `free' call if it is no longer in use. */
+char *odnormalizeword(const char *asis);
+
+
+/* Get the common elements of two sets of documents.
+   `apairs' specifies the pointer to the former document array.
+   `anum' specifies the number of the elements of the former document array.
+   `bpairs' specifies the pointer to the latter document array.
+   `bnum' specifies the number of the elements of the latter document array.
+   `np' specifies the pointer to a variable to which the number of the elements of the return
+   value is assigned.
+   The return value is the pointer to a new document array whose elements commonly belong to
+   the specified two sets.
+   Elements of the array are sorted in descending order of their scores.  Because the region of
+   the return value is allocated with the `malloc' call, it should be released with the `free'
+   call if it is no longer in use. */
+ODPAIR *odpairsand(ODPAIR *apairs, int anum, ODPAIR *bpairs, int bnum, int *np);
+
+
+/* Get the sum of elements of two sets of documents.
+   `apairs' specifies the pointer to the former document array.
+   `anum' specifies the number of the elements of the former document array.
+   `bpairs' specifies the pointer to the latter document array.
+   `bnum' specifies the number of the elements of the latter document array.
+   `np' specifies the pointer to a variable to which the number of the elements of the return
+   value is assigned.
+   The return value is the pointer to a new document array whose elements belong to both or
+   either of the specified two sets.
+   Elements of the array are sorted in descending order of their scores.  Because the region of
+   the return value is allocated with the `malloc' call, it should be released with the `free'
+   call if it is no longer in use. */
+ODPAIR *odpairsor(ODPAIR *apairs, int anum, ODPAIR *bpairs, int bnum, int *np);
+
+
+/* Get the difference set of documents.
+   `apairs' specifies the pointer to the former document array.
+   `anum' specifies the number of the elements of the former document array.
+   `bpairs' specifies the pointer to the latter document array of the sum of elements.
+   `bnum' specifies the number of the elements of the latter document array.
+   `np' specifies the pointer to a variable to which the number of the elements of the return
+   value is assigned.
+   The return value is the pointer to a new document array whose elements belong to the former
+   set but not to the latter set.
+   Elements of the array are sorted in descending order of their scores.  Because the region of
+   the return value is allocated with the `malloc' call, it should be released with the `free'
+   call if it is no longer in use. */
+ODPAIR *odpairsnotand(ODPAIR *apairs, int anum, ODPAIR *bpairs, int bnum, int *np);
+
+
+/* Sort a set of documents in descending order of scores.
+   `pairs' specifies the pointer to a document array.
+   `pnum' specifies the number of the elements of the document array. */
+void odpairssort(ODPAIR *pairs, int pnum);
+
+
+/* Get the natural logarithm of a number.
+   `x' specifies a number.
+   The return value is the natural logarithm of the number.  If the number is equal to or less
+   than 1.0, the return value is 0.0.
+   This function is useful when an application calculates the IDF of search results. */
+double odlogarithm(double x);
+
+
+/* Get the cosine of the angle of two vectors.
+   `avec' specifies the pointer to one array of numbers.
+   `bvec' specifies the pointer to the other array of numbers.
+   `vnum' specifies the number of elements of each array.
+   The return value is the cosine of the angle of two vectors.
+   This function is useful when an application calculates similarity of documents. */
+double odvectorcosine(const int *avec, const int *bvec, int vnum);
+
+
+/* Set the global tuning parameters.
+   `ibnum' specifies the number of buckets for inverted indexes.
+   `idnum' specifies the division number of inverted index.
+   `cbnum' specifies the number of buckets for dirty buffers.
+   `csiz' specifies the maximum bytes to use memory for dirty buffers.
+   The default setting is equivalent to `odsettuning(32749, 7, 262139, 8388608)'.  This function
+   should be called before opening a handle. */
+void odsettuning(int ibnum, int idnum, int cbnum, int csiz);
+
+
+/* Break a text into words and store appearance forms and normalized form into lists.
+   `odeum' specifies a database handle.
+   `text' specifies the string of a text.
+   `awords' specifies a list handle into which appearance form is store.
+   `nwords' specifies a list handle into which normalized form is store.  If it is `NULL', it is
+   ignored.
+   Words are separated with space characters and such delimiters as period, comma and so on. */
+void odanalyzetext(ODEUM *odeum, const char *text, CBLIST *awords, CBLIST *nwords);
+
+
+/* Set the classes of characters used by `odanalyzetext'.
+   `odeum' specifies a database handle.
+   `spacechars' spacifies a string contains space characters.
+   `delimchars' spacifies a string contains delimiter characters.
+   `gluechars' spacifies a string contains glue characters. */
+void odsetcharclass(ODEUM *odeum, const char *spacechars, const char *delimchars,
+                    const char *gluechars);
+
+
+/* Query a database using a small boolean query language.
+   `odeum' specifies a database handle.
+   'query' specifies the text of the query.
+   `np' specifies the pointer to a variable to which the number of the elements of the return
+   value is assigned.
+   `errors' specifies a list handle into which error messages are stored.  If it is `NULL', it
+   is ignored.
+   If successful, the return value is the pointer to an array, else, it is `NULL'.  Each
+   element of the array is a pair of the ID number and the score of a document, and sorted in
+   descending order of their scores.  Even if no document corresponds to the specified condition,
+   it is not error but returns an dummy array.
+   Because the region of the return value is allocated with the `malloc' call, it should be
+   released with the `free' call if it is no longer in use.  Note that each element of the array
+   of the return value can be data of a deleted document. */
+ODPAIR *odquery(ODEUM *odeum, const char *query, int *np, CBLIST *errors);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Get the internal database handle for documents.
+   `odeum' specifies a database handle.
+   The return value is the internal database handle for documents.
+   Note that the the returned handle should not be updated. */
+CURIA *odidbdocs(ODEUM *odeum);
+
+
+/* Get the internal database handle for the inverted index.
+   `odeum' specifies a database handle.
+   The return value is the internal database handle for the inverted index.
+   Note that the the returned handle should not be updated. */
+CURIA *odidbindex(ODEUM *odeum);
+
+
+/* Get the internal database handle for the reverse dictionary.
+   `odeum' specifies a database handle.
+   The return value is the internal database handle for the reverse dictionary.
+   Note that the the returned handle should not be updated. */
+VILLA *odidbrdocs(ODEUM *odeum);
+
+
+/* Set the call back function called in merging.
+   `otcb' specifires the pointer to a function to report outturn.  Its first argument is the name
+   of processing function.  Its second argument is the handle of the database being processed.
+   Its third argument is ths string of a log message.  If it is `NULL', the call back function is
+   cleared. */
+void odsetotcb(void (*otcb)(const char *, ODEUM *, const char *));
+
+
+/* Get the positive one of square roots of a number.
+   `x' specifies a number.
+   The return value is the positive one of square roots of a number.  If the number is equal to
+   or less than 0.0, the return value is 0.0. */
+double odsquareroot(double x);
+
+
+/* Get the absolute of a vector.
+   `vec' specifies the pointer to an array of numbers.
+   `vnum' specifies the number of elements of the array.
+   The return value is the absolute of a vector. */
+double odvecabsolute(const int *vec, int vnum);
+
+
+/* Get the inner product of two vectors.
+   `avec' specifies the pointer to one array of numbers.
+   `bvec' specifies the pointer to the other array of numbers.
+   `vnum' specifies the number of elements of each array.
+   The return value is the inner product of two vectors. */
+double odvecinnerproduct(const int *avec, const int *bvec, int vnum);
+
+
+
+#undef MYEXTERN
+
+#if defined(__cplusplus)                 /* export for C++ */
+}
+#endif
+
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/odeum.h
___________________________________________________________________
Added: svn:mime-type
   + text/x-chdr
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/odidx.c
===================================================================
--- box/trunk/qdbm/odidx.c	                        (rev 0)
+++ box/trunk/qdbm/odidx.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,890 @@
+/*************************************************************************************************
+ * Utility for indexing document files into a database of Odeum
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <cabin.h>
+#include <odeum.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <signal.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define PATHCHR        '/'               /* delimiter character of path */
+#define EXTCHR         '.'               /* delimiter character of extension */
+#define CDIRSTR        "."               /* string of current directory */
+#define PDIRSTR        ".."              /* string of parent directory */
+#define MTDBNAME       "_mtime"          /* name of the database for last modified times */
+#define MTDBLRM        81                /* records in a leaf node of time database */
+#define MTDBNIM        192               /* records in a non-leaf node of time database */
+#define MTDBLCN        64                /* number of leaf cache of time database */
+#define MTDBNCN        32                /* number of non-leaf cache of time database */
+#define SCDBNAME       "_score"          /* name of the database for scores */
+#define SCDBBNUM       32749             /* bucket number of the score database */
+#define SCDBALIGN      -3                /* alignment of the score database */
+#define PATHBUFSIZ     2048              /* size of a path buffer */
+#define MAXLOAD        0.85              /* max ratio of bucket loading */
+#define KEYNUM         32                /* number of keywords to store */
+
+
+/* for Win32 and RISC OS */
+#if defined(_WIN32)
+#undef PATHCHR
+#define PATHCHR        '\\'
+#undef EXTCHR
+#define EXTCHR         '.'
+#undef CDIRSTR
+#define CDIRSTR        "."
+#undef PDIRSTR
+#define PDIRSTR        ".."
+#elif defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#undef PATHCHR
+#define PATHCHR        '.'
+#undef EXTCHR
+#define EXTCHR         '/'
+#undef CDIRSTR
+#define CDIRSTR        "@"
+#undef PDIRSTR
+#define PDIRSTR        "^"
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+int sigterm;                             /* flag for termination signal */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void setsignals(void);
+void sigtermhandler(int num);
+void usage(void);
+int runregister(int argc, char **argv);
+int runrelate(int argc, char **argv);
+int runpurge(int argc, char **argv);
+int bwimatchlist(const char *str, const CBLIST *keys);
+char *fgetl(FILE *ifp);
+void otcb(const char *fname, ODEUM *odeum, const char *msg);
+void pdperror(const char *name);
+void printferror(const char *format, ...);
+void printfinfo(const char *format, ...);
+const char *datestr(time_t t);
+int proclist(const char *name, const char *lfile, int wmax,
+             const CBLIST *tsuflist, const CBLIST *hsuflist);
+int procdir(const char *name, const char *dir, int wmax,
+            const CBLIST *tsuflist, const CBLIST *hsuflist);
+int indexdir(ODEUM *odeum, VILLA *mtdb, const char *name, const char *dir, int wmax,
+             const CBLIST *tsuflist, const CBLIST *hsuflist);
+int indexfile(ODEUM *odeum, VILLA *mtdb, const char *name, const char *file, int wmax,
+              const CBLIST *tsuflist, const CBLIST *hsuflist);
+char *filetouri(const char *file);
+ODDOC *makedocplain(const char *uri, const char *text, const char *date);
+ODDOC *makedochtml(const char *uri, const char *html, const char *date);
+CBMAP *htmlescpairs(void);
+int procrelate(const char *name);
+int procpurge(const char *name);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  sigterm = FALSE;
+  setsignals();
+  if(argc < 2) usage();
+  odsetotcb(otcb);
+  rv = 0;
+  if(!strcmp(argv[1], "register")){
+    rv = runregister(argc, argv);
+  } else if(!strcmp(argv[1], "relate")){
+    rv = runrelate(argc, argv);
+  } else if(!strcmp(argv[1], "purge")){
+    rv = runpurge(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* set signal handlers */
+void setsignals(void){
+  signal(1, sigtermhandler);
+  signal(2, sigtermhandler);
+  signal(3, sigtermhandler);
+  signal(13, sigtermhandler);
+  signal(15, sigtermhandler);
+}
+
+
+/* handler of termination signal */
+void sigtermhandler(int num){
+  signal(num, SIG_DFL);
+  sigterm = TRUE;
+  printfinfo("the termination signal %d catched", num);
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: indexer of document files\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s register [-l file] [-wmax num] [-tsuf sufs] [-hsuf sufs] name [dir]\n",
+          progname);
+  fprintf(stderr, "  %s relate name\n", progname);
+  fprintf(stderr, "  %s purge name\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of register command */
+int runregister(int argc, char **argv){
+  char *name, *dir, *lfile, *tsuf, *hsuf, path[PATHBUFSIZ];
+  int i, wmax, plen, rv;
+  CBLIST *tsuflist, *hsuflist;
+  name = NULL;
+  dir = NULL;
+  lfile = NULL;
+  tsuf = NULL;
+  hsuf = NULL;
+  wmax = -1;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-l")){
+        if(++i >= argc) usage();
+        lfile = argv[i];
+      } else if(!strcmp(argv[i], "-wmax")){
+        if(++i >= argc) usage();
+        wmax = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-tsuf")){
+        if(++i >= argc) usage();
+        tsuf = argv[i];
+      } else if(!strcmp(argv[i], "-hsuf")){
+        if(++i >= argc) usage();
+        hsuf = argv[i];
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!dir){
+      dir = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  if(!dir) dir = CDIRSTR;
+  plen = sprintf(path, "%s", dir);
+  if(plen > 1 && path[plen-1] == PATHCHR) path[plen-1] = '\0';
+  tsuflist = cbsplit(tsuf ? tsuf : ".txt,.text", -1, ",");
+  hsuflist = cbsplit(hsuf ? hsuf : ".html,.htm", -1, ",");
+  if(lfile){
+    rv = proclist(name, lfile, wmax, tsuflist, hsuflist);
+  } else {
+    rv = procdir(name, path, wmax, tsuflist, hsuflist);
+  }
+  cblistclose(hsuflist);
+  cblistclose(tsuflist);
+  return rv;
+}
+
+
+/* parse arguments of relate command */
+int runrelate(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = procrelate(name);
+  return rv;
+}
+
+
+/* parse arguments of purge command */
+int runpurge(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = procpurge(name);
+  return rv;
+}
+
+
+/* case insensitive backward matching with a list */
+int bwimatchlist(const char *str, const CBLIST *keys){
+  int i;
+  for(i = 0; i < cblistnum(keys); i++){
+    if(cbstrbwimatch(str, cblistval(keys, i, NULL))) return TRUE;
+  }
+  return FALSE;
+}
+
+
+/* read a line */
+char *fgetl(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 = cbrealloc(buf, blen + 1);
+    if(c == '\n') c = '\0';
+    buf[len++] = c;
+    if(c == '\0') break;
+  }
+  if(!buf) return NULL;
+  buf[len] = '\0';
+  return buf;
+}
+
+
+/* report the outturn */
+void otcb(const char *fname, ODEUM *odeum, const char *msg){
+  char *name;
+  name = odname(odeum);
+  printf("%s: %s: %s: %s\n", progname, fname, name, msg);
+  free(name);
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  printf("%s: ERROR: %s: %s\n", progname, name, dperrmsg(dpecode));
+  fflush(stdout);
+}
+
+
+/* print formatted error string and flush the buffer */
+void printferror(const char *format, ...){
+  va_list ap;
+  va_start(ap, format);
+  printf("%s: ERROR: ", progname);
+  vprintf(format, ap);
+  putchar('\n');
+  fflush(stdout);
+  va_end(ap);
+}
+
+
+/* print formatted information string and flush the buffer */
+void printfinfo(const char *format, ...){
+  va_list ap;
+  va_start(ap, format);
+  printf("%s: INFO: ", progname);
+  vprintf(format, ap);
+  putchar('\n');
+  fflush(stdout);
+  va_end(ap);
+}
+
+
+/* get static string of the date */
+const char *datestr(time_t t){
+  static char buf[32];
+  struct tm *stp;
+  if(!(stp = localtime(&t))) return "0000/00/00 00:00:00";
+  sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d",
+          stp->tm_year + 1900, stp->tm_mon + 1, stp->tm_mday,
+          stp->tm_hour, stp->tm_min, stp->tm_sec);
+  return buf;
+}
+
+
+/* processing with finding files in a list file */
+int proclist(const char *name, const char *lfile, int wmax,
+             const CBLIST *tsuflist, const CBLIST *hsuflist){
+  ODEUM *odeum;
+  VILLA *mtdb;
+  FILE *ifp;
+  char *line, path[PATHBUFSIZ];
+  int err, fatal;
+  if(!strcmp(lfile, "-")){
+    ifp = stdin;
+  } else {
+    if(!(ifp = fopen(lfile, "rb"))){
+      printferror("%s: file cannot be opened", lfile);
+      return 1;
+    }
+  }
+  printfinfo("%s: registration started", name);
+  if(!(odeum = odopen(name, OD_OWRITER | OD_OCREAT))){
+    pdperror(name);
+    if(ifp != stdin) fclose(ifp);
+    return 1;
+  }
+  sprintf(path, "%s%c%s", name, PATHCHR, MTDBNAME);
+  if(!(mtdb = vlopen(path, VL_OWRITER | VL_OCREAT, VL_CMPLEX))){
+    pdperror(name);
+    odclose(odeum);
+    if(ifp != stdin) fclose(ifp);
+    return 1;
+  }
+  vlsettuning(mtdb, MTDBLRM, MTDBNIM, MTDBLCN, MTDBNCN);
+  printfinfo("%s: database opened: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+             name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+  err = FALSE;
+  while((line = fgetl(ifp)) != NULL){
+    if(sigterm){
+      printferror("aborting due to a termination signal");
+      free(line);
+      err = TRUE;
+      break;
+    }
+    if(!indexfile(odeum, mtdb, name, line, wmax, tsuflist, hsuflist)) err = TRUE;
+    free(line);
+  }
+  fatal = odfatalerror(odeum);
+  printfinfo("%s: database closing: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+             name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+  if(!vlclose(mtdb)){
+    pdperror(name);
+    err = TRUE;
+  }
+  if(!odclose(odeum)){
+    pdperror(name);
+    err = TRUE;
+  }
+  if(ifp != stdin) fclose(ifp);
+  if(err){
+    printfinfo("%s: registration was over%s", name, fatal ? " with fatal error" : "");
+  } else {
+    printfinfo("%s: registration completed successfully", name);
+  }
+  return err ? 1 : 0;
+}
+
+
+/* processing with finding files in a directory */
+int procdir(const char *name, const char *dir, int wmax,
+            const CBLIST *tsuflist, const CBLIST *hsuflist){
+  ODEUM *odeum;
+  VILLA *mtdb;
+  char path[PATHBUFSIZ];
+  int err, fatal;
+  printfinfo("%s: registration started", name);
+  if(!(odeum = odopen(name, OD_OWRITER | OD_OCREAT))){
+    pdperror(name);
+    return 1;
+  }
+  sprintf(path, "%s%c%s", name, PATHCHR, MTDBNAME);
+  if(!(mtdb = vlopen(path, VL_OWRITER | VL_OCREAT, VL_CMPLEX))){
+    pdperror(name);
+    odclose(odeum);
+    return 1;
+  }
+  vlsettuning(mtdb, MTDBLRM, MTDBNIM, MTDBLCN, MTDBNCN);
+  printfinfo("%s: database opened: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+             name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+  err = FALSE;
+  if(!indexdir(odeum, mtdb, name, dir, wmax, tsuflist, hsuflist)) err = TRUE;
+  fatal = odfatalerror(odeum);
+  printfinfo("%s: database closing: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+             name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+  if(!vlclose(mtdb)){
+    pdperror(name);
+    err = TRUE;
+  }
+  if(!odclose(odeum)){
+    pdperror(name);
+    err = TRUE;
+  }
+  if(err){
+    printfinfo("%s: registration was over%s", name, fatal ? " with fatal error" : "");
+  } else {
+    printfinfo("%s: registration completed successfully", name);
+  }
+  return err ? 1 : 0;
+}
+
+
+/* find and index files in a directory */
+int indexdir(ODEUM *odeum, VILLA *mtdb, const char *name, const char *dir, int wmax,
+             const CBLIST *tsuflist, const CBLIST *hsuflist){
+  CBLIST *files;
+  const char *file;
+  char path[PATHBUFSIZ];
+  int i, isroot, isdir, err;
+  if(!(files = cbdirlist(dir))){
+    printferror("%s: directory cannot be opened", dir);
+    return FALSE;
+  }
+  isroot = dir[0] == PATHCHR && dir[1] == '\0';
+  err = FALSE;
+  for(i = 0; i < cblistnum(files); i++){
+    if(sigterm){
+      printferror("aborting due to a termination signal");
+      cblistclose(files);
+      return FALSE;
+    }
+    file = cblistval(files, i, NULL);
+    if(!strcmp(file, CDIRSTR) || !strcmp(file, PDIRSTR)) continue;
+    if(isroot){
+      sprintf(path, "%s%s", dir, file);
+    } else {
+      sprintf(path, "%s%c%s", dir, PATHCHR, file);
+    }
+    if(!cbfilestat(path, &isdir, NULL, NULL)){
+      printferror("%s: file does not exist", file);
+      err = TRUE;
+      continue;
+    }
+    if(isdir){
+      if(!indexdir(odeum, mtdb, name, path, wmax, tsuflist, hsuflist)) err = TRUE;
+    } else {
+      if(!indexfile(odeum, mtdb, name, path, wmax, tsuflist, hsuflist)) err = TRUE;
+    }
+  }
+  cblistclose(files);
+  return err ? FALSE : TRUE;
+}
+
+
+/* index a file into the database */
+int indexfile(ODEUM *odeum, VILLA *mtdb, const char *name, const char *file, int wmax,
+              const CBLIST *tsuflist, const CBLIST *hsuflist){
+  static int cnt = 0;
+  char *vbuf, *buf, *uri;
+  const char *title;
+  int size, hot, vsiz, wnum, bnum;
+  time_t mtime;
+  ODDOC *doc;
+  if(!cbfilestat(file, NULL, &size, &mtime)){
+    printferror("%s: file does not exist", file);
+    return FALSE;
+  }
+  hot = TRUE;
+  if((vbuf = vlget(mtdb, file, -1, &vsiz)) != NULL){
+    if(vsiz == sizeof(int) && mtime <= *(int *)vbuf) hot = FALSE;
+    free(vbuf);
+  }
+  if(!hot){
+    printfinfo("%s: passed", file);
+    return TRUE;
+  }
+  doc = NULL;
+  uri = filetouri(file);
+  if(bwimatchlist(file, tsuflist)){
+    if(!(buf = cbreadfile(file, NULL))){
+      printferror("%s: file cannot be opened", file);
+      return FALSE;
+    }
+    doc = makedocplain(uri, buf, datestr(mtime));
+    free(buf);
+  } else if(bwimatchlist(file, hsuflist)){
+    if(!(buf = cbreadfile(file, NULL))){
+      printferror("%s: file cannot be opened", file);
+      return FALSE;
+    }
+    doc = makedochtml(uri, buf, datestr(mtime));
+    free(buf);
+  }
+  free(uri);
+  if(doc){
+    if(!(title = oddocgetattr(doc, "title")) || strlen(title) < 1){
+      if((title = strrchr(file, PATHCHR)) != NULL){
+        title++;
+      }  else {
+        title = file;
+      }
+      oddocaddattr(doc, "title", title);
+    }
+    if(odput(odeum, doc, wmax, TRUE) &&
+       vlput(mtdb, file, -1, (char *)&mtime, sizeof(int), VL_DOVER)){
+      printfinfo("%s: registered: id=%d wnum=%d",
+                 file, oddocid(doc), cblistnum(oddocnwords(doc)));
+      cnt++;
+    } else {
+      pdperror(file);
+    }
+    oddocclose(doc);
+  }
+  wnum = odwnum(odeum);
+  bnum = odbnum(odeum);
+  if(wnum != -1 && bnum != -1 && (double)wnum / (double)bnum > MAXLOAD){
+    printfinfo("%s: optimizing started: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+               name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+    if(!odoptimize(odeum)){
+      pdperror(file);
+      return FALSE;
+    }
+    printfinfo("%s: optimizing completed: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+               name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+  }
+  if(cnt >= 256){
+    printfinfo("%s: database status: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+               name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+    cnt = 0;
+  }
+  return TRUE;
+}
+
+
+/* make the url from file path */
+char *filetouri(const char *file){
+  CBLIST *list;
+  char str[PATHBUFSIZ], *wp, *enc;
+  const char *name;
+  int i, nsiz;
+  sprintf(str, "%c", PATHCHR);
+  list = cbsplit(file, -1, str);
+  wp = str;
+  for(i = 0; i < cblistnum(list); i++){
+    if(i > 0) *(wp++) = '/';
+    name = cblistval(list, i, &nsiz);
+    enc = cburlencode(name, nsiz);
+    wp += sprintf(wp, "%s", enc);
+    free(enc);
+  }
+  cblistclose(list);
+  *wp = '\0';
+  return cbmemdup(str, -1);
+}
+
+
+/* make a document of plain text */
+ODDOC *makedocplain(const char *uri, const char *text, const char *date){
+  ODDOC *doc;
+  CBLIST *awords;
+  const char *asis;
+  char *normal;
+  int i;
+  doc = oddocopen(uri);
+  if(date) oddocaddattr(doc, "date", date);
+  awords = odbreaktext(text);
+  for(i = 0; i < cblistnum(awords); i++){
+    asis = cblistval(awords, i, NULL);
+    normal = odnormalizeword(asis);
+    oddocaddword(doc, normal, asis);
+    free(normal);
+  }
+  cblistclose(awords);
+  return doc;
+}
+
+
+/* make a document of HTML */
+ODDOC *makedochtml(const char *uri, const char *html, const char *date){
+  ODDOC *doc;
+  CBMAP *pairs;
+  CBLIST *elems, *awords;
+  const char *text, *asis;
+  char *rtext, *normal;
+  int i, j, body;
+  pairs = htmlescpairs();
+  doc = oddocopen(uri);
+  if(date) oddocaddattr(doc, "date", date);
+  elems = cbxmlbreak(html, TRUE);
+  body = FALSE;
+  for(i = 0; i < cblistnum(elems); i++){
+    text = cblistval(elems, i, NULL);
+    if(cbstrfwimatch(text, "<title")){
+      i++;
+      if(i < cblistnum(elems)){
+        text = cblistval(elems, i, NULL);
+        if(text[0] == '<') text = "";
+        rtext = cbreplace(text, pairs);
+        for(j = 0; rtext[j] != '\0'; j++){
+          if(strchr("\t\n\v\f\r", rtext[j])) rtext[j] = ' ';
+        }
+        while(--j >= 0){
+          if(rtext[j] != ' ') break;
+          rtext[j] = '\0';
+        }
+        for(j = 0; rtext[j] != '\0'; j++){
+          if(rtext[j] != ' ') break;
+        }
+        oddocaddattr(doc, "title", rtext + j);
+        awords = odbreaktext(rtext);
+        for(j = 0; j < cblistnum(awords); j++){
+          asis = cblistval(awords, j, NULL);
+          normal = odnormalizeword(asis);
+          oddocaddword(doc, normal, "");
+          free(normal);
+        }
+        cblistclose(awords);
+        free(rtext);
+      }
+    } else if(cbstrfwimatch(text, "<body")){
+      body = TRUE;
+    } else if(body && text[0] != '<'){
+      rtext = cbreplace(text, pairs);
+      awords = odbreaktext(rtext);
+      for(j = 0; j < cblistnum(awords); j++){
+        asis = cblistval(awords, j, NULL);
+        normal = odnormalizeword(asis);
+        oddocaddword(doc, normal, asis);
+        free(normal);
+      }
+      cblistclose(awords);
+      free(rtext);
+    }
+  }
+  if(!body){
+    for(i = 0; i < cblistnum(elems); i++){
+      text = cblistval(elems, i, NULL);
+      if(cbstrfwimatch(text, "<title")){
+        i++;
+      } else if(text[0] != '<'){
+        rtext = cbreplace(text, pairs);
+        awords = odbreaktext(rtext);
+        for(j = 0; j < cblistnum(awords); j++){
+          asis = cblistval(awords, j, NULL);
+          normal = odnormalizeword(asis);
+          oddocaddword(doc, normal, asis);
+          free(normal);
+        }
+        cblistclose(awords);
+        free(rtext);
+      }
+    }
+  }
+  cblistclose(elems);
+  return doc;
+}
+
+
+/* get pairs of escaping characters */
+CBMAP *htmlescpairs(void){
+  char *latinext[] = {
+    " ", "!", "(cent)", "(pound)", "(currency)", "(yen)", "|", "(section)", "\"", "(C)",
+    "", "<<", "(not)", "-", "(R)", "~", "(degree)", "+-", "^2", "^3",
+    "'", "(u)", "(P)", "*", ",", "^1", "", ">>", "(1/4)", "(1/2)",
+    "(3/4)", "?", "A", "A", "A", "A", "A", "A", "AE", "C",
+    "E", "E", "E", "E", "I", "I", "I", "I", "D", "N",
+    "O", "O", "O", "O", "O", "*", "O", "U", "U", "U",
+    "U", "Y", "P", "s", "a", "a", "a", "a", "a", "a",
+    "ae", "c", "e", "e", "e", "e", "i", "i", "i", "i",
+    "o", "n", "o", "o", "o", "o", "o", "/", "o", "u",
+    "u", "u", "u", "y", "p", "y", NULL
+  };
+  static CBMAP *pairs = NULL;
+  char kbuf[8], vbuf[8];
+  int i, ksiz, vsiz;
+  if(pairs) return pairs;
+  pairs = cbmapopen();
+  cbglobalgc(pairs, (void (*)(void *))cbmapclose);
+  cbmapput(pairs, "&", -1, "&", -1, TRUE);
+  cbmapput(pairs, "<", -1, "<", -1, TRUE);
+  cbmapput(pairs, ">", -1, ">", -1, TRUE);
+  cbmapput(pairs, """, -1, "\"", -1, TRUE);
+  cbmapput(pairs, "'", -1, "'", -1, TRUE);
+  cbmapput(pairs, " ", -1, " ", -1, TRUE);
+  cbmapput(pairs, "©", -1, "(C)", -1, TRUE);
+  cbmapput(pairs, "®", -1, "(R)", -1, TRUE);
+  cbmapput(pairs, "™", -1, "(TM)", -1, TRUE);
+  for(i = 1; i <= 127; i++){
+    ksiz = sprintf(kbuf, "&#%d;", i);
+    vsiz = sprintf(vbuf, "%c", i);
+    cbmapput(pairs, kbuf, ksiz, vbuf, vsiz, TRUE);
+  }
+  cbmapput(pairs, "‚", -1, ",", -1, TRUE);
+  cbmapput(pairs, "„", -1, ",,", -1, TRUE);
+  cbmapput(pairs, "…", -1, "...", -1, TRUE);
+  cbmapput(pairs, "‹", -1, "<", -1, TRUE);
+  cbmapput(pairs, "‘", -1, "'", -1, TRUE);
+  cbmapput(pairs, "’", -1, "'", -1, TRUE);
+  cbmapput(pairs, "“", -1, "\"", -1, TRUE);
+  cbmapput(pairs, "”", -1, "\"", -1, TRUE);
+  cbmapput(pairs, "–", -1, "-", -1, TRUE);
+  cbmapput(pairs, "—", -1, "-", -1, TRUE);
+  cbmapput(pairs, "˜", -1, "~", -1, TRUE);
+  cbmapput(pairs, "™", -1, "(TM)", -1, TRUE);
+  cbmapput(pairs, "›", -1, ">", -1, TRUE);
+  for(i = 0; latinext[i]; i++){
+    ksiz = sprintf(kbuf, "&#%d;", i + 160);
+    cbmapput(pairs, kbuf, ksiz, latinext[i], -1, TRUE);
+  }
+  return pairs;
+}
+
+
+/* register scores of documents */
+int procrelate(const char *name){
+  ODEUM *odeum;
+  DEPOT *scdb;
+  ODDOC *doc;
+  CBMAP *scores;
+  const char *file;
+  char path[PATHBUFSIZ], *mbuf;
+  int err, fatal, id, msiz;
+  printfinfo("%s: relating started", name);
+  if(!(odeum = odopen(name, OD_OWRITER))){
+    pdperror(name);
+    return 1;
+  }
+  sprintf(path, "%s%c%s", name, PATHCHR, SCDBNAME);
+  if(!(scdb = dpopen(path, OD_OWRITER | OD_OCREAT, SCDBBNUM))){
+    pdperror(name);
+    odclose(odeum);
+    return 1;
+  }
+  if(!dpsetalign(scdb, SCDBALIGN)){
+    pdperror(name);
+    dpclose(scdb);
+    odclose(odeum);
+    return 1;
+  }
+  printfinfo("%s: database opened: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+             name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+  err = FALSE;
+  if(!oditerinit(odeum)){
+    pdperror(name);
+    err = TRUE;
+  } else {
+    while(TRUE){
+      if(sigterm){
+        printferror("aborting due to a termination signal");
+        err = TRUE;
+        break;
+      }
+      if(!(doc = oditernext(odeum))){
+        if(dpecode != DP_ENOITEM){
+          pdperror(name);
+          err = TRUE;
+        }
+        break;
+      }
+      file = oddocuri(doc);
+      id = oddocid(doc);
+      scores = oddocscores(doc, KEYNUM, odeum);
+      mbuf = cbmapdump(scores, &msiz);
+      if(!dpput(scdb, (char *)&id, sizeof(int), mbuf, msiz, DP_DOVER)){
+        pdperror(name);
+        err = TRUE;
+      } else {
+        printfinfo("%s: related", file);
+      }
+      free(mbuf);
+      cbmapclose(scores);
+      oddocclose(doc);
+      if(err) break;
+    }
+  }
+  fatal = odfatalerror(odeum);
+  printfinfo("%s: database closing: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+             name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+  if(!dpclose(scdb)){
+    pdperror(name);
+    err = TRUE;
+  }
+  if(!odclose(odeum)){
+    pdperror(name);
+    err = TRUE;
+  }
+  if(err){
+    printfinfo("%s: relating was over%s", name, fatal ? " with fatal error" : "");
+  } else {
+    printfinfo("%s: relating completed successfully", name);
+  }
+  return err ? 1 : 0;
+}
+
+
+/* purge documents which is not existing. */
+int procpurge(const char *name){
+  ODEUM *odeum;
+  ODDOC *doc;
+  const char *file;
+  int err, fatal;
+  printfinfo("%s: purging started", name);
+  if(!(odeum = odopen(name, OD_OWRITER))){
+    pdperror(name);
+    return 1;
+  }
+  printfinfo("%s: database opened: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+             name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+  err = FALSE;
+  if(!oditerinit(odeum)){
+    pdperror(name);
+    err = TRUE;
+  } else {
+    while(TRUE){
+      if(sigterm){
+        printferror("aborting due to a termination signal");
+        err = TRUE;
+        break;
+      }
+      if(!(doc = oditernext(odeum))){
+        if(dpecode != DP_ENOITEM){
+          pdperror(name);
+          err = TRUE;
+        }
+        break;
+      }
+      file = oddocuri(doc);
+      if(cbfilestat(file, NULL, NULL, NULL)){
+        printfinfo("%s: passed", file);
+      } else {
+        if(!odout(odeum, file)){
+          pdperror(file);
+          err = TRUE;
+        }
+        printfinfo("%s: purged", file);
+      }
+      oddocclose(doc);
+    }
+  }
+  fatal = odfatalerror(odeum);
+  printfinfo("%s: database closing: fsiz=%.0f dnum=%d wnum=%d bnum=%d",
+             name, odfsiz(odeum), oddnum(odeum), odwnum(odeum), odbnum(odeum));
+  if(!odclose(odeum)){
+    pdperror(name);
+    err = TRUE;
+  }
+  if(err){
+    printfinfo("%s: purging was over%s", name, fatal ? " with fatal error" : "");
+  } else {
+    printfinfo("%s: purging completed successfully", name);
+  }
+  return err ? 1 : 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/odidx.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/odmgr.c
===================================================================
--- box/trunk/qdbm/odmgr.c	                        (rev 0)
+++ box/trunk/qdbm/odmgr.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,1085 @@
+/*************************************************************************************************
+ * Utility for debugging Odeum and its applications
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <cabin.h>
+#include <odeum.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define MAXSRCHWORDS   256               /* max number of search words */
+#define WOCCRPOINT     10000             /* points per occurence */
+#define MAXKEYWORDS    8                 /* max number of keywords */
+#define SUMMARYWIDTH   16                /* width of each phrase in a summary */
+#define MAXSUMMARY     128               /* max number of words in a summary */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+char *readstdin(int *sp);
+void otcb(const char *fname, ODEUM *odeum, const char *msg);
+int runcreate(int argc, char **argv);
+int runput(int argc, char **argv);
+int runout(int argc, char **argv);
+int runget(int argc, char **argv);
+int runsearch(int argc, char **argv);
+int runlist(int argc, char **argv);
+int runoptimize(int argc, char **argv);
+int runinform(int argc, char **argv);
+int runmerge(int argc, char **argv);
+int runremove(int argc, char **argv);
+int runbreak(int argc, char **argv);
+void pdperror(const char *name);
+void printdoc(const ODDOC *doc, int tb, int hb, int score, ODEUM *odeum, const CBLIST *skeys);
+char *docsummary(const ODDOC *doc, const CBLIST *kwords, int num, int hilight);
+CBMAP *listtomap(const CBLIST *list);
+int docreate(const char *name);
+int doput(const char *name, const char *text, const char *uri, const char *title,
+          const char *author, const char *date, int wmax, int keep);
+int doout(const char *name, const char *uri, int id);
+int doget(const char *name, const char *uri, int id, int tb, int hb);
+int dosearch(const char *name, const char *text, int max, int or, int idf, int ql,
+             int tb, int hb, int nb);
+int dolist(const char *name, int tb, int hb);
+int dooptimize(const char *name);
+int doinform(const char *name);
+int domerge(const char *name, const CBLIST *elems);
+int doremove(const char *name);
+int dobreak(const char *text, int hb, int kb, int sb);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  char *env;
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if((env = getenv("QDBMDBGFD")) != NULL) dpdbgfd = atoi(env);
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "create")){
+    odsetotcb(otcb);
+    rv = runcreate(argc, argv);
+  } else if(!strcmp(argv[1], "put")){
+    odsetotcb(otcb);
+    rv = runput(argc, argv);
+  } else if(!strcmp(argv[1], "out")){
+    odsetotcb(otcb);
+    rv = runout(argc, argv);
+  } else if(!strcmp(argv[1], "get")){
+    rv = runget(argc, argv);
+  } else if(!strcmp(argv[1], "search")){
+    rv = runsearch(argc, argv);
+  } else if(!strcmp(argv[1], "list")){
+    rv = runlist(argc, argv);
+  } else if(!strcmp(argv[1], "optimize")){
+    odsetotcb(otcb);
+    rv = runoptimize(argc, argv);
+  } else if(!strcmp(argv[1], "inform")){
+    rv = runinform(argc, argv);
+  } else if(!strcmp(argv[1], "merge")){
+    odsetotcb(otcb);
+    rv = runmerge(argc, argv);
+  } else if(!strcmp(argv[1], "remove")){
+    rv = runremove(argc, argv);
+  } else if(!strcmp(argv[1], "break")){
+    rv = runbreak(argc, argv);
+  } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){
+    printf("Powered by QDBM version %s\n", dpversion);
+    printf("Copyright (c) 2000-2007 Mikio Hirabayashi\n");
+    rv = 0;
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: administration utility for Odeum\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s create name\n", progname);
+  fprintf(stderr, "  %s put [-uri str] [-title str] [-author str] [-date str]"
+          " [-wmax num] [-keep] name [file]\n", progname);
+  fprintf(stderr, "  %s out [-id] name expr\n", progname);
+  fprintf(stderr, "  %s get [-id] [-t|-h] name expr\n", progname);
+  fprintf(stderr, "  %s search [-max num] [-or] [-idf] [-t|-h|-n] name words...\n", progname);
+  fprintf(stderr, "  %s list [-t|-h] name\n", progname);
+  fprintf(stderr, "  %s optimize name\n", progname);
+  fprintf(stderr, "  %s inform name\n", progname);
+  fprintf(stderr, "  %s merge name elems...\n", progname);
+  fprintf(stderr, "  %s remove name\n", progname);
+  fprintf(stderr, "  %s break [-h|-k|-s] [file]\n", progname);
+  fprintf(stderr, "  %s version\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* read the standard input */
+char *readstdin(int *sp){
+  char *buf;
+  int i, blen, c;
+  blen = 256;
+  buf = cbmalloc(blen);
+  for(i = 0; (c = getchar()) != EOF; i++){
+    if(i >= blen - 1) buf = cbrealloc(buf, blen *= 2);
+    buf[i] = c;
+  }
+  buf[i] = '\0';
+  *sp = i;
+  return buf;
+}
+
+
+/* report the outturn */
+void otcb(const char *fname, ODEUM *odeum, const char *msg){
+  char *name;
+  name = odname(odeum);
+  printf("%s: %s: %s: %s\n", progname, fname, name, msg);
+  free(name);
+}
+
+
+/* parse arguments of create command */
+int runcreate(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = docreate(name);
+  return rv;
+}
+
+
+/* parse arguments of put command */
+int runput(int argc, char **argv){
+  char *name, *file, *uri, *title, *author, *date, *text;
+  int i, wmax, keep, size, rv;
+  name = NULL;
+  file = NULL;
+  uri = NULL;
+  title = NULL;
+  author = NULL;
+  date = NULL;
+  wmax = -1;
+  keep = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-uri")){
+        if(++i >= argc) usage();
+        uri = argv[i];
+      } else if(!strcmp(argv[i], "-uri")){
+        if(++i >= argc) usage();
+        uri = argv[i];
+      } else if(!strcmp(argv[i], "-title")){
+        if(++i >= argc) usage();
+        title = argv[i];
+      } else if(!strcmp(argv[i], "-author")){
+        if(++i >= argc) usage();
+        author = argv[i];
+      } else if(!strcmp(argv[i], "-date")){
+        if(++i >= argc) usage();
+        date = argv[i];
+      } else if(!strcmp(argv[i], "-wmax")){
+        if(++i >= argc) usage();
+        wmax = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-keep")){
+        keep = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  if(!uri) uri = file;
+  if(!uri) usage();
+  if(file){
+    if(!(text = cbreadfile(file, &size))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    text = readstdin(&size);
+  }
+  rv = doput(name, text, uri, title, author, date, wmax, keep);
+  free(text);
+  return rv;
+}
+
+
+/* parse arguments of out command */
+int runout(int argc, char **argv){
+  char *name, *expr;
+  int i, ib, id, rv;
+  name = NULL;
+  expr = NULL;
+  ib = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-id")){
+        ib = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!expr){
+      expr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !expr) usage();
+  id = -1;
+  if(ib){
+    id = atoi(expr);
+    if(id < 1) usage();
+  }
+  rv = doout(name, expr, id);
+  return rv;
+}
+
+
+/* parse arguments of get command */
+int runget(int argc, char **argv){
+  char *name, *expr;
+  int i, ib, tb, hb, id, rv;
+  name = NULL;
+  expr = NULL;
+  ib = FALSE;
+  tb = FALSE;
+  hb = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-id")){
+        ib = TRUE;
+      } else if(!strcmp(argv[i], "-t")){
+        tb = TRUE;
+      } else if(!strcmp(argv[i], "-h")){
+        hb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!expr){
+      expr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !expr) usage();
+  id = -1;
+  if(ib){
+    id = atoi(expr);
+    if(id < 1) usage();
+  }
+  rv = doget(name, expr, id, tb, hb);
+  return rv;
+}
+
+
+/* parse arguments of search command */
+int runsearch(int argc, char **argv){
+  char *name, *srchwords[MAXSRCHWORDS];
+  int i, wnum, max, or, idf, ql, tb, hb, nb, rv;
+  CBDATUM *text;
+  name = NULL;
+  wnum = 0;
+  max = -1;
+  or = FALSE;
+  idf = FALSE;
+  ql = FALSE;
+  tb = FALSE;
+  hb = FALSE;
+  nb = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-max")){
+        if(++i >= argc) usage();
+        max = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-or")){
+        or = TRUE;
+      } else if(!strcmp(argv[i], "-idf")){
+        idf = TRUE;
+      } else if(!strcmp(argv[i], "-ql")){
+        ql = TRUE;
+      } else if(!strcmp(argv[i], "-t")){
+        tb = TRUE;
+      } else if(!strcmp(argv[i], "-h")){
+        hb = TRUE;
+      } else if(!strcmp(argv[i], "-n")){
+        nb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(wnum < MAXSRCHWORDS){
+      srchwords[wnum++] = argv[i];
+    }
+  }
+  if(!name) usage();
+  text = cbdatumopen(NULL, -1);
+  for(i = 0; i < wnum; i++){
+    if(i > 0) cbdatumcat(text, " ", 1);
+    cbdatumcat(text, srchwords[i], -1);
+  }
+  rv = dosearch(name, cbdatumptr(text), max, or, idf, ql, tb, hb, nb);
+  cbdatumclose(text);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+int runlist(int argc, char **argv){
+  char *name;
+  int i, tb, hb, rv;
+  name = NULL;
+  tb = FALSE;
+  hb = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-t")){
+        tb = TRUE;
+      } else if(!strcmp(argv[i], "-h")){
+        hb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dolist(name, tb, hb);
+  return rv;
+}
+
+
+/* parse arguments of optimize command */
+int runoptimize(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dooptimize(name);
+  return rv;
+}
+
+
+/* parse arguments of inform command */
+int runinform(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doinform(name);
+  return rv;
+}
+
+
+/* parse arguments of merge command */
+int runmerge(int argc, char **argv){
+  char *name;
+  CBLIST *elems;
+  int i, rv;
+  name = NULL;
+  elems = cblistopen();
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      cblistpush(elems, argv[i], -1);
+    }
+  }
+  if(!name) usage();
+  if(cblistnum(elems) < 1){
+    cblistclose(elems);
+    usage();
+  }
+  rv = domerge(name, elems);
+  cblistclose(elems);
+  return rv;
+}
+
+
+/* parse arguments of remove command */
+int runremove(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doremove(name);
+  return rv;
+}
+
+
+/* parse arguments of break command */
+int runbreak(int argc, char **argv){
+  char *file, *text;
+  int i, hb, kb, sb, size, rv;
+  file = NULL;
+  hb = FALSE;
+  kb = FALSE;
+  sb = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!file && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-h")){
+        hb = TRUE;
+      } else if(!strcmp(argv[i], "-k")){
+        kb = TRUE;
+      } else if(!strcmp(argv[i], "-s")){
+        sb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(file){
+    if(!(text = cbreadfile(file, &size))){
+      fprintf(stderr, "%s: %s: cannot open\n", progname, file);
+      return 1;
+    }
+  } else {
+    text = readstdin(&size);
+  }
+  rv = dobreak(text, hb, kb, sb);
+  free(text);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* print the contents of a document */
+void printdoc(const ODDOC *doc, int tb, int hb, int score, ODEUM *odeum, const CBLIST *skeys){
+  const CBLIST *words;
+  CBMAP *scores;
+  CBLIST *kwords;
+  const char *title, *author, *word, *date;
+  char *summary;
+  int i, wsiz;
+  title = oddocgetattr(doc, "title");
+  author = oddocgetattr(doc, "author");
+  date = oddocgetattr(doc, "date");
+  if(hb){
+    printf("ID: %d\n", oddocid(doc));
+    printf("URI: %s\n", oddocuri(doc));
+    if(title) printf("TITLE: %s\n", title);
+    if(author) printf("AUTHOR: %s\n", author);
+    if(date) printf("DATE: %s\n", date);
+    if(score >= 0) printf("SCORE: %d\n", score);
+    scores = oddocscores(doc, MAXKEYWORDS, odeum);
+    kwords = cblistopen();
+    printf("KEYWORDS: ");
+    cbmapiterinit(scores);
+    while((word = cbmapiternext(scores, &wsiz)) != NULL){
+      if(cblistnum(kwords) > 0) printf(", ");
+      printf("%s (%s)", word, cbmapget(scores, word, wsiz, NULL));
+      cblistpush(kwords, word, wsiz);
+    }
+    putchar('\n');
+    summary = docsummary(doc, skeys ? skeys : kwords, MAXSUMMARY, skeys != NULL);
+    printf("SUMMARY: %s\n", summary);
+    free(summary);
+    cblistclose(kwords);
+    cbmapclose(scores);
+    printf("\n\n");
+  } else if(tb){
+    printf("%d\t%s\t%s\t%s\t%s\t%d\n", oddocid(doc), oddocuri(doc),
+           title ? title : "", author ? author : "", date ? date : "", score);
+    words = oddocnwords(doc);
+    for(i = 0; i < cblistnum(words); i++){
+      word = cblistval(words, i, &wsiz);
+      if(i > 0) putchar('\t');
+      printf("%s", word);
+    }
+    putchar('\n');
+    words = oddocawords(doc);
+    for(i = 0; i < cblistnum(words); i++){
+      word = cblistval(words, i, &wsiz);
+      if(i > 0) putchar('\t');
+      printf("%s", word);
+    }
+    putchar('\n');
+  } else {
+    printf("%d\t%s\t%d\n", oddocid(doc), oddocuri(doc), score);
+  }
+}
+
+
+/* get a list handle contains summary of a document */
+char *docsummary(const ODDOC *doc, const CBLIST *kwords, int num, int hilight){
+  const CBLIST *nwords, *awords;
+  CBMAP *kmap, *map;
+  const char *normal, *asis;
+  char *sbuf;
+  int i, j, bsiz, ssiz, lnum, nwsiz, awsiz, pv, bi, first;
+  bsiz = 256;
+  sbuf = cbmalloc(bsiz);
+  ssiz = 0;
+  nwords = oddocnwords(doc);
+  awords = oddocawords(doc);
+  kmap = listtomap(kwords);
+  map = listtomap(kwords);
+  lnum = cblistnum(nwords);
+  first = TRUE;
+  for(i = 0; i < lnum && i < SUMMARYWIDTH; i++){
+    normal = cblistval(nwords, i, &nwsiz);
+    asis = cblistval(awords, i, &awsiz);
+    if(awsiz < 1) continue;
+    cbmapout(map, normal, nwsiz);
+    if(ssiz + awsiz + 16 >= bsiz){
+      bsiz = bsiz * 2 + awsiz;
+      sbuf = cbrealloc(sbuf, bsiz);
+    }
+    if(!first) ssiz += sprintf(sbuf + ssiz, " ");
+    if(hilight && normal[0] != '\0' && cbmapget(kmap, normal, nwsiz, NULL)){
+      ssiz += sprintf(sbuf + ssiz, "<<%s>>", asis);
+    } else {
+      ssiz += sprintf(sbuf + ssiz, "%s", asis);
+    }
+    first = FALSE;
+    num--;
+  }
+  ssiz += sprintf(sbuf + ssiz, " ...");
+  pv = i;
+  while(i < lnum){
+    if(cbmaprnum(map) < 1){
+      cbmapclose(map);
+      map = listtomap(kwords);
+    }
+    normal = cblistval(nwords, i, &nwsiz);
+    if(cbmapget(map, normal, nwsiz, NULL)){
+      bi = i - SUMMARYWIDTH / 2;
+      bi = bi > pv ? bi : pv;
+      for(j = bi; j < lnum && j <= bi + SUMMARYWIDTH; j++){
+        normal = cblistval(nwords, j, &nwsiz);
+        asis = cblistval(awords, j, &awsiz);
+        if(awsiz < 1) continue;
+        cbmapout(map, normal, nwsiz);
+        if(ssiz + awsiz + 16 >= bsiz){
+          bsiz = bsiz * 2 + awsiz;
+          sbuf = cbrealloc(sbuf, bsiz);
+        }
+        ssiz += sprintf(sbuf + ssiz, " ");
+        if(hilight && normal[0] != '\0' && cbmapget(kmap, normal, nwsiz, NULL)){
+          ssiz += sprintf(sbuf + ssiz, "<<%s>>", asis);
+        } else {
+          ssiz += sprintf(sbuf + ssiz, "%s", asis);
+        }
+        num--;
+      }
+      ssiz += sprintf(sbuf + ssiz, " ...");
+      i = j;
+      pv = i;
+    } else {
+      i++;
+    }
+    if(num <= 0) break;
+  }
+  cbmapclose(map);
+  cbmapclose(kmap);
+  return sbuf;
+}
+
+
+/* get a map made from a list */
+CBMAP *listtomap(const CBLIST *list){
+  CBMAP *map;
+  const char *tmp;
+  int i, tsiz;
+  map = cbmapopen();
+  for(i = 0; i < cblistnum(list); i++){
+    tmp = cblistval(list, i, &tsiz);
+    cbmapput(map, tmp, tsiz, "", 0, FALSE);
+  }
+  return map;
+}
+
+
+/* perform create command */
+int docreate(const char *name){
+  ODEUM *odeum;
+  if(!(odeum = odopen(name, OD_OWRITER | OD_OCREAT | OD_OTRUNC))){
+    pdperror(name);
+    return 1;
+  }
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform put command */
+int doput(const char *name, const char *text, const char *uri, const char *title,
+          const char *author, const char *date, int wmax, int keep){
+  ODEUM *odeum;
+  ODDOC *doc;
+  CBLIST *awords;
+  const char *asis;
+  char *normal;
+  int i;
+  if(!(odeum = odopen(name, OD_OWRITER))){
+    pdperror(name);
+    return 1;
+  }
+  doc = oddocopen(uri);
+  if(title) oddocaddattr(doc, "title", title);
+  if(author) oddocaddattr(doc, "author", author);
+  if(date) oddocaddattr(doc, "date", date);
+  awords = odbreaktext(text);
+  for(i = 0; i < cblistnum(awords); i++){
+    asis = cblistval(awords, i, NULL);
+    normal = odnormalizeword(asis);
+    oddocaddword(doc, normal, asis);
+    free(normal);
+  }
+  cblistclose(awords);
+  if(!odput(odeum, doc, wmax, keep ? FALSE : TRUE)){
+    pdperror(name);
+    oddocclose(doc);
+    odclose(odeum);
+    return 1;
+  }
+  oddocclose(doc);
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform out command */
+int doout(const char *name, const char *uri, int id){
+  ODEUM *odeum;
+  if(!(odeum = odopen(name, OD_OWRITER))){
+    pdperror(name);
+    return 1;
+  }
+  if(id > 0){
+    if(!odoutbyid(odeum, id)){
+      pdperror(name);
+      odclose(odeum);
+      return 1;
+    }
+  } else {
+    if(!odout(odeum, uri)){
+      pdperror(name);
+      odclose(odeum);
+      return 1;
+    }
+  }
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform get command */
+int doget(const char *name, const char *uri, int id, int tb, int hb){
+  ODEUM *odeum;
+  ODDOC *doc;
+  if(!(odeum = odopen(name, OD_OREADER))){
+    pdperror(name);
+    return 1;
+  }
+  if(id > 0){
+    if(!(doc = odgetbyid(odeum, id))){
+      pdperror(name);
+      odclose(odeum);
+      return 1;
+    }
+  } else {
+    if(!(doc = odget(odeum, uri))){
+      pdperror(name);
+      odclose(odeum);
+      return 1;
+    }
+  }
+  printdoc(doc, tb, hb, -1, odeum, NULL);
+  oddocclose(doc);
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform search command */
+int dosearch(const char *name, const char *text, int max, int or, int idf, int ql,
+             int tb, int hb, int nb){
+  ODEUM *odeum;
+  CBLIST *awords, *nwords, *uris, *hits;
+  ODPAIR *pairs, *last, *tmp;
+  ODDOC *doc;
+  const char *asis;
+  char *normal, numbuf[32];
+  int i, j, pnum, lnum, hnum, tnum, shows;
+  double ival;
+  if(!(odeum = odopen(name, OD_OREADER))){
+    pdperror(name);
+    return 1;
+  }
+  awords = odbreaktext(text);
+  nwords = cblistopen();
+  uris = cblistopen();
+  hits = cblistopen();
+  last = NULL;
+  lnum = 0;
+  if(ql){
+    last= odquery(odeum, text, &lnum, NULL);
+  } else {
+    for(i = 0; i < cblistnum(awords); i++){
+      asis = cblistval(awords, i, NULL);
+      normal = odnormalizeword(asis);
+      cblistpush(nwords, normal, -1);
+      if(strlen(normal) < 1){
+        free(normal);
+        continue;
+      }
+      if(!(pairs = odsearch(odeum, normal, or ? max : -1, &pnum))){
+        pdperror(name);
+        free(normal);
+        continue;
+      }
+      if((hnum = odsearchdnum(odeum, normal)) < 0) hnum = 0;
+      if(idf){
+        ival = odlogarithm(hnum);
+        ival = (ival * ival) / 4.0;
+        if(ival < 4.0) ival = 4.0;
+        for(j = 0; j < pnum; j++){
+          pairs[j].score = (int)(pairs[j].score / ival);
+        }
+      }
+      cblistpush(uris, normal, -1);
+      sprintf(numbuf, "%d", hnum);
+      cblistpush(hits, numbuf, -1);
+      if(last){
+        if(or){
+          tmp = odpairsor(last, lnum, pairs, pnum, &tnum);
+        } else {
+          tmp = odpairsand(last, lnum, pairs, pnum, &tnum);
+        }
+        free(last);
+        free(pairs);
+        last = tmp;
+        lnum = tnum;
+      } else {
+        last = pairs;
+        lnum = pnum;
+      }
+      free(normal);
+    }
+  }
+  if(hb){
+    printf("TOTAL: %d\n", lnum);
+    printf("EACHWORD: ");
+  } else {
+    printf("%d", lnum);
+  }
+  for(i = 0; i < cblistnum(uris); i++){
+    if(hb){
+      if(i > 0) printf(", ");
+      printf("%s(%s)", cblistval(uris, i, NULL), cblistval(hits, i, NULL));
+    } else {
+      printf("\t%s\t%s", cblistval(uris, i, NULL), cblistval(hits, i, NULL));
+    }
+  }
+  putchar('\n');
+  if(hb) putchar('\n');
+  if(last){
+    if(max < 0) max = lnum;
+    shows = 0;
+    for(i = 0; i < lnum && shows < max; i++){
+      if(nb){
+        printf("%d\t%d\n", last[i].id, last[i].score);
+        shows++;
+      } else {
+        if(!(doc = odgetbyid(odeum, last[i].id))) continue;
+        printdoc(doc, tb, hb, last[i].score, odeum, nwords);
+        oddocclose(doc);
+        shows++;
+      }
+    }
+    free(last);
+  }
+  cblistclose(uris);
+  cblistclose(hits);
+  cblistclose(nwords);
+  cblistclose(awords);
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform list command */
+int dolist(const char *name, int tb, int hb){
+  ODEUM *odeum;
+  ODDOC *doc;
+  if(!(odeum = odopen(name, OD_OREADER))){
+    pdperror(name);
+    return 1;
+  }
+  if(!oditerinit(odeum)){
+    odclose(odeum);
+    pdperror(name);
+    return 1;
+  }
+  while(TRUE){
+    if(!(doc = oditernext(odeum))){
+      if(dpecode == DP_ENOITEM) break;
+      odclose(odeum);
+      pdperror(name);
+      return 1;
+    }
+    printdoc(doc, tb, hb, -1, odeum, NULL);
+    oddocclose(doc);
+  }
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform optimize command */
+int dooptimize(const char *name){
+  ODEUM *odeum;
+  if(!(odeum = odopen(name, OD_OWRITER))){
+    pdperror(name);
+    return 1;
+  }
+  if(!odoptimize(odeum)){
+    pdperror(name);
+    odclose(odeum);
+    return 1;
+  }
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform inform command */
+int doinform(const char *name){
+  ODEUM *odeum;
+  char *tmp;
+  if(!(odeum = odopen(name, OD_OREADER))){
+    pdperror(name);
+    return 1;
+  }
+  tmp = odname(odeum);
+  printf("name: %s\n", tmp ? tmp : "(null)");
+  free(tmp);
+  printf("file size: %.0f\n", odfsiz(odeum));
+  printf("index buckets: %d\n", odbnum(odeum));
+  printf("used buckets: %d\n", odbusenum(odeum));
+  printf("all documents: %d\n", oddnum(odeum));
+  printf("all words: %d\n", odwnum(odeum));
+  printf("inode number: %d\n", odinode(odeum));
+  printf("modified time: %.0f\n", (double)odmtime(odeum));
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform merge command */
+int domerge(const char *name, const CBLIST *elems){
+  if(!odmerge(name, elems)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform remove command */
+int doremove(const char *name){
+  if(!odremove(name)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform break command */
+int dobreak(const char *text, int hb, int kb, int sb){
+  CBLIST *awords, *kwords;
+  CBMAP *scores;
+  ODDOC *doc;
+  const char *asis;
+  char *normal, *summary;
+  int i, first;
+  awords = odbreaktext(text);
+  if(kb || sb){
+    doc = oddocopen("");
+    for(i = 0; i < cblistnum(awords); i++){
+      asis = cblistval(awords, i, NULL);
+      normal = odnormalizeword(asis);
+      oddocaddword(doc, normal, asis);
+      free(normal);
+    }
+    scores = oddocscores(doc, MAXKEYWORDS, NULL);
+    cbmapiterinit(scores);
+    kwords = cbmapkeys(scores);
+    if(kb){
+      for(i = 0; i < cblistnum(kwords); i++){
+        if(i > 0) putchar('\t');
+        printf("%s", cblistval(kwords, i, NULL));
+      }
+      putchar('\n');
+    } else {
+      summary = docsummary(doc, kwords, MAXSUMMARY, FALSE);
+      printf("%s\n", summary);
+      free(summary);
+    }
+    cblistclose(kwords);
+    cbmapclose(scores);
+    oddocclose(doc);
+  } else if(hb){
+    printf("NWORDS: ");
+    first = TRUE;
+    for(i = 0; i < cblistnum(awords); i++){
+      asis = cblistval(awords, i, NULL);
+      normal = odnormalizeword(asis);
+      if(normal[0] == '\0'){
+        free(normal);
+        continue;
+      }
+      if(!first) putchar(' ');
+      first = FALSE;
+      printf("%s", normal);
+      free(normal);
+    }
+    putchar('\n');
+    printf("AWORDS: ");
+    first = TRUE;
+    for(i = 0; i < cblistnum(awords); i++){
+      asis = cblistval(awords, i, NULL);
+      if(asis[0] == '\0') continue;
+      if(!first) putchar(' ');
+      first = FALSE;
+      printf("%s", asis);
+    }
+    putchar('\n');
+  } else {
+    for(i = 0; i < cblistnum(awords); i++){
+      asis = cblistval(awords, i, NULL);
+      normal = odnormalizeword(asis);
+      printf("%s\t%s\n", normal, asis);
+      free(normal);
+    }
+  }
+  cblistclose(awords);
+  return 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/odmgr.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/odtest.c
===================================================================
--- box/trunk/qdbm/odtest.c	                        (rev 0)
+++ box/trunk/qdbm/odtest.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,694 @@
+/*************************************************************************************************
+ * Test cases of Odeum
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <cabin.h>
+#include <odeum.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <time.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define DOCBUFSIZ      256               /* buffer for documents */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+int runwrite(int argc, char **argv);
+int runread(int argc, char **argv);
+int runcombo(int argc, char **argv);
+int runwicked(int argc, char **argv);
+int printfflush(const char *format, ...);
+void pdperror(const char *name);
+int myrand(void);
+ODDOC *makedoc(int id, int wnum, int pnum);
+int dowrite(const char *name, int dnum, int wnum, int pnum,
+            int ibnum, int idnum, int cbnum, int csiz);
+int doread(const char *name);
+int docombo(const char *name);
+int dowicked(const char *name, int dnum);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  char *env;
+  int rv;
+  cbstdiobin();
+  if((env = getenv("QDBMDBGFD")) != NULL) dpdbgfd = atoi(env);
+  progname = argv[0];
+  if(argc < 2) usage();
+  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], "combo")){
+    rv = runcombo(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else {
+    usage();
+  }
+  return 0;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: test cases for Odeum\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write [-tune ibnum idnum cbnum csiz] name dnum wnum pnum\n", progname);
+  fprintf(stderr, "  %s read name\n", progname);
+  fprintf(stderr, "  %s combo name\n", progname);
+  fprintf(stderr, "  %s wicked name dnum\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of write command */
+int runwrite(int argc, char **argv){
+  char *name, *dstr, *wstr, *pstr;
+  int i, dnum, wnum, pnum, ibnum, idnum, cbnum, csiz, rv;
+  name = NULL;
+  dstr = NULL;
+  wstr = NULL;
+  pstr = NULL;
+  dnum = 0;
+  wnum = 0;
+  pnum = 0;
+  ibnum = -1;
+  idnum = -1;
+  cbnum = -1;
+  csiz = -1;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-tune")){
+        if(++i >= argc) usage();
+        ibnum = atoi(argv[i]);
+        if(++i >= argc) usage();
+        idnum = atoi(argv[i]);
+        if(++i >= argc) usage();
+        cbnum = atoi(argv[i]);
+        if(++i >= argc) usage();
+        csiz = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!dstr){
+      dstr = argv[i];
+    } else if(!wstr){
+      wstr = argv[i];
+    } else if(!pstr){
+      pstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !dstr || !wstr || !pstr) usage();
+  dnum = atoi(dstr);
+  wnum = atoi(wstr);
+  pnum = atoi(pstr);
+  if(dnum < 1 || wnum < 1 || pnum < 1) usage();
+  rv = dowrite(name, dnum, wnum, pnum, ibnum, idnum, cbnum, csiz);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+int runread(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doread(name);
+  return rv;
+}
+
+
+/* parse arguments of combo command */
+int runcombo(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = docombo(name);
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+int runwicked(int argc, char **argv){
+  char *name, *dstr;
+  int i, dnum, rv;
+  name = NULL;
+  dstr = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else if(!dstr){
+      dstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !dstr) usage();
+  dnum = atoi(dstr);
+  if(dnum < 1) usage();
+  rv = dowicked(name, dnum);
+  return rv;
+}
+
+
+/* print formatted string and flush the buffer */
+int printfflush(const char *format, ...){
+  va_list ap;
+  int rv;
+  va_start(ap, format);
+  rv = vprintf(format, ap);
+  if(fflush(stdout) == EOF) rv = -1;
+  va_end(ap);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* pseudo random number generator */
+int myrand(void){
+  static int cnt = 0;
+  if(cnt == 0) srand(time(NULL));
+  return (rand() * rand() + (rand() >> (sizeof(int) * 4)) + (cnt++)) & INT_MAX;
+}
+
+
+/* create a document */
+ODDOC *makedoc(int id, int wnum, int pnum){
+  ODDOC *doc;
+  char buf[DOCBUFSIZ];
+  int i;
+  sprintf(buf, "%08d", id);
+  doc = oddocopen(buf);
+  oddocaddattr(doc, "title", buf);
+  oddocaddattr(doc, "author", buf);
+  oddocaddattr(doc, "date", buf);
+  for(i = 0; i < wnum; i++){
+    sprintf(buf, "%08d", myrand() % pnum);
+    oddocaddword(doc, buf, buf);
+  }
+  return doc;
+}
+
+
+/* perform write command */
+int dowrite(const char *name, int dnum, int wnum, int pnum,
+            int ibnum, int idnum, int cbnum, int csiz){
+  ODEUM *odeum;
+  ODDOC *doc;
+  int i, err;
+  printfflush("<Writing Test>\n  name=%s  dnum=%d  wnum=%d  pnum=%d"
+              "  ibnum=%d  idnum=%d  cbnum=%d  csiz=%d\n\n",
+              name, dnum, wnum, pnum, ibnum, idnum, cbnum, csiz);
+  /* open a database */
+  if(ibnum > 0) odsettuning(ibnum, idnum, cbnum, csiz);
+  if(!(odeum = odopen(name, OD_OWRITER | OD_OCREAT | OD_OTRUNC))){
+    pdperror(name);
+    return 1;
+  }
+  err = FALSE;
+  /* loop for each document */
+  for(i = 1; i <= dnum; i++){
+    /* store a document */
+    doc = makedoc(i, wnum, pnum);
+    if(!odput(odeum, doc, -1, FALSE)){
+      pdperror(name);
+      oddocclose(doc);
+      err = TRUE;
+      break;
+    }
+    oddocclose(doc);
+    /* print progression */
+    if(dnum > 250 && i % (dnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == dnum || i % (dnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform read command */
+int doread(const char *name){
+  ODEUM *odeum;
+  ODDOC *doc;
+  char buf[DOCBUFSIZ];
+  int i, dnum, err;
+  printfflush("<Reading Test>\n  name=%s\n\n", name);
+  /* open a database */
+  if(!(odeum = odopen(name, OD_OREADER))){
+    pdperror(name);
+    return 1;
+  }
+  /* get the number of documents */
+  dnum = oddnum(odeum);
+  err = FALSE;
+  /* loop for each document */
+  for(i = 1; i <= dnum; i++){
+    /* retrieve a document */
+    sprintf(buf, "%08d", i);
+    if(!(doc = odget(odeum, buf))){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    oddocclose(doc);
+    /* print progression */
+    if(dnum > 250 && i % (dnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == dnum || i % (dnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform combo command */
+int docombo(const char *name){
+  ODEUM *odeum;
+  ODDOC *doc;
+  const CBLIST *nwords, *awords;
+  CBLIST *tawords, *tnwords, *oawords;
+  ODPAIR *pairs;
+  const char *asis;
+  char buf[DOCBUFSIZ], *normal;
+  int i, j, pnum;
+  printfflush("<Combination Test>\n  name=%s\n\n", name);
+  printfflush("Creating a database with ... ");
+  if(!(odeum = odopen(name, OD_OWRITER | OD_OCREAT | OD_OTRUNC))){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 20 documents including about 200 words ... ");
+  for(i = 1; i <= 20; i++){
+    sprintf(buf, "%08d", i);
+    doc = makedoc(i, 120 + myrand() % 160, myrand() % 500 + 500);
+    if(!odput(odeum, doc, 180 + myrand() % 40, FALSE)){
+      pdperror(name);
+      oddocclose(doc);
+      odclose(odeum);
+      return 1;
+    }
+    oddocclose(doc);
+  }
+  printfflush("ok\n");
+  printfflush("Checking documents ... ");
+  for(i = 1; i <= 20; i++){
+    sprintf(buf, "%08d", i);
+    if(!(doc = odget(odeum, buf))){
+      pdperror(name);
+      return 1;
+    }
+    nwords = oddocnwords(doc);
+    awords = oddocawords(doc);
+    if(!oddocuri(doc) || !oddocgetattr(doc, "title") || cblistnum(nwords) != cblistnum(awords)){
+      fprintf(stderr, "%s: %s: invalid document\n", progname, name);
+      oddocclose(doc);
+      odclose(odeum);
+      return 1;
+    }
+    for(j = 0; j < cblistnum(nwords); j++){
+      if(strcmp(cblistval(nwords, j, NULL), cblistval(nwords, j, NULL))){
+        fprintf(stderr, "%s: %s: invalid words\n", progname, name);
+        oddocclose(doc);
+        odclose(odeum);
+        return 1;
+      }
+    }
+    oddocclose(doc);
+  }
+  printfflush("ok\n");
+  printfflush("Syncing the database ... ");
+  if(!odsync(odeum)){
+    pdperror(name);
+    odclose(odeum);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Overwriting 1 - 10 documents ... ");
+  for(i = 1; i <= 10; i++){
+    sprintf(buf, "%08d", i);
+    doc = makedoc(i, 120 + myrand() % 160, myrand() % 500 + 500);
+    if(!odput(odeum, doc, 180 + myrand() % 40, TRUE)){
+      pdperror(name);
+      oddocclose(doc);
+      odclose(odeum);
+      return 1;
+    }
+    oddocclose(doc);
+  }
+  printfflush("ok\n");
+  printfflush("Deleting 11 - 20 documents ... ");
+  for(i = 11; i <= 20; i++){
+    sprintf(buf, "%08d", i);
+    if(!odout(odeum, buf)){
+      pdperror(name);
+      odclose(odeum);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking documents ... ");
+  for(i = 1; i <= 10; i++){
+    sprintf(buf, "%08d", i);
+    if(!(doc = odget(odeum, buf))){
+      pdperror(name);
+      return 1;
+    }
+    nwords = oddocnwords(doc);
+    awords = oddocawords(doc);
+    if(!oddocuri(doc) || !oddocgetattr(doc, "title") || cblistnum(nwords) != cblistnum(awords)){
+      fprintf(stderr, "%s: %s: invalid document\n", progname, name);
+      oddocclose(doc);
+      odclose(odeum);
+      return 1;
+    }
+    for(j = 0; j < cblistnum(nwords); j++){
+      if(strcmp(cblistval(nwords, j, NULL), cblistval(nwords, j, NULL))){
+        fprintf(stderr, "%s: %s: invalid words\n", progname, name);
+        oddocclose(doc);
+        odclose(odeum);
+        return 1;
+      }
+    }
+    oddocclose(doc);
+  }
+  if(oddnum(odeum) != 10){
+    fprintf(stderr, "%s: %s: invalid document number\n", progname, name);
+    odclose(odeum);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Optimizing the database ... ");
+  if(!odoptimize(odeum)){
+    pdperror(name);
+    odclose(odeum);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 10 documents including about 200 words ... ");
+  for(i = 11; i <= 20; i++){
+    sprintf(buf, "%08d", i);
+    doc = makedoc(i, 120 + myrand() % 160, myrand() % 500 + 500);
+    if(!odput(odeum, doc, 180 + myrand() % 40, FALSE)){
+      pdperror(name);
+      oddocclose(doc);
+      odclose(odeum);
+      return 1;
+    }
+    oddocclose(doc);
+  }
+  printfflush("ok\n");
+  printfflush("Deleting 6 - 15 documents ... ");
+  for(i = 6; i <= 15; i++){
+    sprintf(buf, "%08d", i);
+    if(!odout(odeum, buf)){
+      pdperror(name);
+      odclose(odeum);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Retrieving documents 100 times ... ");
+  for(i = 1; i <= 100; i++){
+    sprintf(buf, "%08d", myrand() % 1000 + 1);
+    if((pairs = odsearch(odeum, buf, -1, &pnum)) != NULL){
+      for(j = 0; j < pnum; j++){
+        if((doc = odgetbyid(odeum, pairs[j].id)) != NULL){
+          oddocclose(doc);
+        } else if(dpecode != DP_ENOITEM){
+          pdperror(name);
+          odclose(odeum);
+          return 1;
+        }
+      }
+      free(pairs);
+    } else if(dpecode != DP_ENOITEM){
+      pdperror(name);
+      odclose(odeum);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Analyzing text ... ");
+  tawords = cblistopen();
+  tnwords = cblistopen();
+  odanalyzetext(odeum, "I'd like to ++see++ Mr. X-men tomorrow.", tawords, tnwords);
+  odanalyzetext(odeum, "=== :-) SMILE . @ . SAD :-< ===", tawords, tnwords);
+  for(i = 0; i < DOCBUFSIZ - 1; i++){
+    buf[i] = myrand() % 255 + 1;
+  }
+  buf[DOCBUFSIZ-1] = '\0';
+  cblistclose(tnwords);
+  cblistclose(tawords);
+  for(i = 0; i < 1000; i++){
+    for(j = 0; j < DOCBUFSIZ - 1; j++){
+      if((j + 1) % 32 == 0){
+        buf[j] = ' ';
+      } else {
+        buf[j] = myrand() % 255 + 1;
+      }
+    }
+    buf[DOCBUFSIZ-1] = '\0';
+    tawords = cblistopen();
+    tnwords = cblistopen();
+    odanalyzetext(odeum, buf, tawords, tnwords);
+    oawords = odbreaktext(buf);
+    if(cblistnum(tawords) != cblistnum(oawords) || cblistnum(tnwords) != cblistnum(oawords)){
+      fprintf(stderr, "%s: %s: invalid analyzing\n", progname, name);
+      cblistclose(oawords);
+      cblistclose(tnwords);
+      cblistclose(tawords);
+      odclose(odeum);
+      return 1;
+    }
+    for(j = 0; j < cblistnum(oawords); j++){
+      asis = cblistval(oawords, j, NULL);
+      normal = odnormalizeword(asis);
+      if(strcmp(asis, cblistval(oawords, j, NULL)) || strcmp(normal, cblistval(tnwords, j, NULL))){
+        fprintf(stderr, "%s: %s: invalid analyzing\n", progname, name);
+        free(normal);
+        cblistclose(oawords);
+        cblistclose(tnwords);
+        cblistclose(tawords);
+        odclose(odeum);
+        return 1;
+      }
+      free(normal);
+    }
+    cblistclose(oawords);
+    cblistclose(tnwords);
+    cblistclose(tawords);
+  }
+  printfflush("ok\n");
+  printfflush("Closing the database ... ");
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("all ok\n\n");
+  return 0;
+}
+
+
+/* perform wicked command */
+int dowicked(const char *name, int dnum){
+  ODEUM *odeum;
+  ODDOC *doc;
+  ODPAIR *pairs;
+  char buf[DOCBUFSIZ];
+  int i, j, pnum, err;
+  printfflush("<Wicked Writing Test>\n  name=%s  dnum=%d\n\n", name, dnum);
+  err = FALSE;
+  if(!(odeum = odopen(name, OD_OWRITER | OD_OCREAT | OD_OTRUNC))){
+    pdperror(name);
+    return 1;
+  }
+  for(i = 1; i <= dnum; i++){
+    switch(myrand() % 8){
+    case 1:
+      putchar('K');
+      doc = makedoc(myrand() % dnum + 1, myrand() % 10 + 10, myrand() % dnum + 500);
+      if(!odput(odeum, doc, 5, FALSE) && dpecode != DP_EKEEP) err = TRUE;
+      oddocclose(doc);
+      break;
+    case 3:
+      putchar('D');
+      if(!odoutbyid(odeum, myrand() % dnum + 1) && dpecode != DP_ENOITEM) err = TRUE;
+      break;
+    case 4:
+      putchar('R');
+      sprintf(buf, "%08d", myrand() % (dnum + 500) + 1);
+      if((pairs = odsearch(odeum, buf, 5, &pnum)) != NULL){
+        if(myrand() % 5 == 0){
+          for(j = 0; j < pnum; j++){
+            if((doc = odgetbyid(odeum, pairs[j].id)) != NULL){
+              oddocclose(doc);
+            } else if(dpecode != DP_ENOITEM){
+              err = TRUE;
+              break;
+            }
+          }
+        }
+        free(pairs);
+      } else if(dpecode != DP_ENOITEM){
+        err = TRUE;
+      }
+      break;
+    default:
+      putchar('O');
+      doc = makedoc(myrand() % dnum + 1, myrand() % 10 + 10, myrand() % dnum + 500);
+      if(!odput(odeum, doc, 5, TRUE)) err = TRUE;
+      oddocclose(doc);
+      break;
+    }
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+    if(err){
+      pdperror(name);
+      break;
+    }
+  }
+  if(!odoptimize(odeum)){
+    pdperror(name);
+    err = TRUE;
+  }
+  for(i = 1; i <= dnum; i++){
+    doc = makedoc(i, 5, 5);
+    if(!odput(odeum, doc, 5, FALSE) && dpecode != DP_EKEEP){
+      pdperror(name);
+      oddocclose(doc);
+      err = TRUE;
+      break;
+    }
+    oddocclose(doc);
+    putchar(':');
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  if(!odoptimize(odeum)){
+    pdperror(name);
+    err = TRUE;
+  }
+  for(i = 1; i <= dnum; i++){
+    sprintf(buf, "%08d", i);
+    if(!(doc = odget(odeum, buf))){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    oddocclose(doc);
+    putchar('=');
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  if(!oditerinit(odeum)){
+    pdperror(name);
+    err = TRUE;
+  }
+  for(i = 1; i <= dnum; i++){
+    if(!(doc = oditernext(odeum))){
+      pdperror(name);
+      err = TRUE;
+      break;
+    }
+    oddocclose(doc);
+    putchar('@');
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+  }
+  if(!odclose(odeum)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/odtest.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/qdbm.def
===================================================================
--- box/trunk/qdbm/qdbm.def	                        (rev 0)
+++ box/trunk/qdbm/qdbm.def	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,424 @@
+EXPORTS
+  VL_CMPDEC = VL_CMPDEC DATA
+  VL_CMPINT = VL_CMPINT DATA
+  VL_CMPLEX = VL_CMPLEX DATA
+  VL_CMPNUM = VL_CMPNUM DATA
+  VST_CMPDEC = VST_CMPDEC DATA
+  VST_CMPINT = VST_CMPINT DATA
+  VST_CMPLEX = VST_CMPLEX DATA
+  VST_CMPNUM = VST_CMPNUM DATA
+  cbfatalfunc = cbfatalfunc DATA
+  dpdbgfd = dpdbgfd DATA
+  dpisreentrant = dpisreentrant DATA
+  dpsysname = dpsysname DATA
+  dpversion = dpversion DATA
+  gdbm_version = gdbm_version DATA
+  odcachebnum = odcachebnum DATA
+  odcachesiz = odcachesiz DATA
+  odindexbnum = odindexbnum DATA
+  odindexdnum = odindexdnum DATA
+  odotcb = odotcb DATA
+  cbbasedecode = cbbasedecode
+  cbbaseencode = cbbaseencode
+  cbbzdecode = cbbzdecode
+  cbbzencode = cbbzencode
+  cbcalendar = cbcalendar
+  cbcsvcells = cbcsvcells
+  cbcsvescape = cbcsvescape
+  cbcsvrows = cbcsvrows
+  cbcsvunescape = cbcsvunescape
+  cbdatestrhttp = cbdatestrhttp
+  cbdatestrwww = cbdatestrwww
+  cbdatumcat = cbdatumcat
+  cbdatumclose = cbdatumclose
+  cbdatumdup = cbdatumdup
+  cbdatumopen = cbdatumopen
+  cbdatumopenbuf = cbdatumopenbuf
+  cbdatumprintf = cbdatumprintf
+  cbdatumptr = cbdatumptr
+  cbdatumsetbuf = cbdatumsetbuf
+  cbdatumsetsize = cbdatumsetsize
+  cbdatumsize = cbdatumsize
+  cbdatumtomalloc = cbdatumtomalloc
+  cbdayofweek = cbdayofweek
+  cbdeflate = cbdeflate
+  cbdirlist = cbdirlist
+  cbencname = cbencname
+  cbfilestat = cbfilestat
+  cbfree = cbfree
+  cbgetcrc = cbgetcrc
+  cbggcsweep = cbggcsweep
+  cbglobalgc = cbglobalgc
+  cbgzdecode = cbgzdecode
+  cbgzencode = cbgzencode
+  cbheapclose = cbheapclose
+  cbheapdup = cbheapdup
+  cbheapinsert = cbheapinsert
+  cbheapnum = cbheapnum
+  cbheapopen = cbheapopen
+  cbheaptomalloc = cbheaptomalloc
+  cbheapval = cbheapval
+  cbhsort = cbhsort
+  cbiconv = cbiconv
+  cbinflate = cbinflate
+  cbisort = cbisort
+  cbjetlag = cbjetlag
+  cblistbsearch = cblistbsearch
+  cblistclose = cblistclose
+  cblistdump = cblistdump
+  cblistdup = cblistdup
+  cblistinsert = cblistinsert
+  cblistload = cblistload
+  cblistlsearch = cblistlsearch
+  cblistnum = cblistnum
+  cblistopen = cblistopen
+  cblistover = cblistover
+  cblistpop = cblistpop
+  cblistpush = cblistpush
+  cblistpushbuf = cblistpushbuf
+  cblistremove = cblistremove
+  cblistshift = cblistshift
+  cblistsort = cblistsort
+  cblistunshift = cblistunshift
+  cblistval = cblistval
+  cblzodecode = cblzodecode
+  cblzoencode = cblzoencode
+  cbmalloc = cbmalloc
+  cbmapclose = cbmapclose
+  cbmapdump = cbmapdump
+  cbmapdup = cbmapdup
+  cbmapget = cbmapget
+  cbmapiterinit = cbmapiterinit
+  cbmapiternext = cbmapiternext
+  cbmapiterval = cbmapiterval
+  cbmapkeys = cbmapkeys
+  cbmapload = cbmapload
+  cbmaploadone = cbmaploadone
+  cbmapmove = cbmapmove
+  cbmapopen = cbmapopen
+  cbmapopenex = cbmapopenex
+  cbmapout = cbmapout
+  cbmapput = cbmapput
+  cbmapputcat = cbmapputcat
+  cbmaprnum = cbmaprnum
+  cbmapvals = cbmapvals
+  cbmemdup = cbmemdup
+  cbmimebreak = cbmimebreak
+  cbmimedecode = cbmimedecode
+  cbmimeencode = cbmimeencode
+  cbmimeparts = cbmimeparts
+  cbmyfatal = cbmyfatal
+  cbproctime = cbproctime
+  cbqsort = cbqsort
+  cbquotedecode = cbquotedecode
+  cbquoteencode = cbquoteencode
+  cbreadfile = cbreadfile
+  cbreadlines = cbreadlines
+  cbrealloc = cbrealloc
+  cbremove = cbremove
+  cbreplace = cbreplace
+  cbsplit = cbsplit
+  cbsprintf = cbsprintf
+  cbssort = cbssort
+  cbstdiobin = cbstdiobin
+  cbstrbwimatch = cbstrbwimatch
+  cbstrbwmatch = cbstrbwmatch
+  cbstrcountutf = cbstrcountutf
+  cbstrcututf = cbstrcututf
+  cbstrfwimatch = cbstrfwimatch
+  cbstrfwmatch = cbstrfwmatch
+  cbstricmp = cbstricmp
+  cbstrmktime = cbstrmktime
+  cbstrsqzspc = cbstrsqzspc
+  cbstrstrbm = cbstrstrbm
+  cbstrstrkmp = cbstrstrkmp
+  cbstrtolower = cbstrtolower
+  cbstrtoupper = cbstrtoupper
+  cbstrtrim = cbstrtrim
+  cburlbreak = cburlbreak
+  cburldecode = cburldecode
+  cburlencode = cburlencode
+  cburlresolve = cburlresolve
+  cbvmemavail = cbvmemavail
+  cbwritefile = cbwritefile
+  cbxmlattrs = cbxmlattrs
+  cbxmlbreak = cbxmlbreak
+  cbxmlescape = cbxmlescape
+  cbxmlunescape = cbxmlunescape
+  crbnum = crbnum
+  crbusenum = crbusenum
+  crclose = crclose
+  crexportdb = crexportdb
+  crfatalerror = crfatalerror
+  crfsiz = crfsiz
+  crfsizd = crfsizd
+  crget = crget
+  crgetflags = crgetflags
+  crgetlob = crgetlob
+  crgetlobfd = crgetlobfd
+  crgetwb = crgetwb
+  crimportdb = crimportdb
+  crinode = crinode
+  criterinit = criterinit
+  criternext = criternext
+  crmemflush = crmemflush
+  crmemsync = crmemsync
+  crmtime = crmtime
+  crname = crname
+  cropen = cropen
+  croptimize = croptimize
+  crout = crout
+  croutlob = croutlob
+  crput = crput
+  crputlob = crputlob
+  crremove = crremove
+  crrepair = crrepair
+  crrnum = crrnum
+  crrnumlob = crrnumlob
+  crsetalign = crsetalign
+  crsetfbpsiz = crsetfbpsiz
+  crsetflags = crsetflags
+  crsnaffle = crsnaffle
+  crsync = crsync
+  crvsiz = crvsiz
+  crvsizlob = crvsizlob
+  crwritable = crwritable
+  dbm_clearerr = dbm_clearerr
+  dbm_close = dbm_close
+  dbm_delete = dbm_delete
+  dbm_dirfno = dbm_dirfno
+  dbm_error = dbm_error
+  dbm_fetch = dbm_fetch
+  dbm_firstkey = dbm_firstkey
+  dbm_nextkey = dbm_nextkey
+  dbm_open = dbm_open
+  dbm_pagfno = dbm_pagfno
+  dbm_rdonly = dbm_rdonly
+  dbm_store = dbm_store
+  dpbnum = dpbnum
+  dpbusenum = dpbusenum
+  dpclose = dpclose
+  dpecodeptr = dpecodeptr
+  dpecodeset = dpecodeset
+  dperrmsg = dperrmsg
+  dpexportdb = dpexportdb
+  dpfatalerror = dpfatalerror
+  dpfdesc = dpfdesc
+  dpfsiz = dpfsiz
+  dpget = dpget
+  dpgetflags = dpgetflags
+  dpgetwb = dpgetwb
+  dpimportdb = dpimportdb
+  dpinnerhash = dpinnerhash
+  dpinode = dpinode
+  dpiterinit = dpiterinit
+  dpiternext = dpiternext
+  dpmemflush = dpmemflush
+  dpmemsync = dpmemsync
+  dpmtime = dpmtime
+  dpname = dpname
+  dpopen = dpopen
+  dpoptimize = dpoptimize
+  dpout = dpout
+  dpouterhash = dpouterhash
+  dpprimenum = dpprimenum
+  dpput = dpput
+  dpremove = dpremove
+  dprepair = dprepair
+  dprnum = dprnum
+  dpsetalign = dpsetalign
+  dpsetfbpsiz = dpsetfbpsiz
+  dpsetflags = dpsetflags
+  dpsnaffle = dpsnaffle
+  dpsync = dpsync
+  dpvsiz = dpvsiz
+  dpwritable = dpwritable
+  gdbm_close = gdbm_close
+  gdbm_delete = gdbm_delete
+  gdbm_errnoptr = gdbm_errnoptr
+  gdbm_exists = gdbm_exists
+  gdbm_fdesc = gdbm_fdesc
+  gdbm_fetch = gdbm_fetch
+  gdbm_firstkey = gdbm_firstkey
+  gdbm_nextkey = gdbm_nextkey
+  gdbm_open = gdbm_open
+  gdbm_open2 = gdbm_open2
+  gdbm_reorganize = gdbm_reorganize
+  gdbm_setopt = gdbm_setopt
+  gdbm_store = gdbm_store
+  gdbm_strerror = gdbm_strerror
+  gdbm_sync = gdbm_sync
+  odanalyzetext = odanalyzetext
+  odbnum = odbnum
+  odbreaktext = odbreaktext
+  odbusenum = odbusenum
+  odcheck = odcheck
+  odclose = odclose
+  oddnum = oddnum
+  oddocaddattr = oddocaddattr
+  oddocaddword = oddocaddword
+  oddocawords = oddocawords
+  oddocclose = oddocclose
+  oddocgetattr = oddocgetattr
+  oddocid = oddocid
+  oddocnwords = oddocnwords
+  oddocopen = oddocopen
+  oddocscores = oddocscores
+  oddocuri = oddocuri
+  odfatalerror = odfatalerror
+  odfsiz = odfsiz
+  odget = odget
+  odgetbyid = odgetbyid
+  odgetidbyuri = odgetidbyuri
+  odidbdocs = odidbdocs
+  odidbindex = odidbindex
+  odidbrdocs = odidbrdocs
+  odinode = odinode
+  oditerinit = oditerinit
+  oditernext = oditernext
+  odlogarithm = odlogarithm
+  odmerge = odmerge
+  odmtime = odmtime
+  odname = odname
+  odnormalizeword = odnormalizeword
+  odopen = odopen
+  odoptimize = odoptimize
+  odout = odout
+  odoutbyid = odoutbyid
+  odpairsand = odpairsand
+  odpairsnotand = odpairsnotand
+  odpairsor = odpairsor
+  odpairssort = odpairssort
+  odput = odput
+  odquery = odquery
+  odremove = odremove
+  odsearch = odsearch
+  odsearchdnum = odsearchdnum
+  odsetcharclass = odsetcharclass
+  odsetotcb = odsetotcb
+  odsettuning = odsettuning
+  odsquareroot = odsquareroot
+  odsync = odsync
+  odvecabsolute = odvecabsolute
+  odvecinnerproduct = odvecinnerproduct
+  odvectorcosine = odvectorcosine
+  odwnum = odwnum
+  odwritable = odwritable
+  vlclose = vlclose
+  vlcrdnumptr = vlcrdnumptr
+  vlcurfirst = vlcurfirst
+  vlcurjump = vlcurjump
+  vlcurkey = vlcurkey
+  vlcurkeycache = vlcurkeycache
+  vlcurlast = vlcurlast
+  vlcurnext = vlcurnext
+  vlcurout = vlcurout
+  vlcurprev = vlcurprev
+  vlcurput = vlcurput
+  vlcurval = vlcurval
+  vlcurvalcache = vlcurvalcache
+  vlexportdb = vlexportdb
+  vlfatalerror = vlfatalerror
+  vlfsiz = vlfsiz
+  vlget = vlget
+  vlgetcache = vlgetcache
+  vlgetcat = vlgetcat
+  vlgetflags = vlgetflags
+  vlgetlist = vlgetlist
+  vlimportdb = vlimportdb
+  vlinode = vlinode
+  vllnum = vllnum
+  vlmemflush = vlmemflush
+  vlmemsync = vlmemsync
+  vlmtime = vlmtime
+  vlmulcurclose = vlmulcurclose
+  vlmulcurfirst = vlmulcurfirst
+  vlmulcurjump = vlmulcurjump
+  vlmulcurkey = vlmulcurkey
+  vlmulcurkeycache = vlmulcurkeycache
+  vlmulcurlast = vlmulcurlast
+  vlmulcurnext = vlmulcurnext
+  vlmulcuropen = vlmulcuropen
+  vlmulcurprev = vlmulcurprev
+  vlmulcurval = vlmulcurval
+  vlmulcurvalcache = vlmulcurvalcache
+  vlname = vlname
+  vlnnum = vlnnum
+  vlopen = vlopen
+  vloptimize = vloptimize
+  vlout = vlout
+  vloutlist = vloutlist
+  vlput = vlput
+  vlputlist = vlputlist
+  vlremove = vlremove
+  vlrepair = vlrepair
+  vlrnum = vlrnum
+  vlsetfbpsiz = vlsetfbpsiz
+  vlsetflags = vlsetflags
+  vlsettuning = vlsettuning
+  vlsync = vlsync
+  vltranabort = vltranabort
+  vltranbegin = vltranbegin
+  vltrancommit = vltrancommit
+  vlvnum = vlvnum
+  vlvsiz = vlvsiz
+  vlwritable = vlwritable
+  vstclose = vstclose
+  vstcrdnumptr = vstcrdnumptr
+  vstcurfirst = vstcurfirst
+  vstcurjump = vstcurjump
+  vstcurkey = vstcurkey
+  vstcurkeycache = vstcurkeycache
+  vstcurlast = vstcurlast
+  vstcurnext = vstcurnext
+  vstcurout = vstcurout
+  vstcurprev = vstcurprev
+  vstcurput = vstcurput
+  vstcurval = vstcurval
+  vstcurvalcache = vstcurvalcache
+  vstexportdb = vstexportdb
+  vstfatalerror = vstfatalerror
+  vstfsiz = vstfsiz
+  vstget = vstget
+  vstgetcache = vstgetcache
+  vstgetcat = vstgetcat
+  vstgetflags = vstgetflags
+  vstgetlist = vstgetlist
+  vstimportdb = vstimportdb
+  vstinode = vstinode
+  vstlnum = vstlnum
+  vstmemflush = vstmemflush
+  vstmemsync = vstmemsync
+  vstmtime = vstmtime
+  vstmulcurclose = vstmulcurclose
+  vstmulcurfirst = vstmulcurfirst
+  vstmulcurjump = vstmulcurjump
+  vstmulcurkey = vstmulcurkey
+  vstmulcurkeycache = vstmulcurkeycache
+  vstmulcurlast = vstmulcurlast
+  vstmulcurnext = vstmulcurnext
+  vstmulcuropen = vstmulcuropen
+  vstmulcurprev = vstmulcurprev
+  vstmulcurval = vstmulcurval
+  vstmulcurvalcache = vstmulcurvalcache
+  vstname = vstname
+  vstnnum = vstnnum
+  vstopen = vstopen
+  vstoptimize = vstoptimize
+  vstout = vstout
+  vstoutlist = vstoutlist
+  vstput = vstput
+  vstputlist = vstputlist
+  vstremove = vstremove
+  vstrepair = vstrepair
+  vstrnum = vstrnum
+  vstsetfbpsiz = vstsetfbpsiz
+  vstsetflags = vstsetflags
+  vstsettuning = vstsettuning
+  vstsync = vstsync
+  vsttranabort = vsttranabort
+  vsttranbegin = vsttranbegin
+  vsttrancommit = vsttrancommit
+  vstvnum = vstvnum
+  vstvsiz = vstvsiz
+  vstwritable = vstwritable

Added: box/trunk/qdbm/qdbm.pc.in
===================================================================
--- box/trunk/qdbm/qdbm.pc.in	                        (rev 0)
+++ box/trunk/qdbm/qdbm.pc.in	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+bindir=@bindir@
+libdir=@libdir@
+libexecdir=@libexecdir@
+includedir=@includedir@
+datadir=@datadir@
+
+Name: QDBM
+Description: a high performance embedded database library
+Version: @PACKAGE_VERSION@
+Requires:
+Libs: -L${libdir} -lqdbm @LIBS@
+Cflags: -I${includedir}

Added: box/trunk/qdbm/qdbm.spec.in
===================================================================
--- box/trunk/qdbm/qdbm.spec.in	                        (rev 0)
+++ box/trunk/qdbm/qdbm.spec.in	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,218 @@
+#================================================================
+# RPM Specification for QDBM
+#================================================================
+
+
+
+%define name @PACKAGE_NAME@
+%define version @PACKAGE_VERSION@
+%define release 1
+%define libver @LIBVER@
+%define librev @LIBREV@
+%define disturl http://qdbm.sourceforge.net/
+%define homeurl http://qdbm.sourceforge.net/
+
+Summary: Quick Database Manager
+Name: %{name}
+Version: %{version}
+Release: %{release}
+Source: %{disturl}%{name}-%{version}.tar.gz
+Copyright: LGPL
+Group: Development/Libraries
+Packager: Mikio Hirabayashi <mikio at users.sourceforge.net>
+Distribution: Private
+Vendor: Private
+Url: %{homeurl}
+Requires: zlib
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+
+%description
+QDBM is an embeded database library compatible with GDBM and NDBM.
+It features hash database and B+ tree database and is developed referring
+to GDBM for the purpose of the following three points: higher processing
+speed, smaller size of a database file, and simpler API.
+This package includes APIs for C, C++, and Java.  CGI scripts are also
+contained.  APIs for Perl and Ruby should be installed with a source package.
+
+%package devel
+Summary: Headers, libraries, utilities and documentation for QDBM
+Group: Development/Libraries
+Requires: %{name} = %{version}
+
+%description devel
+This package contains header files and libraries needed to develop programs
+using the QDBM library.  Some utility commands are also provided.
+
+%package plus
+Summary: C++ libraries for QDBM
+Group: Development/Libraries
+Requires: %{name} = %{version}
+
+%description plus
+This package contains libraries needed to develop and run programs using the
+QDBM C++ bindings.
+
+%package java
+Summary: Java libraries for QDBM
+Group: Development/Libraries
+Requires: %{name} = %{version}
+
+%description java
+This package contains libraries needed to develop and run programs using the
+QDBM Java bindings.
+
+%package cgi
+Summary: CGI scripts with QDBM
+Group: Development/Libraries
+Requires: %{name} = %{version}
+
+%description cgi
+This package contains CGI scripts with QDBM, for administration of databases,
+file uploading, and full-text search.
+
+%prep
+rm -rf $RPM_BUILD_ROOT
+
+%setup -q
+
+%build
+( cd . ; %{configure} --prefix=%{_usr} --mandir=%{_mandir} \
+  --enable-stable --enable-zlib --enable-iconv --enable-pthread ; make )
+( cd plus ; %{configure} --prefix=%{_usr} --mandir=%{_mandir} ; make )
+( cd java ; %{configure} --prefix=%{_usr} --mandir=%{_mandir} ; make )
+( cd cgi ; %{configure} --prefix=%{_usr} --mandir=%{_mandir} ; make )
+
+%install
+rm -rf $RPM_BUILD_ROOT
+( cd . ; make DESTDIR=$RPM_BUILD_ROOT install-strip )
+( cd plus ; make DESTDIR=$RPM_BUILD_ROOT install-strip )
+( cd java ; make DESTDIR=$RPM_BUILD_ROOT install )
+( cd cgi ; make DESTDIR=$RPM_BUILD_ROOT install-strip )
+( mkdir -p $RPM_BUILD_ROOT%{_datadir}/doc/qdbm && \
+  cd $RPM_BUILD_ROOT%{_usr}/share/qdbm && \
+  cp -Rf spex-ja.html spex.html COPYING ChangeLog NEWS THANKS \
+    plus/xspex.html plus/xspex-ja.html plus/xapidoc \
+    java/jspex.html java/jspex-ja.html java/japidoc \
+    cgi/cgispex-ja.html cgi/cgispex.html $RPM_BUILD_ROOT%{_datadir}/doc/qdbm )
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%{_libdir}/libqdbm.so.%{libver}.%{librev}.0
+%{_libdir}/libqdbm.so.%{libver}
+%{_libdir}/libqdbm.so
+
+%files devel
+%defattr(-,root,root)
+%{_includedir}/depot.h
+%{_includedir}/curia.h
+%{_includedir}/relic.h
+%{_includedir}/hovel.h
+%{_includedir}/cabin.h
+%{_includedir}/villa.h
+%{_includedir}/vista.h
+%{_includedir}/odeum.h
+%{_libdir}/libqdbm.a
+%{_bindir}/dpmgr
+%{_bindir}/dptest
+%{_bindir}/dptsv
+%{_bindir}/crmgr
+%{_bindir}/crtest
+%{_bindir}/crtsv
+%{_bindir}/rlmgr
+%{_bindir}/rltest
+%{_bindir}/hvmgr
+%{_bindir}/hvtest
+%{_bindir}/cbtest
+%{_bindir}/cbcodec
+%{_bindir}/vlmgr
+%{_bindir}/vltest
+%{_bindir}/vltsv
+%{_bindir}/odmgr
+%{_bindir}/odtest
+%{_bindir}/odidx
+%{_bindir}/qmttest
+%{_mandir}/man1/dpmgr.1.gz
+%{_mandir}/man1/dptest.1.gz
+%{_mandir}/man1/dptsv.1.gz
+%{_mandir}/man1/crmgr.1.gz
+%{_mandir}/man1/crtest.1.gz
+%{_mandir}/man1/crtsv.1.gz
+%{_mandir}/man1/rlmgr.1.gz
+%{_mandir}/man1/rltest.1.gz
+%{_mandir}/man1/hvmgr.1.gz
+%{_mandir}/man1/hvtest.1.gz
+%{_mandir}/man1/cbtest.1.gz
+%{_mandir}/man1/cbcodec.1.gz
+%{_mandir}/man1/vlmgr.1.gz
+%{_mandir}/man1/vltest.1.gz
+%{_mandir}/man1/vltsv.1.gz
+%{_mandir}/man1/odmgr.1.gz
+%{_mandir}/man1/odtest.1.gz
+%{_mandir}/man1/odidx.1.gz
+%{_mandir}/man1/qmttest.1.gz
+%{_mandir}/man3/qdbm.3.gz
+%{_mandir}/man3/depot.3.gz
+%{_mandir}/man3/dpopen.3.gz
+%{_mandir}/man3/curia.3.gz
+%{_mandir}/man3/cropen.3.gz
+%{_mandir}/man3/relic.3.gz
+%{_mandir}/man3/hovel.3.gz
+%{_mandir}/man3/cabin.3.gz
+%{_mandir}/man3/villa.3.gz
+%{_mandir}/man3/vlopen.3.gz
+%{_mandir}/man3/vista.3.gz
+%{_mandir}/man3/odeum.3.gz
+%{_mandir}/man3/odopen.3.gz
+%{_datadir}/doc/qdbm/spex.html
+%{_datadir}/doc/qdbm/spex-ja.html
+%{_datadir}/doc/qdbm/COPYING
+%{_datadir}/doc/qdbm/ChangeLog
+%{_datadir}/doc/qdbm/NEWS
+%{_datadir}/doc/qdbm/THANKS
+%{_libdir}/pkgconfig/qdbm.pc
+
+%files plus
+%defattr(-,root,root)
+%{_includedir}/xqdbm.h
+%{_includedir}/xadbm.h
+%{_includedir}/xdepot.h
+%{_includedir}/xcuria.h
+%{_includedir}/xvilla.h
+%{_libdir}/libxqdbm.a
+%{_libdir}/libxqdbm.so.3.0.0
+%{_libdir}/libxqdbm.so.3
+%{_libdir}/libxqdbm.so
+%{_bindir}/xdptest
+%{_bindir}/xcrtest
+%{_bindir}/xvltest
+%{_datadir}/doc/qdbm/xspex.html
+%{_datadir}/doc/qdbm/xspex-ja.html
+%{_datadir}/doc/qdbm/xapidoc/
+
+%files java
+%defattr(-,root,root)
+%{_libdir}/qdbm.jar
+%{_libdir}/libjqdbm.so.1.0.0
+%{_libdir}/libjqdbm.so.1
+%{_libdir}/libjqdbm.so
+%{_datadir}/doc/qdbm/jspex.html
+%{_datadir}/doc/qdbm/jspex-ja.html
+%{_datadir}/doc/qdbm/japidoc/
+
+%files cgi
+%defattr(-,root,root)
+%{_libexecdir}/qadm.cgi
+%{_libexecdir}/qupl.cgi
+%{_libexecdir}/qfts.cgi
+%{_datadir}/qdbm/cgi/qadm.conf
+%{_datadir}/qdbm/cgi/qupl.conf
+%{_datadir}/qdbm/cgi/qfts.conf
+%{_datadir}/doc/qdbm/cgispex.html
+%{_datadir}/doc/qdbm/cgispex-ja.html
+
+
+
+# END OF FILE

Added: box/trunk/qdbm/qmttest.c
===================================================================
--- box/trunk/qdbm/qmttest.c	                        (rev 0)
+++ box/trunk/qdbm/qmttest.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,300 @@
+/*************************************************************************************************
+ * Test cases for thread-safety
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <curia.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <time.h>
+
+#if defined(MYPTHREAD)
+#include <sys/types.h>
+#include <pthread.h>
+#endif
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define PATHBUFSIZ     1024              /* buffer for paths */
+#define RECBUFSIZ      32                /* buffer for records */
+
+typedef struct {                         /* type of structure of thread arguments */
+  int id;                                /* ID of the thread */
+  const char *name;                      /* prefix of the database */
+  int rnum;                              /* number of records */
+  int alive;                             /* alive or not */
+} MYARGS;
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+void pdperror(const char *name);
+void *procthread(void *args);
+int dotest(const char *name, int rnum, int tnum);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  char *env, *name;
+  int rv, rnum, tnum;
+  cbstdiobin();
+  if((env = getenv("QDBMDBGFD")) != NULL) dpdbgfd = atoi(env);
+  progname = argv[0];
+  srand(time(NULL));
+  if(argc < 4) usage();
+  name = argv[1];
+  if((rnum = atoi(argv[2])) < 1) usage();
+  if((tnum = atoi(argv[3])) < 1) usage();
+  rv = dotest(name, rnum, tnum);
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: test cases for thread-safety\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s name rnum tnum\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* print formatted string and flush the buffer */
+int printfflush(const char *format, ...){
+#if defined(MYPTHREAD)
+  static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
+  va_list ap;
+  int rv;
+  if(pthread_mutex_lock(&mymutex) != 0) return -1;
+  va_start(ap, format);
+  rv = vprintf(format, ap);
+  if(fflush(stdout) == EOF) rv = -1;
+  va_end(ap);
+  pthread_mutex_unlock(&mymutex);
+  return rv;
+#else
+  va_list ap;
+  int rv;
+  va_start(ap, format);
+  rv = vprintf(format, ap);
+  if(fflush(stdout) == EOF) rv = -1;
+  va_end(ap);
+  return rv;
+#endif
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* pseudo random number generator */
+int myrand(void){
+  static int cnt = 0;
+  return (rand() * rand() + (rand() >> (sizeof(int) * 4)) + (cnt++)) & INT_MAX;
+}
+
+
+/* process the test */
+void *procthread(void *args){
+  MYARGS *myargs;
+  DEPOT *depot;
+  CURIA *curia;
+  VILLA *villa;
+  CBLIST *list;
+  CBMAP *map;
+  char name[PATHBUFSIZ], buf[RECBUFSIZ];
+  int i, err, len;
+  myargs = (MYARGS *)args;
+  err = FALSE;
+  sprintf(name, "%s-%04d", myargs->name, myargs->id);
+  dpremove(name);
+  crremove(name);
+  vlremove(name);
+  switch(myrand() % 4){
+  case 0:
+    printfflush("\n[Depot Test]  name=%s  rnum=%d\n", name, myargs->rnum);
+    if(!(depot = dpopen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, -1))){
+      pdperror(name);
+      return "error";
+    }
+    for(i = 1; i <= myargs->rnum; i++){
+      len = sprintf(buf, "%d", myrand() % i + 1);
+      if(!dpput(depot, buf, len, buf, len, i % 2 == 0 ? DP_DOVER : DP_DCAT)){
+        pdperror(name);
+        err = TRUE;
+      }
+      if(myargs->rnum > 250 && i % (myargs->rnum / 250) == 0){
+        printfflush(".");
+        if(i == myargs->rnum || i % (myargs->rnum / 10) == 0){
+          printfflush("\n%s: (%d)\n", name, i);
+        }
+      }
+    }
+    if(!dpclose(depot)){
+      pdperror(name);
+      err = TRUE;
+    }
+    printfflush("\n%s: finished\n", name);
+    break;
+  case 1:
+    printfflush("\n[Curia Test]  name=%s  rnum=%d\n", name, myargs->rnum);
+    if(!(curia = cropen(name, CR_OWRITER | CR_OCREAT | CR_OTRUNC, -1, -1))){
+      pdperror(name);
+      return "error";
+    }
+    for(i = 1; i <= myargs->rnum; i++){
+      len = sprintf(buf, "%d", myrand() % i + 1);
+      if(!crput(curia, buf, len, buf, len, i % 2 == 0 ? CR_DOVER : CR_DCAT)){
+        pdperror(name);
+        err = TRUE;
+      }
+      if(myargs->rnum > 250 && i % (myargs->rnum / 250) == 0){
+        printfflush(".");
+        if(i == myargs->rnum || i % (myargs->rnum / 10) == 0){
+          printfflush("\n%s: (%d)\n", name, i);
+        }
+      }
+    }
+    if(!crclose(curia)){
+      pdperror(name);
+      err = TRUE;
+    }
+    printfflush("\n%s: finished\n", name);
+    break;
+  case 2:
+    printfflush("\n[Villa Test]  name=%s  rnum=%d\n", name, myargs->rnum);
+    if(!(villa = vlopen(name, VL_OWRITER | VL_OCREAT | VL_OTRUNC, VL_CMPLEX))){
+      pdperror(name);
+      return "error";
+    }
+    for(i = 1; i <= myargs->rnum; i++){
+      len = sprintf(buf, "%d", myrand() % i + 1);
+      if(!vlput(villa, buf, len, buf, len, i % 2 == 0 ? VL_DOVER : VL_DDUP)){
+        pdperror(name);
+        err = TRUE;
+      }
+      if(myargs->rnum > 250 && i % (myargs->rnum / 250) == 0){
+        printfflush(".");
+        if(i == myargs->rnum || i % (myargs->rnum / 10) == 0){
+          printfflush("\n%s: (%d)\n", name, i);
+        }
+      }
+    }
+    if(!vlclose(villa)){
+      pdperror(name);
+      err = TRUE;
+    }
+    printfflush("\n%s: finished\n", name);
+    break;
+  case 3:
+    printfflush("\n[Cabin Test]  name=%s  rnum=%d\n", name, myargs->rnum);
+    list = cblistopen();
+    map = cbmapopen();
+    for(i = 1; i <= myargs->rnum; i++){
+      len = sprintf(buf, "%d", myrand() % i + 1);
+      cblistpush(list, buf, len);
+      cbmapput(map, buf, len, buf, len, i % 2 == 0 ? TRUE : FALSE);
+      if(myargs->rnum > 250 && i % (myargs->rnum / 250) == 0){
+        printfflush(".");
+        if(i == myargs->rnum || i % (myargs->rnum / 10) == 0){
+          printfflush("\n%s: (%d)\n", name, i);
+        }
+      }
+    }
+    cbmapclose(map);
+    cblistclose(list);
+    printfflush("\n%s: finished\n", name);
+    break;
+  }
+  return err ? "error" : NULL;
+}
+
+
+/* drive the test */
+int dotest(const char *name, int rnum, int tnum){
+#if defined(MYPTHREAD)
+  pthread_t *thary;
+  MYARGS *argsary;
+  char *rv;
+  int i, err;
+  printfflush("<Thread-Safety Test>\n  name=%s  rnum=%d  tnum=%d\n", name, rnum, tnum);
+  err = FALSE;
+  thary = cbmalloc(tnum * sizeof(pthread_t));
+  argsary = cbmalloc(tnum * sizeof(MYARGS));
+  for(i = 0; i < tnum; i++){
+    argsary[i].id = i + 1;
+    argsary[i].name = name;
+    argsary[i].rnum = rnum;
+    argsary[i].alive = TRUE;
+    if(pthread_create(thary + i, NULL, procthread, argsary + i) != 0){
+      argsary[i].alive = FALSE;
+      err = TRUE;
+    }
+  }
+  for(i = 0; i < tnum; i++){
+    if(!argsary[i].alive) continue;
+    if(pthread_join(thary[i], (void *)&rv) != 0 || rv) err = TRUE;
+  }
+  free(argsary);
+  free(thary);
+  if(!err) printfflush("\nall ok\n");
+  return err ? 1 : 0;
+#else
+  MYARGS *argsary;
+  int i, err;
+  printfflush("<Thread-Safety Test>\n  name=%s  rnum=%d  tnum=%d\n", name, rnum, tnum);
+  err = FALSE;
+  argsary = cbmalloc(tnum * sizeof(MYARGS));
+  for(i = 0; i < tnum; i++){
+    argsary[i].id = i + 1;
+    argsary[i].name = name;
+    argsary[i].rnum = rnum;
+    argsary[i].alive = TRUE;
+    if(procthread(argsary + i)) err = TRUE;
+  }
+  free(argsary);
+  if(!err) printfflush("\nall ok\n");
+  return err ? 1 : 0;
+#endif
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/qmttest.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/relic.c
===================================================================
--- box/trunk/qdbm/relic.c	                        (rev 0)
+++ box/trunk/qdbm/relic.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,266 @@
+/*************************************************************************************************
+ * Implementation of Relic
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#define QDBM_INTERNAL  1
+
+#include "relic.h"
+#include "myconf.h"
+
+#define RL_NAMEMAX     512               /* max size of a database name */
+#define RL_DIRFSUF     MYEXTSTR "dir"    /* suffix of a directory file */
+#define RL_DATAFSUF    MYEXTSTR "pag"    /* suffix of a page file */
+#define RL_PATHBUFSIZ  1024              /* size of a path buffer */
+#define RL_INITBNUM    1913              /* initial bucket number */
+#define RL_ALIGNSIZ    16                /* size of alignment */
+#define RL_MAXLOAD     1.25              /* max ratio of bucket loading */
+#define RL_DIRMAGIC    "[depot]\0\v"     /* magic number of a directory file */
+
+
+/* private function prototypes */
+static void dbm_writedummy(int fd);
+static int dbm_writestr(int fd, const char *str);
+
+
+
+/*************************************************************************************************
+ * public objects
+ *************************************************************************************************/
+
+
+/* Get a database handle. */
+DBM *dbm_open(char *name, int flags, int mode){
+  DBM *db;
+  DEPOT *depot;
+  int dpomode;
+  char path[RL_PATHBUFSIZ];
+  int dfd, fd;
+  assert(name);
+  if(strlen(name) > RL_NAMEMAX) return NULL;
+  dpomode = DP_OREADER;
+  if((flags & O_WRONLY) || (flags & O_RDWR)){
+    dpomode = DP_OWRITER;
+    if(flags & O_CREAT) dpomode |= DP_OCREAT;
+    if(flags & O_TRUNC) dpomode |= DP_OTRUNC;
+  }
+  mode |= 00600;
+  sprintf(path, "%s%s", name, RL_DIRFSUF);
+  if((dfd = open(path, flags, mode)) == -1) return NULL;
+  dbm_writedummy(dfd);
+  sprintf(path, "%s%s", name, RL_DATAFSUF);
+  if((fd = open(path, flags, mode)) == -1 || close(fd) == -1){
+    close(dfd);
+    return NULL;
+  }
+  if(!(depot = dpopen(path, dpomode, RL_INITBNUM))){
+    close(dfd);
+    return NULL;
+  }
+  if(dpomode & DP_OWRITER){
+    if(!dpsetalign(depot, RL_ALIGNSIZ)){
+      close(dfd);
+      dpclose(depot);
+      return NULL;
+    }
+  }
+  if(!(db = malloc(sizeof(DBM)))){
+    close(dfd);
+    dpclose(depot);
+    return NULL;
+  }
+  db->depot = depot;
+  db->dfd = dfd;
+  db->dbm_fetch_vbuf = NULL;
+  db->dbm_nextkey_kbuf = NULL;
+  return db;
+}
+
+
+/* Close a database handle. */
+void dbm_close(DBM *db){
+  assert(db);
+  free(db->dbm_fetch_vbuf);
+  free(db->dbm_nextkey_kbuf);
+  close(db->dfd);
+  dpclose(db->depot);
+  free(db);
+}
+
+
+/* Store a record. */
+int dbm_store(DBM *db, datum key, datum content, int flags){
+  int dmode;
+  int bnum, rnum;
+  assert(db);
+  if(!key.dptr || key.dsize < 0 || !content.dptr || content.dsize < 0) return -1;
+  dmode = (flags == DBM_INSERT) ? DP_DKEEP : DP_DOVER;
+  if(!dpput(db->depot, key.dptr, key.dsize, content.dptr, content.dsize, dmode)){
+    if(dpecode == DP_EKEEP) return 1;
+    return -1;
+  }
+  bnum = dpbnum(db->depot);
+  rnum = dprnum(db->depot);
+  if(bnum > 0 && rnum > 0 && ((double)rnum / (double)bnum > RL_MAXLOAD)){
+    if(!dpoptimize(db->depot, -1)) return -1;
+  }
+  return 0;
+}
+
+
+/* Delete a record. */
+int dbm_delete(DBM *db, datum key){
+  assert(db);
+  if(!key.dptr || key.dsize < 0) return -1;
+  if(!dpout(db->depot, key.dptr, key.dsize)) return -1;
+  return 0;
+}
+
+
+/* Retrieve a record. */
+datum dbm_fetch(DBM *db, datum key){
+  datum content;
+  char *vbuf;
+  int vsiz;
+  assert(db);
+  if(!key.dptr || key.dsize < 0 ||
+     !(vbuf = dpget(db->depot, key.dptr, key.dsize, 0, -1, &vsiz))){
+    content.dptr = NULL;
+    content.dsize = 0;
+    return content;
+  }
+  free(db->dbm_fetch_vbuf);
+  db->dbm_fetch_vbuf = vbuf;
+  content.dptr = vbuf;
+  content.dsize = vsiz;
+  return content;
+}
+
+
+/* Get the first key of a database. */
+datum dbm_firstkey(DBM *db){
+  assert(db);
+  dpiterinit(db->depot);
+  return dbm_nextkey(db);
+}
+
+
+/* Get the next key of a database. */
+datum dbm_nextkey(DBM *db){
+  datum key;
+  char *kbuf;
+  int ksiz;
+  if(!(kbuf = dpiternext(db->depot, &ksiz))){
+    key.dptr = NULL;
+    key.dsize = 0;
+    return key;
+  }
+  free(db->dbm_nextkey_kbuf);
+  db->dbm_nextkey_kbuf = kbuf;
+  key.dptr = kbuf;
+  key.dsize = ksiz;
+  return key;
+}
+
+
+/* Check whether a database has a fatal error or not. */
+int dbm_error(DBM *db){
+  assert(db);
+  return dpfatalerror(db->depot) ? TRUE : FALSE;
+}
+
+
+/* No effect. */
+int dbm_clearerr(DBM *db){
+  assert(db);
+  return 0;
+}
+
+
+/* Check whether a handle is read-only or not. */
+int dbm_rdonly(DBM *db){
+  assert(db);
+  return dpwritable(db->depot) ? FALSE : TRUE;
+}
+
+
+/* Get the file descriptor of a directory file. */
+int dbm_dirfno(DBM *db){
+  assert(db);
+  return db->dfd;
+}
+
+
+/* Get the file descriptor of a data file. */
+int dbm_pagfno(DBM *db){
+  assert(db);
+  return dpfdesc(db->depot);
+}
+
+
+
+/*************************************************************************************************
+ * private objects
+ *************************************************************************************************/
+
+
+/* Write dummy data into a dummy file.
+   `fd' specifies a file descriptor. */
+static void dbm_writedummy(int fd){
+  struct stat sbuf;
+  if(fstat(fd, &sbuf) == -1 || sbuf.st_size > 0) return;
+  write(fd, RL_DIRMAGIC, sizeof(RL_DIRMAGIC) - 1);
+  dbm_writestr(fd, "\n\n");
+  dbm_writestr(fd, "\x20\x20\xa2\xca\xa1\xb2\xa2\xca\x20\x20\x20\x20\x20\xa1\xbf\xa1");
+  dbm_writestr(fd, "\xb1\xa1\xb1\xa1\xb1\xa1\xb1\xa1\xb1\xa1\xb1\xa1\xb1\xa1\xb1\xa1");
+  dbm_writestr(fd, "\xb1\x0a\xa1\xca\x20\xa1\xad\xa2\xcf\xa1\xae\xa1\xcb\xa1\xe3\x20");
+  dbm_writestr(fd, "\x20\x4e\x44\x42\x4d\x20\x43\x6f\x6d\x70\x61\x74\x69\x62\x69\x6c");
+  dbm_writestr(fd, "\x69\x74\x79\x0a\xa1\xca\x20\x20\x20\x20\x20\x20\x20\xa1\xcb\x20");
+  dbm_writestr(fd, "\x20\xa1\xc0\xa1\xb2\xa1\xb2\xa1\xb2\xa1\xb2\xa1\xb2\xa1\xb2\xa1");
+  dbm_writestr(fd, "\xb2\xa1\xb2\xa1\xb2\x0a\x20\xa1\xc3\x20\x20\xa1\xc3\x20\xa1\xc3");
+  dbm_writestr(fd, "\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20");
+  dbm_writestr(fd, "\x20\x20\x20\x20\x20\x20\x20\x0a\xa1\xca\x5f\x5f\xa1\xb2\xa1\xcb");
+  dbm_writestr(fd, "\x5f\xa1\xcb\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20");
+  dbm_writestr(fd, "\x20\x20\x20\x20\x20\x20\x20\x20\x20\x0a");
+}
+
+
+/* Write a string into a file.
+   `fd' specifies a file descriptor.
+   `str' specifies a string. */
+static int dbm_writestr(int fd, const char *str){
+  const char *lbuf;
+  int size, rv, wb;
+  assert(fd >= 0 && str);
+  lbuf = str;
+  size = strlen(str);
+  rv = 0;
+  do {
+    wb = write(fd, lbuf, size);
+    switch(wb){
+    case -1: if(errno != EINTR) return -1;
+    case 0: break;
+    default:
+      lbuf += wb;
+      size -= wb;
+      rv += wb;
+      break;
+    }
+  } while(size > 0);
+  return rv;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/relic.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/relic.h
===================================================================
--- box/trunk/qdbm/relic.h	                        (rev 0)
+++ box/trunk/qdbm/relic.h	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,170 @@
+/*************************************************************************************************
+ * The NDBM-compatible API of QDBM
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _RELIC_H                         /* duplication check */
+#define _RELIC_H
+
+#if defined(__cplusplus)                 /* export for C++ */
+extern "C" {
+#endif
+
+
+#include <depot.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+#if defined(_MSC_VER) && !defined(QDBM_INTERNAL) && !defined(QDBM_STATIC)
+#define MYEXTERN extern __declspec(dllimport)
+#else
+#define MYEXTERN extern
+#endif
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+typedef struct {                         /* type of structure for a database handle */
+  DEPOT *depot;                          /* internal database handle */
+  int dfd;                               /* file descriptor of a dummy file */
+  char *dbm_fetch_vbuf;                  /* buffer for dbm_fetch */
+  char *dbm_nextkey_kbuf;                /* buffer for dbm_nextkey */
+} DBM;
+
+typedef struct {                         /* type of structure for a key or a value */
+  void *dptr;                            /* pointer to the region */
+  size_t dsize;                          /* size of the region */
+} datum;
+
+enum {                                   /* enumeration for write modes */
+  DBM_INSERT,                            /* keep an existing value */
+  DBM_REPLACE                            /* overwrite an existing value */
+};
+
+
+/* Get a database handle.
+   `name' specifies the name of a database.  The file names are concatenated with suffixes.
+   `flags' is the same as the one of `open' call, although `O_WRONLY' is treated as `O_RDWR'
+   and additional flags except for `O_CREAT' and `O_TRUNC' have no effect.
+   `mode' specifies the mode of the database file as the one of `open' call does.
+   The return value is the database handle or `NULL' if it is not successful. */
+DBM *dbm_open(char *name, int flags, int mode);
+
+
+/* Close a database handle.
+   `db' specifies a database handle.
+   Because the region of the closed handle is released, it becomes impossible to use the
+   handle. */
+void dbm_close(DBM *db);
+
+
+/* Store a record.
+   `db' specifies a database handle.
+   `key' specifies a structure of a key.
+   `content' specifies a structure of a value.
+   `flags' specifies behavior when the key overlaps, by the following values: `DBM_REPLACE',
+   which means the specified value overwrites the existing one, `DBM_INSERT', which means the
+   existing value is kept.
+   The return value is 0 if it is successful, 1 if it gives up because of overlaps of the key,
+   -1 if other error occurs. */
+int dbm_store(DBM *db, datum key, datum content, int flags);
+
+
+/* Delete a record.
+   `db' specifies a database handle.
+   `key' specifies a structure of a key.
+   The return value is 0 if it is successful, -1 if some errors occur. */
+int dbm_delete(DBM *db, datum key);
+
+
+/* Retrieve a record.
+   `db' specifies a database handle.
+   `key' specifies a structure of a key.
+   The return value is a structure of the result.
+   If a record corresponds, the member `dptr' of the structure is the pointer to the region of
+   the value.  If no record corresponds or some errors occur, `dptr' is `NULL'.  `dptr' points
+   to the region related with the handle.  The region is available until the next time of
+   calling this function with the same handle. */
+datum dbm_fetch(DBM *db, datum key);
+
+
+/* Get the first key of a database.
+   `db' specifies a database handle.
+   The return value is a structure of the result.
+   If a record corresponds, the member `dptr' of the structure is the pointer to the region of
+   the first key.  If no record corresponds or some errors occur, `dptr' is `NULL'.  `dptr'
+   points to the region related with the handle.  The region is available until the next time
+   of calling this function or the function `dbm_nextkey' with the same handle. */
+datum dbm_firstkey(DBM *db);
+
+
+/* Get the next key of a database.
+   `db' specifies a database handle.
+   The return value is a structure of the result.
+   If a record corresponds, the member `dptr' of the structure is the pointer to the region of
+   the next key.  If no record corresponds or some errors occur, `dptr' is `NULL'.  `dptr'
+   points to the region related with the handle.  The region is available until the next time
+   of calling this function or the function `dbm_firstkey' with the same handle. */
+datum dbm_nextkey(DBM *db);
+
+
+/* Check whether a database has a fatal error or not.
+   `db' specifies a database handle.
+   The return value is true if the database has a fatal error, false if not. */
+int dbm_error(DBM *db);
+
+
+/* No effect.
+   `db' specifies a database handle.
+   The return value is 0.
+   The function is only for compatibility. */
+int dbm_clearerr(DBM *db);
+
+
+/* Check whether a handle is read-only or not.
+   `db' specifies a database handle.
+   The return value is true if the handle is read-only, or false if not read-only. */
+int dbm_rdonly(DBM *db);
+
+
+/* Get the file descriptor of a directory file.
+   `db' specifies a database handle.
+   The return value is the file descriptor of the directory file. */
+int dbm_dirfno(DBM *db);
+
+
+/* Get the file descriptor of a data file.
+   `db' specifies a database handle.
+   The return value is the file descriptor of the data file. */
+int dbm_pagfno(DBM *db);
+
+
+
+#undef MYEXTERN
+
+#if defined(__cplusplus)                 /* export for C++ */
+}
+#endif
+
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/relic.h
___________________________________________________________________
Added: svn:mime-type
   + text/x-chdr
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/rlmgr.c
===================================================================
--- box/trunk/qdbm/rlmgr.c	                        (rev 0)
+++ box/trunk/qdbm/rlmgr.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,465 @@
+/*************************************************************************************************
+ * Utility for debugging Relic and its applications
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <relic.h>
+#include <cabin.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+char *hextoobj(const char *str, int *sp);
+int runcreate(int argc, char **argv);
+int runstore(int argc, char **argv);
+int rundelete(int argc, char **argv);
+int runfetch(int argc, char **argv);
+int runlist(int argc, char **argv);
+void pmyerror(const char *name, const char *msg);
+void printobj(const char *obj, int size);
+void printobjhex(const char *obj, int size);
+int docreate(char *name);
+int dostore(char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int ins);
+int dodelete(char *name, const char *kbuf, int ksiz);
+int dofetch(char *name, const char *kbuf, int ksiz, int ox, int nb);
+int dolist(char *name, int ox);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "create")){
+    rv = runcreate(argc, argv);
+  } else if(!strcmp(argv[1], "store")){
+    rv = runstore(argc, argv);
+  } else if(!strcmp(argv[1], "delete")){
+    rv = rundelete(argc, argv);
+  } else if(!strcmp(argv[1], "fetch")){
+    rv = runfetch(argc, argv);
+  } else if(!strcmp(argv[1], "list")){
+    rv = runlist(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: administration utility for Relic\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s create name\n", progname);
+  fprintf(stderr, "  %s store [-kx] [-vx|-vf] [-insert] name key val\n", progname);
+  fprintf(stderr, "  %s delete [-kx] name key\n", progname);
+  fprintf(stderr, "  %s fetch [-kx] [-ox] [-n] name key\n", progname);
+  fprintf(stderr, "  %s list [-ox] name\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* create a binary object from a hexadecimal string */
+char *hextoobj(const char *str, int *sp){
+  char *buf, mbuf[3];
+  int len, i, j;
+  len = strlen(str);
+  if(!(buf = malloc(len + 1))) return NULL;
+  j = 0;
+  for(i = 0; i < len; i += 2){
+    while(strchr(" \n\r\t\f\v", str[i])){
+      i++;
+    }
+    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 */
+int runcreate(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = docreate(name);
+  return rv;
+}
+
+
+/* parse arguments of store command */
+int runstore(int argc, char **argv){
+  char *name, *key, *val, *kbuf, *vbuf;
+  int i, kx, vx, vf, ins, ksiz, vsiz, rv;
+  name = NULL;
+  kx = FALSE;
+  vx = FALSE;
+  vf = FALSE;
+  ins = FALSE;
+  key = NULL;
+  val = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-vx")){
+        vx = TRUE;
+      } else if(!strcmp(argv[i], "-vf")){
+        vf = TRUE;
+      } else if(!strcmp(argv[i], "-insert")){
+        ins = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else if(!val){
+      val = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key || !val) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = strlen(kbuf);
+  }
+  if(vx){
+    vbuf = hextoobj(val, &vsiz);
+  } else if(vf){
+    vbuf = cbreadfile(val, &vsiz);
+  } else {
+    vbuf = cbmemdup(val, -1);
+    vsiz = strlen(vbuf);
+  }
+  if(kbuf && vbuf){
+    rv = dostore(name, kbuf, ksiz, vbuf, vsiz, ins);
+  } else {
+    if(vf){
+      fprintf(stderr, "%s: %s: cannot read\n", progname, val);
+    } else {
+      fprintf(stderr, "%s: out of memory\n", progname);
+    }
+    rv = 1;
+  }
+  free(kbuf);
+  free(vbuf);
+  return rv;
+}
+
+
+/* parse arguments of delete command */
+int rundelete(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, kx, ksiz, rv;
+  name = NULL;
+  kx = FALSE;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = strlen(kbuf);
+  }
+  if(kbuf){
+    rv = dodelete(name, kbuf, ksiz);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of fetch command */
+int runfetch(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, kx, ox, nb, ksiz, rv;
+  name = NULL;
+  kx = FALSE;
+  ox = FALSE;
+  nb = FALSE;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else if(!strcmp(argv[i], "-n")){
+        nb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = strlen(kbuf);
+  }
+  if(kbuf){
+    rv = dofetch(name, kbuf, ksiz, ox, nb);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+int runlist(int argc, char **argv){
+  char *name;
+  int i, ox, rv;
+  name = NULL;
+  ox = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dolist(name, ox);
+  return rv;
+}
+
+
+/* print an error message */
+void pmyerror(const char *name, const char *msg){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, msg);
+}
+
+
+/* print an object */
+void printobj(const char *obj, int size){
+  int i;
+  for(i = 0; i < size; i++){
+    putchar(obj[i]);
+  }
+}
+
+
+/* print an object as a hexadecimal string */
+void printobjhex(const char *obj, int size){
+  int i;
+  for(i = 0; i < size; i++){
+    printf("%s%02X", i > 0 ? " " : "", ((const unsigned char *)obj)[i]);
+  }
+}
+
+
+/* perform create command */
+int docreate(char *name){
+  DBM *db;
+  if(!(db = dbm_open(name, O_RDWR | O_CREAT | O_TRUNC, 00644))){
+    pmyerror(name, "dbm_open failed");
+    return 1;
+  }
+  dbm_close(db);
+  return 0;
+}
+
+
+/* perform store command */
+int dostore(char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int ins){
+  DBM *db;
+  datum key, content;
+  int rv;
+  if(!(db = dbm_open(name, O_RDWR, 00644))){
+    pmyerror(name, "dbm_open failed");
+    return 1;
+  }
+  key.dptr = (char *)kbuf;
+  key.dsize = ksiz;
+  content.dptr = (char *)vbuf;
+  content.dsize = vsiz;
+  switch(dbm_store(db, key, content, ins ? DBM_INSERT : DBM_REPLACE)){
+  case 0:
+    rv = 0;
+    break;
+  case 1:
+    pmyerror(name, "dbm_store failed by insert");
+    rv = 1;
+    break;
+  default:
+    pmyerror(name, "dbm_store failed");
+    rv = 1;
+    break;
+  }
+  dbm_close(db);
+  return rv;
+}
+
+
+/* perform delete command */
+int dodelete(char *name, const char *kbuf, int ksiz){
+  DBM *db;
+  datum key;
+  int rv;
+  if(!(db = dbm_open((char *)name, O_RDWR, 00644))){
+    pmyerror(name, "dbm_open failed");
+    return 1;
+  }
+  key.dptr = (char *)kbuf;
+  key.dsize = ksiz;
+  if(dbm_delete(db, key) == 0){
+    rv = 0;
+  } else {
+    pmyerror(name, "dbm_delete failed");
+    rv = 1;
+  }
+  dbm_close(db);
+  return rv;
+}
+
+
+/* perform fetch command */
+int dofetch(char *name, const char *kbuf, int ksiz, int ox, int nb){
+  DBM *db;
+  datum key, content;
+  int rv;
+  if(!(db = dbm_open((char *)name, O_RDONLY, 00644))){
+    pmyerror(name, "dbm_open failed");
+    return 1;
+  }
+  key.dptr = (char *)kbuf;
+  key.dsize = ksiz;
+  content = dbm_fetch(db, key);
+  if(content.dptr){
+    if(ox){
+      printobjhex(content.dptr, content.dsize);
+    } else {
+      printobj(content.dptr, content.dsize);
+    }
+    if(!nb) putchar('\n');
+    rv = 0;
+  } else {
+    pmyerror(name, "dbm_fetch failed");
+    rv = 1;
+  }
+  dbm_close(db);
+  return rv;
+}
+
+
+/* perform list command */
+int dolist(char *name, int ox){
+  DBM *db;
+  datum key, val;
+  if(!(db = dbm_open((char *)name, O_RDONLY, 00644))){
+    pmyerror(name, "dbm_open failed");
+    return 1;
+  }
+  for(key = dbm_firstkey(db); key.dptr != NULL; key = dbm_nextkey(db)){
+    val = dbm_fetch(db, key);
+    if(!val.dptr) break;
+    if(ox){
+      printobjhex(key.dptr, key.dsize);
+      putchar('\t');
+      printobjhex(val.dptr, val.dsize);
+    } else {
+      printobj(key.dptr, key.dsize);
+      putchar('\t');
+      printobj(val.dptr, val.dsize);
+    }
+    putchar('\n');
+  }
+  dbm_close(db);
+  return 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/rlmgr.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/rltest.c
===================================================================
--- box/trunk/qdbm/rltest.c	                        (rev 0)
+++ box/trunk/qdbm/rltest.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,241 @@
+/*************************************************************************************************
+ * Test cases of Relic
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <relic.h>
+#include <cabin.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define RECBUFSIZ      32                /* buffer for records */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+int runwrite(int argc, char **argv);
+int runread(int argc, char **argv);
+int printfflush(const char *format, ...);
+void pmyerror(const char *name, const char *msg);
+int dowrite(char *name, int rnum);
+int doread(char *name, int rnum);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "write")){
+    rv = runwrite(argc, argv);
+  } else if(!strcmp(argv[1], "read")){
+    rv = runread(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: test cases for Relic\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write name rnum\n", progname);
+  fprintf(stderr, "  %s read name rnum\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of write command */
+int runwrite(int argc, char **argv){
+  char *name, *rstr;
+  int i, rnum, rv;
+  name = NULL;
+  rstr = NULL;
+  rnum = 0;
+  for(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();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = dowrite(name, rnum);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+int runread(int argc, char **argv){
+  char *name, *rstr;
+  int i, rnum, rv;
+  name = NULL;
+  rstr = NULL;
+  rnum = 0;
+  for(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();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = doread(name, rnum);
+  return rv;
+}
+
+
+/* print formatted string and flush the buffer */
+int printfflush(const char *format, ...){
+  va_list ap;
+  int rv;
+  va_start(ap, format);
+  rv = vprintf(format, ap);
+  if(fflush(stdout) == EOF) rv = -1;
+  va_end(ap);
+  return rv;
+}
+
+
+/* print an error message */
+void pmyerror(const char *name, const char *msg){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, msg);
+}
+
+
+/* perform write command */
+int dowrite(char *name, int rnum){
+  DBM *db;
+  datum key, content;
+  int i, err, len;
+  char buf[RECBUFSIZ];
+  printfflush("<Writing Test>\n  name=%s  rnum=%d\n\n", name, rnum);
+  /* open a database */
+  if(!(db = dbm_open(name, O_RDWR | O_CREAT | O_TRUNC, 00644))){
+    pmyerror(name, "dbm_open failed");
+    return 1;
+  }
+  err = FALSE;
+  /* loop for each record */
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", i);
+    key.dptr = buf;
+    key.dsize = len;
+    content.dptr = buf;
+    content.dsize = len;
+    /* store a record */
+    if(dbm_store(db, key, content, DBM_REPLACE) < 0){
+      pmyerror(name, "dbm_store failed");
+      err = TRUE;
+      break;
+    }
+    /* print progression */
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  dbm_close(db);
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+/* perform read command */
+int doread(char *name, int rnum){
+  DBM *db;
+  datum key, content;
+  int i, err, len;
+  char buf[RECBUFSIZ];
+  printfflush("<Reading Test>\n  name=%s  rnum=%d\n\n", name, rnum);
+  /* open a database */
+  if(!(db = dbm_open(name, O_RDONLY, 00644))){
+    pmyerror(name, "dbm_open failed");
+    return 1;
+  }
+  err = FALSE;
+  /* loop for each record */
+  for(i = 1; i <= rnum; i++){
+    /* retrieve a record */
+    len = sprintf(buf, "%08d", i);
+    key.dptr = buf;
+    key.dsize = len;
+    content = dbm_fetch(db, key);
+    if(!content.dptr){
+      pmyerror(name, "dbm_fetch failed");
+      err = TRUE;
+      break;
+    }
+    /* print progression */
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  dbm_close(db);
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/rltest.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/spex-ja.html
===================================================================
--- box/trunk/qdbm/spex-ja.html	                        (rev 0)
+++ box/trunk/qdbm/spex-ja.html	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,4348 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta http-equiv="Content-Style-Type" content="text/css" />
+<meta name="author" content="Mikio Hirabayashi" />
+<meta name="keywords" content="QDBM, DBM, database, hash, B+ tree" />
+<meta name="description" content="fundamental specifications of QDBM" />
+<link rel="contents" href="./" />
+<link rel="alternate" href="spex.html" hreflang="en" title="the English version" />
+<link rev="made" href="mailto:mikio at users.sourceforge.net" />
+<title>Specifications of QDBM Version 1 (Japanese)</title>
+<style type="text/css">html { margin: 0em 0em; padding: 0em 0em; background: #eeeeee none; }
+body { margin: 2em 2em; padding: 0em 0em;
+  background: #eeeeee none; color: #111111;
+  font-style: normal; font-weight: normal; }
+h1 { margin-top: 1.8em; margin-bottom: 1.3em; font-weight: bold; }
+h2 { margin-top: 1.8em; margin-bottom: 1.1em; font-weight: bold;
+  border-left: solid 0.6em #445555; border-bottom: solid 1pt #bbbbbb;
+  padding: 0.5em 0.5em; width: 60%; }
+h3 { margin-top: 1.8em; margin-bottom: 0.8em; font-weight: bold; }
+hr { margin-top: 2.5em; margin-bottom: 1.5em; height: 1pt;
+  color: #999999; background-color: #999999; border: none; }
+div.note,div.navi { text-align: right; }
+div.logo { text-align: center; margin: 3em 0em; }
+div.logo img { border: inset 2pt #ccccdd; }
+p { margin: 0.8em 0em; line-height: 140%; }
+p,dd { text-indent: 0.8em; }
+div,pre { margin-left: 1.7em; margin-right: 1.7em; }
+pre { background-color: #ddddee; padding: 0.2em; border: 1pt solid #bbbbcc; font-size: smaller; }
+kbd { color: #111111; font-style: normal; font-weight: bold; }
+a { color: #0022aa; text-decoration: none; }
+a:hover,a:focus { color: #0033ee; text-decoration: underline; }
+a.head { color: #111111; text-decoration: none; }
+table { padding: 1pt 2pt 1pt 2pt; border: none; margin-left: 1.7em; border-collapse: collapse; }
+th { padding: 1pt 4pt 1pt 4pt; border-style: none;
+  text-align: left; vertical-align: bottom; }
+td { padding: 1pt 4pt 1pt 4pt; border: 1pt solid #333333;
+  text-align: left; vertical-align: top; }
+ul,ol,dl { line-height: 140%; }
+dt { margin-left: 1.2em; }
+dd { margin-left: 2.0em; }
+ul.lines { list-style-type: none; }
+ at media print {
+  html,body { margin: 0em 0em; background-color: #ffffff; color: #000000; }
+  h1 { padding: 8em 0em 0.5em 0em; text-align: center; }
+  h2 { page-break-before: always; }
+  div.note { text-align: center; }
+  div.navi,div.logo { display: none }
+  hr { display: none; }
+  pre { margin: 0.8em 0.8em; background-color: #ffffff;
+    border: 1pt solid #aaaaaa; font-size: smaller; }
+  a,kbd { color: #000000; text-decoration: none; }
+  h1,h2,h3 { font-family: sans-serif; }
+  p,div,li,dt,dd { font-family: serif; }
+  pre,kbd { font-family: monospace; }
+  dd { font-size: smaller; }
+}
+</style>
+</head>
+
+<body>
+
+<h1>QDBMバージョン1基本仕様書</h1>
+
+<div class="note">Copyright (C) 2000-2007 Mikio Hirabayashi</div>
+<div class="note">Last Update: Thu, 26 Oct 2006 15:00:20 +0900</div>
+<div class="navi">[<a href="spex.html" hreflang="en">English</a>] [<a href="http://qdbm.sourceforge.net/">Home</a>]</div>
+
+<hr />
+
+<h2>目次</h2>
+
+<ol>
+<li><a href="#overview">概要</a></li>
+<li><a href="#features">特徴</a></li>
+<li><a href="#installation">インストール</a></li>
+<li><a href="#depotapi">Depot: 基本API</a></li>
+<li><a href="#depotcli">Depot用コマンド</a></li>
+<li><a href="#curiaapi">Curia: æ‹¡å¼µAPI</a></li>
+<li><a href="#curiacli">Curia用コマンド</a></li>
+<li><a href="#relicapi">Relic: NDBM互換API</a></li>
+<li><a href="#reliccli">Relic用コマンド</a></li>
+<li><a href="#hovelapi">Hovel: GDBM互換API</a></li>
+<li><a href="#hovelcli">Hovel用コマンド</a></li>
+<li><a href="#cabinapi">Cabin: ユーティリティAPI</a></li>
+<li><a href="#cabincli">Cabin用コマンド</a></li>
+<li><a href="#villaapi">Villa: 上級API</a></li>
+<li><a href="#villacli">Villa用コマンド</a></li>
+<li><a href="#odeumapi">Odeum: 転置API</a></li>
+<li><a href="#odeumcli">Odeum用コマンド</a></li>
+<li><a href="#fileformat">ファイルフォーマット</a></li>
+<li><a href="#porting">移植方法</a></li>
+<li><a href="#bugs">バグ</a></li>
+<li><a href="#faq">よく聞かれる質問</a></li>
+<li><a href="#copying">ライセンス</a></li>
+</ol>
+
+<hr />
+
+<h2><a name="overview" id="overview" class="head">概要</a></h2>
+
+<p>QDBMはデータベースを扱うルーチン群のライブラリである。データベースといっても単純なものであり、キーと値のペアからなるレコード群を格納したデータファイルである。キーと値は任意の長さを持つ一連のバイト列であり、文字列でもバイナリでも扱うことができる。テーブルやデータ型の概念はない。レコードはハッシュ表またはB+木で編成される。</p>
+
+<p>ハッシュ表のデータベースでは、キーはデータベース内で一意であり、キーが重複する複数のレコードを格納することはできない。このデータベースに対しては、キーと値を指定してレコードを格納したり、キーを指定して対応するレコードを削除したり、キーを指定して対応するレコードを検索したりすることができる。また、データベースに格納してある全てのキーを順不同に一つずつ取り出すこともできる。このような操作は、UNIX標準で定義されているDBMライブラリおよびその追従であるNDBMやGDBMに類するものである。QDBMはDBMのより良い代替として利用することができる。</p>
+
+<p>B+木のデータベースでは、キーが重複する複数のレコードを格納することができる。このデータベースに対しては、ハッシュ表のデータベースと同様に、キーを指定してレコードを格納したり取り出したり削除したりすることができる。レコードはユーザが指示した比較関数に基づいて整列されて格納される。カーソルを用いて各レコードを昇順または降順で参照することができる。この機構によって、文字列の前方一致検索や数値の範囲検索が可能になる。また、B+木のデータベースではトランザクションが利用できる。</p>
+
+<p>QDBMはCで記述され、C、C++、Java、PerlおよびRubyのAPIとして提供される。QDBMはPOSIX準拠のAPIを備えるプラットフォームで利用できる。QDBMはGNU Lesser General Public Licenseに基づくフリーソフトウェアである。</p>
+
+<hr />
+
+<h2><a name="features" id="features" class="head">特徴</a></h2>
+
+<h3>効率的なハッシュデータベースの実装</h3>
+
+<p>QDBMはGDBMを参考に次の三点を目標として開発された。処理がより高速であること、データベースファイルがより小さいこと、APIがより単純であること。これらの目標は達成されている。また、GDBMと同様に、伝統的なDBMが抱える三つの制限事項を回避している。すなわち、プロセス内で複数のデータベースを扱うことができ、キーと値のサイズに制限がなく、データベースファイルがスパースではない。</p>
+
+<p>QDBMはレコードの探索にハッシュアルゴリズムを用いる。バケット配列に十分な要素数があれば、レコードの探索にかかる時間計算量は O(1) である。すなわち、レコードの探索に必要な時間はデータベースの規模に関わらず一定である。追加や削除に関しても同様である。ハッシュ値の衝突はセパレートチェーン法で管理する。チェーンのデータ構造は二分探索木である。バケット配列の要素数が著しく少ない場合でも、探索等の時間計算量は O(log n) に抑えられる。</p>
+
+<p>QDBMはバケット配列を全てRAM上に保持することによって、処理の高速化を図る。バケット配列がRAM上にあれば、ほぼ1パスのファイル操作でレコードに該当するファイル上の領域を参照することができる。ファイルに記録されたバケット配列は `read' コールでRAM上に読み込むのではなく、`mmap' コールでRAMに直接マッピングされる。したがって、データベースに接続する際の準備時間が極めて短く、また、複数のプロセスでメモリマップを共有することができる。</p>
+
+<p>バケット配列の要素数が格納するレコード数の半分ほどであれば、データの性質によって多少前後するが、ハッシュ値の衝突率は56.7%ほどである(等倍だと36.8%、2倍だと21.3%、4倍だと11.5%、8倍だと6.0%ほど)。そのような場合、平均2パス以下のファイル操作でレコードを探索することができる。これを性能指標とするならば、例えば100万個のレコードを格納するためには50万要素のバケット配列が求められる。バケット配列の各要素は4バイトである。すなわち、2MバイトのRAMが利用できれば100万レコードのデータベースが構築できる。</p>
+
+<p>QDBMにはデータベースに接続するモードとして、「リーダ」と「ライタ」の二種類がある。リーダは読み込み専用であり、ライタは読み書き両用である。データベースにはファイルロックによってプロセス間での排他制御が行われる。ライタが接続している間は、他のプロセスはリーダとしてもライタとしても接続できない。リーダが接続している間は、他のプロセスのリーダは接続できるが、ライタは接続できない。この機構によって、マルチタスク環境での同時接続に伴うデータの整合性が保証される。</p>
+
+<p>伝統的なDBMにはレコードの追加操作に関して「挿入」モードと「置換」モードがある。前者では、キーが既存のレコードと重複する際に既存の値を残す。後者では、キーが既存のレコードと重複した際に新しい値に置き換える。QDBMはその2つに加えて「連結」モードがある。既存の値の末尾に指定された値を連結して格納する操作である。レコードの値を配列として扱う場合、要素を追加するには連結モードが役に立つ。また、DBMではレコードの値を取り出す際にはその全ての領域を処理対象にするしか方法がないが、QDBMでは値の領域の一部のみを選択して取り出すことができる。レコードの値を配列として扱う場合にはこの機能も役に立つ。</p>
+
+<p>一般的に、データベースの更新処理を続けるとファイル内の利用可能領域の断片化が起き、ファイルのサイズが肥大化してしまう。QDBMは隣接する不要領域を連結して再利用し、またデータベースの最適化機能を備えることによってこの問題に対処する。既存のレコードの値をより大きなサイズの値に上書きする場合、そのレコードの領域をファイル中の別の位置に移動させる必要がある。この処理の時間計算量はレコードのサイズに依存するので、値を拡張していく場合には効率が悪い。しかし、QDBMはアラインメントによってこの問題に対処する。増分がパディングに収まれば領域を移動させる必要はない。</p>
+
+<p>多くのファイルシステムでは、2GBを越えるサイズのファイルを扱うことができない。この問題に対処するために、QDBMは複数のデータベースファイルを含むディレクトリからなるデータベースを扱う機能を提供する。レコードをどのファイルに格納するかはキーに別のハッシュ関数を適用することによって決められる。この機能によって、理論的にはレコードの合計サイズが1TBまでのデータベースを構築することができる。また、データベースファイルを複数のディスクに振り分けることができるため、RAID-0(ストライピング)に見られるような更新操作の高速化が期待できる。NFS等を利用すれば複数のファイルサーバにデータベースを分散させることもできる。</p>
+
+<h3>便利なB+木データベースの実装</h3>
+
+<p>B+木データベースはハッシュデータベースより遅いが、ユーザが定義した順序に基づいて各レコードを参照できることが特長である。B+木は複数のレコードを整列させた状態で論理的なページにまとめて管理する。各ページに対してはB木すなわち多進平衡木によって階層化された疎インデックスが維持される。したがって、各レコードの探索等にかかる時間計算量は O(log n) である。各レコードを順番に参照するためにカーソルが提供される。カーソルの場所はキーを指定して飛ばすことができ、また現在の場所から次のレコードに進めたり前のレコードに戻したりすることができる。各ページは双方向リンクリストで編成されるので、カーソルを前後に移動させる時間計算量は O(1) である。</p>
+
+<p>B+木データベースは上述のハッシュデータベースを基盤として実装される。B+木の各ページはハッシュデータベースのレコードとして記録されるので、ハッシュデータベースの記憶管理の効率性を継承している。B+木では各レコードのヘッダが小さく、各ページのアラインメントはページサイズに応じて調整されるので、ほとんどの場合、ハッシュデータベースに較べてデータベースファイルのサイズが半減する。B+木を更新する際には多くのページを操作する必要があるが、QDBMはページをキャッシュすることによってファイル操作を減らして処理を効率化する。ほとんどの場合、疎インデックス全体がメモリ上にキャッシュされるので、各レコードを参照するのに必要なファイル操作は平均1パス以下である。</p>
+
+<p>B+木データベースはトランザクション機構を提供する。トランザクションを開始してから終了するまでの一連の操作を一括してデータベースにコミットしたり、一連の更新操作を破棄してデータベースの状態をトランザクションの開始前の状態にロールバックしたりすることができる。トランザクションの間にアプリケーションのプロセスがクラッシュしてもデータベースファイルは破壊されない。</p>
+
+<p>可逆データ圧縮ライブラリであるZLIBかLZOかBZIP2を有効化してQDBMをビルドすると、B+木の各ページの内容は圧縮されてファイルに書き込まれる。同一ページ内の各レコードは似たようなパターンを持つため、Lempel-Zivなどのアルゴリズムを適用すると高い圧縮効率が期待できる。テキストデータを扱う場合、データベースのサイズが元の25%程度になる。データベースの規模が大きくディスクI/Oがボトルネックとなる場合は、圧縮機能を有効化すると処理速度が大幅に改善される。</p>
+
+<h3>単純だが多様なインタフェース群</h3>
+
+<p>QDBMのAPIは非常に単純である。ANSI Cで定義された `FILE' ポインタを用いた通常のファイル入出力と同じようにデータベースファイルに対する入出力を行うことができる。QDBMの基本APIでは、データベースの実体は単一のファイルに記録される。拡張APIでは、データベースの実体は単一のディレクトリに含まれる複数のファイルに記録される。二つのAPIは互いに酷似しているので、アプリケーションを一方から他方に移植することはたやすい。</p>
+
+<p>NDBMおよびGDBMに互換するAPIも提供される。NDBMやGDBMのアプリケーションは市場に数多く存在するが、それらをQDBMに移植するのはたやすい。ほとんどの場合、ヘッダファイルの取り込み(#include)を書き換えてコンパイルしなおせばよい。ただし、オリジナルのNDBMやGDBMで作成したデータベースファイルをQDBMで扱うことはできない。</p>
+
+<p>メモリ上でレコードを簡単に扱うために、ユーティリティAPIが提供される。メモリ確保関数とソート関数と拡張可能なデータと配列リストとハッシュマップ等の実装である。それらを用いると、C言語でもPerlやRuby等のスクリプト言語のような手軽さでレコードを扱うことができる。</p>
+
+<p>B+木データベースは上級APIを介して利用する。上級APIは基本APIとユーティリティAPIを利用して実装される。上級APIも基本APIや拡張APIに類似した書式を持つので、使い方を覚えるのは容易である。</p>
+
+<p>全文検索システムで利用される転置インデックスを扱うために、転置APIが提供される。文書群の転置インデックスを容易に扱うことができれば、アプリケーションはテキスト処理や自然言語処理に注力できる。このAPIは文字コードや言語に依存しないので、ユーザの多様な要求に応える全文検索システムを実装することが可能となる。</p>
+
+<p>QDBMはC言語の他にも、C++、Java、PerlおよびRubyのAPIを提供する。C言語のAPIには、基本API、拡張API、NDBM互換API、GDBM互換API、ユーティリティAPI、上級APIおよび転置APIの七種類がある。各APIに対応したコマンドラインインタフェースも用意されている。それらはプロトタイピングやテストやデバッグなどで活躍する。C++用APIは基本APIと拡張APIと上級APIのデータベース操作関数群をC++のクラス機構でカプセル化したものである。Java用APIはJava Native Interfaceを用いて基本APIと拡張APIと上級APIを呼び出すものである。Perl用APIはXS言語を用いて基本APIと拡張APIと上級APIを呼び出すものである。Ruby用APIはRubyのモジュールとして基本APIと拡張APIと上級APIを呼び出すものである。データベースの管理とファイルアップロードと全文検索のためのCGIスクリプ
 トも提供される。</p>
+
+<h3>幅広い移植性</h3>
+
+<p>QDBMはANSI C(C89)の記法に従い、ANSI CまたはPOSIXで定義されたAPIのみを用いて実装される。したがって、ほとんどのUNIXおよびその互換をうたうOSで動作させることができる。C言語のAPIに関しては、少なくとも以下のプラットフォームで動作確認されている。</p>
+
+<ul>
+<li>Linux (2.2, 2.4, 2.6) (IA32, IA64, AMD64, PA-RISC, Alpha, PowerPC, M68000, ARM)</li>
+<li>FreeBSD (4.9, 5.0, 5.1, 5.2, 5.3) (IA32, IA64, SPARC, Alpha)</li>
+<li>NetBSD (1.6) (IA32)</li>
+<li>OpenBSD (3.4) (IA32)</li>
+<li>SunOS (5.6, 5.7, 5.8, 5.9, 5.10) (IA32, SPARC)</li>
+<li>HP-UX (11.11, 11.23) (IA64, PA-RISC)</li>
+<li>AIX (5.2) (POWER)</li>
+<li>Windows (2000, XP) (IA32, IA64, AMD64) (Cygwin, MinGW, Visual C++)</li>
+<li>Mac OS X (10.2, 10.3, 10.4) (IA32, PowerPC)</li>
+<li>Tru64 (5.1) (Alpha)</li>
+<li>RISC OS (5.03) (ARM)</li>
+</ul>
+
+<p>QDBMが作成したデータベースファイルは処理系のバイトオーダに依存するが、その対策として、バイトオーダに依存しない形式のデータをダンプするユーティリティが提供される。</p>
+
+<hr />
+
+<h2><a name="installation" id="installation" class="head">インストール</a></h2>
+
+<h3>準備</h3>
+
+<p>ソースパッケージを用いてQDBMをインストールするには、GCCのバージョン2.8以降と `make' が必要である。</p>
+
+<p>QDBMの配布用アーカイブファイルを展開したら、生成されたディレクトリに入ってインストール作業を行う。</p>
+
+<h3>普通の手順</h3>
+
+<p>LinuxとBSDとSunOSでは以下の手順に従う。</p>
+
+<p>ビルド環境を設定する。</p>
+
+<pre>./configure
+</pre>
+
+<p>プログラムをビルドする。</p>
+
+<pre>make
+</pre>
+
+<p>プログラムの自己診断テストを行う。</p>
+
+<pre>make check
+</pre>
+
+<p>プログラムをインストールする。作業は `root' ユーザで行う。</p>
+
+<pre>make install
+</pre>
+
+<h3>GNU Libtoolを使う場合</h3>
+
+<p>上記の方法でうまくいかない場合、以下の手順に従う。この手順には、GNU Libtoolのバージョン1.5以降が必要である。</p>
+
+<p>ビルド環境を設定する。</p>
+
+<pre>./configure
+</pre>
+
+<p>プログラムをビルドする。</p>
+
+<pre>make -f LTmakefile
+</pre>
+
+<p>プログラムの自己診断テストを行う。</p>
+
+<pre>make -f LTmakefile check
+</pre>
+
+<p>プログラムをインストールする。作業は `root' ユーザで行う。</p>
+
+<pre>make -f LTmakefile install
+</pre>
+
+<h3>結果</h3>
+
+<p>一連の作業が終ると、以下のファイルがインストールされる。その他にも、マニュアルが `/usr/local/man/man1' と `/usr/local/man/man3' に、それ以外の文書が `/usr/local/share/qdbm' に、`pkg-config' 用の設定ファイルが `/usr/local/lib/pkgconfig' にインストールされる。</p>
+
+<pre>/usr/local/include/depot.h
+/usr/local/include/curia.h
+/usr/local/include/relic.h
+/usr/local/include/hovel.h
+/usr/local/include/cabin.h
+/usr/local/include/villa.h
+/usr/local/include/vista.h
+/usr/local/include/odeum.h
+/usr/local/lib/libqdbm.a
+/usr/local/lib/libqdbm.so.14.13.0
+/usr/local/lib/libqdbm.so.14
+/usr/local/lib/libqdbm.so
+/usr/local/bin/dpmgr
+/usr/local/bin/dptest
+/usr/local/bin/dptsv
+/usr/local/bin/crmgr
+/usr/local/bin/crtest
+/usr/local/bin/crtsv
+/usr/local/bin/rlmgr
+/usr/local/bin/rltest
+/usr/local/bin/hvmgr
+/usr/local/bin/hvtest
+/usr/local/bin/cbtest
+/usr/local/bin/cbcodec
+/usr/local/bin/vlmgr
+/usr/local/bin/vltest
+/usr/local/bin/vltsv
+/usr/local/bin/odmgr
+/usr/local/bin/odtest
+/usr/local/bin/odidx
+/usr/local/bin/qmttest
+</pre>
+
+<p>`libqdbm.so' と動的にリンクしたプログラムを実行する際には、ライブラリの検索パスに `/usr/local/lib' を含めるべきである。環境変数 `LD_LIBRARY_PATH' でライブラリの検索パスを設定することができる。</p>
+
+<p>QDBMをアンインストールするには、`./configure' をした後の状態で以下のコマンドを実行する。作業は `root' ユーザで行う。</p>
+
+<pre>make uninstall
+</pre>
+
+<p>QDBMの古いバージョンがインストールされている場合、それをアンインストールしてからインストール作業を行うべきである。</p>
+
+<p>C言語以外のAPIとCGIスクリプトはデフォルトではインストールされない。C++用APIのインストール方法については、サブディレクトリ `plus' にある `xspex-ja.html' を参照すること。JAVA用APIのインストール方法については、サブディレクトリ `java' にある `jspex-ja.html' を参照すること。Perl用APIのインストール方法については、サブディレクトリ `perl' にある `plspex-ja.html' を参照すること。Ruby用APIのインストール方法については、サブディレクトリ `ruby' にある `rbspex-ja.html' を参照すること。CGIスクリプトのインストール方法については、サブディレクトリ `cgi' にある `cgispex.html' を参照すること。</p>
+
+<p>RPM等のバイナリパッケージを用いてインストールを行う場合は、それぞれのパッケージマネージャのマニュアルを参照すること。例えば、RPMを用いる場合、以下のようなコマンドを `root' ユーザで実行する。</p>
+
+<pre>rpm -ivh qdbm-1.x.x-x.i386.rpm
+</pre>
+
+<h3>Windowsの場合</h3>
+
+<p>Windows(Cygwin)にインストールする場合、以下の手順に従う。</p>
+
+<p>ビルド環境を設定する。</p>
+
+<pre>./configure
+</pre>
+
+<p>プログラムをビルドする。</p>
+
+<pre>make win
+</pre>
+
+<p>プログラムの自己診断テストを行う。</p>
+
+<pre>make check-win
+</pre>
+
+<p>プログラムをインストールする。なお、アンインストールする場合は `make uninstall-win' とする。</p>
+
+<pre>make install-win
+</pre>
+
+<p>Windowsでは、静的ライブラリ `libqdbm.a' に加えてインポートライブラリ `libqdbm.dll.a' が生成され、動的ライブラリ `libqdbm.so' 等の代わりにダイナミックリンクライブラリ `qdbm.dll' が生成される。`qdbm.dll' は `/usr/local/bin' にインストールされる。</p>
+
+<p>Cygwin環境でMinGWを用いてビルドするには、`make win' の代わりに `make mingw' を用いる。CygwinのUNIXエミュレーション層を用いる場合、生成されるプログラムは `cygwin1.dll' に依存したものになる(GNU GPLの影響を受ける)。MinGWによってWin32のネイティブDLLとリンクさせればこの問題を回避できる。</p>
+
+<p>Visual C++を用いてビルドするには、`VCmakefile' を編集してヘッダとライブラリの検索パスを設定した上で、`nmake /f VCMakefile' とすればよい。生成された `qdbm.dll' とリンクするアプリケーションは、コンパイラの `/MD' または `/MDd' オプションを用いて `msvcrt.dll' とリンクさせる必要がある。詳細設定に関しては `VCmakefile' を参照のこと。</p>
+
+<h3>Mac OS Xの場合</h3>
+
+<p>Mac OS X(Darwin)にインストールする場合、以下の手順に従う。</p>
+
+<p>ビルド環境を設定する。</p>
+
+<pre>./configure
+</pre>
+
+<p>プログラムをビルドする。</p>
+
+<pre>make mac
+</pre>
+
+<p>プログラムの自己診断テストを行う。</p>
+
+<pre>make check-mac
+</pre>
+
+<p>プログラムをインストールする。なお、アンインストールする場合は `make uninstall-mac' とする。</p>
+
+<pre>make install-mac
+</pre>
+
+<p>Mac OS Xでは、`libqdbm.so' 等の代わりに `libqdbm.dylib' 等が生成される。ライブラリの検索パスの指定は環境変数 `DYLD_LIBRARY_PATH' で行うことができる。</p>
+
+<h3>HP-UXの場合</h3>
+
+<p>HP-UXにインストールする場合、以下の手順に従う。</p>
+
+<p>ビルド環境を設定する。</p>
+
+<pre>./configure
+</pre>
+
+<p>プログラムをビルドする。</p>
+
+<pre>make hpux
+</pre>
+
+<p>プログラムの自己診断テストを行う。</p>
+
+<pre>make check-hpux
+</pre>
+
+<p>プログラムをインストールする。なお、アンインストールする場合は `make uninstall-hpux' とする。</p>
+
+<pre>make install-hpux
+</pre>
+
+<p>HP-UXでは、`libqdbm.so' 等の代わりに `libqdbm.sl' が生成される。ライブラリの検索パスの指定は環境変数 `SHLIB_PATH' で行うことができる。</p>
+
+<h3>RISC OSの場合</h3>
+
+<p>RISC OSにインストールする場合、以下の手順に従う。</p>
+
+<p>プログラムをビルドする。デフォルトではコンパイラに `cc' を用いるようになっているが、`gcc' を用いたければ `CC=gcc' という引数を加えればよい。</p>
+
+<pre>make -f RISCmakefile
+</pre>
+
+<p>一連の作業が終ると、`libqdbm' というライブラリファイルと `dpmgr' 等のコマンドが生成される。それらのインストール方法は定義されていないので、手動で任意の場所にコピーしてインストールすること。`depot.h' 等のヘッダファイルも同様に手動でインストールすること。</p>
+
+<h3>詳細設定</h3>
+
+<p>`./configure' を実行する際に以下のオプション引数を指定することで、ビルド方法の詳細な設定を行うことができる。</p>
+
+<ul class="lines">
+<li><kbd>--enable-debug</kbd> : デバッグ用にビルドする。デバッグシンボルを有効化し、最適化を行わず、静的にリンクする。</li>
+<li><kbd>--enable-devel</kbd> : 開発用にビルドする。デバッグシンボルを有効化し、最適化を行い、動的にリンクする。</li>
+<li><kbd>--enable-stable</kbd> : 安定版のリリース用にビルドする。保守的な最適化を行い、動的にリンクする。</li>
+<li><kbd>--enable-pthread</kbd> : POSIXスレッドを用い、グローバル変数をスレッド固有データとして扱う。</li>
+<li><kbd>--disable-lock</kbd> : ファイルロッキングが実装されていない環境用にビルドする。</li>
+<li><kbd>--disable-mmap</kbd> : メモリマッピングが実装されていない環境用にビルドする。</li>
+<li><kbd>--enable-zlib</kbd> : ZLIBによるB+木と転置インデックスのレコード圧縮を機能させる。</li>
+<li><kbd>--enable-lzo</kbd> : LZOによるB+木と転置インデックスのレコード圧縮を機能させる。</li>
+<li><kbd>--enable-bzip</kbd> : BZIP2によるB+木と転置インデックスのレコード圧縮を機能させる。</li>
+<li><kbd>--enable-iconv</kbd> : ICONVによる文字コード変換ユーティリティを機能させる。</li>
+</ul>
+
+<p>通常、QDBMとそのアプリケーションは `libqdbm.*' 以外の非標準のライブラリには依存しないでビルドすることができる。ただし、POSIXスレッドを有効にした場合は `libpthread.*' に依存し、ZLIBを有効にした場合は `libz.*' に依存し、LZOを有効にした場合は `liblzo2.*' に依存し、BZIP2を有効にした場合は `libbz2.*' に依存し、ICONVを有効にした場合は `libiconv.*' に依存するようになる。</p>
+
+<p>LZOのライセンスはGNU GPLなので、`liblzo2.*' とリンクしたアプリケーションはGNU GPLの制約を受けることに注意すること。</p>
+
+<hr />
+
+<h2><a name="depotapi" id="depotapi" class="head">Depot: 基本API</a></h2>
+
+<h3>概要</h3>
+
+<p>DepotはQDBMの基本APIである。QDBMが提供するデータベース管理機能のほぼ全てがDepotによって実装される。その他のAPIはDepotのラッパーにすぎない。したがって、QDBMのAPIの中でDepotが最も高速に動作する。</p>
+
+<p>Depotを使うためには、`depot.h' と `stdlib.h' をインクルードすべきである。通常、ソースファイルの冒頭付近で以下の記述を行う。</p>
+
+<dl>
+<dt><kbd>#include <depot.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+</dl>
+
+<p>Depotでデータベースを扱う際には、`DEPOT' 型へのポインタをハンドルとして用いる。これは、`stdio.h' の各種ルーチンがファイル入出力に `FILE' 型へのポインタを用いるのに似ている。ハンドルは、関数 `dpopen' で開き、関数 `dpclose' で閉じる。ハンドルのメンバを直接参照することは推奨されない。データベースに致命的なエラーが起きた場合は、以後そのハンドルに対する `dpclose' を除く全ての操作は何もせずにエラーを返す。ひとつのプロセスで複数のデータベースファイルを同時に利用することは可能であるが、同じデータベースファイルの複数のハンドルを利用してはならない。</p>
+
+<h3>API</h3>
+
+<p>外部変数 `dpversion' はバージョン情報の文字列である。</p>
+
+<dl>
+<dt><kbd>extern const char *dpversion;</kbd></dt>
+<dd>この変数の指す領域は書き込み禁止である。</dd>
+</dl>
+
+<p>外部変数 `dpecode' には直前のエラーコードが記録される。エラーコードの詳細については `depot.h' を参照すること。</p>
+
+<dl>
+<dt><kbd>extern int dpecode;</kbd></dt>
+<dd>この変数の初期値は `DP_ENOERR' である。その他の値として、`DP_EFATAL'、`DP_EMODE'、`DP_EBROKEN'、`DP_EKEEP'、`DP_ENOITEM'、`DP_EALLOC'、`DP_EMAP'、`DP_EOPEN'、`DP_ECLOSE'、`DP_ETRUNC'、`DP_ESYNC'、`DP_ESTAT'、`DP_ESEEK'、`DP_EREAD'、`DP_EWRITE'、`DP_ELOCK'、`DP_EUNLINK'、`DP_EMKDIR'、`DP_ERMDIR' および `DP_EMISC' がある。</dd>
+</dl>
+
+<p>エラーコードに対応するメッセージ文字列を得るには、関数 `dperrmsg' を用いる。</p>
+
+<dl>
+<dt><kbd>const char *dperrmsg(int <var>ecode</var>);</kbd></dt>
+<dd>`ecode' はエラーコードを指定する。戻り値はエラーメッセージの文字列であり、その領域は書き込み禁止である。</dd>
+</dl>
+
+<p>データベースのハンドルを作成するには、関数 `dpopen' を用いる。</p>
+
+<dl>
+<dt><kbd>DEPOT *dpopen(const char *<var>name</var>, int <var>omode</var>, int <var>bnum</var>);</kbd></dt>
+<dd>`name' はデータベースファイルの名前を指定する。`omode' は接続モードを指定し、`DP_OREADER' ならリーダ、`DP_OWRITER' ならライタとなる。`DP_OWRITER' の場合、`DP_OCREAT' または `DP_OTRUNC' とのビット論理和にすることができる。`DP_OCREAT' はファイルが無い場合に新規作成することを指示し、`DP_OTRUNC' はファイルが存在しても作り直すことを指示する。`DP_OREADER' と `DP_OWRITER' の両方で `DP_ONOLCK' または `DP_OLCKNB' とのビット論理和にすることができるが、前者はファイルロックをかけずにデータベースを開くことを指示し、後者はブロックせずにロックをかけることを指示する。`DP_OCREAT' は `DP_OSPARSE' とのビット論理和にすることができるが、それは生成されるファイルをスパースにすることを指示する。`bnum' はバケット配列の要素数の目安を指å
 ®šã™ã‚‹ãŒã€0 以下ならデフォルト値が使われる。バケット配列の要素数はデータベースを作成する時に決められ、最適化以外の手段で変更することはできない。バケット配列の要素数は、格納するレコード数の半分から4倍程度にするのがよい。戻り値はデータベースハンドルであるか、エラーなら `NULL' である。ライタ(読み書き両用モード)でデータベースファイルを開く際にはそのファイルに対して排他ロックがかけられ、リーダ(読み込み専用モード)で開く際には共有ロックがかけられる。その際には該当のロックがかけられるまで制御がブロックする。`DP_ONOLCK' を使う場合、アプリケーションが排他制御の責任を負う。</dd>
+</dl>
+
+<p>データベースとの接続を閉じてハンドルを破棄するには、関数 `dpclose' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpclose(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。データベースの更新内容は、接続を閉じた時点で初めてファイルと同期される。ライタでデータベースを開いた場合、適切に接続を閉じないとデータベースが破壊される。閉じたハンドルの領域は解放されるので、以後は利用できなくなる。</dd>
+</dl>
+
+<p>レコードを追加するには、関数 `dpput' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpput(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>dmode</var>);</kbd></dt>
+<dd>`depot' はライタで接続したデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`vbuf' は値のデータ領域へのポインタを指定する。`vsiz' は値のデータ領域のサイズを指定するか、負数なら `strlen(vbuf)' の値となる。`dmode' はキーが既存レコードと重複した際の制御を指定する。`DP_DOVER' は既存のレコードの値を上書きし、`DP_DKEEP' は既存のレコードを残してエラーを返し、`DP_DCAT' は指定された値を既存の値の末尾に加える。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>レコードを削除するには、関数 `dpout' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpout(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`depot' はライタで接続したデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は正常なら真であり、エラーなら偽である。該当のレコードがない場合も偽を返す。</dd>
+</dl>
+
+<p>レコードを取得するには、関数 `dpget' を用いる。</p>
+
+<dl>
+<dt><kbd>char *dpget(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`start' は値の領域から抽出する最初のバイトのオフセットを指定する。`max' は値の領域から抽出するサイズを指定するか、負数なら無制限となる。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら値を格納した領域へのポインタであり、エラーなら `NULL' である。該当のレコードがない場合も `NULL' を返す。取り出そうとした値のサイズが `start' より小さかった場合には該当とみなさない。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `mall
 oc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>レコードを取得してバッファに書き込むには、関数 `dpgetwb' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpgetwb(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, char *<var>vbuf</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`start' は値の領域から抽出する最初のバイトのオフセットを指定する。`max' は値の領域から抽出するサイズを指定する。それは書き込み用のバッファのサイズ以下である必要がある。`vbuf' は抽出したデータを書き込むバッファへのポインタを指定する。戻り値は正常ならバッファに書き込まれたデータのサイズであり、エラーなら -1 である。該当のレコードがない場合も -1 を返す。取り出そうとした値のサイズが `start' より小さかった場合には該当とみなさない。書き込み用バッファの末尾に終端文字が追加されないことに注意すべきである。</dd>
+</dl>
+
+<p>レコードの値のサイズを取得するには、関数 `dpvsiz' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpvsiz(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は該当レコードの値のサイズであるが、該当がない場合やエラーの場合は -1 である。この関数はレコードの有無を調べるのにも便利である。`dpget' と違って実データを読み込まないので効率がよい。</dd>
+</dl>
+
+<p>データベースのイテレータを初期化するには、関数 `dpiterinit' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpiterinit(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。イテレータは、データベースに格納された全てのレコードを参照するために用いられる。</dd>
+</dl>
+
+<p>データベースのイテレータから次のレコードのキーを取り出すには、関数 `dpiternext' を用いる。</p>
+
+<dl>
+<dt><kbd>char *dpiternext(DEPOT *<var>depot</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常ならキーを格納した領域へのポインタであり、エラーなら `NULL' である。イテレータが最後まできて該当のレコードがない場合も `NULL' を返す。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数を繰り返して呼ぶことによって全てのレコードを一度ずつ参照することができる。ただし、繰り返しの間にデータベースの更新があった場合はその限りではない。なお、取り出すレコードの順序は制御できず、格納した順番でレコードを取り出せるとは限らã
 ªã„。</dd>
+</dl>
+
+<p>データベースのアラインメントを設定するには、関数 `dpsetalign' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpsetalign(DEPOT *<var>depot</var>, int <var>align</var>);</kbd></dt>
+<dd>`depot' はライタで接続したデータベースハンドルを指定する。`align' はアラインメントのサイズを指定する。戻り値は正常なら真であり、エラーなら偽である。アラインメントを設定しておくと、レコードの上書きを頻繁にする場合の処理効率が良くなる。アラインメントには、一連の更新操作をした後の状態での標準的な値のサイズを指定するのがよい。アラインメントが正数の場合、レコードの領域のサイズがアラインメントの倍数になるようにパディングがとられる。アラインメントが負数の場合、`vsiz' を値のサイズとして、パディングのサイズは `(vsiz / pow(2, abs(align) - 1))' として算出される。アラインメントの設定はデータベースに保存されないので、データベースを開く度に指定する必要がある。</dd>
+</dl>
+
+<p>データベースのフリーブロックプールのサイズ設定するには、関数 `dpsetfbpsiz' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpsetfbpsiz(DEPOT *<var>depot</var>, int <var>size</var>);</kbd></dt>
+<dd>`depot' はライタで接続したデータベースハンドルを指定する。`size' はフリーブロックプールのサイズを指定する。戻り値は正常なら真であり、エラーなら偽である。フリーブロックプールのデフォルトのサイズは16である。サイズをより大きくすると、レコードの上書きを繰り返す際の空間効率は上がるが、時間効率が下がる。</dd>
+</dl>
+
+<p>データベースを更新した内容をファイルとデバイスに同期させるには、関数 `dpsync' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpsync(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。この関数はデータベースを閉じないうちに別プロセスにデータベースファイルを利用させる場合に役立つ。</dd>
+</dl>
+
+<p>データベースを最適化するには、関数 `dpoptimize' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpoptimize(DEPOT *<var>depot</var>, int <var>bnum</var>);</kbd></dt>
+<dd>`depot' はライタで接続したデータベースハンドルを指定する。`bnum' は新たなバケット配列の要素数を指定するが、0 以下なら現在のレコード数に最適な値が指定される。戻り値は正常なら真であり、エラーなら偽である。レコードを削除したり、置換モードや連結モードで書き込みを繰り返したりする場合は、データベース内に不要な領域が蓄積するが、この関数はそれを解消するのに役立つ。</dd>
+</dl>
+
+<p>データベースの名前を得るには、関数 `dpname' を用いる。</p>
+
+<dl>
+<dt><kbd>char *dpname(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値は正常なら名前を格納した領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>データベースファイルのサイズを得るには、関数 `dpfsiz' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpfsiz(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値は正常ならデータベースファイルのサイズであり、エラーなら -1 である。</dd>
+</dl>
+
+<p>データベースのバケット配列の要素数を得るには、関数 `dpbnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpbnum(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値は正常ならデータベースのバケット配列の要素数であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>データベースのバケット配列の利用済みの要素数を得るには、関数 `dpbusenum' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpbusenum(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値は正常ならバケット配列の利用済みの要素数であり、エラーなら -1 である。この関数はバケット配列の全ての要素を参照するので、効率が悪い。</dd>
+</dl>
+
+<p>データベースのレコード数を得るには、関数 `dprnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int dprnum(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値は正常ならデータベースのレコード数であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>データベースハンドルがライタかどうかを調べるには、関数 `dpwritable' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpwritable(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値はライタなら真であり、そうでなければ偽である。</dd>
+</dl>
+
+<p>データベースに致命的エラーが起きたかどうかを調べるには、関数 `dpfatalerror' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpfatalerror(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値は致命的エラーがあれば真であり、そうでなければ偽である。</dd>
+</dl>
+
+<p>データベースファイルのinode番号を得るには、関数 `dpinode' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpinode(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値はデータベースファイルのinode番号である。</dd>
+</dl>
+
+<p>データベースの最終更新時刻を得るには、関数 `dpmtime' を用いる。</p>
+
+<dl>
+<dt><kbd>time_t dpmtime(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値はデータベースの最終更新時刻である。</dd>
+</dl>
+
+<p>データベースファイルのファイルディスクリプタを得るには、関数 `dpfdesc' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpfdesc(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。戻り値はデータベースファイルのファイルディスクリプタである。データベースのファイルディスクリプタを直接操ることは推奨されない。</dd>
+</dl>
+
+<p>データベースファイルを削除するには、関数 `dpremove' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpremove(const char *<var>name</var>);</kbd></dt>
+<dd>`name' はデータベースファイルの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>壊れたデータベースファイルを修復するには、関数 `dprepair' を用いる。</p>
+
+<dl>
+<dt><kbd>int dprepair(const char *<var>name</var>);</kbd></dt>
+<dd>`name' はデータベースファイルの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。修復されたデータベースの全レコードが元来もしくは期待される状態に戻る保証はない。</dd>
+</dl>
+
+<p>全てのレコードをエンディアン非依存のデータとしてダンプするには、関数 `dpexportdb' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpexportdb(DEPOT *<var>depot</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`depot' はデータベースハンドルを指定する。`name' は出力ファイルの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>エンディアン非依存データから全てのレコードをロードするには、関数 `dpimportdb' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpimportdb(DEPOT *<var>depot</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`depot' はライタで接続したデータベースハンドルを指定する。データベースは空でなければならない。`name' は入力ファイルの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>データベースファイルからレコードを直接取得するには、関数 `dpsnaffle' を用いる。</p>
+
+<dl>
+<dt><kbd>char *dpsnaffle(const char *<var>name</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`name' はデータベースファイルの名前を指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`start' は値の領域から抽出する最初のバイトのオフセットを指定する。`max' は値の領域から抽出するサイズを指定するか、負数なら無制限となる。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら値を格納した領域へのポインタであり、エラーなら `NULL' である。該当のレコードがない場合も `NULL' を返す。取り出そうとした値のサイズが `start' より小さかった場合には該当とみなさない。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域ã
 ¯ `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はデータベースファイルが別のプロセスにロックされていても利用できるが、最新の更新が反映されている保証はない。</dd>
+</dl>
+
+<p>データベースの内部で用いるハッシュ関数として、関数 `dpinnerhash' がある。</p>
+
+<dl>
+<dt><kbd>int dpinnerhash(const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値はキーから31ビット長のハッシュ値を算出した値である。この関数はアプリケーションがバケット配列の状態を予測する際に役立つ。</dd>
+</dl>
+
+<p>データベースの内部で用いるハッシュ関数と独立したハッシュ関数として、関数 `dpouterhash' がある。</p>
+
+<dl>
+<dt><kbd>int dpouterhash(const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値はキーから31ビット長のハッシュ値を算出した値である。この関数はアプリケーションがデータベースの更に上でハッシュアルゴリズムを利用する際に役立つ。</dd>
+</dl>
+
+<p>ある数以上の自然数の素数を得るには、関数 `dpprimenum' を用いる。</p>
+
+<dl>
+<dt><kbd>int dpprimenum(int <var>num</var>);</kbd></dt>
+<dd>`num' は適当な自然数を指定する。戻り値は、指定した数と同じかより大きくかつなるべく小さい自然数の素数である。この関数はアプリケーションが利用するバケット配列のサイズを決める場合に役立つ。</dd>
+</dl>
+
+<h3>サンプルコード</h3>
+
+<p>名前と対応させて電話番号を格納し、それを検索するアプリケーションのサンプルコードを以下に示す。</p>
+
+<pre>#include <depot.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NAME     "mikio"
+#define NUMBER   "000-1234-5678"
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  DEPOT *depot;
+  char *val;
+
+  /* データベースを開く */
+  if(!(depot = dpopen(DBNAME, DP_OWRITER | DP_OCREAT, -1))){
+    fprintf(stderr, "dpopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* レコードを格納する */
+  if(!dpput(depot, NAME, -1, NUMBER, -1, DP_DOVER)){
+    fprintf(stderr, "dpput: %s\n", dperrmsg(dpecode));
+  }
+
+  /* レコードを取得する */
+  if(!(val = dpget(depot, NAME, -1, 0, -1, NULL))){
+    fprintf(stderr, "dpget: %s\n", dperrmsg(dpecode));
+  } else {
+    printf("Name: %s\n", NAME);
+    printf("Number: %s\n", val);
+    free(val);
+  }
+
+  /* データベースを閉じる */
+  if(!dpclose(depot)){
+    fprintf(stderr, "dpclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<p>データベースの全てのレコードを表示するアプリケーションのサンプルコードを以下に示す。</p>
+
+<pre>#include <depot.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  DEPOT *depot;
+  char *key, *val;
+
+  /* データベースを開く */
+  if(!(depot = dpopen(DBNAME, DP_OREADER, -1))){
+    fprintf(stderr, "dpopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* イテレータを初期化する */
+  if(!dpiterinit(depot)){
+    fprintf(stderr, "dpiterinit: %s\n", dperrmsg(dpecode));
+  }
+
+  /* イテレータを走査する */
+  while((key = dpiternext(depot, NULL)) != NULL){
+    if(!(val = dpget(depot, key, -1, 0, -1, NULL))){
+      fprintf(stderr, "dpget: %s\n", dperrmsg(dpecode));
+      free(key);
+      break;
+    }
+    printf("%s: %s\n", key, val);
+    free(val);
+    free(key);
+  }
+
+  /* データベースを閉じる */
+  if(!dpclose(depot)){
+    fprintf(stderr, "dpclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<h3>注記</h3>
+
+<p>Depotを利用したプログラムをビルドするには、ライブラリ `libqdbm.a' または `libqdbm.so' をリンク対象に加える必要がある。例えば、`sample.c' から `sample' を作るには、以下のようにビルドを行う。</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>POSIXスレッドを有効にしてQDBMをビルドした場合、外部変数 `dpecode' はスレッド固有データへの参照として扱われ、Depotの各関数はリエントラントになる。その場合、スレッド間で同時に同じハンドルにアクセスしない限りは、各関数はスレッドセーフである。ただし、`errno' や `malloc' 等がスレッドセーフな処理系であることが前提となる。</p>
+
+<hr />
+
+<h2><a name="depotcli" id="depotcli" class="head">Depot用コマンド</a></h2>
+
+<p>Depotに対応するコマンドラインインタフェースは以下のものである。</p>
+
+<p>コマンド `dpmgr' はDepotやそのアプリケーションのデバッグに役立つツールである。データベースを更新したり、データベースの状態を調べたりする機能を持つ。シェルスクリプトでデータベースアプリケーションを作るのにも利用できる。以下の書式で用いる。`name' はデータベース名、`key' はレコードのキー、`val' はレコードの値を指定する。</p>
+
+<dl>
+<dt><kbd>dpmgr create [-s] [-bnum <var>num</var>] <var>name</var></kbd></dt>
+<dd>データベースファイルを作成する。</dd>
+<dt><kbd>dpmgr put [-kx|-ki] [-vx|-vi|-vf] [-keep|-cat] [-na] <var>name</var> <var>key</var> <var>val</var></kbd></dt>
+<dd>キーと値に対応するレコードを追加する。</dd>
+<dt><kbd>dpmgr out [-kx|-ki] <var>name</var> <var>key</var></kbd></dt>
+<dd>キーに対応するレコードを削除する。</dd>
+<dt><kbd>dpmgr get [-nl] [-kx|-ki] [-start <var>num</var>] [-max <var>num</var>] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>キーに対応するレコードの値を取得して標準出力する。</dd>
+<dt><kbd>dpmgr list [-nl] [-k|-v] [-ox] <var>name</var></kbd></dt>
+<dd>データベース内の全てのレコードのキーと値をタブと改行で区切って標準出力する。</dd>
+<dt><kbd>dpmgr optimize [-bnum <var>num</var>] [-na] <var>name</var></kbd></dt>
+<dd>データベースを最適化する。</dd>
+<dt><kbd>dpmgr inform [-nl] <var>name</var></kbd></dt>
+<dd>データベースの雑多な情報を出力する。</dd>
+<dt><kbd>dpmgr remove <var>name</var></kbd></dt>
+<dd>データベースファイルを削除する。</dd>
+<dt><kbd>dpmgr repair <var>name</var></kbd></dt>
+<dd>壊れたデータベースファイルを修復する。</dd>
+<dt><kbd>dpmgr exportdb <var>name</var> <var>file</var></kbd></dt>
+<dd>全てのレコードをエンディアン非依存のデータとしてダンプする。</dd>
+<dt><kbd>dpmgr importdb [-bnum <var>num</var>] <var>name</var> <var>file</var></kbd></dt>
+<dd>エンディアン非依存データから全てのレコードをロードする。</dd>
+<dt><kbd>dpmgr snaffle [-kx|-ki] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>ロックされたデータベースからキーに対応するレコードの値を取得して標準出力する。</dd>
+<dt><kbd>dpmgr version</kbd></dt>
+<dd>QDBMのバージョン情報を標準出力する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-s</kbd> : ファイルをスパースにする。</li>
+<li><kbd>-bnum <var>num</var></kbd> : バケット配列の要素数を `num' に指定する。</li>
+<li><kbd>-kx</kbd> : 2桁単位の16進数によるバイナリ表現として `key' を扱う。</li>
+<li><kbd>-ki</kbd> : 10進数による数値表現として `key' を扱う。</li>
+<li><kbd>-vx</kbd> : 2桁単位の16進数によるバイナリ表現として `val' を扱う。</li>
+<li><kbd>-vi</kbd> : 10進数による数値表現として `val' を扱う。</li>
+<li><kbd>-vf</kbd> : 名前が `val' のファイルのデータを値として読み込む。</li>
+<li><kbd>-keep</kbd> : 既存のレコードとキーが重複時に上書きせずにエラーにする。</li>
+<li><kbd>-cat</kbd> : 既存のレコードとキーが重複時に値を末尾に追加する。</li>
+<li><kbd>-na</kbd> : アラインメントを設定しない。</li>
+<li><kbd>-nl</kbd> : ファイルロックをかけずにデータベースを開く。</li>
+<li><kbd>-start</kbd> : 値から取り出すデータの開始オフセットを指定する。</li>
+<li><kbd>-max</kbd> : 値から取り出すデータの最大の長さを指定する。</li>
+<li><kbd>-ox</kbd> : 2桁単位の16進数によるバイナリ表現として標準出力を行う。</li>
+<li><kbd>-n</kbd> : 標準出力の末尾に付加される改行文字の出力を抑制する。</li>
+<li><kbd>-k</kbd> : キーのみを出力する。</li>
+<li><kbd>-v</kbd> : 値のみを出力する。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。環境変数 `QDBMDBGFD' の値として、変数 `dpecode' の変更履歴を出力するファイルディスクリプタを指定ことができる。</p>
+
+<p>コマンド `dptest' はDepotの機能テストや性能テストに用いるツールである。このコマンドによって生成されたデータベースファイルを `dpmgr' によって解析したり、`time' コマンドによってこのコマンドの実行時間を計ったりするとよい。以下の書式で用いる。`name' はデータベース名、`rnum' はレコード数、`bnum' はバケット配列の要素数、`pnum' はキーのパターン数、`align' はアラインメントの基本サイズ、`fbpsiz' はフリーブロックプールのサイズを指定する。</p>
+
+<dl>
+<dt><kbd>dptest write [-s] <var>name</var> <var>rnum</var> <var>bnum</var></kbd></dt>
+<dd>`00000001'、`00000002' のように変化する8バイトのキーと適当な8バイトの値を連続してデータベースに追加する。</dd>
+<dt><kbd>dptest read [-wb] <var>name</var></kbd></dt>
+<dd>上記で生成したデータベースの全レコードを検索する。</dd>
+<dt><kbd>dptest rcat [-c] <var>name</var> <var>rnum</var> <var>bnum</var> <var>pnum</var> <var>align</var> <var>fbpsiz</var></kbd></dt>
+<dd>キーがある程度重複するようにレコードの追加を行い、連結モードで処理する。</dd>
+<dt><kbd>dptest combo <var>name</var></kbd></dt>
+<dd>各種操作の組み合わせテストを行う。</dd>
+<dt><kbd>dptest wicked [-c] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>各種更新操作を無作為に選択して実行する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-s</kbd> : ファイルをスパースにする。</li>
+<li><kbd>-wb</kbd> : 関数 `dpget' の代わりに関数 `dpgetwb' を用いる。</li>
+<li><kbd>-c</kbd> : Cabinのマップを使って比較テストを行う。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。環境変数 `QDBMDBGFD' の値として、変数 `dpecode' の変更履歴を出力するファイルディスクリプタを指定ことができる。</p>
+
+<p>コマンド `dptsv' はタブ区切りでキーと値を表現した行からなるTSVファイルとDepotのデータベースを相互変換する。このコマンドは、QDBMの他のバージョンや他のDBMとの間でデータの交換を行う際に役立つ。また、バイトオーダの違うシステムの間でデータを交換する際にも役立つ。以下の書式で用いる。`name' はデータベース名を指定する。`export' サブコマンドではTSVのデータは標準入力から読み込む。キーが重複するレコードは後者を優先する。`-bnum' オプションの引数 `num' はバケット配列の要素数を指定する。`import' サブコマンドではTSVのデータが標準出力に書き出される。</p>
+
+<dl>
+<dt><kbd>dptsv import [-bnum <var>num</var>] [-bin] <var>name</var></kbd></dt>
+<dd>TSVファイルを読み込んでデータベースを作成する。</dd>
+<dt><kbd>dptsv export [-bin] <var>name</var></kbd></dt>
+<dd>データベースの全てのレコードをTSVファイルとして出力する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-bnum <var>num</var></kbd> : バケット配列の要素数を `num' に指定する。</li>
+<li><kbd>-bin</kbd> : Base64形式でレコードを扱う。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<p>Depotのコマンド群を駆使すると、簡単なデータベースシステムが構築できる。例えば `/etc/password' をユーザ名で検索するためのデータベースを作成するには、以下のようにする。</p>
+
+<pre>cat /etc/passwd | tr ':' '\t' | dptsv import casket
+</pre>
+
+<p>そして、`mikio' というユーザの情報を取り出すには、以下のようにする。</p>
+
+<pre>dpmgr get casket mikio
+</pre>
+
+<p>これらのコマンドと同等の機能をDepotのAPIを用いて実装することも容易である。</p>
+
+<hr />
+
+<h2><a name="curiaapi" id="curiaapi" class="head">Curia: æ‹¡å¼µAPI</a></h2>
+
+<h3>概要</h3>
+
+<p>CuriaはQDBMの拡張APIであり、複数のデータベースファイルをディレクトリで一括して扱う機能を提供する。データベースを複数のファイルに分割することで、ファイルシステムによるファイルサイズの制限を回避することができる。複数のデバイスにファイルを分散させれば、スケーラビリティを向上させることができる。</p>
+
+<p>Depotではファイル名を指定してデータベースを構築するが、Curiaではディレクトリ名を指定してデータベースを構築する。指定したディレクトリの直下には、`depot' という名前のデータベースファイルが生成される。これはディレクトリの属性を保持するものであり、レコードの実データは格納されない。それとは別に、データベースを分割した個数だけ、4桁の10進数値の名前を持つサブディレクトリが生成され、各々のサブディレクトリの中には `depot' という名前でデータベースファイルが生成される。レコードの実データはそれらに格納される。例えば、`casket' という名前のデータベースを作成し、分割数を3にする場合、`casket/depot'、`casket/0001/depot'、`casket/0002/depot'、`casket/0003/depot' が生成される。データベースを作成する際にすã
 §ã«ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªãŒå­˜åœ¨ã—ていてもエラーとはならない。したがって、予めサブディレクトリを生成しておいて、各々に異なるデバイスのファイルシステムをマウントしておけば、データベースファイルを複数のデバイスに分散させることができる。</p>
+
+<p>Curiaにはラージオブジェクトを扱う機能がある。通常のレコードのデータはデータベースファイルに格納されるが、ラージオブジェクトのレコードのデータは個別のファイルに格納される。ラージオブジェクトのファイルはハッシュ値を元にディレクトリに分けて格納されるので、通常のレコードには劣るが、それなりの速度で参照できる。サイズが大きく参照頻度が低いデータは、ラージオブジェクトとしてデータベースファイルから分離すべきである。そうすれば、通常のレコードに対する処理速度が向上する。ラージオブジェクトのディレクトリ階層はデータベースファイルが格納されるサブディレクトリの中の `lob' という名前のディレクトリの中に作られる。通常のデータベースとラージオブジェクトのデータベースはキãƒ
 ¼ç©ºé–“が異なり、互いに干渉することはない。</p>
+
+<p>Curiaを使うためには、`depot.h' と `curia.h' と `stdlib.h' をインクルードすべきである。通常、ソースファイルの冒頭付近で以下の記述を行う。</p>
+
+<dl>
+<dt><kbd>#include <depot.h></kbd></dt>
+<dt><kbd>#include <curia.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+</dl>
+
+<p>Curiaでデータベースを扱う際には、`CURIA' 型へのポインタをハンドルとして用いる。これは、`stdio.h' の各種ルーチンがファイル入出力に `FILE' 型へのポインタを用いるのに似ている。ハンドルは、関数 `cropen' で開き、関数 `crclose' で閉じる。ハンドルのメンバを直接参照することは推奨されない。データベースに致命的なエラーが起きた場合は、以後そのハンドルに対する `crclose' を除く全ての操作は何もせずにエラーを返す。ひとつのプロセスで複数のデータベースディレクトリを同時に利用することは可能であるが、同じデータベースディレクトリの複数のハンドルを利用してはならない。</p>
+
+<p>CuriaでもDepotと同じく外部変数 `dpecode' に直前のエラーコードが記録される。エラーコードに対応するメッセージ文字列を得るには、関数 `dperrmsg' を用いる。</p>
+
+<h3>API</h3>
+
+<p>データベースのハンドルを作成するには、関数 `cropen' を用いる。</p>
+
+<dl>
+<dt><kbd>CURIA *cropen(const char *<var>name</var>, int <var>omode</var>, int <var>bnum</var>, int <var>dnum</var>);</kbd></dt>
+<dd>`name' はデータベースディレクトリの名前を指定する。`omode' は接続モードを指定し、`CR_OREADER' ならリーダ、`CR_OWRITER' ならライタとなる。`CR_OWRITER' の場合、`CR_OCREAT' または `CR_OTRUNC' とのビット論理和にすることができる。`CR_OCREAT' はファイルが無い場合に新規作成することを指示し、`CR_OTRUNC' はファイルが存在しても作り直すことを指示する。`CR_OREADER' と `CR_OWRITER' の両方で `CR_ONOLCK' または `CR_OLCKNB' とのビット論理和にすることができるが、前者はファイルロックをかけずにデータベースを開くことを指示し、後者はブロックせずにロックをかけることを指示する。`CR_OCREAT' は `CR_OSPARSE' とのビット論理和にすることができるが、それは生成されるファイルをスパースにすることを指示する。`bnum' はバケット配列の要素数の目安ã
 ‚’指定するが、0 以下ならデフォルト値が使われる。バケット配列の要素数はデータベースを作成する時に決められ、最適化以外の手段で変更することはできない。バケット配列の要素数は、格納するレコード数の半分から4倍程度にするのがよい。`dnum' は要素データベースの数を指定するが、0 以下ならデフォルト値が使われる。データベースファイルの分割数はデータベースを作成する時に指定したものから変更することはできない。データベースファイルの分割数の最大値は 512 個である。戻り値はデータベースハンドルであるか、エラーなら `NULL' である。ライタ(読み書き両用モード)でデータベースファイルを開く際にはそのファイルに対して排他ロックがかけられ、リーダ(読み込み専用モード)で開く際には共有ロックã
 Œã‹ã‘られる。その際には該当のロックがかけられるまで制御がブロックする。`CR_ONOLCK' を使う場合、アプリケーションが排他制御の責任を負う。</dd>
+</dl>
+
+<p>データベースとの接続を閉じてハンドルを破棄するには、関数 `crclose' を用いる。</p>
+
+<dl>
+<dt><kbd>int crclose(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。データベースの更新内容は、接続を閉じた時点で初めてファイルと同期される。ライタでデータベースを開いた場合、適切に接続を閉じないとデータベースが破壊される。閉じたハンドルの領域は解放されるので、以後は利用できなくなる。</dd>
+</dl>
+
+<p>レコードを追加するには、関数 `crput' を用いる。</p>
+
+<dl>
+<dt><kbd>int crput(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>dmode</var>);</kbd></dt>
+<dd>`curia' はライタで接続したデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`vbuf' は値のデータ領域へのポインタを指定する。`vsiz' は値のデータ領域のサイズを指定するか、負数なら `strlen(vbuf)' の値となる。`dmode' はキーが既存レコードと重複した際の制御を指定する。`CR_DOVER' は既存のレコードの値を上書きし、`CR_DKEEP' は既存のレコードを残してエラーを返し、`DP_DCAT' は指定された値を既存の値の末尾に加える。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>レコードを削除するには、関数 `crout' を用いる。</p>
+
+<dl>
+<dt><kbd>int crout(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`curia' はライタで接続したデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は正常なら真であり、エラーなら偽である。該当のレコードがない場合も偽を返す。</dd>
+</dl>
+
+<p>レコードを取得するには、関数 `crget' を用いる。</p>
+
+<dl>
+<dt><kbd>char *crget(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`start' は値の領域から抽出する最初のバイトのオフセットを指定する。`max' は値の領域から抽出するサイズを指定するか、負数なら無制限となる。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら値を格納した領域へのポインタであり、エラーなら `NULL' である。該当のレコードがない場合も `NULL' を返す。取り出そうとした値のサイズが `start' より小さかった場合には該当とみなさない。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `mall
 oc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>レコードを取得してバッファに書き込むには、関数 `crgetwb' を用いる。</p>
+
+<dl>
+<dt><kbd>int crgetwb(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, char *<var>vbuf</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`start' は値の領域から抽出する最初のバイトのオフセットを指定する。`max' は値の領域から抽出するサイズを指定する。それは書き込み用のバッファのサイズ以下である必要がある。`vbuf' は抽出したデータを書き込むバッファへのポインタを指定する。戻り値は正常ならバッファに書き込まれたデータのサイズであり、エラーなら -1 である。該当のレコードがない場合も -1 を返す。取り出そうとした値のサイズが `start' より小さかった場合には該当とみなさない。書き込み用バッファの末尾に終端文字が追加されないことに注意すべきである。</dd>
+</dl>
+
+<p>レコードの値のサイズを取得するには、関数 `crvsiz' を用いる。</p>
+
+<dl>
+<dt><kbd>int crvsiz(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は該当レコードの値のサイズであるが、該当がない場合やエラーの場合は -1 である。この関数はレコードの有無を調べるのにも便利である。`crget' と違って実データを読み込まないので効率がよい。</dd>
+</dl>
+
+<p>データベースのイテレータを初期化するには、関数 `criterinit' を用いる。</p>
+
+<dl>
+<dt><kbd>int criterinit(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。イテレータは、データベースに格納された全てのレコードを参照するために用いられる。</dd>
+</dl>
+
+<p>データベースのイテレータから次のレコードのキーを取り出すには、関数 `criternext' を用いる。</p>
+
+<dl>
+<dt><kbd>char *criternext(CURIA *<var>curia</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常ならキーを格納した領域へのポインタであり、エラーなら `NULL' である。イテレータが最後まできて該当のレコードがない場合も `NULL' を返す。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数を繰り返して呼ぶことによって全てのレコードを一度ずつ参照することができる。ただし、繰り返しの間にデータベースの更新があった場合はその限りではない。なお、取り出すレコードの順序は制御できず、格納した順番でレコードを取り出せるとは限らã
 ªã„。</dd>
+</dl>
+
+<p>データベースのアラインメントを設定するには、関数 `crsetalign' を用いる。</p>
+
+<dl>
+<dt><kbd>int crsetalign(CURIA *<var>curia</var>, int <var>align</var>);</kbd></dt>
+<dd>`curia' はライタで接続したデータベースハンドルを指定する。`align' はアラインメントのサイズを指定する。戻り値は正常なら真であり、エラーなら偽である。アラインメントを設定しておくと、レコードの上書きを頻繁にする場合の処理効率が良くなる。アラインメントには、一連の更新操作をした後の状態での標準的な値のサイズを指定するのがよい。アラインメントが正数の場合、レコードの領域のサイズがアラインメントの倍数になるようにパディングがとられる。アラインメントが負数の場合、`vsiz' を値のサイズとして、パディングのサイズは `(vsiz / pow(2, abs(align) - 1))' として算出される。アラインメントの設定はデータベースに保存されないので、データベースを開く度に指定する必要がある。</dd>
+</dl>
+
+<p>データベースのフリーブロックプールのサイズ設定するには、関数 `crsetfbpsiz' を用いる。</p>
+
+<dl>
+<dt><kbd>int crsetfbpsiz(CURIA *<var>curia</var>, int <var>size</var>);</kbd></dt>
+<dd>`curia' はライタで接続したデータベースハンドルを指定する。`size' はフリーブロックプールのサイズを指定する。戻り値は正常なら真であり、エラーなら偽である。フリーブロックプールのデフォルトのサイズは16である。サイズをより大きくすると、レコードの上書きを繰り返す際の空間効率は上がるが、時間効率が下がる。</dd>
+</dl>
+
+<p>データベースを更新した内容をファイルとデバイスに同期させるには、関数 `crsync' を用いる。</p>
+
+<dl>
+<dt><kbd>int crsync(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。この関数はデータベースを閉じないうちに別プロセスにデータベースファイルを利用させる場合に役立つ。</dd>
+</dl>
+
+<p>データベースを最適化するには、関数 `croptimize' を用いる。</p>
+
+<dl>
+<dt><kbd>int croptimize(CURIA *<var>curia</var>, int <var>bnum</var>);</kbd></dt>
+<dd>`curia' はライタで接続したデータベースハンドルを指定する。`bnum' は新たなバケット配列の要素数を指定するが、0 以下なら現在のレコード数に最適な値が指定される。戻り値は正常なら真であり、エラーなら偽である。レコードを削除したり、置換モードや連結モードで書き込みを繰り返したりする場合は、データベース内に不要な領域が蓄積するが、この関数はそれを解消するのに役立つ。</dd>
+</dl>
+
+<p>データベースの名前を得るには、関数 `crname' を用いる。</p>
+
+<dl>
+<dt><kbd>char *crname(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値は正常なら名前を格納した領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>データベースファイルのサイズの合計を得るには、関数 `crfsiz' を用いる。</p>
+
+<dl>
+<dt><kbd>int crfsiz(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値は正常ならデータベースファイルのサイズの合計であり、エラーなら -1 である。戻り値が2GBを越えた場合は桁溢れが起こる。</dd>
+</dl>
+
+<p>データベースファイルのサイズの合計を倍精度浮動小数として得るには、関数 `crfsizd' を用いる。</p>
+
+<dl>
+<dt><kbd>double crfsizd(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値は正常ならデータベースファイルのサイズの合計の倍精度値であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>データベースのバケット配列の要素数の合計を得るには、関数 `crbnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int crbnum(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値は正常ならデータベースのバケット配列の要素数の合計であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>データベースのバケット配列の利用済みの要素数の合計を得るには、関数 `crbusenum' を用いる。</p>
+
+<dl>
+<dt><kbd>int crbusenum(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値は正常ならバケット配列の利用済みの要素数の合計であり、エラーなら -1 である。この関数はバケット配列の全ての要素を参照するので、効率が悪い。</dd>
+</dl>
+
+<p>データベースのレコード数を得るには、関数 `crrnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int crrnum(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値は正常ならデータベースのレコード数であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>データベースハンドルがライタかどうかを調べるには、関数 `crwritable' を用いる。</p>
+
+<dl>
+<dt><kbd>int crwritable(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値はライタなら真であり、そうでなければ偽である。</dd>
+</dl>
+
+<p>データベースに致命的エラーが起きたかどうかを調べるには、関数 `crfatalerror' を用いる。</p>
+
+<dl>
+<dt><kbd>int crfatalerror(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値は致命的エラーがあれば真であり、そうでなければ偽である。</dd>
+</dl>
+
+<p>データベースディレクトリのinode番号を得るには、関数 `crinode' を用いる。</p>
+
+<dl>
+<dt><kbd>int crinode(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値はデータベースディレクトリのinode番号である。</dd>
+</dl>
+
+<p>データベースの最終更新時刻を得るには、関数 `crmtime' を用いる。</p>
+
+<dl>
+<dt><kbd>time_t crmtime(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値はデータベースの最終更新時刻である。</dd>
+</dl>
+
+<p>データベースディレクトリを削除するには、関数 `crremove' を用いる。</p>
+
+<dl>
+<dt><kbd>int crremove(const char *<var>name</var>);</kbd></dt>
+<dd>`name' はデータベースディレクトリの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>壊れたデータベースディレクトリを修復するには、関数 `crrepair' を用いる。</p>
+
+<dl>
+<dt><kbd>int crrepair(const char *<var>name</var>);</kbd></dt>
+<dd>`name' はデータベースファイルの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。修復されたデータベースの全レコードが元来もしくは期待される状態に戻る保証はない。</dd>
+</dl>
+
+<p>全てのレコードをエンディアン非依存のデータとしてダンプするには、関数 `crexportdb' を用いる。</p>
+
+<dl>
+<dt><kbd>int crexportdb(CURIA *<var>curia</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。`name' は出力ディレクトリの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>エンディアン非依存データから全てのレコードをロードするには、関数 `crimportdb' を用いる。</p>
+
+<dl>
+<dt><kbd>int crimportdb(CURIA *<var>curia</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`curia' はライタで接続したデータベースハンドルを指定する。データベースは空でなければならない。`name' は入力ディレクトリの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>データベースディレクトリからレコードを直接取得するには、関数 `crsnaffle' を用いる。</p>
+
+<dl>
+<dt><kbd>char *crsnaffle(const char *<var>name</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`name' はデータベースディレクトリの名前を指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`start' は値の領域から抽出する最初のバイトのオフセットを指定する。`max' は値の領域から抽出するサイズを指定するか、負数なら無制限となる。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら値を格納した領域へのポインタであり、エラーなら `NULL' である。該当のレコードがない場合も `NULL' を返す。取り出そうとした値のサイズが `start' より小さかった場合には該当とみなさない。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値のé
  ˜åŸŸã¯ `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はデータベースディレクトリが別のプロセスにロックされていても利用できるが、最新の更新が反映されている保証はない。</dd>
+</dl>
+
+<p>ラージオブジェクト用データベースにレコードを追加するには、関数 `crputlob' を用いる。</p>
+
+<dl>
+<dt><kbd>int crputlob(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>dmode</var>);</kbd></dt>
+<dd>`curia' はライタで接続したデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`vbuf' は値のデータ領域へのポインタを指定する。`vsiz' は値のデータ領域のサイズを指定するか、負数なら `strlen(vbuf)' の値となる。`dmode' はキーが既存レコードと重複した際の制御を指定する。`CR_DOVER' は既存のレコードの値を上書きし、`CR_DKEEP' は既存のレコードを残してエラーを返し、`DP_DCAT' は指定された値を既存の値の末尾に加える。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>ラージオブジェクト用データベースからレコードを削除するには、関数 `croutlob' を用いる。</p>
+
+<dl>
+<dt><kbd>int croutlob(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`curia' はライタで接続したデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は正常なら真であり、エラーなら偽である。該当のレコードがない場合も偽を返す。</dd>
+</dl>
+
+<p>ラージオブジェクト用データベースからレコードの値を取得するには、関数 `crgetlob' を用いる。</p>
+
+<dl>
+<dt><kbd>char *crgetlob(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`start' は値の領域から抽出する最初のバイトのオフセットを指定する。`max' は値の領域から抽出するサイズを指定するか、負数なら無制限となる。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら値を格納した領域へのポインタであり、エラーなら `NULL' である。該当のレコードがない場合も `NULL' を返す。取り出そうとした値のサイズが `start' より小さかった場合には該当とみなさない。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `mall
 oc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>ラージオブジェクト用データベースにあるレコードのファイルディスクリプタを取得するには、関数 `crgetlobfd' を用いる。</p>
+
+<dl>
+<dt><kbd>int crgetlobfd(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は正常なら該当のファイルディスクリプタであり、エラーなら -1 である。該当のレコードがない場合も -1 を返す。戻り値のファイルディスクリプタは `open' コールで開かれる。データベースがライタで接続された場合はそのディスクリプタは書き込み可能(O_RDWR)であり、そうでなければ書き込み不可能(O_RDONLY)である。ディスクリプタが不要になったら `close' で閉じるべきである。</dd>
+</dl>
+
+<p>ラージオブジェクト用データベースにあるレコードの値のサイズを取得するには、関数 `crvsizlob' を用いる。</p>
+
+<dl>
+<dt><kbd>int crvsizlob(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は該当レコードの値のサイズであり、該当がない場合やエラーの場合は -1 である。この関数はレコードの有無を調べるのにも便利である。`crgetlob' と違って実データを読み込まないので効率がよい。</dd>
+</dl>
+
+<p>ラージオブジェクト用データベースのレコード数の合計を得るには、関数 `crrnumlob' を用いる。</p>
+
+<dl>
+<dt><kbd>int crrnumlob(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' はデータベースハンドルを指定する。戻り値は正常ならデータベースのレコード数の合計であり、エラーなら -1 である。</dd>
+</dl>
+
+<h3>サンプルコード</h3>
+
+<p>名前と対応させて電話番号を格納し、それを検索するアプリケーションのサンプルコードを以下に示す。</p>
+
+<pre>#include <depot.h>
+#include <curia.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NAME     "mikio"
+#define NUMBER   "000-1234-5678"
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  CURIA *curia;
+  char *val;
+
+  /* データベースを開く */
+  if(!(curia = cropen(DBNAME, CR_OWRITER | CR_OCREAT, -1, -1))){
+    fprintf(stderr, "cropen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* レコードを格納する */
+  if(!crput(curia, NAME, -1, NUMBER, -1, CR_DOVER)){
+    fprintf(stderr, "crput: %s\n", dperrmsg(dpecode));
+  }
+
+  /* レコードを取得する */
+  if(!(val = crget(curia, NAME, -1, 0, -1, NULL))){
+    fprintf(stderr, "crget: %s\n", dperrmsg(dpecode));
+  } else {
+    printf("Name: %s\n", NAME);
+    printf("Number: %s\n", val);
+    free(val);
+  }
+
+  /* データベースを閉じる */
+  if(!crclose(curia)){
+    fprintf(stderr, "crclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<p>データベースの全てのレコードを表示するアプリケーションのサンプルコードを以下に示す。</p>
+
+<pre>#include <depot.h>
+#include <curia.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  CURIA *curia;
+  char *key, *val;
+
+  /* データベースを開く */
+  if(!(curia = cropen(DBNAME, CR_OREADER, -1, -1))){
+    fprintf(stderr, "cropen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* イテレータを初期化する */
+  if(!criterinit(curia)){
+    fprintf(stderr, "criterinit: %s\n", dperrmsg(dpecode));
+  }
+
+  /* イテレータを走査する */
+  while((key = criternext(curia, NULL)) != NULL){
+    if(!(val = crget(curia, key, -1, 0, -1, NULL))){
+      fprintf(stderr, "crget: %s\n", dperrmsg(dpecode));
+      free(key);
+      break;
+    }
+    printf("%s: %s\n", key, val);
+    free(val);
+    free(key);
+  }
+
+  /* データベースを閉じる */
+  if(!crclose(curia)){
+    fprintf(stderr, "crclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<h3>注記</h3>
+
+<p>Curiaを利用したプログラムをビルドする方法は、Depotの場合と全く同じである。</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>POSIXスレッドを有効にしてQDBMをビルドした場合、外部変数 `dpecode' はスレッド固有データへの参照として扱われ、Curiaの各関数はリエントラントになる。その場合、スレッド間で同時に同じハンドルにアクセスしない限りは、各関数はスレッドセーフである。ただし、`errno' や `malloc' 等がスレッドセーフな処理系であることが前提となる。</p>
+
+<hr />
+
+<h2><a name="curiacli" id="curiacli" class="head">Curia用コマンド</a></h2>
+
+<p>Curiaに対応するコマンドラインインタフェースは以下のものである。</p>
+
+<p>コマンド `crmgr' はCuriaやそのアプリケーションのデバッグに役立つツールである。データベースを更新したり、データベースの状態を調べたりする機能を持つ。シェルスクリプトでデータベースアプリケーションを作るのにも利用できる。以下の書式で用いる。`name' はデータベース名、`key' はレコードのキー、`val' はレコードの値を指定する。</p>
+
+<dl>
+<dt><kbd>crmgr create [-s] [-bnum <var>num</var>] [-dnum <var>num</var>] <var>name</var></kbd></dt>
+<dd>データベースディレクトリを作成する。</dd>
+<dt><kbd>crmgr put [-kx|-ki] [-vx|-vi|-vf] [-keep|-cat] [-lob] [-na] <var>name</var> <var>key</var> <var>val</var></kbd></dt>
+<dd>キーと値に対応するレコードを追加する。</dd>
+<dt><kbd>crmgr out [-kx|-ki] [-lob] <var>name</var> <var>key</var></kbd></dt>
+<dd>キーに対応するレコードを削除する。</dd>
+<dt><kbd>crmgr get [-nl] [-kx|-ki] [-start <var>num</var>] [-max <var>num</var>] [-ox] [-lob] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>キーに対応するレコードの値を取得して標準出力する。</dd>
+<dt><kbd>crmgr list [-nl] [-k|-v] [-ox] <var>name</var></kbd></dt>
+<dd>データベース内の全てのレコードのキーと値をタブと改行で区切って標準出力する。</dd>
+<dt><kbd>crmgr optimize [-bnum <var>num</var>] [-na] <var>name</var></kbd></dt>
+<dd>データベースを最適化する。</dd>
+<dt><kbd>crmgr inform [-nl] <var>name</var></kbd></dt>
+<dd>データベースの雑多な情報を出力する。</dd>
+<dt><kbd>crmgr remove <var>name</var></kbd></dt>
+<dd>データベースディレクトリを削除する。</dd>
+<dt><kbd>crmgr repair <var>name</var></kbd></dt>
+<dd>壊れたデータベースディレクトリを修復する。</dd>
+<dt><kbd>crmgr exportdb <var>name</var> <var>dir</var></kbd></dt>
+<dd>全てのレコードをエンディアン非依存のデータとしてダンプする。</dd>
+<dt><kbd>crmgr importdb [-bnum <var>num</var>] [-dnum <var>num</var>] <var>name</var> <var>dir</var></kbd></dt>
+<dd>エンディアン非依存データから全てのレコードをロードする。</dd>
+<dt><kbd>crmgr snaffle [-kx|-ki] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>ロックされたデータベースからキーに対応するレコードの値を取得して標準出力する。</dd>
+<dt><kbd>crmgr version</kbd></dt>
+<dd>QDBMのバージョン情報を標準出力する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-s</kbd> : ファイルをスパースにする。</li>
+<li><kbd>-bnum <var>num</var></kbd> : バケット配列の要素数を `num' に指定する。</li>
+<li><kbd>-dnum <var>num</var></kbd> : データベースファイルの分割数を `num' に指定する。</li>
+<li><kbd>-kx</kbd> : 2桁単位の16進数によるバイナリ表現として `key' を扱う。</li>
+<li><kbd>-ki</kbd> : 10進数による数値表現として `key' を扱う。</li>
+<li><kbd>-vx</kbd> : 2桁単位の16進数によるバイナリ表現として `val' を扱う。</li>
+<li><kbd>-vi</kbd> : 10進数による数値表現として `val' を扱う。</li>
+<li><kbd>-vf</kbd> : 名前が `val' のファイルのデータを値として読み込む。</li>
+<li><kbd>-keep</kbd> : 既存のレコードとキーが重複時に上書きせずにエラーにする。</li>
+<li><kbd>-cat</kbd> : 既存のレコードとキーが重複時に値を末尾に追加する。</li>
+<li><kbd>-na</kbd> : アラインメントを設定しない。</li>
+<li><kbd>-nl</kbd> : ファイルロックをかけずにデータベースを開く。</li>
+<li><kbd>-start</kbd> : 値から取り出すデータの開始オフセットを指定する。</li>
+<li><kbd>-max</kbd> : 値から取り出すデータの最大の長さを指定する。</li>
+<li><kbd>-ox</kbd> : 2桁単位の16進数によるバイナリ表現として標準出力を行う。</li>
+<li><kbd>-lob</kbd> : ラージオブジェクトを扱う。</li>
+<li><kbd>-n</kbd> : 標準出力の末尾に付加される改行文字の出力を抑制する。</li>
+<li><kbd>-k</kbd> : キーのみを出力する。</li>
+<li><kbd>-v</kbd> : 値のみを出力する。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。環境変数 `QDBMDBGFD' の値として、変数 `dpecode' の変更履歴を出力するファイルディスクリプタを指定ことができる。</p>
+
+<p>コマンド `crtest' はCuriaの機能テストや性能テストに用いるツールである。`crtest' によって生成されたデータベースディレクトリを `crmgr' によって解析したり、`time' コマンドによってこのコマンドの実行時間を計ったりするとよい。以下の書式で用いる。`name' はデータベース名、`rnum' はレコード数、`bnum' はバケット配列の要素数、`dnum' はデータベースファイルの分割数、`pnum' はキーのパターン数、`align' はアラインメントの基本サイズ、`fbpsiz' はフリーブロックプールのサイズを指定する。</p>
+
+<dl>
+<dt><kbd>crtest write [-s] [-lob] <var>name</var> <var>rnum</var> <var>bnum</var> <var>dnum</var></kbd></dt>
+<dd>`00000001'、`00000002' のように変化する8バイトのキーと適当な8バイトの値を連続してデータベースに追加する。</dd>
+<dt><kbd>crtest read [-wb] [-lob] <var>name</var></kbd></dt>
+<dd>上記で生成したデータベースの全レコードを検索する。</dd>
+<dt><kbd>crtest rcat [-c] <var>name</var> <var>rnum</var> <var>bnum</var> <var>dnum</var> <var>pnum</var> <var>align</var> <var>fbpsiz</var></kbd></dt>
+<dd>キーがある程度重複するようにレコードの追加を行い、連結モードで処理する。</dd>
+<dt><kbd>crtest combo <var>name</var></kbd></dt>
+<dd>各種操作の組み合わせテストを行う。</dd>
+<dt><kbd>crtest wicked [-c] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>各種更新操作を無作為に選択して実行する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-s</kbd> : ファイルをスパースにする。</li>
+<li><kbd>-lob</kbd> : ラージオブジェクトを扱う。</li>
+<li><kbd>-wb</kbd> : 関数 `crget' の代わりに関数 `crgetwb' を用いる。</li>
+<li><kbd>-c</kbd> : Cabinのマップを使って比較テストを行う。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。環境変数 `QDBMDBGFD' の値として、変数 `dpecode' の変更履歴を出力するファイルディスクリプタを指定ことができる。</p>
+
+<p>コマンド `crtsv' はタブ区切りでキーと値を表現した行からなるTSVファイルとCuriaのデータベースを相互変換する。このコマンドは、QDBMの他のバージョンや他のDBMとの間でデータの交換を行う際に役立つ。また、バイトオーダの違うシステムの間でデータを交換する際にも役立つ。以下の書式で用いる。`name' はデータベース名を指定する。`export' サブコマンドではTSVのデータは標準入力から読み込む。キーが重複するレコードは後者を優先する。`-bnum' オプションの引数 `num' はバケット配列の要素数を指定する。`-dnum' オプションの引数 `num' は要素データベースの数を指定する。`import' サブコマンドではTSVのデータが標準出力に書き出される。</p>
+
+<dl>
+<dt><kbd>crtsv import [-bnum <var>num</var>] [-dnum <var>num</var>] [-bin] <var>name</var></kbd></dt>
+<dd>TSVファイルを読み込んでデータベースを作成する。</dd>
+<dt><kbd>crtsv export [-bin] <var>name</var></kbd></dt>
+<dd>データベースの全てのレコードをTSVファイルとして出力する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-bnum <var>num</var></kbd> : バケット配列の要素数を `num' に指定する。</li>
+<li><kbd>-dnum <var>num</var></kbd> : データベースファイルの分割数を `num' に指定する。</li>
+<li><kbd>-bin</kbd> : Base64形式でレコードを扱う。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<p>Curiaのコマンド群を駆使すると、簡単なデータベースシステムが構築できる。例えば `/etc/password' をユーザ名で検索するためのデータベースを作成するには、以下のようにする。</p>
+
+<pre>cat /etc/passwd | tr ':' '\t' | crtsv import casket
+</pre>
+
+<p>そして、`mikio' というユーザの情報を取り出すには、以下のようにする。</p>
+
+<pre>crmgr get casket mikio
+</pre>
+
+<p>これらのコマンドと同等の機能をCuriaのAPIを用いて実装することも容易である。</p>
+
+<hr />
+
+<h2><a name="relicapi" id="relicapi" class="head">Relic: NDBM互換API</a></h2>
+
+<h3>概要</h3>
+
+<p>Relicは、NDBMと互換するAPIである。すなわち、Depotの関数群をNDBMのAPIで包んだものである。Relicを使ってNDBMのアプリケーションをQDBMに移植するのはたやすい。ほとんどの場合、インクルードするヘッダファイルを `ndbm.h' から `relic.h' に換え、ビルドの際のリンカオプションを `-lndbm' から `-lqdbm' に換えるだけでよい。</p>
+
+<p>オリジナルのNDBMでは、データベースは二つのファイルの対からなる。ひとつは接尾辞に `.dir' がつく名前で、キーのビットマップを格納する「ディレクトリファイル」である。もうひとつは接尾辞に `.pag' がつく名前で、データの実体を格納する「データファイル」である。Relicではディレクトリファイルは単なるダミーとして作成し、データファイルをデータベースとする。RelicではオリジナルのNDBMと違い、格納するデータのサイズに制限はない。なお、オリジナルのNDBMで生成したデータベースファイルをRelicで扱うことはできない。</p>
+
+<p>Relicを使うためには、`relic.h' と `stdlib.h' と `sys/types.h' と `sys/stat.h' と `fcntl.h' をインクルードすべきである。通常、ソースファイルの冒頭付近で以下の記述を行う。</p>
+
+<dl>
+<dt><kbd>#include <relic.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+<dt><kbd>#include <sys/types.h></kbd></dt>
+<dt><kbd>#include <sys/stat.h></kbd></dt>
+<dt><kbd>#include <fcntl.h></kbd></dt>
+</dl>
+
+<p>Relicでデータベースを扱う際には、`DBM' 型へのポインタをハンドルとして用いる。ハンドルは、関数 `dbm_open' で開き、関数 `dbm_close' で閉じる。ハンドルのメンバを直接参照することは推奨されない。</p>
+
+<h3>API</h3>
+
+<p>データの格納、削除、検索に用いる関数とのデータの授受には、キーと値を表現するのに `datum' 型の構造体を用いる。</p>
+
+<dl>
+<dt><kbd>typedef struct { void *dptr; size_t dsize; } datum;</kbd></dt>
+<dd>`dptr' はデータ領域へのポインタである。`dsize' はデータ領域のサイズである。</dd>
+</dl>
+
+<p>データベースのハンドルを作成するには、関数 `dbm_open' を用いる。</p>
+
+<dl>
+<dt><kbd>DBM *dbm_open(char *<var>name</var>, int <var>flags</var>, int <var>mode</var>);</kbd></dt>
+<dd>`name' はデータベースの名前を指定するが、ファイル名はそれに接尾辞をつけたものになる。`flags' は `open' コールに渡すものと同じだが、`O_WRONLY' は `O_RDWR' と同じになり、追加フラグでは `O_CREAT' と `O_TRUNC' のみが有効である。`mode' は `open' コールに渡すものと同じでファイルのモードを指定する。戻り値は正常ならデータベースハンドルであり、エラーなら `NULL' である。</dd>
+</dl>
+
+<p>データベースとの接続を閉じてハンドルを破棄するには、関数 `dbm_close' を用いる。</p>
+
+<dl>
+<dt><kbd>void dbm_close(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。閉じたハンドルの領域は解放されるので、以後は利用することができなくなる。</dd>
+</dl>
+
+<p>レコードを追加するには、関数 `dbm_store' を用いる。</p>
+
+<dl>
+<dt><kbd>int dbm_store(DBM *<var>db</var>, datum <var>key</var>, datum <var>content</var>, int <var>flags</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。`key' はキーの構造体を指定する。`content' は値の構造体を指定する。`frags' が `DBM_INSERT' ならキーの重複時に書き込みを断念し、`DBM_REPLACE' なら上書きを行う。戻り値は正常なら 0 であり、重複での断念なら 1 であり、その他のエラーなら -1 である。</dd>
+</dl>
+
+<p>レコードを削除するには、関数 `dbm_delete' を用いる。</p>
+
+<dl>
+<dt><kbd>int dbm_delete(DBM *<var>db</var>, datum <var>key</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。`key' はキーの構造体を指定する。戻り値は正常なら 0 であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>レコードを取得するには、関数 `dbm_fetch' を用いる。</p>
+
+<dl>
+<dt><kbd>datum dbm_fetch(DBM *<var>db</var>, datum <var>key</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。`key' はキーの構造体を指定する。戻り値は値の構造体である。該当があればメンバ `dptr' がその領域を指し、メンバ `dsize' がそのサイズを示す。該当がなければ `dptr' の値は `NULL' となる。`dptr' の指す領域はハンドルに関連づけられて確保され、同じハンドルに対して次にこの関数を呼び出すか、ハンドルを閉じるまで、有効なデータを保持する。</dd>
+</dl>
+
+<p>最初のレコードのキーを得るには、関数 `dbm_firstkey' を用いる。</p>
+
+<dl>
+<dt><kbd>datum dbm_firstkey(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。戻り値はキーの構造体である。該当があればメンバ `dptr' がその領域を指し、`dsize' がそのサイズを示す。該当がなければ `dptr' の値は `NULL' となる。`dptr' の指す領域はハンドルに関連づけられて確保され、同じハンドルに対して次にこの関数もしくは関数 `dbm_nextkey' を呼び出すか、ハンドルを閉じるまで、有効なデータを保持する。</dd>
+</dl>
+
+<p>次レコードのキーを得るには、関数 `dbm_nextkey' を用いる。</p>
+
+<dl>
+<dt><kbd>datum dbm_nextkey(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。戻り値はキーの構造体である。該当があればメンバ `dptr' がその領域を指し、`dsize' がそのサイズを示す。該当がなければ `dptr' の値は `NULL' となる。`dptr' の指す領域はハンドルに関連づけられて確保され、同じハンドルに対して次にこの関数もしくは関数 `dbm_firstkey' を呼び出すか、ハンドルを閉じるまで、有効なデータを保持する。</dd>
+</dl>
+
+<p>データベースに致命的エラーが起きたかどうかを調べるには、関数 `dbm_error' を用いる。</p>
+
+<dl>
+<dt><kbd>int dbm_error(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。戻り値は致命的エラーがあれば真であり、そうでなければ偽である。</dd>
+</dl>
+
+<p>関数 `dbm_clearerr' は何もしない。</p>
+
+<dl>
+<dt><kbd>int dbm_clearerr(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。戻り値は 0 である。この関数は互換性のためにのみ存在する。</dd>
+</dl>
+
+<p>データベースが読み込み専用かどうかを調べるには、関数 `dbm_rdonly' を用いる。</p>
+
+<dl>
+<dt><kbd>int dbm_rdonly(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。戻り値は読み込み専用なら真であり、そうでなければ偽である。</dd>
+</dl>
+
+<p>ディレクトリファイルのファイルディスクリプタを得るには、関数 `dbm_dirfno' を用いる。</p>
+
+<dl>
+<dt><kbd>int dbm_dirfno(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。戻り値はディレクトリファイルのファイルディスクリプタである。</dd>
+</dl>
+
+<p>データファイルのファイルディスクリプタを得るには、関数 `dbm_pagfno' を用いる。</p>
+
+<dl>
+<dt><kbd>int dbm_pagfno(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' はデータベースハンドルを指定する。戻り値はデータファイルのファイルディスクリプタである。</dd>
+</dl>
+
+<h3>サンプルコード</h3>
+
+<p>名前と対応させて電話番号を格納し、それを検索するアプリケーションのサンプルコードを以下に示す。</p>
+
+<pre>#include <relic.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+
+#define NAME     "mikio"
+#define NUMBER   "000-1234-5678"
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  DBM *db;
+  datum key, val;
+  int i;
+
+  /* データベースを開く */
+  if(!(db = dbm_open(DBNAME, O_RDWR | O_CREAT, 00644))){
+    perror("dbm_open");
+    return 1;
+  }
+
+  /* レコードを準備する */
+  key.dptr = NAME;
+  key.dsize = strlen(NAME);
+  val.dptr = NUMBER;
+  val.dsize = strlen(NUMBER);
+
+  /* レコードを格納する */
+  if(dbm_store(db, key, val, DBM_REPLACE) != 0){
+    perror("dbm_store");
+  }
+
+  /* レコードを検索する */
+  val = dbm_fetch(db, key);
+  if(val.dptr){
+    printf("Name: %s\n", NAME);
+    printf("Number: ");
+    for(i = 0; i < val.dsize; i++){
+      putchar(((char *)val.dptr)[i]);
+    }
+    putchar('\n');
+  } else {
+    perror("dbm_fetch");
+  }
+
+  /* データベースを閉じる */
+  dbm_close(db);
+
+  return 0;
+}
+</pre>
+
+<h3>注記</h3>
+
+<p>Relicを利用したプログラムをビルドする方法は、Depotの場合と全く同じである。リンカに渡すオプションは `-lndbm' ではなく `-lqdbm' である。</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>スレッド間で同時に同じハンドルにアクセスしない限りは、Relicの各関数はスレッドセーフである。ただし、`errno' や `malloc' 等がスレッドセーフな処理系であることが前提となる。</p>
+
+<hr />
+
+<h2><a name="reliccli" id="reliccli" class="head">Relic用コマンド</a></h2>
+
+<p>Relicに対応するコマンドラインインタフェースは以下のものである。</p>
+
+<p>コマンド `rlmgr' はRelicやそのアプリケーションのデバッグに役立つツールである。データベースを更新したり、データベースの状態を調べたりする機能を持つ。シェルスクリプトでデータベースアプリケーションを作るのにも利用できる。以下の書式で用いる。`name' はデータベース名、`key' はレコードのキー、`val' はレコードの値を指定する。</p>
+
+<dl>
+<dt><kbd>rlmgr create <var>name</var></kbd></dt>
+<dd>データベースファイルを作成する。</dd>
+<dt><kbd>rlmgr store [-kx] [-vx|-vf] [-insert] <var>name</var> <var>key</var> <var>val</var></kbd></dt>
+<dd>キーと値に対応するレコードを追加する。</dd>
+<dt><kbd>rlmgr delete [-kx] <var>name</var> <var>key</var></kbd></dt>
+<dd>キーに対応するレコードを削除する。</dd>
+<dt><kbd>rlmgr fetch [-kx] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>キーに対応するレコードの値を取得して標準出力する。</dd>
+<dt><kbd>rlmgr list [-ox] <var>name</var></kbd></dt>
+<dd>データベース内の全てのレコードのキーと値をタブと改行で区切って標準出力する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-kx</kbd> : 2桁単位の16進数によるバイナリ表現として `key' を扱う。</li>
+<li><kbd>-vx</kbd> : 2桁単位の16進数によるバイナリ表現として `val' を扱う。</li>
+<li><kbd>-vf</kbd> : 名前が `val' のファイルのデータを値として読み込む。</li>
+<li><kbd>-insert</kbd> : 既存のレコードとキーが重複時に上書きせずにエラーにする。</li>
+<li><kbd>-ox</kbd> : 2桁単位の16進数によるバイナリ表現として標準出力を行う。</li>
+<li><kbd>-n</kbd> : 標準出力の末尾に付加される改行文字の出力を抑制する。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<p>コマンド `rltest' はRelicの機能テストや性能テストに用いるツールである。このコマンドによって生成されたデータベースファイルを `rlmgr' によって解析したり、`time' コマンドによってこのコマンドの実行時間を計ったりするとよい。以下の書式で用いる。`name' はデータベース名、`rnum' はレコード数を指定する。</p>
+
+<dl>
+<dt><kbd>rltest write <var>name</var> <var>rnum</var></kbd></dt>
+<dd>`00000001'、`00000002' のように変化する8バイトのキーと適当な8バイトの値を連続してデータベースに追加する。</dd>
+<dt><kbd>rltest read <var>name</var> <var>rnum</var></kbd></dt>
+<dd>上記で生成したデータベースを検索する。</dd>
+</dl>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<hr />
+
+<h2><a name="hovelapi" id="hovelapi" class="head">Hovel: GDBM互換API</a></h2>
+
+<h3>概要</h3>
+
+<p>Hovelは、GDBMと互換するAPIである。すなわち、DepotおよびCuriaの関数群をGDBMのAPIで包んだものである。Hovelを使ってGDBMのアプリケーションをQDBMに移植するのはたやすい。ほとんどの場合、インクルードするヘッダファイルを `gdbm.h' から `hovel.h' に換え、ビルドの際のリンカオプションを `-lgdbm' から `-lqdbm' に換えるだけでよい。なお、オリジナルのGDBMで生成したデータベースファイルをHovelで扱うことはできない。</p>
+
+<p>Hovelを使うためには、`hovel.h' と `stdlib.h' と `sys/types.h' と `sys/stat.h' をインクルードすべきである。通常、ソースファイルの冒頭付近で以下の記述を行う。</p>
+
+<dl>
+<dt><kbd>#include <hovel.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+<dt><kbd>#include <sys/types.h></kbd></dt>
+<dt><kbd>#include <sys/stat.h></kbd></dt>
+</dl>
+
+<p>Hovelでデータベースを扱う際には、`GDBM_FILE' 型のオブジェクト(それ自体がポインタ型)をハンドルとして用いる。ハンドルは、関数 `gdbm_open' で開き、関数 `gdbm_close' で閉じる。ハンドルのメンバを直接参照することは推奨されない。Hovelは通常はDepotのラッパーとして動作してデータベースファイルを扱うが、ハンドルを開く際に関数 `gdbm_open2' を用いることによってCuriaのラッパーとしてデータベースディレクトリを扱うようにすることができる。</p>
+
+<h3>API</h3>
+
+<p>データの格納、削除、検索に用いる関数とのデータの授受には、キーと値を表現するのに `datum' 型の構造体を用いる。</p>
+
+<dl>
+<dt><kbd>typedef struct { char *dptr; size_t dsize; } datum;</kbd></dt>
+<dd>`dptr' はデータ領域へのポインタである。`dsize' はデータ領域のサイズである。</dd>
+</dl>
+
+<p>外部変数 `gdbm_version' はバージョン情報の文字列である。</p>
+
+<dl>
+<dt><kbd>extern char *gdbm_version;</kbd></dt>
+<dd>この変数の指す領域は書き込み禁止である。</dd>
+</dl>
+
+<p>外部変数 `gdbm_errno' には直前のエラーコードが記録される。エラーコードの詳細については `hovel.h' を参照すること。</p>
+
+<dl>
+<dt><kbd>extern gdbm_error gdbm_errno;</kbd></dt>
+<dd>この変数の初期値は `GDBM_NO_ERROR' である。その他の値として、`GDBM_MALLOC_ERROR'、`GDBM_BLOCK_SIZE_ERROR'、`GDBM_FILE_OPEN_ERROR'、`GDBM_FILE_WRITE_ERROR'、`GDBM_FILE_SEEK_ERROR'、`GDBM_FILE_READ_ERROR'、`GDBM_BAD_MAGIC_NUMBER'、`GDBM_EMPTY_DATABASE'、`GDBM_CANT_BE_READER'、`GDBM_CANT_BE_WRITER'、`GDBM_READER_CANT_DELETE'、`GDBM_READER_CANT_STORE'、`GDBM_READER_CANT_REORGANIZE'、`GDBM_UNKNOWN_UPDATE'、`GDBM_ITEM_NOT_FOUND'、`GDBM_REORGANIZE_FAILED'、`GDBM_CANNOT_REPLACE'、`GDBM_ILLEGAL_DATA'、`GDBM_OPT_ALREADY_SET' および `GDBM_OPT_ILLEGAL' がある。</dd>
+</dl>
+
+<p>エラーコードに対応するメッセージ文字列を得るには、関数 `gdbm_strerror' を用いる。</p>
+
+<dl>
+<dt><kbd>char *gdbm_strerror(gdbm_error <var>gdbmerrno</var>);</kbd></dt>
+<dd>`gdbmerrno' はエラーコードを指定する。戻り値はエラーメッセージの文字列であり、その領域は書き込み禁止領域である。</dd>
+</dl>
+
+<p>GDBM流にデータベースのハンドルを作成するには、関数 `gdbm_open' を用いる。</p>
+
+<dl>
+<dt><kbd>GDBM_FILE gdbm_open(char *<var>name</var>, int <var>block_size</var>, int <var>read_write</var>, int <var>mode</var>, void (*<var>fatal_func</var>)(void));</kbd></dt>
+<dd>`name' はデータベースの名前を指定する。`block_size' は無視される。`read_write' は接続モードを指定し、`GDBM_READER' ならリーダ、`GDBM_WRITER' と `GDBM_WRCREAT' と `GDBM_NEWDB' ならライタとなる。`GDBM_WRCREAT' の場合はデータベースが存在しなければ作成し、`GDBM_NEWDB' の場合は既に存在していても新しいデータベースを作成する。ライタに対しては、`GDBM_SYNC' か `GDBM_NOLOCK' か `GDBM_LOCKNB' か `GDBM_FAST' か `GDBM_SPARSE' とのビット論理和にすることができる。`GDBM_SYNC' は全てのデータベース操作をディスクと同期させ、`GDBM_NOLOCK' はファイルロックを伴わずにデータベースを開き、`GDBM_LOCKNB' はブロックなしのロックを行い、`GDBM_FAST' は無視される。`GDBM_SPARSE' はQDBM独自のものであり、作成するファイルをスパースにする。`mode' は `open' コールに渡すものと
 同じでファイルのモードを指定する。`fatal_func' は無視される。戻り値は正常ならデータベースハンドルであり、エラーなら `NULL' である。</dd>
+</dl>
+
+<p>QDBM流にデータベースのハンドルを作成するには、関数 `gdbm_open2' を用いる。</p>
+
+<dl>
+<dt><kbd>GDBM_FILE gdbm_open2(char *<var>name</var>, int <var>read_write</var>, int <var>mode</var>, int <var>bnum</var>, int <var>dnum</var>, int <var>align</var>);</kbd></dt>
+<dd>`name' はデータベースの名前を指定する。`read_write' は接続モードを指定し、`GDBM_READER' ならリーダ、`GDBM_WRITER' と `GDBM_WRCREAT' と `GDBM_NEWDB' ならライタとなる。`GDBM_WRCREAT' の場合はデータベースが存在しなければ作成し、`GDBM_NEWDB' の場合は既に存在していても新しいデータベースを作成する。ライタに対しては、`GDBM_SYNC' か `GDBM_NOLOCK' か `GDBM_LOCKNB' か `GDBM_FAST' か `GDBM_SPARSE' とのビット論理和にすることができる。`GDBM_SYNC' は全てのデータベース操作をディスクと同期させ、`GDBM_NOLOCK' はファイルロックを伴わずにデータベースを開き、`GDBM_LOCKNB' はブロックなしのロックを行い、`GDBM_FAST' は無視される。`GDBM_SPARSE' はQDBM独自のものであり、作成するファイルをスパースにする。`mode' は `open' コールもしくは `mkdir' コールに渡すものと同ã
 ˜ã§ãƒ•ã‚¡ã‚¤ãƒ«ã‚„ディレクトリのモードを指定する。`bnum' はバケット配列の要素数の目安を指定するが、0 以下ならデフォルト値が使われる。`dnum' は要素データベースの数を指定するが、0 以下なら返されるハンドルはDepotのラッパーとして生成され、そうでなければCuriaのラッパーになる。`align' はアラインメントの基本サイズを指定する。戻り値は正常ならデータベースハンドルであり、エラーなら `NULL' である。既にデータベースが存在する場合、それがDepotのものかCuriaのものかが自動的に判断される。</dd>
+</dl>
+
+<p>データベースとの接続を閉じてハンドルを破棄するには、関数 `gdbm_close' を用いる。</p>
+
+<dl>
+<dt><kbd>void gdbm_close(GDBM_FILE <var>dbf</var>);</kbd></dt>
+<dd>`dbf' はデータベースハンドルを指定する。閉じたハンドルの領域は解放されるので、以後は利用することができなくなる。</dd>
+</dl>
+
+<p>レコードを追加するには、関数 `gdbm_store' を用いる。</p>
+
+<dl>
+<dt><kbd>int gdbm_store(GDBM_FILE <var>dbf</var>, datum <var>key</var>, datum <var>content</var>, int <var>flag</var>);</kbd></dt>
+<dd>`dbf' はライタで接続したデータベースハンドルを指定する。`key' はキーの構造体を指定する。`content' は値の構造体を指定する。`frags' が `GDBM_INSERT' ならキーの重複時に書き込みを断念し、`GDBM_REPLACE' なら上書きを行う。戻り値は正常なら 0 、重複での断念なら 1 、その他のエラーなら -1 である。</dd>
+</dl>
+
+<p>レコードを削除するには、関数 `gdbm_delete' を用いる。</p>
+
+<dl>
+<dt><kbd>int gdbm_delete(GDBM_FILE <var>dbf</var>, datum <var>key</var>);</kbd></dt>
+<dd>`dbf' はライタで接続したデータベースハンドルを指定する。`key' はキーの構造体を指定する。戻り値は正常なら 0 、エラーなら -1 である。</dd>
+</dl>
+
+<p>レコードを取得するには、関数 `gdbm_fetch' を用いる。</p>
+
+<dl>
+<dt><kbd>datum gdbm_fetch(GDBM_FILE <var>dbf</var>, datum <var>key</var>);</kbd></dt>
+<dd>`dbf' はデータベースハンドルを指定する。`key' はキーの構造体を指定する。戻り値は値の構造体である。該当があればメンバ `dptr' がその領域を指し、`dsize' がそのサイズを示す。該当がなければ `dptr' の値は `NULL' となる。戻り値のメンバ `dptr' の指す領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>レコードが存在するか調べるには、関数 `gdbm_exists' を用いる。</p>
+
+<dl>
+<dt><kbd>int gdbm_exists(GDBM_FILE <var>dbf</var>, datum <var>key</var>);</kbd></dt>
+<dd>`dbf' はデータベースハンドルを指定する。`key' はキーの構造体を指定する。戻り値は該当があれば真であり、該当がない場合やエラーの場合は偽である。</dd>
+</dl>
+
+<p>最初のレコードのキーを得るには、関数 `gdbm_firstkey' を用いる。</p>
+
+<dl>
+<dt><kbd>datum gdbm_firstkey(GDBM_FILE <var>dbf</var>);</kbd></dt>
+<dd>`dbf' はデータベースハンドルを指定する。戻り値はキーの構造体である。該当があればメンバ `dptr' がその領域を指し、`dsize' がそのサイズを示す。該当がなければ `dptr' の値は `NULL' となる。戻り値のメンバ `dptr' の指す領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>次のレコードのキーを得るには、関数 gdbm_nextkey を用いる。</p>
+
+<dl>
+<dt><kbd>datum gdbm_nextkey(GDBM_FILE <var>dbf</var>, datum <var>key</var>);</kbd></dt>
+<dd>`dbf' はデータベースハンドルを指定する。`key' は無視される。戻り値はキーの構造体である。該当があればメンバ `dptr' がその領域を指し、`dsize' がそのサイズを示す。該当がなければ `dptr' の値は `NULL' となる。戻り値のメンバ `dptr' の指す領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>データベースを更新した内容をファイルとデバイスに同期させるには、関数 `gdbm_sync' を用いる。</p>
+
+<dl>
+<dt><kbd>void gdbm_sync(GDBM_FILE <var>dbf</var>);</kbd></dt>
+<dd>`dbf' はライタで接続したデータベースハンドルを指定する。</dd>
+</dl>
+
+<p>データベースを最適化するには、関数 `gdbm_reorganize' を用いる。</p>
+
+<dl>
+<dt><kbd>int gdbm_reorganize(GDBM_FILE <var>dbf</var>);</kbd></dt>
+<dd>`dbf' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら 0 であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>データベースファイルのファイルディスクリプタを得るには、関数 `gdbm_fdesc' を用いる。</p>
+
+<dl>
+<dt><kbd>int gdbm_fdesc(GDBM_FILE <var>dbf</var>);</kbd></dt>
+<dd>`dbf' はデータベースハンドルを指定する。戻り値はデータベースファイルのファイルディスクリプタである。データベースがディレクトリなら、戻り値は -1 である。</dd>
+</dl>
+
+<p>関数 `gdbm_setopt' は何もしない。</p>
+
+<dl>
+<dt><kbd>int gdbm_setopt(GDBM_FILE <var>dbf</var>, int <var>option</var>, int *<var>value</var>, int <var>size</var>);</kbd></dt>
+<dd>`dbf' はデータベースハンドルを指定する。`option' は無視される。`value' は無視される。`size' は無視される。戻り値は 0 である。この関数は互換性のためにのみ存在する。</dd>
+</dl>
+
+<h3>サンプルコード</h3>
+
+<p>名前と対応させて電話番号を格納し、それを検索するアプリケーションのサンプルコードを以下に示す。</p>
+
+<pre>#include <hovel.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+
+#define NAME     "mikio"
+#define NUMBER   "000-1234-5678"
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  GDBM_FILE dbf;
+  datum key, val;
+  int i;
+
+  /* データベースを開く */
+  if(!(dbf = gdbm_open(DBNAME, 0, GDBM_WRCREAT, 00644, NULL))){
+    fprintf(stderr, "gdbm_open: %s\n", gdbm_strerror(gdbm_errno));
+    return 1;
+  }
+
+  /* レコードを準備する */
+  key.dptr = NAME;
+  key.dsize = strlen(NAME);
+  val.dptr = NUMBER;
+  val.dsize = strlen(NUMBER);
+
+  /* レコードを格納する */
+  if(gdbm_store(dbf, key, val, GDBM_REPLACE) != 0){
+    fprintf(stderr, "gdbm_store: %s\n", gdbm_strerror(gdbm_errno));
+  }
+
+  /* レコードを検索する */
+  val = gdbm_fetch(dbf, key);
+  if(val.dptr){
+    printf("Name: %s\n", NAME);
+    printf("Number: ");
+    for(i = 0; i < val.dsize; i++){
+      putchar(val.dptr[i]);
+    }
+    putchar('\n');
+    free(val.dptr);
+  } else {
+    fprintf(stderr, "gdbm_fetch: %s\n", gdbm_strerror(gdbm_errno));
+  }
+
+  /* データベースを閉じる */
+  gdbm_close(dbf);
+
+  return 0;
+}
+</pre>
+
+<h3>注記</h3>
+
+<p>Hovelを利用したプログラムをビルドする方法は、Depotの場合と全く同じである。リンカに渡すオプションは `-lgdbm' ではなく `-lqdbm' である。</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>POSIXスレッドを有効にしてQDBMをビルドした場合、外部変数 `gdbm_errno' はスレッド固有データへの参照として扱われ、Hovelの各関数はリエントラントになる。その場合、スレッド間で同時に同じハンドルにアクセスしない限りは、各関数はスレッドセーフである。ただし、`errno' や `malloc' 等がスレッドセーフな処理系であることが前提となる。</p>
+
+<hr />
+
+<h2><a name="hovelcli" id="hovelcli" class="head">Hovel用コマンド</a></h2>
+
+<p>Hovelに対応するコマンドラインインタフェースは以下のものである。</p>
+
+<p>コマンド `hvmgr' はHovelやそのアプリケーションのデバッグに役立つツールである。データベースを更新したり、データベースの状態を調べたりす機能を持つ。シェルスクリプトでデータベースアプリケーションを作るのにも利用できる。以下の書式で用いる。`name' はデータベース名、`key' はレコードのキー、`val' はレコードの値を指定する。</p>
+
+<dl>
+<dt><kbd>hvmgr [-qdbm bnum dnum] [-s] create <var>name</var></kbd></dt>
+<dd>データベースファイルを作成する。</dd>
+<dt><kbd>hvmgr store [-qdbm] [-kx] [-vx|-vf] [-insert] <var>name</var> <var>key</var> <var>val</var></kbd></dt>
+<dd>キーと値に対応するレコードを追加する。</dd>
+<dt><kbd>hvmgr delete [-qdbm] [-kx] <var>name</var> <var>key</var></kbd></dt>
+<dd>キーに対応するレコードを削除する。</dd>
+<dt><kbd>hvmgr fetch [-qdbm] [-kx] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>キーに対応するレコードの値を取得して標準出力する。</dd>
+<dt><kbd>hvmgr list [-qdbm] [-ox] <var>name</var></kbd></dt>
+<dd>データベース内の全てのレコードのキーと値をタブと改行で区切って標準出力する。</dd>
+<dt><kbd>hvmgr optimize [-qdbm] <var>name</var></kbd></dt>
+<dd>データベースを最適化する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-qdbm [<var>bnum</var> <var>dnum</var>]</kbd> : `gdbm_open2' でデータベースを開く。`bnum' と `dnum' はバケット配列の要素数とデータベースの分割数を指定する。</li>
+<li><kbd>-s</kbd> : ファイルをスパースにする。</li>
+<li><kbd>-kx</kbd> : 2桁単位の16進数によるバイナリ表現として `key' を扱う。</li>
+<li><kbd>-vx</kbd> : 2桁単位の16進数によるバイナリ表現として `val' を扱う。</li>
+<li><kbd>-vf</kbd> : 名前が `val' のファイルのデータを値として読み込む。</li>
+<li><kbd>-insert</kbd> : 既存のレコードとキーが重複時に上書きせずにエラーにする。</li>
+<li><kbd>-ox</kbd> : 2桁単位の16進数によるバイナリ表現として標準出力を行う。</li>
+<li><kbd>-n</kbd> : 標準出力の末尾に付加される改行文字の出力を抑制する。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<p>コマンド `hvtest' はHovelの機能テストや性能テストに用いるツールである。このコマンドによって生成されたデータベースファイルを `hvmgr' によって解析したり、`time' コマンドによってこのコマンドの実行時間を計ったりするとよい。以下の書式で用いる。`name' はデータベース名、`rnum' はレコード数を指定する。</p>
+
+<dl>
+<dt><kbd>hvtest write [-qdbm] [-s] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>`00000001'、`00000002' のように変化する8バイトのキーと適当な8バイトの値を連続してデータベースに追加する。</dd>
+<dt><kbd>hvtest read [-qdbm] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>上記で生成したデータベースを検索する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-qdbm</kbd> : `gdbm_open2' を用いてCuriaのハンドルを開く。</li>
+<li><kbd>-s</kbd> : ファイルをスパースにする。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<hr />
+
+<h2><a name="cabinapi" id="cabinapi" class="head">Cabin: ユーティリティAPI</a></h2>
+
+<h3>概要</h3>
+
+<p>Cabinはメモリ上で簡単にレコードを扱うためのメモリ確保関数や整列関数や拡張可能なデータや配列リストやハッシュマップやヒープ配列など提供するユーティリティのAPIである。MIMEやCSVやXMLを解析する機能や、各種の符号化と復号を行う機能も備える。</p>
+
+<p>Cabinを使うためには、`cabin.h' と `stdlib.h' をインクルードすべきである。通常、ソースファイルの冒頭付近で以下の記述を行う。</p>
+
+<dl>
+<dt><kbd>#include <cabin.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+</dl>
+
+<p>拡張可能なデータを扱う際には、`CBDATUM' 型へのポインタをハンドルとして用いる。データハンドルは、関数 `cbdatumopen' で開き、関数 `cbdatumclose' で閉じる。リストを扱う際には、`CBLIST' 型へのポインタをハンドルとして用いる。リストハンドルは、関数 `cblistopen' で開き、関数 `cblistclose' で閉じる。マップを扱う際には、`CBMAP' 型へのポインタをハンドルとして用いる。マップハンドルは、関数 `cbmapopen' で開き、関数 `cbmapclose' で閉じる。ヒープ配列を扱う際には `CBHEAP' 型へのポインタをハンドルとして用いる。ヒープハンドルは関数 `cbheapopen' で開き、関数 `cbheapclose' で閉じる。各ハンドルのメンバを直接参照することは推奨されない。
+
+</p>
+
+<h3>API</h3>
+
+<p>外部変数 `cbfatalfunc' は致命的エラーをハンドリングするコールバック関数である。</p>
+
+<dl>
+<dt><kbd>extern void (*cbfatalfunc)(const char *<var>message</var>);</kbd></dt>
+<dd>引数はエラーメッセージを指定する。この変数の初期値は `NULL' であり、`NULL' ならば致命的エラーの発生時にはデフォルトの関数が呼ばれる。致命的エラーはメモリの割り当てに失敗した際に起こる。</dd>
+</dl>
+
+<p>メモリ上に領域を確保するには、関数 `cbmalloc' を用いる。</p>
+
+<dl>
+<dt><kbd>void *cbmalloc(size_t <var>size</var>);</kbd></dt>
+<dd>`size' は領域のサイズを指定する。戻り値は確保した領域へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>メモリ上の領域を再確保するには、関数 `cbrealloc' を用いる。</p>
+
+<dl>
+<dt><kbd>void *cbrealloc(void *<var>ptr</var>, size_t <var>size</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' は領域のサイズを指定する。戻り値は再確保した領域へのポインタである。戻り値の領域は `remalloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>メモリ上の領域を複製するには、関数 `cbmemdup' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbmemdup(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' は領域のサイズを指定する。戻り値は再確保した領域へのポインタである。戻り値は複製の領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>メモリ上の開放を解放するには、関数 `cbfree' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbfree(void *<var>ptr</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定するが、`NULL' の場合は何もしない。この関数は `free' のラッパーに過ぎないが、`malloc' シリーズの別のパッケージを使うアプリケーションにおいてQDBMが確保した領域を解放するのに便利である。</dd>
+</dl>
+
+<p>オブジェクトのポインタかハンドルをグローバルガベージコレクタに登録するには、関数 `cbglobalgc' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbglobalgc(void *<var>ptr</var>, void (*<var>func</var>)(void *));</kbd></dt>
+<dd>`ptr' はオブジェクトのポインタかハンドルを指定する。`func' はオブジェクトのリソースを解放する関数を指定する。その引数は解放するオブジェクトのポインタかハンドルである。この関数は、`main' 関数がリターンするか `exit' 関数が呼ばれてプロセスが正常終了する際に、オブジェクトのリソースが解放されることを保証する。</dd>
+</dl>
+
+<p>グローバルガベージコレクタを明示的に発動させるには、関数 `cbggcsweep' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbggcsweep(void);</kbd></dt>
+<dd>この関数を呼んだ後はグローバルガベージコレクタに登録してあったオブジェクトは利用することができなくなることに注意すること。グローバルガベージコレクタは初期化されるので、新しいオブジェクトを入れることができるようになる。</dd>
+</dl>
+
+<p>仮想メモリの割り当て可能性を調べるには、関数 `cbvmemavail' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbvmemavail(size_t <var>size</var>);</kbd></dt>
+<dd>`size' は新たに割り当て可能であるべき領域のサイズを指定する。戻り値は割り当てが可能であれば真、そうでなければ偽である。</dd>
+</dl>
+
+<p>配列の各要素を挿入ソートで整列させるには、関数 `cbisort' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbisort(void *<var>base</var>, int <var>nmemb</var>, int <var>size</var>, int(*<var>compar</var>)(const void *, const void *));</kbd></dt>
+<dd>`base' は配列のポインタを指定する。`nmemb' は配列の要素数を指定する。`size' は各要素のサイズを指定する。`compar' は比較関数を指定する。二つの引数は要素へのポインタである。比較関数は前者が大きければ正数を、後者が大きければ負数を、両者が等しければ 0 を返すべきである。挿入ソートは、ほとんどの要素が既に整列済みの場合にのみ有用である。</dd>
+</dl>
+
+<p>配列の各要素をシェルソートで整列させるには、関数 `cbssort' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbssort(void *<var>base</var>, int <var>nmemb</var>, int <var>size</var>, int(*<var>compar</var>)(const void *, const void *));</kbd></dt>
+<dd>`base' は配列のポインタを指定する。`nmemb' は配列の要素数を指定する。`size' は各要素のサイズを指定する。`compar' は比較関数を指定する。二つの引数は要素へのポインタである。比較関数は前者が大きければ正数を、後者が大きければ負数を、両者が等しければ 0 を返すべきである。ほとんどの要素が整列済みの場合、シェルソートの方がヒープソートやクイックソートより速いかもしれない。</dd>
+</dl>
+
+<p>配列の各要素をヒープソートで整列させるには、関数 `cbhsort' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbhsort(void *<var>base</var>, int <var>nmemb</var>, int <var>size</var>, int(*<var>compar</var>)(const void *, const void *));</kbd></dt>
+<dd>`base' は配列のポインタを指定する。`nmemb' は配列の要素数を指定する。`size' は各要素のサイズを指定する。`compar' は比較関数を指定する。二つの引数は要素へのポインタである。比較関数は前者が大きければ正数を、後者が大きければ負数を、両者が等しければ 0 を返すべきである。ヒープソートは入力の偏りに対して頑丈であるが、ほとんどの場合でクイックソートの方が速い。</dd>
+</dl>
+
+<p>配列の各要素をクイックソートで整列させるには、関数 `cbqsort' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbqsort(void *<var>base</var>, int <var>nmemb</var>, int <var>size</var>, int(*<var>compar</var>)(const void *, const void *));</kbd></dt>
+<dd>`base' は配列のポインタを指定する。`nmemb' は配列の要素数を指定する。`size' は各要素のサイズを指定する。`compar' は比較関数を指定する。二つの引数は要素へのポインタである。比較関数は前者が大きければ正数を、後者が大きければ負数を、両者が等しければ 0 を返すべきである。入力の偏りに敏感ではあるが、クイックソートは最速の整列アルゴリズムである。</dd>
+</dl>
+
+<p>大文字と小文字の違いを無視して二つの文字列を比較するには、関数 `cbstricmp' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbstricmp(const char *<var>astr</var>, const char *<var>bstr</var>);</kbd></dt>
+<dd>`astr' は一方の文字列へのポインタを指定する。`bstr' は他方の文字列へのポインタを指定する。戻り値は前者が大きければ正、後者が大きければ負、両者が等価なら 0 である。ASCIIコード中のアルファベットの大文字と小文字は区別されない。</dd>
+</dl>
+
+<p>文字列があるキーで始まっているか調べるには、関数 `cbstrfwmatch' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbstrfwmatch(const char *<var>str</var>, const char *<var>key</var>);</kbd></dt>
+<dd>`str' は対象の文字列へのポインタを指定する。`key' は前方一致のキーの文字列へのポインタを指定する。戻り値は対象の文字列がキーで始まっていれば真、そうでなければ偽である。</dd>
+</dl>
+
+<p>大文字と小文字の違いを無視しつつ、文字列があるキーで始まっているか調べるには、関数 `cbstrfwimatch' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbstrfwimatch(const char *<var>str</var>, const char *<var>key</var>);</kbd></dt>
+<dd>`str' は対象の文字列へのポインタを指定する。`key' は前方一致のキーの文字列へのポインタを指定する。戻り値は対象の文字列がキーで始まっていれば真、そうでなければ偽である。ASCIIコード中のアルファベットの大文字と小文字は区別されない。</dd>
+</dl>
+
+<p>文字列があるキーで終っているか調べるには、関数 `cbstrbwmatch' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbstrbwmatch(const char *<var>str</var>, const char *<var>key</var>);</kbd></dt>
+<dd>`str' は対象の文字列へのポインタを指定する。`key' は後方一致のキーの文字列へのポインタを指定する。戻り値は対象の文字列がキーで終っていれば真、そうでなければ偽である。</dd>
+</dl>
+
+<p>大文字と小文字の違いを無視しつつ、文字列があるキーで終っているか調べるには、関数 `cbstrbwimatch' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbstrbwimatch(const char *<var>str</var>, const char *<var>key</var>);</kbd></dt>
+<dd>`str' は対象の文字列へのポインタを指定する。`key' は後方一致のキーの文字列へのポインタを指定する。戻り値は対象の文字列がキーで終っていれば真、そうでなければ偽である。ASCIIコード中のアルファベットの大文字と小文字は区別されない。</dd>
+</dl>
+
+<p>KMP法を用いて文字列の部分文字列の位置を得るには、関数 `cbstrstrkmp' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbstrstrkmp(const char *<var>haystack</var>, const char *<var>needle</var>);</kbd></dt>
+<dd>`haystack' は文字列へのポインタを指定する。`needle' は探すべき部分文字列へのポインタを指定する。戻り値は部分文字列の開始を指すポインタか、見つからなければ `NULL' である。大抵の場合、この関数よりコンパイラのビルドインである `strstr' の方が高速である。</dd>
+</dl>
+
+<p>BM法を用いて文字列の部分文字列の位置を得るには、関数 `cbstrstrbm' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbstrstrbm(const char *<var>haystack</var>, const char *<var>needle</var>);</kbd></dt>
+<dd>`haystack' は文字列へのポインタを指定する。`needle' は探すべき部分文字列へのポインタを指定する。戻り値は部分文字列の開始を指すポインタか、見つからなければ `NULL' である。大抵の場合、この関数よりコンパイラのビルドインである `strstr' の方が高速である。</dd>
+</dl>
+
+<p>文字列の全ての文字を大文字に変換するには、関数 `cbstrtoupper' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbstrtoupper(char *<var>str</var>);</kbd></dt>
+<dd>`str' は変換対象の文字列へのポインタを指定する。戻り値はその文字列へのポインタである。</dd>
+</dl>
+
+<p>文字列の全ての文字を小文字に変換するには、関数 `cbstrtolower' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbstrtolower(char *<var>str</var>);</kbd></dt>
+<dd>`str' は変換対象の文字列へのポインタを指定する。戻り値はその文字列へのポインタである。</dd>
+</dl>
+
+<p>文字列の先頭と末尾にある空白文字を削除するには、関数 `cbstrtrim' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbstrtrim(char *<var>str</var>);</kbd></dt>
+<dd>`str' は変換対象の文字列へのポインタを指定する。戻り値はその文字列へのポインタである。</dd>
+</dl>
+
+<p>文字列内の連続する空白を絞って整形するには、関数 `cbstrsqzcpc' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbstrsqzspc(char *<var>str</var>);</kbd></dt>
+<dd>`str' は変換対象の文字列へのポインタを指定する。戻り値はその文字列へのポインタである。</dd>
+</dl>
+
+<p>UTF-8の文字列に含まれる文字数を数えるには、関数 `cbstrcountutf' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbstrcountutf(const char *<var>str</var>);</kbd></dt>
+<dd>`str' はUTF-8の文字列へのポインタを指定する。戻り値はその文字列に含まれる文字数である。</dd>
+</dl>
+
+<p>UTF-8の文字列を指定した文字数で切るには、関数 `cbstrcututf' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbstrcututf(char *<var>str</var>, int <var>num</var>);</kbd></dt>
+<dd>`str' はUTF-8の文字列へのポインタを指定する。`num' は保持する文字数を指定する。戻り値はその文字列へのポインタである。</dd>
+</dl>
+
+<p>データハンドルを作成するには、関数 `cbdatumopen' を用いる。</p>
+
+<dl>
+<dt><kbd>CBDATUM *cbdatumopen(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' は初期内容の領域へのポインタを指定するか、`NULL' なら空のデータを作成する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。戻り値はデータハンドルである。</dd>
+</dl>
+
+<p>データを複製するには、関数 `cbdatumdup' を用いる。</p>
+
+<dl>
+<dt><kbd>CBDATUM *cbdatumdup(const CBDATUM *<var>datum</var>);</kbd></dt>
+<dd>`datum' はデータハンドルを指定する。戻り値は新しいデータハンドルである。</dd>
+</dl>
+
+<p>データハンドルを破棄するには、関数 `cbdatumclose' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbdatumclose(CBDATUM *<var>datum</var>);</kbd></dt>
+<dd>`datum' はデータハンドルを指定する。閉じたハンドルの領域は解放されるので、以後は利用することができなくなる。</dd>
+</dl>
+
+<p>データに別の領域を連結するには、関数 `cbdatumcat' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbdatumcat(CBDATUM *<var>datum</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`datum' はデータハンドルを指定する。`ptr' は連結する領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。</dd>
+</dl>
+
+<p>データの領域へのポインタを得るには、関数 `cbdatumptr' を用いる。</p>
+
+<dl>
+<dt><kbd>const char *cbdatumptr(const CBDATUM *<var>datum</var>);</kbd></dt>
+<dd>`datum' はデータハンドルを指定する。戻り値はデータの領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。</dd>
+</dl>
+
+<p>データの領域のサイズを得るには、関数 `cbdatumsize' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbdatumsize(const CBDATUM *<var>datum</var>);</kbd></dt>
+<dd>`datum' はデータハンドルを指定する。戻り値はデータの領域のサイズである。</dd>
+</dl>
+
+<p>データの領域のサイズを変更するには、関数 `cbdatumsetsize' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbdatumsetsize(CBDATUM *<var>datum</var>, int <var>size</var>);</kbd></dt>
+<dd>`datum' はデータハンドルを指定する。`size' は領域の新しいサイズを指定する。新しいサイズが既存のサイズより大きい場合、余った領域は終端文字で埋められる。</dd>
+</dl>
+
+<p>データに書式出力を行なうには、関数 `cbdatumprintf' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbdatumprintf(CBDATUM *<var>datum</var>, const char *<var>format</var>, ...);</kbd></dt>
+<dd>`format' はprintf風の書式文字列を指定する。変換文字 `%' を `s'、`d'、`o'、`u'、`x'、`X'、`c'、`e'、`E'、`f'、`g'、`G'、`@'、`?'、`:'、`%' と併せて利用することができる。`@' は `s' と同様に機能するが、XMLのメタ文字をエスケープする。`?' は `s' と同様に機能するが、URLのメタ文字をエスケープする。`:' は `s' と同様に機能するが、UTF-8としてのMIMEエンコーディングを施す。それ以外の変換文字は元来のものと同様に機能する。</dd>
+</dl>
+
+<p>データを確保された領域に変換するには、関数 `cbdatumtomalloc' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbdatumtomalloc(CBDATUM *<var>datum</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`datum' はデータハンドルを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値はデータの領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。元のデータの領域は解放されるので、それを再び解放してはならない。</dd>
+</dl>
+
+<p>リストハンドルを作成するには、関数 `cblistopen' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cblistopen(void);</kbd></dt>
+<dd>戻り値はリストハンドルである。</dd>
+</dl>
+
+<p>リストを複製するには、関数 `cblistdup' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cblistdup(const CBLIST *<var>list</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。戻り値は新しいリストハンドルである。</dd>
+</dl>
+
+<p>リストハンドルを破棄するには、関数 `cblistclose' を用いる。</p>
+
+<dl>
+<dt><kbd>void cblistclose(CBLIST *<var>list</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。閉じたハンドルの領域は解放されるので、以後は利用することができなくなる。</dd>
+</dl>
+
+<p>リストに格納された要素数を得るには、関数 `cblistnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int cblistnum(const CBLIST *<var>list</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。戻り値はリストに格納された要素数である。</dd>
+</dl>
+
+<p>リスト内のある要素の領域へのポインタを得るには、関数 `cblistval' を用いる。</p>
+
+<dl>
+<dt><kbd>const char *cblistval(const CBLIST *<var>list</var>, int <var>index</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。`index' は取り出す要素のインデックスを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は該当要素の領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。`index' が要素数以上ならば、戻り値は `NULL' である。</dd>
+</dl>
+
+<p>要素をリストの末尾に加えるには、関数 `cblistpush' を用いる。</p>
+
+<dl>
+<dt><kbd>void cblistpush(CBLIST *<var>list</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。`ptr' は追加する要素の領域へのポインタを指定する。`size' はその領域のサイズを指定するが、負数なら `strlen(ptr)' の値となる。</dd>
+</dl>
+
+<p>リストの末尾の要素を削除するには、関数 `cblistpop' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cblistpop(CBLIST *<var>list</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は該当要素の領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。リストが空ならば、戻り値は `NULL' である。</dd>
+</dl>
+
+<p>要素をリストの先頭に加えるには、関数 `cblistunshift' を用いる。</p>
+
+<dl>
+<dt><kbd>void cblistunshift(CBLIST *<var>list</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。`ptr' は追加する要素の領域へのポインタを指定する。`size' はその領域のサイズを指定するが、負数なら `strlen(ptr)' の値となる。</dd>
+</dl>
+
+<p>リストの先頭の要素を削除するには、関数 `cblistshift' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cblistshift(CBLIST *<var>list</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は該当要素の領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。リストが空ならば、戻り値は `NULL' である。</dd>
+</dl>
+
+<p>リスト内の指定した位置に要素を加えるには、関数 `cblistinsert' を用いる。</p>
+
+<dl>
+<dt><kbd>void cblistinsert(CBLIST *<var>list</var>, int <var>index</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。`index' は追加する要素のインデックスを指定する。`ptr' は追加する要素の領域へのポインタを指定する。`size' はその領域のサイズを指定するが、負数なら `strlen(ptr)' の値となる。</dd>
+</dl>
+
+<p>リスト内の指定した位置の要素を削除するには、関数 `cblistremove' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cblistremove(CBLIST *<var>list</var>, int <var>index</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。`index' は削除する要素のインデックスを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は該当要素の領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。`index' が要素数以上ならば、要素は削除されず、戻り値は `NULL' である。</dd>
+</dl>
+
+<p>リスト内の指定した位置の要素を上書きするには、関数 `cblistover' を用いる。</p>
+
+<dl>
+<dt><kbd>void cblistover(CBLIST *<var>list</var>, int <var>index</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。`index' は削除する要素のインデックスを指定する。`ptr' は新しい内容の領域へのポインタを指定する。`size' はその領域のサイズを指定するが、負数なら `strlen(ptr)' の値となる。`index' が要素数以上ならば、この関数は何もしない。</dd>
+</dl>
+
+<p>リストの要素を辞書順で整列させるには、関数 `cblistsort' を用いる。</p>
+
+<dl>
+<dt><kbd>void cblistsort(CBLIST *<var>list</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。整列にはクイックソートが用いられる。</dd>
+</dl>
+
+<p>リストの要素を線形探索を使って検索するには、関数 `cblistlsearch' を用いる。</p>
+
+<dl>
+<dt><kbd>int cblistlsearch(const CBLIST *<var>list</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。`ptr' は検索キーの領域へのポインタを指定する。`size' はその領域のサイズを指定するが、負数なら `strlen(ptr)' の値となる。戻り値は該当の要素のインデックスであるが、該当がなければ -1 である。複数の要素が該当した場合、前者が返される。</dd>
+</dl>
+
+<p>リストの要素を二分探索を使って検索するには、関数 `cblistbsearch' を用いる。</p>
+
+<dl>
+<dt><kbd>int cblistbsearch(const CBLIST *<var>list</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。リストは辞書順にソートされている必要がある。`ptr' は検索キーの領域へのポインタを指定する。`size' はその領域のサイズを指定するが、負数なら `strlen(ptr)' の値となる。戻り値は該当の要素のインデックスであるが、該当がなければ -1 である。複数の要素が該当した場合にどちらが返るかは未定義である。</dd>
+</dl>
+
+<p>リストを直列化してバイト配列にするには、関数 `cblistdump' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cblistdump(const CBLIST *<var>list</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`list' はリストハンドルを指定する。`sp' は抽出した領域のサイズを格納する領域へのポインタを指定する。戻り値は直列化された領域へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>直列化されたリストを復元するには、関数 `cblistload' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cblistload(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' はバイト配列へのポインタを指定する。`size' はその領域のサイズを指定する。戻り値は新しいリストハンドルである。</dd>
+</dl>
+
+<p>マップハンドルを作成するには、関数 `cbmapopen' を用いる。</p>
+
+<dl>
+<dt><kbd>CBMAP *cbmapopen(void);</kbd></dt>
+<dd>戻り値はマップハンドルである。</dd>
+</dl>
+
+<p>マップを複製するには、関数 `cbmapdup' を用いる。</p>
+
+<dl>
+<dt><kbd>CBMAP *cbmapdup(CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。戻り値は新しいマップハンドルである。コピー元のマップのイテレータは初期化される。</dd>
+</dl>
+
+<p>マップハンドルを破棄するには、関数 `cbmapclose' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbmapclose(CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。閉じたハンドルの領域は解放されるので、以後は利用できなくなる。</dd>
+</dl>
+
+<p>マップにレコードを追加するには、関数 `cbmapput' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbmapput(CBMAP *<var>map</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>over</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`vbuf' は値のデータ領域へのポインタを指定する。`vsiz' は値のデータ領域のサイズを指定するか、負数なら `strlen(vbuf)' の値となる。`over' は重複したレコードを上書きするか否かを指定する。`over' が偽でキーが重複した場合は戻り値は偽であるが、そうでない場合は真である。</dd>
+</dl>
+
+<p>既存のレコードの値の末尾に値を連結するには、関数 `cbmapputcat' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbmapputcat(CBMAP *<var>map</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`vbuf' は値のデータ領域へのポインタを指定する。`vsiz' は値のデータ領域のサイズを指定するか、負数なら `strlen(vbuf)' の値となる。該当のレコードが存在しない場合は新しいレコードが作られる。</dd>
+</dl>
+
+<p>マップのレコードを削除するには、関数 `cbmapout' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbmapout(CBMAP *<var>map</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は正常なら真であり、該当のレコードがない場合は偽である。</dd>
+</dl>
+
+<p>マップのレコードを取得するには、関数 `cbmapget' を用いる。</p>
+
+<dl>
+<dt><kbd>const char *cbmapget(const CBMAP *<var>map</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら値を格納した領域へのポインタであり、該当のレコードがない場合は `NULL' である。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。</dd>
+</dl>
+
+<p>レコードをマップの端に移動させるには、関数 `cbmapmove' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbmapmove(CBMAP *<var>map</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>head</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`head' は移動先を指定し、真なら先頭、偽なら末尾となる。戻り値は正常なら真であり、該当のレコードがない場合は偽である。</dd>
+</dl>
+
+<p>マップのイテレータを初期化するには、関数 `cbmapiterinit' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbmapiterinit(CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。イテレータは、マップに格納された全てのレコードを参照するために用いられる。</dd>
+</dl>
+
+<p>マップのイテレータから次のレコードのキーを取り出すには、関数 `cbmapiternext' を用いる。</p>
+
+<dl>
+<dt><kbd>const char *cbmapiternext(CBMAP *<var>map</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常ならキーを格納した領域へのポインタであり、エラーなら `NULL' である。イテレータが最後まできて該当のレコードがない場合も `NULL' を返す。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。取り出す順番は格納した際の順番に一致することが保証されている。</dd>
+</dl>
+
+<p>マップのイテレータから取り出したキーに対応する値を取り出すには、関数 `cbmapiterval' を用いる。</p>
+
+<dl>
+<dt><kbd>const char *cbmapiterval(const char *<var>kbuf</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`kbuf' はイテレータから取り出したキーの領域へのポインタを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は値を格納した領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。</dd>
+</dl>
+
+<p>マップのレコード数を得るには、関数 `cbmaprnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbmaprnum(const CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。戻り値はデータベースのレコード数である。</dd>
+</dl>
+
+<p>マップに含まれる全てのキーを含むリストを得るには、関数 `cbmapkeys' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cbmapkeys(CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。戻り値はマップに含まれる全てのキーを含むリストハンドルである。戻り値のハンドルは関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。</dd>
+</dl>
+
+<p>マップに含まれる全ての値を含むリストを得るには、関数 `cbmapvals' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cbmapvals(CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。戻り値はマップに含まれる全ての値を含むリストハンドルである。戻り値のハンドルは関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。</dd>
+</dl>
+
+<p>マップを直列化してバイト配列にするには、関数 `cbmapdump' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbmapdump(const CBMAP *<var>map</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`map' はマップハンドルを指定する。`sp' は抽出した領域のサイズを格納する領域へのポインタを指定する。戻り値は直列化された領域へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>直列化されたマップを復元するには、関数 `cbmapload' を用いる。</p>
+
+<dl>
+<dt><kbd>CBMAP *cbmapload(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' はバイト配列へのポインタを指定する。`size' はその領域のサイズを指定する。戻り値は新しいマップハンドルである。</dd>
+</dl>
+
+<p>直列化したマップからひとつのレコードを抽出するには、関数 `cbmaploadone' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbmaploadone(const char *<var>ptr</var>, int <var>size</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' はバイト配列へのポインタを指定する。`size' はその領域のサイズを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら値を格納した領域へのポインタであり、該当のレコードがない場合は `NULL' である。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。</dd>
+</dl>
+
+<p>ヒープハンドルを作成するには、関数 `cbheapopen' を用いる。</p>
+
+<dl>
+<dt><kbd>CBHEAP *cbheapopen(int <var>size</var>, int <var>max</var>, int(*<var>compar</var>)(const void *, const void *));</kbd></dt>
+<dd>`size' は各レコードの領域のサイズを指定する。`max' はヒープに格納するレコードの最大数を指定する。`compar' は比較関数を指定する。二つの引数はレコードへのポインタである。比較関数は前者が大きければ正数を、後者が大きければ負数を、両者が等しければ 0 を返すべきである。戻り値はヒープハンドル。</dd>
+</dl>
+
+<p>ヒープハンドルを複製するには、関数 `cbheapdup' を用いる。</p>
+
+<dl>
+<dt><kbd>CBHEAP *cbheapdup(CBHEAP *<var>heap</var>);</kbd></dt>
+<dd>`heap' はヒープハンドルを指定する。戻り値は新しいヒープハンドルである。</dd>
+</dl>
+
+<p>ヒープハンドルを破棄するには、関数 `cbheapclose' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbheapclose(CBHEAP *<var>heap</var>);</kbd></dt>
+<dd>`heap' はヒープハンドルを指定する。閉じたハンドルの領域は解放されるので、以後は利用することができなくなる。</dd>
+</dl>
+
+<p>ヒープに格納されたレコード数を得るには、関数 `cbheapnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbheapnum(CBHEAP *<var>heap</var>);</kbd></dt>
+<dd>`heap' はヒープハンドルを指定する。戻り値はリストに格納されたレコード数である。</dd>
+</dl>
+
+<p>ヒープにレコードを挿入するには、関数 `cbheapinsert' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbheapinsert(CBHEAP *<var>heap</var>, const void *<var>ptr</var>);</kbd></dt>
+<dd>`heap' はヒープハンドルを指定する。`ptr' は追加するレコードの領域へのポインタを指定する。戻り値はレコードが追加されれば真であり、そうでなければ偽である。新しいレコードの値が既存のレコードの最大値より大きければ、新しいレコードは追加されない。新しいレコードが追加されてレコード数が最大数を越えた場合、既存のレコードの最大値のものが削除される。</dd>
+</dl>
+
+<p>ヒープ内のあるレコードの領域へのポインタを取得するには、関数 `cbheapget' を用いる。</p>
+
+<dl>
+<dt><kbd>void *cbheapval(CBHEAP *<var>heap</var>, int <var>index</var>);</kbd></dt>
+<dd>`heap' はヒープハンドルを指定する。`index' は取り出すレコードのインデックスを指定する。戻り値は該当レコードの領域へのポインタである。`index' が要素数以上ならば、戻り値は `NULL' である。レコードは内部的には比較関数の負値に基づいて組織化されていることに注意すべきである。</dd>
+</dl>
+
+<p>ヒープを確保された領域に変換するには、関数 `cbheaptomalloc' を用いる。</p>
+
+<dl>
+<dt><kbd>void *cbheaptomalloc(CBHEAP *<var>heap</var>, int *<var>np</var>);</kbd></dt>
+<dd>`heap' はヒープハンドルを指定する。`np' が `NULL' でなければ、その参照先に抽出した領域のレコード数を格納する。戻り値はヒープの領域へのポインタである。レコードはソート済みになる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。元のデータの領域は解放されるので、それを再び解放してはならない。</dd>
+</dl>
+
+<p>書式に基づいた文字列をメモリ上で確保するには、関数 `cbsprintf' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbsprintf(const char *<var>format</var>, ...);</kbd></dt>
+<dd>`format' はprintf風の書式文字列を指定する。変換文字 `%' をフラグ文字 `d'、`o'、`u'、`x'、`X'、`e'、`E'、`f'、`g'、`G'、`c'、`s' および `%' を伴わせて使用することができる。フィールドの幅と精度の指示子を変換文字とフラグ文字の間に置くことができる。その指示子は10進数字、`.'、`+'、`-' およびスペース文字からなる。その他の引数は書式文字列によって利用される。戻り値は結果の文字列の領域へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>文字列中のパターンを置換するには、関数 `cbreplace' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbreplace(const char *<var>str</var>, CBMAP *<var>pairs</var>);</kbd></dt>
+<dd>`str' は置換前の文字列を指定する。`pairs' は置換のペアからなるマップのハンドルを指定する。各ペアのキーは置換前のパターンを指定し、値は置換後のパターンを指定する。戻り値は結果の文字列の領域へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>一連のデータを分割してリストを作成するには、関数 `cbsplit' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cbsplit(const char *<var>ptr</var>, int <var>size</var>, const char *<var>delim</var>);</kbd></dt>
+<dd>`ptr' は内容の領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。`delim' は区切り文字を含む文字列を指定するか、`NULL' なら終端文字を区切り文字とする。戻り値はリストハンドルである。区切り文字が連続している場合でも、その間に空の要素があるとみなす。戻り値のハンドルは、関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。</dd>
+</dl>
+
+<p>ファイルの全データを読み込むには、関数 `cbreadfile' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbreadfile(const char *<var>name</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`name' はファイルの名前を指定するが、`NULL' なら標準入力を読み込む。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら読み込んだデータを格納した領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>一連のデータをファイルに書き込むには、関数 `cbwritefile' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbwritefile(const char *<var>name</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`name' はファイル名を指定するが、`NULL' なら標準出力に書き出される。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。戻り値は正常なら真であり、該当のエラーなら偽である。ファイルが存在する場合には上書きされ、そうでない場合は新しいファイルが生成される。</dd>
+</dl>
+
+<p>ファイルの各行を読み込むには、関数 `cbreadlines' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cbreadlines(const char *<var>name</var>);</kbd></dt>
+<dd>`name' はファイルの名前を指定するが、`NULL' なら標準入力を読み込む。成功すれば戻り値は各行のデータを保持するリストのハンドルであり、失敗なら `NULL' である。改行文字は削除される。戻り値のハンドルは関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。</dd>
+</dl>
+
+<p>ディレクトリに含まれるファイルの名前のリストを得るには、関数 `cbdirlist' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cbdirlist(const char *<var>name</var>);</kbd></dt>
+<dd>`name' はディレクトリの名前を指定する。成功すれば戻り値は各ファイルの名前を保持するリストのハンドルであり、失敗なら `NULL' である。戻り値のハンドルは関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。</dd>
+</dl>
+
+<p>ファイルやディレクトリの状態を得るには、関数 `cbfilestat' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbfilestat(const char *<var>name</var>, int *<var>isdirp</var>, int *<var>sizep</var>, int *<var>mtimep</var>);</kbd></dt>
+<dd>`name' はファイルやディレクトリの名前を指定する。`dirp' が `NULL' でなければ、その参照先にファイルがディレクトリか否かを格納する。`sizep' が `NULL' でなければ、その参照先にファイルのサイズを格納する。`mtimep' が `NULL' でなければ、その参照先にファイルの最終更新時刻を格納する。戻り値は正常なら真であり、エラーなら偽である。ファイルが存在しない場合やパーミッションがない場合も偽を返す。</dd>
+</dl>
+
+<p>ファイルかディレクトリとその内容を削除するには、関数 `cbremove' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbremove(const char *<var>name</var>);</kbd></dt>
+<dd>`name' はファイルやディレクトリの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。ファイルが存在しない場合やパーミッションがない場合も偽を返す。</dd>
+</dl>
+
+<p>URLを構成要素に分解するには、関数 `cburlbreak' を用いる。</p>
+
+<dl>
+<dt><kbd>CBMAP *cburlbreak(const char *<var>str</var>);</kbd></dt>
+<dd>`str' はURLの文字列へのポインタを指定する。戻り値はマップハンドルである。マップの各キーは要素名である。キー "self" はURLそれ自体を指示する。キー "scheme" はURLのスキームを指示する。キー "host" はサーバのホスト名を指示する。キー "port" はサーバのポート番号を指示する。キー "authority" は認証情報を指示する。キー "path" はリソースのパスを指示する。キー "file" はディレクトリ部分を除いたファイル名を指示する。キー "query" はクエリ文字列を指示する。キー "fragment" はフラグメント文字列を指示する。サポートされるスキームはHTTPとHTTPSとFTPとFILEである。絶対URLにも相対URLにも対応する。戻り値のハンドルは関数 `cbmapopen' で開かれるので、不要になったら `cbmapclose' で閉じるべきである。</dd>
+</dl>
+
+<p>相対URLを絶対URLを用いて解決するには、関数 `cburlresolve' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cburlresolve(const char *<var>base</var>, const char *<var>target</var>);</kbd></dt>
+<dd>`base' はベースロケーションの絶対URLを指定する。`target' は解決すべきURLを指定する。戻り値は解決されたURLである。ターゲットURLが相対URLの場合、ベースロケーションからの相対位置のURLを返す。ターゲットURLが絶対URLの場合、ターゲットURLのコピーを返す。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>一連のオブジェクトをURLエンコーディングで符号化するには、関数 `cburlencode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cburlencode(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。戻り値は結果の文字列へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>URLエンコーディングで符号化された文字列を復元するには、関数 `cburldecode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cburldecode(const char *<var>str</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`str' は符号化された文字列へのポインタを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は結果の領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>一連のオブジェクトをBase64エンコーディングで符号化するには、関数 `cbbaseencode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbbaseencode(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。戻り値は結果の文字列へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>Base64エンコーディングで符号化された文字列を復元するには、関数 `cbbasedecode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbbasedecode(const char *<var>str</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`str' は符号化された文字列へのポインタを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は結果の領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>一連のオブジェクトをquoted-printableエンコーディングで符号化するには、関数 `cbquoteencode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbquoteencode(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。戻り値は結果の文字列へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>quoted-printableエンコーディングで符号化された文字列を復元するには、関数 `cbquotedecode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbquotedecode(const char *<var>str</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`str' は符号化された文字列へのポインタを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は結果の領域へのポインタである。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>MIMEの文字列をヘッダとボディに分割するには、関数 `cbmimebreak' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbmimebreak(const char *<var>ptr</var>, int <var>size</var>, CBMAP *<var>attrs</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' はMIMEのデータの領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。`attrs' は属性を保存するためのマップハンドルを指定するが、`NULL' の場合には利用されない。マップの各キーは小文字に正規化された属性名である。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値はボディの文字列である。コンテントタイプが指定されている場合、マップのキー "TYPE" はそのタイプを指示する。文字コードが指定されている場合、キー "CHARSET" はそのコード名を指示する。マルチパートの区切り文字列が指定されている場合、キー "BOUNDARY" はその文字列を指示する。コンテントディスポジションが指定されている場合、キー "DISPOSITION" はその方針を指示す
 る。ファイル名が指定されている場合、キー "FILENAME" はその名前を指示する。属性名が指定されている場合、キー "NAME" はその名前を指示する。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>MIMEのマルチパートデータの文字列を各パートに分割するには、関数 `cbmimeparts' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cbmimeparts(const char *<var>ptr</var>, int <var>size</var>, const char *<var>boundary</var>);</kbd></dt>
+<dd>`ptr' はMIMEのデータの領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。`boundary' は区切り文字列へのポインタを指定する。戻り値はリストハンドルである。リストの各要素はパートの文字列である。戻り値のハンドルは関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。</dd>
+</dl>
+
+<p>文字列をMIMEエンコーディングで符号化するには、関数 `cbmimeencode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbmimeencode(const char *<var>str</var>, const char *<var>encname</var>, int <var>base</var>);</kbd></dt>
+<dd>`str' は文字列へのポインタを指定する。`encname' は文字コード名の文字列を指定する。`base' はBase64エンコードを使うか否かを指定する。偽の場合はquoted-printableが用いられる。戻り値は結果の文字列へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>MIMEエンコーディングで符号化された文字列を復元するには、関数 `cbmimedecode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbmimedecode(const char *<var>str</var>, char *<var>enp</var>);</kbd></dt>
+<dd>`str' は符号化された文字列へのポインタを指定する。`enp' は文字コード名を書き込むための領域へのポインタを指定するが、`NULL' の場合は利用されない。バッファのサイズは32バイト以上でなければならない。戻り値は結果の文字列へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>CSVの文字列を行に分割するには、関数 `cbcsvrows' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cbcsvrows(const char *<var>str</var>);</kbd></dt>
+<dd>`str' はCSVの文字列へのポインタを指定する。戻り値はリストハンドルである。リストの各要素は行の文字列である。戻り値のハンドルは関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。入力文字列の文字コードはUS-ASCII、UTF-8、ISO-8859-*、EUC-*、Shift_JISのどれかである必要がある。MS-Excelと互換して、これらCSV用関数群は、ダブルクォートで囲んでコンマなどのメタ文字を含めたセルを扱うことができる。</dd>
+</dl>
+
+<p>CSVの行の文字列をセルに分割するには、関数 `cbcsvcells' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cbcsvcells(const char *<var>str</var>);</kbd></dt>
+<dd>`str' はCSVの行の文字列へのポインタを指定する。戻り値はリストハンドルである。リストの各要素はセル内容をアンエスケープした文字列である。戻り値のハンドルは関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。</dd>
+</dl>
+
+<p>CSVのメタ文字をエスケープした文字列を得るには、関数 `cbcsvescape' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbcsvescape(const char *<var>str</var>);</kbd></dt>
+<dd>`str' は対象の文字列へのポインタを指定する。戻り値はメタ文字を無効化した結果の文字列へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>XMLの実体参照をアンエスケープした文字列を得るには、関数 `cbcsvunescape' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbcsvunescape(const char *<var>str</var>);</kbd></dt>
+<dd>`str' は対象の文字列へのポインタを指定する。戻り値はメタ文字を伴った結果の文字列へのポインタである。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>XMLの文字列をタグとテキストセクションに分割するには、関数 `cbxmlbreak' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *cbxmlbreak(const char *<var>str</var>, int <var>cr</var>);</kbd></dt>
+<dd>`str' はXMLの文字列へのポインタを指定する。`cr' はコメントを削除するか否かを指定する。戻り値はリストハンドルである。リストの各要素はタグかテキストセクションの文字列である。戻り値のハンドルは関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。入力文字列の文字コードはUS-ASCII、UTF-8、ISO-8859-*、EUC-*、Shift_JISのどれかである必要がある。これらXML用関数群は妥当性検証を行うXMLパーザではないので、HTMLやSGMLも扱うことができる。</dd>
+</dl>
+
+<p>XMLのタグの属性のマップを得るには、関数 `cbxmlattrs' を用いる。</p>
+
+<dl>
+<dt><kbd>CBMAP *cbxmlattrs(const char *<var>str</var>);</kbd></dt>
+<dd>`str' はタグの文字列へのポインタを指定する。戻り値はマップハンドルである。マップの各キーは属性の名前である。各値はアンエスケープされる。空文字列をキーにするとタグの名前を取り出すことができる。戻り値のハンドルは関数 `cbmapopen' で開かれるので、不要になったら `cbmapclose' で閉じるべきである。</dd>
+</dl>
+
+<p>XMLのメタ文字をエスケープした文字列を得るには、関数 `cbxmlescape' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbxmlescape(const char *<var>str</var>);</kbd></dt>
+<dd>`str' は対象の文字列へのポインタを指定する。戻り値はメタ文字を無効化した結果の文字列へのポインタである。この関数は `&'、 `<'、`>'、`"' のみを変換する。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>XMLの実体参照をアンエスケープした文字列を得るには、関数 `cbxmlunescape' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbxmlunescape(const char *<var>str</var>);</kbd></dt>
+<dd>`str' は対象の文字列へのポインタを指定する。戻り値はメタ文字を伴った結果の文字列へのポインタである。この関数は `&amp;'、`&lt;'、`&gt;'、`&quot;' のみを復元する。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>ZLIBを用いて一連のオブジェクトを圧縮するには、関数 `cbdeflate' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbdeflate(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。`sp' の参照先には抽出した領域のサイズを格納する。戻り値は正常なら結果の領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はQDBMがZLIBを有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>ZLIBを用いて圧縮されたオブジェクトを伸長するには、関数 `cbinflate' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbinflate(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら結果の領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はQDBMがZLIBを有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>GZIPを用いて一連のオブジェクトを圧縮するには、関数 `cbgzencode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbgzencode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。`sp' の参照先には抽出した領域のサイズを格納する。戻り値は正常なら結果の領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はQDBMがZLIBを有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>GZIPを用いて圧縮されたオブジェクトを伸長するには、関数 `cbgzdecode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbgzdecode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら結果の領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はQDBMがZLIBを有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>一連のオブジェクトのCRC32チェックサムを得るには、関数 `cbgetcrc' を用いる。</p>
+
+<dl>
+<dt><kbd>unsigned int cbgetcrc(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。戻り値はオブジェクトのCRC32チェックサムである。この関数はQDBMがZLIBを有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>LZOを用いて一連のオブジェクトを圧縮するには、関数 `cblzoencode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cblzoencode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。`sp' の参照先には抽出した領域のサイズを格納する。戻り値は正常なら結果の領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はQDBMがLZOを有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>LZOを用いて圧縮されたオブジェクトを伸長するには、関数 `cblzodecode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cblzodecode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら結果の領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はQDBMがLZOを有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>BZIP2を用いて一連のオブジェクトを圧縮するには、関数 `cbbzencode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbbzencode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。`sp' の参照先には抽出した領域のサイズを格納する。戻り値は正常なら結果の領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はQDBMがBZIP2を有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>BZIP2を用いて圧縮されたオブジェクトを伸長するには、関数 `cbbzdecode' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbbzdecode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら結果の領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はQDBMがBZIP2を有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>文字列の文字コードを変換するには、関数 `cbiconv' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbiconv(const char *<var>ptr</var>, int <var>size</var>, const char *<var>icode</var>, const char *<var>ocode</var>, int *<var>sp</var>, int *<var>mp</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。`icode' は入力文字列の文字コードの名前を指定する。`outcode' は出力文字列の文字コードの名前を指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。`mp' が `NULL' でなければ、その参照先に変換に失敗した文字数を格納する。戻り値は正常なら結果の領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。この関数はQDBMがICONVを有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>文字列の文字コードを自動判定するには、関数 `cbencname' を用いる。</p>
+
+<dl>
+<dt><kbd>const char *cbencname(const char *<var>str</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' は領域へのポインタを指定する。`size' はその領域のサイズを指定するか、負数なら `strlen(ptr)' の値となる。`icode' は入力文字列の文字コードの名前を指定する。戻り値はエンコードの名前の文字列である。現状では、US-ASCII、ISO-2022-JP、Shift_JIS、CP932、EUC-JP、UTF-8、UTF-16、UTF-16BEおよびUTF-16LEに対応している。そのいずれでもない場合は、ISO-8859-1と判定する。この関数はQDBMがICONVを有効にしてビルドされた場合のみ利用できる。</dd>
+</dl>
+
+<p>ローカル時間の秒単位の時差を得るには、関数 `cbjetlag' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbjetlag(void);</kbd></dt>
+<dd>戻り値はローカル時間の秒単位の時差である。</dd>
+</dl>
+
+<p>ある時間のグレゴリオ暦を得るには、関数 `cbcalendar' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbcalendar(time_t <var>t</var>, int <var>jl</var>, int *<var>yearp</var>, int *<var>monp</var>, int *<var>dayp</var>, int *<var>hourp</var>, int *<var>minp</var>, int *<var>secp</var>);</kbd></dt>
+<dd>`t' は対象の時間を指定するが、負数なら現在時間が使われる。`jl' はある場所の時差を秒単位で指定する。`yearp' が `NULL' でなければ、その参照先に年を格納する。`monp' が `NULL' でなければ、その参照先に月を格納する。1は1月を意味し、12は12月を意味する。`dayp' が `NULL' でなければ、その参照先に日を格納する。`hourp' が `NULL' でなければ、その参照先に時を格納する。`minp' が `NULL' でなければ、その参照先に分を格納する。`secp' が `NULL' でなければ、その参照先に秒を格納する。</dd>
+</dl>
+
+<p>ある日付の曜日を得るには、関数 `cbdayofweek' を用いる。</p>
+
+<dl>
+<dt><kbd>int cbdayofweek(int <var>year</var>, int <var>mon</var>, int <var>day</var>);</kbd></dt>
+<dd>`year' は日付の年を指定する。`mon' は日付の月を指定する。`day' は日付の日を指定する。戻り値は曜日の値である。0は日曜を意味し、6は土曜を意味する。</dd>
+</dl>
+
+<p>ある日付をW3CDTFの書式で表した文字列を得るには、関数 `cbdatestrwww' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbdatestrwww(time_t <var>t</var>, int <var>jl</var>);</kbd></dt>
+<dd>`t' は対象の時間を指定するが、負数なら現在時間が使われる。`jl' はある場所の時差を秒単位で指定する。戻り値はW3CDTFの書式(YYYY-MM-DDThh:mm:ddTZD)の文字列である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>ある日付をRFC 1123の書式で表した文字列を得るには、関数 `cbdatestrhttp' を用いる。</p>
+
+<dl>
+<dt><kbd>char *cbdatestrhttp(time_t <var>t</var>, int <var>jl</var>);</kbd></dt>
+<dd>`t' は対象の時間を指定するが、負数なら現在時間が使われる。`jl' はある場所の時差を秒単位で指定する。戻り値はRFC 1123の書式(Wdy, DD-Mon-YYYY hh:mm:dd TZD)の文字列である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>10進数か16進数かW3CDTFかRFC 822(1123)の書式で表した文字列から時間の値を得るには、関数 `cbstrmktime' を用いる。</p>
+
+<dl>
+<dt><kbd>time_t cbstrmktime(const char *<var>str</var>);</kbd></dt>
+<dd>`str' は10進数か16進数かW3CDTFかRFC 822(1123)の書式で表した文字列を指定する。戻り値はその時間の値か、書式が不正な場合は -1 である。10進数に "s" を後置すると秒単位を意味し、"m" を後置すると分単位を意味し、"h" を後置すると時単位を意味し、"d" を後置すると日単位を意味する。</dd>
+</dl>
+
+<p>ユーザとシステムのプロセス時間を得るには、関数 `cbproctime' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbproctime(double *<var>usrp</var>, double *<var>sysp</var>);</kbd></dt>
+<dd>`usrp' が `NULL' でなければ、その参照先にユーザ時間を格納する。時間の単位は秒である。`sysp' が `NULL' でなければ、その参照先にシステム時間を格納する。時間の単位は秒である。</dd>
+</dl>
+
+<p>標準入出力がバイナリモードであることを保証するには、関数 `cbstdiobin' を用いる。</p>
+
+<dl>
+<dt><kbd>void cbstdiobin(void);</kbd></dt>
+<dd>この関数はDOS的なファイルシステムの上のアプリケーションで有用である。</dd>
+</dl>
+
+<h3>サンプルコード</h3>
+
+<p>以下のサンプルコードは典型的な利用例である。</p>
+
+<pre>#include <cabin.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(int argc, char **argv){
+  CBDATUM *datum;
+  CBLIST *list;
+  CBMAP *map;
+  char *buf1, *buf2;
+  int i;
+
+  /* データハンドルを開く */
+  datum = cbdatumopen("123", -1);
+  /* データを連結する */
+  cbdatumcat(datum, "abc", -1);
+  /* データを表示する */
+  printf("%s\n", cbdatumptr(datum));
+  /* データハンドルを閉じる */
+  cbdatumclose(datum);
+
+  /* リストハンドルを開く */
+  list = cblistopen();
+  /* リストに要素を追加する */
+  cblistpush(list, "apple", -1);
+  cblistpush(list, "orange", -1);
+  /* 全ての要素を表示する */
+  for(i = 0; i < cblistnum(list); i++){
+    printf("%s\n", cblistval(list, i, NULL));
+  }
+  /* リストハンドルを閉じる */
+  cblistclose(list);
+
+  /* マップハンドルを開く */
+  map = cbmapopen();
+  /* マップにレコードを追加する */
+  cbmapput(map, "dog", -1, "bowwow", -1, 1);
+  cbmapput(map, "cat", -1, "meow", -1, 1);
+  /* 値を取得して表示する */
+  printf("%s\n", cbmapget(map, "dog", -1, NULL));
+  printf("%s\n", cbmapget(map, "cat", -1, NULL));
+  /* マップハンドルを閉じる */
+  cbmapclose(map);
+
+  /* Base64の符号化を行う */
+  buf1 = cbbaseencode("I miss you.", -1);
+  printf("%s\n", buf1);
+  /* Base64の復元を行う */
+  buf2 = cbbasedecode(buf1, NULL);
+  printf("%s\n", buf2);
+  /* リソースを解放する */
+  free(buf2);
+  free(buf1);
+
+  /* 単純なポインタをグローバルガベージコレクタに登録する */
+  buf1 = cbmemdup("Take it easy.", -1);
+  cbglobalgc(buf1, free);
+  /* ポインタは利用できるが解放する必要はない */
+  printf("%s\n", buf1);
+
+  /* リストをグローバルガベージコレクタに登録する */
+  list = cblistopen();
+  cbglobalgc(list, (void (*)(void *))cblistclose);
+  /* ハンドルは利用できるが閉じる必要はない */
+  cblistpush(list, "Don't hesitate.", -1);
+  for(i = 0; i < cblistnum(list); i++){
+    printf("%s\n", cblistval(list, i, NULL));
+  }
+
+  return 0;
+}
+</pre>
+
+<h3>注記</h3>
+
+<p>Cabinを利用したプログラムをビルドする方法は、Depotの場合と全く同じである。</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>スレッド間で同時に同じハンドルにアクセスしない限りは、`cbglobalgc' を除いたCabinの各関数はスレッドセーフである。ただし、`errno' や `malloc' 等がスレッドセーフな処理系であることが前提となる。</p>
+
+<hr />
+
+<h2><a name="cabincli" id="cabincli" class="head">Cabin用コマンド</a></h2>
+
+<p>Cabinに対応するコマンドラインインタフェースは以下のものである。</p>
+
+<p>コマンド `cbtest' はCabinの機能テストや性能テストに用いるツールである。`time' コマンドによってこのコマンドの実行時間を計るとよい。以下の書式で用いる。`rnum' はレコード数を指定する。</p>
+
+<dl>
+<dt><kbd>cbtest sort [-d] <var>rnum</var></kbd></dt>
+<dd>ソートアルゴリズムのテストを行う。</dd>
+<dt><kbd>cbtest strstr [-d] <var>rnum</var></kbd></dt>
+<dd>文字列探索アルゴリズムのテストを行う。</dd>
+<dt><kbd>cbtest list [-d] <var>rnum</var></kbd></dt>
+<dd>リストの書き込みテストを行う。</dd>
+<dt><kbd>cbtest map [-d] <var>rnum</var></kbd></dt>
+<dd>マップの書き込みテストを行う。</dd>
+<dt><kbd>cbtest wicked <var>rnum</var></kbd></dt>
+<dd>リストとマップの各種更新操作を無作為に選択して実行する。</dd>
+<dt><kbd>cbtest misc</kbd></dt>
+<dd>雑多なルーチンのテストを実行する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-d</kbd> : 結果のデータを読み込んで表示する。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<p>コマンド `cbcodec' はCabinが提供するエンコードおよびデコードの機能を利用するツールである。以下の書式で用いる。`file' は入力ファイルを指定するが、省略されれば標準入力を読み込む。</p>
+
+<dl>
+<dt><kbd>cbcodec url [-d] [-br] [-rs <var>base</var> <var>target</var>] [-l] [-e <var>expr</var>] [<var>file</var>]</kbd></dt>
+<dd>URLエンコードとそのデコードを行う。</dd>
+<dt><kbd>cbcodec base [-d] [-l] [-c <var>num</var>] [-e <var>expr</var>] [<var>file</var>]</kbd></dt>
+<dd>Base64エンコードとそのデコードを行う。</dd>
+<dt><kbd>cbcodec quote [-d] [-l] [-c <var>num</var>] [-e <var>expr</var>] [<var>file</var>]</kbd></dt>
+<dd>quoted-printableエンコードとそのデコードを行う。</dd>
+<dt><kbd>cbcodec mime [-d] [-hd] [-bd] [-part <var>num</var>] [-l] [-ec <var>code</var>] [-qp] [-dc] [-e <var>expr</var>] [<var>file</var>]</kbd></dt>
+<dd>MIMEエンコードとそのデコードを行う。</dd>
+<dt><kbd>cbcodec csv [-d] [-t] [-l] [-e <var>expr</var>] [-html] [<var>file</var>]</kbd></dt>
+<dd>CSVの処理を行う。デフォルトではメタ文字のエスケープを行う。</dd>
+<dt><kbd>cbcodec xml [-d] [-p] [-l] [-e <var>expr</var>] [-tsv] [<var>file</var>]</kbd></dt>
+<dd>XMLの処理を行う。デフォルトではメタ文字のエスケープを行う。</dd>
+<dt><kbd>cbcodec zlib [-d] [-gz] [-crc] [<var>file</var>]</kbd></dt>
+<dd>ZLIBの圧縮とその伸長を行う。ZLIBを有効化してQDBMをビルドした場合にのみ利用可能である。</dd>
+<dt><kbd>cbcodec lzo [-d] [<var>file</var>]</kbd></dt>
+<dd>LZOの圧縮とその伸長を行う。LZOを有効化してQDBMをビルドした場合にのみ利用可能である。</dd>
+<dt><kbd>cbcodec bzip [-d] [<var>file</var>]</kbd></dt>
+<dd>BZIP2の圧縮とその伸長を行う。BZIP2を有効化してQDBMをビルドした場合にのみ利用可能である。</dd>
+<dt><kbd>cbcodec iconv [-ic <var>code</var>] [-oc <var>code</var>] [-ol <var>ltype</var>] [-cn] [-um] [-wc] [<var>file</var>]</kbd></dt>
+<dd>ICONVによる文字コードの変換を行う。ICONVを有効化してQDBMをビルドした場合にのみ利用可能である。</dd>
+<dt><kbd>cbcodec date [-wf] [-rf] [-utc] [<var>str</var>]</kbd></dt>
+<dd>`str' で指定した日付の文字列の書式を変換する。デフォルトでは、UNIX時間を出力する。`str' が省略されると、現在日時を扱う。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-d</kbd> : エンコード(エスケープ)ではなく、デコード(アンエスケープ)を行う。</li>
+<li><kbd>-br</kbd> : URLを構成要素に分解する。</li>
+<li><kbd>-rs</kbd> : 相対URLを解決する。</li>
+<li><kbd>-l</kbd> : 出力の末尾に改行文字を加える。</li>
+<li><kbd>-e <var>expr</var></kbd> : 入力データを直接指定する。</li>
+<li><kbd>-c <var>num</var></kbd> : エンコードの際の桁数制限を指定する。</li>
+<li><kbd>-hd</kbd> : MIMEの構文解析を行い、ヘッダをTSV形式で抽出する。</li>
+<li><kbd>-bd</kbd> : MIMEの構文解析を行い、ボディを抽出する。</li>
+<li><kbd>-part <var>num</var></kbd> : MIMEの構文解析を行い、特定のパートを抽出する。</li>
+<li><kbd>-ec <var>code</var></kbd> : 入力の文字コードを指定する。デフォルトはUTF-8である。</li>
+<li><kbd>-qp</kbd> : quoted-printableエンコードを用いる。デフォルトはBase64である。</li>
+<li><kbd>-dc</kbd> : デコード結果の文字列でなく、文字コード名を出力する。</li>
+<li><kbd>-t</kbd> : CSVの構造を解析する。TSVに変換して表示する。セル内のタブと改行は削除される。</li>
+<li><kbd>-html</kbd> : CSVの構造を解析する。HTMLに変換して出力する。</li>
+<li><kbd>-p</kbd> : XMLの構文解析を行う。タグとテキストセクションをヘッダで分けて表示する。</li>
+<li><kbd>-tsv</kbd> : XMLの構文解析を行う。結果をTSV形式で出力する。テキストセクションのタブと改行はURLエンコードされる。</li>
+<li><kbd>-gz</kbd> : GZIP形式を用いる。</li>
+<li><kbd>-crc</kbd> : CRC32のチェックサムをビッグエンディアンの16進数で出力する。</li>
+<li><kbd>-ic <var>code</var></kbd> : 入力の文字コードを指定する。デフォルトだと自動判定する。</li>
+<li><kbd>-oc <var>code</var></kbd> : 出力の文字コードを指定する。デフォルトだとUTF-8になる。</li>
+<li><kbd>-ol <var>ltype</var></kbd> : 改行文字を変換する。`unix'(LF)、`dos'(CRLF)、`mac'(CR) のどれかを指定する。</li>
+<li><kbd>-cn</kbd> : 入力の文字コードを自動判定し、その名前を表示する。</li>
+<li><kbd>-wc</kbd> : 入力の文字コードをUTF-8と仮定し、その文字数を表示する。</li>
+<li><kbd>-um</kbd> : UCS-2の文字と、C言語の文字列表現でのUTF-16BEとUTF-8の対応表を出力する。</li>
+<li><kbd>-wf</kbd> : W3CDTFの書式で出力する。</li>
+<li><kbd>-rf</kbd> : RFC 1123の書式で出力する。</li>
+<li><kbd>-utc</kbd> : 協定世界時を出力する。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<hr />
+
+<h2><a name="villaapi" id="villaapi" class="head">Villa: 上級API</a></h2>
+
+<h3>概要</h3>
+
+<p>VillaはQDBMの上級APIであり、B+木のデータベースを管理するルーチンを提供する。各レコードはユーザが指定した順序で整列されて格納される。ハッシュデータベースではレコードの検索はキーの完全一致によるしかなかった。しかし、Villaを用いると範囲を指定してレコードを検索することができる。各レコードを順番に参照するにはカーソルを用いる。データベースにはキーが重複する複数のレコードを格納することができる。また、トランザクション機構によってデータベースの操作を一括して反映させたり破棄したりすることができる。</p>
+
+<p>VillaはDepotおよびCabinを基盤として実装される。VillaのデータベースファイルはDepotのデータベースファイルそのものである。レコードの検索や格納の処理速度はDepotより遅いが、データベースファイルのサイズはより小さい。</p>
+
+<p>Villaを使うためには、`depot.h' と `cabin.h' と `villa.h' と `stdlib.h' をインクルードすべきである。通常、ソースファイルの冒頭付近で以下の記述を行う。</p>
+
+<dl>
+<dt><kbd>#include <depot.h></kbd></dt>
+<dt><kbd>#include <cabin.h></kbd></dt>
+<dt><kbd>#include <villa.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+</dl>
+
+<p>Villaでデータベースを扱う際には、`VILLA' 型へのポインタをハンドルとして用いる。これは、`stdio.h' の各種ルーチンがファイル入出力に `FILE' 型へのポインタを用いるのに似ている。ハンドルは、関数 `vlopen' で開き、関数 `vlclose' で閉じる。ハンドルのメンバを直接参照することは推奨されない。データベースに致命的なエラーが起きた場合は、以後そのハンドルに対する `vlclose' を除く全ての操作は何もせずにエラーを返す。ひとつのプロセスで複数のデータベースファイルを同時に利用することは可能であるが、同じデータベースファイルの複数のハンドルを利用してはならない。カーソルを使う前には `vlcurfirst' か `vlcurlast' か `vlcurjump' のどれかで初期化する必要がある。`vlcurput' と `vlcurout' 以外の関数でレコードの更新や削除ã‚
 ’した後にもカーソルを初期化する必要がある。</p>
+
+<p>VillaでもDepotと同じく外部変数 `dpecode' に直前のエラーコードが記録される。エラーコードに対応するメッセージ文字列を得るには、関数 `dperrmsg' を用いる。</p>
+
+<h3>API</h3>
+
+<p>レコードの順番を指定するためには、比較関数を定義する。比較関数には以下の型を用いる。</p>
+
+<dl>
+<dt><kbd>typedef int(*VLCFUNC)(const char *<var>aptr</var>, int <var>asiz</var>, const char *<var>bptr</var>, int <var>bsiz</var>);</kbd></dt>
+<dd>`aptr' は一方のキーのデータ領域へのポインタを指定する。`asiz' はそのキーのデータ領域のサイズを指定する。`bptr' は他方のキーのデータ領域へのポインタを指定する。`bsiz' はそのキーのデータ領域のサイズを指定する。戻り値は前者が大きければ正、後者が大きければ負、両者が等価なら 0 である。</dd>
+</dl>
+
+<p>データベースのハンドルを作成するには、関数 `vlopen' を用いる。</p>
+
+<dl>
+<dt><kbd>VILLA *vlopen(const char *<var>name</var>, int <var>omode</var>, VLCFUNC <var>cmp</var>);</kbd></dt>
+<dd>`name' はデータベースファイルの名前を指定する。`omode' は接続モードを指定し、`VL_OREADER' ならリーダ、`VL_OWRITER' ならライタとなる。`VL_OWRITER' の場合、`VL_OCREAT' または `VL_OTRUNC' とのビット論理和にすることができる。`VL_OCREAT' はファイルが無い場合に新規作成することを指示し、`VL_OTRUNC' はファイルが存在しても作り直すことを指示し、`VL_OZCOMP' はデータベース内のリーフをZLIBで圧縮することを指示し、`VL_OYCOMP' はデータベース内のリーフをLZOで圧縮することを指示し、`VL_OXCOMP' はデータベース内のリーフをBZIP2で圧縮することを指示する。`VL_OREADER' と `VL_OWRITER' の両方で `VL_ONOLCK' または `VL_OLCKNB' とのビット論理和にすることができるが、前者はファイルロックをかけずにデータベースを開くことを指示し、後者はブロックせずã
 «ãƒ­ãƒƒã‚¯ã‚’かけることを指示する。`cmp' は比較関数を指定する。`VL_CMPLEX' はキーを辞書順で比較する。`VL_CMPINT' はキーを `int' 型のオブジェクトとみなして比較する。`VL_CMPNUM' はキーをビッグエンディアンの数値とみなして比較する。`VL_CMPDEC' はキーを10進数の数値を表す文字列とみなして比較する。`VLCFUNC' 型の宣言に基づく関数であれば比較関数として用いることができる。同じデータベースには常に同じ比較関数を用いる必要がある。戻り値はデータベースハンドルであるか、エラーなら `NULL' である。ライタ(読み書き両用モード)でデータベースファイルを開く際にはそのファイルに対して排他ロックがかけられ、リーダ(読み込み専用モード)で開く際には共有ロックがかけられる。その際には該当のロックがかけられるまで
 制御がブロックする。`VL_OZCOMP' と `VL_OYCOMP' と `VL_OXCOMP' はQDBMがそれぞれZLIBとLZOとBZIP2を有効にしてビルドされた場合にのみ利用できる。`VL_ONOLCK' を使う場合、アプリケーションが排他制御の責任を負う。</dd>
+</dl>
+
+<p>データベースとの接続を閉じてハンドルを破棄するには、関数 `vlclose' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlclose(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。データベースの更新内容は、接続を閉じた時点で初めてファイルと同期される。ライタでデータベースを開いた場合、適切に接続を閉じないとデータベースが破壊される。閉じたハンドルの領域は解放されるので、以後は利用できなくなる。トランザクションが有効でコミットされていない場合、それは破棄される。</dd>
+</dl>
+
+<p>レコードを追加するには、関数 `vlput' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlput(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>dmode</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`vbuf' は値のデータ領域へのポインタを指定する。`vsiz' は値のデータ領域のサイズを指定するか、負数なら `strlen(vbuf)' の値となる。`dmode' はキーが既存レコードと重複した際の制御を指定する。`VL_DOVER' は既存のレコードの値を上書きし、`VL_DKEEP' は既存のレコードを残してエラーを返し、`VL_DCAT' は指定された値を既存の値の末尾に加え、`VL_DDUP' はキーの重複を許して指定された値を最後の値として加え、`VL_DDUPR' はキーの重複を許して指定された値を最初の値として加える。戻り値は正常なら真であり、エラーなら偽である。データã
 ƒ™ãƒ¼ã‚¹ã®æ›´æ–°ã«ã‚ˆã£ã¦ã‚«ãƒ¼ã‚½ãƒ«ã¯ä½¿ç”¨ä¸èƒ½ã«ãªã‚‹ã€‚</dd>
+</dl>
+
+<p>レコードを削除するには、関数 `vlout' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlout(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は正常なら真であり、エラーなら偽である。該当のレコードがない場合も偽を返す。重複するレコード群のキーが指定された場合、その最初のものが削除される。データベースの更新によってカーソルは使用不能になる。</dd>
+</dl>
+
+<p>レコードを取得するには、関数 `vlget' を用いる。</p>
+
+<dl>
+<dt><kbd>char *vlget(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら値を格納した領域へのポインタであり、エラーなら `NULL' である。該当のレコードがない場合も `NULL' を返す。重複するレコード群のキーが指定された場合、その最初のものが選択される。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>レコードの値のサイズを取得するには、関数 `vlvsiz' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlvsiz(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は該当レコードの値のサイズであるが、該当がない場合やエラーの場合は -1 である。複数のレコードが該当する場合は、最初のレコードの値のサイズを返す。</dd>
+</dl>
+
+<p>キーに一致するレコードの数を取得するには、関数 `vlvnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlvnum(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は該当レコードの値の数であり、該当がない場合やエラーの場合は 0 である。</dd>
+</dl>
+
+<p>キーに一致する複数のレコードを追加するには、関数 `vlputlist' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlputlist(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const CBLIST *<var>vals</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`vals' は値のリストのハンドルを指定する。リストは空であってはならない。戻り値は正常なら真であり、エラーなら偽である。データベースの更新によってカーソルは使用不能になる。</dd>
+</dl>
+
+<p>キーに一致する全てのレコードを削除するには、関数 `vloutlist' を用いる。</p>
+
+<dl>
+<dt><kbd>int vloutlist(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は正常なら真であり、エラーなら偽である。該当のレコードがない場合も偽を返す。データベースの更新によってカーソルは使用不能になる。</dd>
+</dl>
+
+<p>キーに一致する全てのレコードを取得するには、関数 `vlgetlist' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *vlgetlist(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。戻り値は一致するレコードの値のリストのハンドルであるか、エラーなら `NULL' である。該当のレコードがない場合も `NULL' を返す。戻り値のハンドルは関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。</dd>
+</dl>
+
+<p>キーに一致する全てのレコードの連結した値を取得するには、関数 `vlgetcat' を用いる。</p>
+
+<dl>
+<dt><kbd>char *vlgetcat(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら連結した値を格納した領域へのポインタであり、エラーなら `NULL' である。該当のレコードがない場合も `NULL' を返す。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>カーソルを最初のレコードに移動させるには、関数 `vlcurfirst' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlcurfirst(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。データベースが空の場合も偽を返す。</dd>
+</dl>
+
+<p>カーソルを最後のレコードに移動させるには、関数 `vlcurlast' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlcurlast(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。データベースが空の場合も偽を返す。</dd>
+</dl>
+
+<p>カーソルを前のレコードに移動させるには、関数 `vlcurprev' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlcurprev(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。前のレコードがない場合も偽を返す。</dd>
+</dl>
+
+<p>カーソルを次のレコードに移動させるには、関数 `vlcurnext' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlcurnext(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。次のレコードがない場合も偽を返す。</dd>
+</dl>
+
+<p>カーソルを特定のレコードの前後に移動させるには、関数 `vlcurjump' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlcurjump(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>jmode</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。`kbuf' はキーのデータ領域へのポインタを指定する。`ksiz' はキーのデータ領域のサイズを指定するか、負数なら `strlen(kbuf)' の値となる。`jmode' は詳細な調整を指定する。`VL_JFORWARD' はキーが同じレコード群の最初のレコードにカーソルが設定され、また完全に一致するレコードがない場合は次の候補にカーソルが設定されることを意味する。`VL_JBACKWORD' はキーが同じレコード群の最後のレコードにカーソルが設定され、また完全に一致するレコードがない場合は前の候補にカーソルが設定されることを意味する。戻り値は正常なら真であり、エラーなら偽である。条件に一致するレコードがない場合も偽を返す。</dd>
+</dl>
+
+<p>カーソルのあるレコードのキーを取得するには、関数 `vlcurkey' を用いる。</p>
+
+<dl>
+<dt><kbd>char *vlcurkey(VILLA *<var>villa</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら値を格納した領域へのポインタであり、エラーなら `NULL' である。該当のレコードがない場合も `NULL' を返す。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>カーソルのあるレコードの値を取得するには、関数 `vlcurkey' を用いる。</p>
+
+<dl>
+<dt><kbd>char *vlcurval(VILLA *<var>villa</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。`sp' が `NULL' でなければ、その参照先に抽出した領域のサイズを格納する。戻り値は正常なら値を格納した領域へのポインタであり、エラーなら `NULL' である。該当のレコードがない場合も `NULL' を返す。戻り値の領域は、実際には1バイト多く確保して終端文字が置かれるので、文字列として利用できる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>カーソルのあるレコードの周辺にレコードを挿入するには、関数 `vlcurput' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlcurput(VILLA *<var>villa</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>cpmode</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。`vbuf' は値のデータ領域へのポインタを指定する。`vsiz' は値のデータ領域のサイズを指定するか、負数なら `strlen(vbuf)' の値となる。`cpmode' は詳細な調整を指定する。`VL_CPCURRENT' は現在のレコードの値を上書きすることを指示し、`VL_CPBEFORE' はカーソルの直前にレコードを挿入することを指示し、`VL_CPAFTER' はカーソルの直後にレコードを挿入することを指示する。戻り値は正常なら真であり、エラーなら偽である。該当のレコードがない場合も偽を返す。挿入操作の後には、カーソルは挿入されたレコードの位置に移動する。</dd>
+</dl>
+
+<p>カーソルのあるレコードを削除するには、関数 `vlcurout' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlcurout(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。該当のレコードがない場合も偽を返す。削除操作の後には、可能であればカーソルは次のレコードに移動する。</dd>
+</dl>
+
+<p>性能を調整するパラメータを指定するには、関数 `vlsettuning' を用いる。</p>
+
+<dl>
+<dt><kbd>void vlsettuning(VILLA *<var>villa</var>, int <var>lrecmax</var>, int <var>nidxmax</var>, int <var>lcnum</var>, int <var>ncnum</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。`lrecmax' はB+木のひとつのリーフに入れるレコードの最大数を指定するが、0 以下ならデフォルト値が使われる。`nidxmax' はB+木の非リーフノードに入れるインデックスの最大数を指定するが、0 以下ならデフォルト値が使われる。`lcnum' はキャッシュに入れるリーフの最大数を指定するが、0 以下ならデフォルト値が使われる。`ncnum' はキャッシュに入れる非リーフノードの最大数を指定するが、0 以下ならデフォルト値が使われる。デフォルトの設定は `vlsettuning(49, 192, 1024, 512)' に相当する。性能調整のパラメータはデータベースに保存されないので、データベースを開く度に指定する必要がある。</dd>
+</dl>
+
+<p>データベースのフリーブロックプールのサイズ設定するには、関数 `vlsetfbpsiz' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlsetfbpsiz(VILLA *<var>villa</var>, int <var>size</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。`size' はフリーブロックプールのサイズを指定する。戻り値は正常なら真であり、エラーなら偽である。フリーブロックプールのデフォルトのサイズは256である。サイズをより大きくすると、レコードの上書きを繰り返す際の空間効率は上がるが、時間効率が下がる。</dd>
+</dl>
+
+<p>データベースを更新した内容をファイルとデバイスに同期させるには、関数 `vlsync' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlsync(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。この関数はデータベースを閉じないうちに別プロセスにデータベースファイルを利用させる場合に役立つ。トランザクションが有効な間はこの関数を使用すべきではない。</dd>
+</dl>
+
+<p>データベースを最適化するには、関数 `vloptimize' を用いる。</p>
+
+<dl>
+<dt><kbd>int vloptimize(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。レコードを削除したり、置換モードや連結モードで書き込みを繰り返したりする場合は、データベース内に不要な領域が蓄積するが、この関数はそれを解消するのに役立つ。トランザクションが有効な間はこの関数を使用すべきではない。</dd>
+</dl>
+
+<p>データベースの名前を得るには、関数 `vlname' を用いる。</p>
+
+<dl>
+<dt><kbd>char *vlname(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は正常なら名前を格納した領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>データベースファイルのサイズを得るには、関数 `vlfsiz' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlfsiz(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は正常ならデータベースファイルのサイズであり、エラーなら -1 である。I/Oバッファにより、戻り値は実際のサイズより小さくなる場合がある。</dd>
+</dl>
+
+<p>B+木のリーフノードの数を得るには、関数 `vllnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int vllnum(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は正常ならリーフノードの数であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>B+木の非リーフノードの数を得るには、関数 `vlnnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlnnum(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は正常なら非リーフノードの数であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>データベースのレコード数を得るには、関数 `vlrnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlrnum(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は正常ならデータベースのレコード数であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>データベースハンドルがライタかどうかを調べるには、関数 `vlwritable' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlwritable(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値はライタなら真であり、そうでなければ偽である。</dd>
+</dl>
+
+<p>データベースに致命的エラーが起きたかどうかを調べるには、関数 `vlfatalerror' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlfatalerror(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値は致命的エラーがあれば真であり、そうでなければ偽である。</dd>
+</dl>
+
+<p>データベースファイルのinode番号を得るには、関数 `vlinode' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlinode(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値はデータベースファイルのinode番号である。</dd>
+</dl>
+
+<p>データベースの最終更新時刻を得るには、関数 `vlmtime' を用いる。</p>
+
+<dl>
+<dt><kbd>time_t vlmtime(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。戻り値はデータベースの最終更新時刻である。</dd>
+</dl>
+
+<p>トランザクションを開始するには、関数 `vltranbegin' を用いる。</p>
+
+<dl>
+<dt><kbd>int vltranbegin(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。この関数はマルチスレッドでの相互排他制御を行わないので、アプリケーションがその責任を負う。一つのデータベースハンドルで同時に有効にできるトランザクションは一つだけである。</dd>
+</dl>
+
+<p>トランザクションをコミットするには、関数 `vltrancommit' を用いる。</p>
+
+<dl>
+<dt><kbd>int vltrancommit(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。トランザクションの中でのデータベースの更新はコミットが成功した時に確定する。</dd>
+</dl>
+
+<p>トランザクションを破棄するには、関数 `vltranabort' を用いる。</p>
+
+<dl>
+<dt><kbd>int vltranabort(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。トランザクションの中でのデータベースの更新はアボートした時には破棄される。データベースの状態はトランザクションの前の状態にロールバックされる。</dd>
+</dl>
+
+<p>データベースファイルを削除するには、関数 `vlremove' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlremove(const char *<var>name</var>);</kbd></dt>
+<dd>`name' はデータベースファイルの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>壊れたデータベースファイルを修復するには、関数 `vlrepair' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlrepair(const char *<var>name</var>, VLCFUNC <var>cmp</var>);</kbd></dt>
+<dd>`name' はデータベースファイルの名前を指定する。`cmp' はデータベースファイルの比較関数を指定する。戻り値は正常なら真であり、エラーなら偽である。修復されたデータベースの全レコードが元来もしくは期待される状態に戻る保証はない。</dd>
+</dl>
+
+<p>全てのレコードをエンディアン非依存のデータとしてダンプするには、関数 `vlexportdb' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlexportdb(VILLA *<var>villa</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`villa' はデータベースハンドルを指定する。`name' は出力ファイルの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>エンディアン非依存データから全てのレコードをロードするには、関数 `vlimportdb' を用いる。</p>
+
+<dl>
+<dt><kbd>int vlimportdb(VILLA *<var>villa</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`villa' はライタで接続したデータベースハンドルを指定する。データベースは空でなければならない。`name' は入力ファイルの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<h3>サンプルコード</h3>
+
+<p>名前と対応させて電話番号を格納し、それを検索するアプリケーションのサンプルコードを以下に示す。</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NAME     "mikio"
+#define NUMBER   "000-1234-5678"
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  VILLA *villa;
+  char *val;
+
+  /* データベースを開く */
+  if(!(villa = vlopen(DBNAME, VL_OWRITER | VL_OCREAT, VL_CMPLEX))){
+    fprintf(stderr, "vlopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* レコードを格納する */
+  if(!vlput(villa, NAME, -1, NUMBER, -1, VL_DOVER)){
+    fprintf(stderr, "vlput: %s\n", dperrmsg(dpecode));
+  }
+
+  /* レコードを取得する */
+  if(!(val = vlget(villa, NAME, -1, NULL))){
+    fprintf(stderr, "vlget: %s\n", dperrmsg(dpecode));
+  } else {
+    printf("Name: %s\n", NAME);
+    printf("Number: %s\n", val);
+    free(val);
+  }
+
+  /* データベースを閉じる */
+  if(!vlclose(villa)){
+    fprintf(stderr, "vlclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<p>文字列の前方一致検索を行うアプリケーションのサンプルコードを以下に示す。</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define DBNAME   "words"
+#define PREFIX   "apple"
+
+int main(int argc, char **argv){
+  VILLA *villa;
+  char *key, *val;
+
+  /* データベースを開く */
+  if(!(villa = vlopen(DBNAME, VL_OWRITER | VL_OCREAT, VL_CMPLEX))){
+    fprintf(stderr, "vlopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* レコードを格納する */
+  if(!vlput(villa, "applet", -1, "little application", -1, VL_DDUP) ||
+     !vlput(villa, "aurora", -1, "polar wonderwork", -1, VL_DDUP) ||
+     !vlput(villa, "apple", -1, "delicious fruit", -1, VL_DDUP) ||
+     !vlput(villa, "amigo", -1, "good friend", -1, VL_DDUP) ||
+     !vlput(villa, "apple", -1, "big city", -1, VL_DDUP)){
+    fprintf(stderr, "vlput: %s\n", dperrmsg(dpecode));
+  }
+
+  /* カーソルを候補の先頭に置く */
+  vlcurjump(villa, PREFIX, -1, VL_JFORWARD);
+
+  /* カーソルを走査する */
+  while((key = vlcurkey(villa, NULL)) != NULL){
+    if(strstr(key, PREFIX) != key){
+      free(key);
+      break;
+    }
+    if(!(val = vlcurval(villa, NULL))){
+      fprintf(stderr, "vlcurval: %s\n", dperrmsg(dpecode));
+      free(key);
+      break;
+    }
+    printf("%s: %s\n", key, val);
+    free(val);
+    free(key);
+    vlcurnext(villa);
+  }
+
+  /* データベースを閉じる */
+  if(!vlclose(villa)){
+    fprintf(stderr, "vlclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<h3>注記</h3>
+
+<p>Villaを利用したプログラムをビルドする方法は、Depotの場合と全く同じである。</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>POSIXスレッドを有効にしてQDBMをビルドした場合、外部変数 `dpecode' はスレッド固有データへの参照として扱われ、Villaの各関数はリエントラントになる。その場合、スレッド間で同時に同じハンドルにアクセスしない限りは、各関数はスレッドセーフである。ただし、`errno' や `malloc' 等がスレッドセーフな処理系であることが前提となる。</p>
+
+<h3>Vista: 拡張上級API</h3>
+
+<p>VistaはVillaを拡張したAPIである。Villaが2GB以上のファイルを扱うことができないという欠点を補うために、VistaではDepotではなくCuriaを用いて内部データベースを管理する。VistaはVillaと同じくB+木のデータ構造とその操作を提供するが、そのデータベースはディレクトリで実現される。</p>
+
+<p>Vistaを使うには、`villa.h' の代わりに `vista.h' をインクルードすればよい。VistaはVillaのシンボルをマクロでオーバーライドして実装されているため、Villaと全く同様のAPIで利用することができる。すなわち、双方のシグネチャは全く同じである。ただし、その副作用として、Vistaを使うモジュール(コンパイルユニット)はVillaを利用することができない(`villa.h' をインクルードしてはならない)。</p>
+
+<hr />
+
+<h2><a name="villacli" id="villacli" class="head">Villa用コマンド</a></h2>
+
+<p>Villaに対応するコマンドラインインタフェースは以下のものである。</p>
+
+<p>コマンド `vlmgr' はVillaやそのアプリケーションのデバッグに役立つツールである。データベースを更新したり、データベースの状態を調べたりする機能を持つ。シェルスクリプトでデータベースアプリケーションを作るのにも利用できる。以下の書式で用いる。`name' はデータベース名、`key' はレコードのキー、`val' はレコードの値を指定する。</p>
+
+<dl>
+<dt><kbd>vlmgr create [-cz|-cy|-cx] <var>name</var></kbd></dt>
+<dd>データベースファイルを作成する。</dd>
+<dt><kbd>vlmgr put [-kx|-ki] [-vx|-vi|-vf] [-keep|-cat|-dup] <var>name</var> <var>key</var> <var>val</var></kbd></dt>
+<dd>キーと値に対応するレコードを追加する。</dd>
+<dt><kbd>vlmgr out [-l] [-kx|-ki] <var>name</var> <var>key</var></kbd></dt>
+<dd>キーに対応するレコードを削除する。</dd>
+<dt><kbd>vlmgr get [-nl] [-l] [-kx|-ki] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>キーに対応するレコードの値を取得して標準出力する。</dd>
+<dt><kbd>vlmgr list [-nl] [-k|-v] [-kx|-ki] [-ox] [-top <var>key</var>] [-bot <var>key</var>] [-gt] [-lt] [-max <var>num</var>] [-desc] <var>name</var></kbd></dt>
+<dd>データベース内の全てのレコードのキーと値をタブと改行で区切って標準出力する。</dd>
+<dt><kbd>vlmgr optimize <var>name</var></kbd></dt>
+<dd>データベースを最適化する。</dd>
+<dt><kbd>vlmgr inform [-nl] <var>name</var></kbd></dt>
+<dd>データベースの雑多な情報を出力する。</dd>
+<dt><kbd>vlmgr remove <var>name</var></kbd></dt>
+<dd>データベースファイルを削除する。</dd>
+<dt><kbd>vlmgr repair [-ki] <var>name</var></kbd></dt>
+<dd>壊れたデータベースファイルを修復する。</dd>
+<dt><kbd>vlmgr exportdb [-ki] <var>name</var> <var>file</var></kbd></dt>
+<dd>全てのレコードをエンディアン非依存のデータとしてダンプする。</dd>
+<dt><kbd>vlmgr importdb [-ki] <var>name</var> <var>file</var></kbd></dt>
+<dd>エンディアン非依存データから全てのレコードをロードする。</dd>
+<dt><kbd>vlmgr version</kbd></dt>
+<dd>QDBMのバージョン情報を標準出力する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-cz</kbd> : データベースのリーフをZLIBで圧縮する。</li>
+<li><kbd>-cy</kbd> : データベースのリーフをLZOで圧縮する。</li>
+<li><kbd>-cx</kbd> : データベースのリーフをBZIP2で圧縮する。</li>
+<li><kbd>-l</kbd> : キーに一致する全てのレコードを処理対象とする。</li>
+<li><kbd>-kx</kbd> : 2桁単位の16進数によるバイナリ表現として `key' を扱う。</li>
+<li><kbd>-ki</kbd> : 10進数による数値表現として `key' を扱う。</li>
+<li><kbd>-vx</kbd> : 2桁単位の16進数によるバイナリ表現として `val' を扱う。</li>
+<li><kbd>-vi</kbd> : 10進数による数値表現として `val' を扱う。</li>
+<li><kbd>-vf</kbd> : 名前が `val' のファイルのデータを値として読み込む。</li>
+<li><kbd>-keep</kbd> : 既存のレコードとキーが重複時に上書きせずにエラーにする。</li>
+<li><kbd>-cat</kbd> : 既存のレコードとキーが重複時に値を末尾に追加する。</li>
+<li><kbd>-dup</kbd> : レコードのキーが重複するのを許す。</li>
+<li><kbd>-nl</kbd> : ファイルロックをかけずにデータベースを開く。</li>
+<li><kbd>-top <var>key</var></kbd> : リストの最小のキーを指定する。</li>
+<li><kbd>-bot <var>key</var></kbd> : リストの最大のキーを指定する。</li>
+<li><kbd>-gt</kbd> : 最小のキーをリストに含めない。</li>
+<li><kbd>-lt</kbd> : 最大のキーをリストに含めない。</li>
+<li><kbd>-max <var>num</var></kbd> : リストする最大数を指定する。</li>
+<li><kbd>-desc</kbd> : リストを降順で行う。</li>
+<li><kbd>-ox</kbd> : 2桁単位の16進数によるバイナリ表現として標準出力を行う。</li>
+<li><kbd>-n</kbd> : 標準出力の末尾に付加される改行文字の出力を抑制する。</li>
+<li><kbd>-k</kbd> : キーのみを出力する。</li>
+<li><kbd>-v</kbd> : 値のみを出力する。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。環境変数 `QDBMDBGFD' の値として、変数 `dpecode' の変更履歴を出力するファイルディスクリプタを指定ことができる。</p>
+
+<p>コマンド `vltest' はVillaの機能テストや性能テストに用いるツールである。このコマンドによって生成されたデータベースファイルを `vlmgr' によって解析したり、`time' コマンドによってこのコマンドの実行時間を計ったりするとよい。以下の書式で用いる。`name' はデータベース名、`rnum' はレコード数、`pnum' はキーのパターン数を指定する。</p>
+
+<dl>
+<dt><kbd>vltest write [-int] [-cz|-cy|-cx] [-tune <var>lrecmax</var> <var>nidxmax</var> <var>lcnum</var> <var>ncnum</var>] [-fbp <var>num</var>] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>`00000001'、`00000002' のように変化する8バイトのキーと適当な8バイトの値を連続してデータベースに追加する。</dd>
+<dt><kbd>vltest read [-int] [-vc] <var>name</var></kbd></dt>
+<dd>上記で生成したデータベースの全レコードを検索する。</dd>
+<dt><kbd>vltest rdup [-int] [-cz|-cy|-cx] [-cc] [-tune <var>lrecmax</var> <var>nidxmax</var> <var>lcnum</var> <var>ncnum</var>] [-fbp <var>num</var>] <var>name</var> <var>rnum</var> <var>pnum</var></kbd></dt>
+<dd>キーがある程度重複するようにレコードの追加を行い、重複モードで処理する。</dd>
+<dt><kbd>vltest combo [-cz|-cy|-cx] <var>name</var></kbd></dt>
+<dd>各種操作の組み合わせテストを行う。</dd>
+<dt><kbd>vltest wicked [-cz|-cy|-cx] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>各種更新操作を無作為に選択して実行する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-int</kbd> : `int' 型のオブジェクトをキーと値に用い、比較関数もそれに合わせる。</li>
+<li><kbd>-cz</kbd> : データベースのリーフをZLIBで圧縮する。</li>
+<li><kbd>-cy</kbd> : データベースのリーフをLZOで圧縮する。</li>
+<li><kbd>-cx</kbd> : データベースのリーフをBZIP2で圧縮する。</li>
+<li><kbd>-vc</kbd> : 揮発性キャッシュを参照する。</li>
+<li><kbd>-cc</kbd> : 連結モードと重複モードを無作為に選択する。</li>
+<li><kbd>-tune <var>lrecmax</var> <var>nidxmax</var> <var>lcnum</var> <var>ncnum</var></kbd> : 性能パラメータを指定する。</li>
+<li><kbd>-fbp <var>num</var></kbd> : フリーブロックプールのサイズを指定する。</li>
+<li><kbd>-c</kbd> : Cabinのマップを使って比較テストを行う。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。環境変数 `QDBMDBGFD' の値として、変数 `dpecode' の変更履歴を出力するファイルディスクリプタを指定ことができる。</p>
+
+<p>コマンド `vltsv' はタブ区切りでキーと値を表現した行からなるTSVファイルとVillaのデータベースを相互変換する。このコマンドは、QDBMの他のバージョンや他のDBMとの間でデータの交換を行う際に役立つ。また、バイトオーダの違うシステムの間でデータを交換する際にも役立つ。以下の書式で用いる。`name' はデータベース名を指定する。`export' サブコマンドではTSVのデータは標準入力から読み込む。`import' サブコマンドではTSVのデータが標準出力に書き出される。</p>
+
+<dl>
+<dt><kbd>vltsv import [-bin] <var>name</var></kbd></dt>
+<dd>TSVファイルを読み込んでデータベースを作成する。</dd>
+<dt><kbd>vltsv export [-bin] <var>name</var></kbd></dt>
+<dd>データベースの全てのレコードをTSVファイルとして出力する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-bin</kbd> : Base64形式でレコードを扱う。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<p>Villaのコマンド群を駆使すると、簡単なデータベースシステムが構築できる。例えば `/etc/password' をユーザ名で検索するためのデータベースを作成するには、以下のようにする。</p>
+
+<pre>cat /etc/passwd | tr ':' '\t' | vltsv import casket
+</pre>
+
+<p>そして、`mikio' というユーザの情報を取り出すには、以下のようにする。</p>
+
+<pre>vlmgr get casket mikio
+</pre>
+
+<p>これらのコマンドと同等の機能をVillaのAPIを用いて実装することも容易である。</p>
+
+<p>コマンド `qmttest' はDepotとCuriaとVillaのマルチスレッドにおける安全性を調査する。このコマンドはPOSIXスレッドを有効にしてQDBMをビルドした場合にのみマルチスレッドで動作する。以下の書式で用いる。`name' はデータベース名の接頭辞を指定する。`rnum' は各データベースに書き込むレコード数を指定する。`tnum' はスレッド数を指定する。</p>
+
+<dl>
+<dt><kbd>qmttest <var>name</var> <var>rnum</var> <var>tnum</var></kbd></dt>
+<dd>マルチスレッドにおける安全性を調査する。</dd>
+</dl>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<hr />
+
+<h2><a name="odeumapi" id="odeumapi" class="head">Odeum: 転置API</a></h2>
+
+<h3>概要</h3>
+
+<p>Odeumは転置インデックスを扱うAPIである。転置インデックスとは、母集団の文書群に含まれる語を抽出して、各語をキーとし、その語を含む文書のリストを検索するためのデータ構造である。転置インデックスを用いると、全文検索システムを容易に実現することができる。Odeumは文書を語や属性の集合として扱うための抽象データ型を提供する。それは、各アプリケーションがOdeumのデータベースに文書を格納する際や、データベースから文書を検索する際に用いられる。</p>
+
+<p>Odeumは元来の文書データからテキストを抽出する方法は提供しない。それはアプリケーションが実装する必要がある。Odeumはテキストを分解して語群を抽出するユーティリティを提供するが、それは空白で語が分割される英語などの言語を指向している。形態素解析やN-gram解析が必要な日本語などの言語を扱う際や、ステミングなどのより高度な自然言語処理を行う際には、アプリケーションは独自の解析方法を適用することができる。検索結果は文書のID番号とそのスコアをメンバに持つ構造体を要素とする配列として得られる。複数の語を用いて検索を行うために、Odeumは結果の配列の集合演算を行うユーティリティを提供する。</p>
+
+<p>OdeumはCuriaとCabinとVillaを基盤として実装される。Odeumではディレクトリ名を指定してデータベースを構築する。特定のディレクトリの直下にCuriaやVillaのデータベースを構築する。例えば、`casket' という名前のデータベースを作成する場合、`casket/docs'、`casket/index' および `casket/rdocs' が生成される。`docs' はCuriaのデータベースディレクトリである。キーは文書のID番号であり、値はURI等の文書属性である。`index' はCuriaのデータベースディレクトリである。キーは語の正規形であり、値はその語を含む文書のID番号とそのスコアを要素とする配列である。`rdocs' はVillaのデータベースファイルである。キーは文書のURIであり、値は文書のID番号である。</p>
+
+<p>Odeumを使うためには、`depot.h' と `cabin.h' と `odeum.h' と `stdlib.h' をインクルードすべきである。通常、ソースファイルの冒頭付近で以下の記述を行う。</p>
+
+<dl>
+<dt><kbd>#include <depot.h></kbd></dt>
+<dt><kbd>#include <cabin.h></kbd></dt>
+<dt><kbd>#include <odeum.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+</dl>
+
+<p>Odeumでデータベースを扱う際には、`ODEUM' 型へのポインタをハンドルとして用いる。ハンドルは、関数 `odopen' で開き、関数 `odclose' で閉じる。ハンドルのメンバを直接参照することは推奨されない。データベースに致命的なエラーが起きた場合は、以後そのハンドルに対する `odclose' を除く全ての操作は何もせずにエラーを返す。ひとつのプロセスで複数のデータベースファイルを同時に利用することは可能であるが、同じデータベースファイルの複数のハンドルを利用してはならない。</p>
+
+<p>各文書を扱う際には、`ODDOC' 型へのポインタをハンドルとして用いる。ハンドルは、関数 `oddocopen' で開き、関数 `oddocclose' で閉じる。ハンドルのメンバを直接参照することは推奨されない。文書は属性と語の集合からなる。語は正規形と出現形のペアとして表現される。</p>
+
+<p>OdeumでもDepotと同じく外部変数 `dpecode' に直前のエラーコードが記録される。エラーコードに対応するメッセージ文字列を得るには、関数 `dperrmsg' を用いる。</p>
+
+<h3>API</h3>
+
+<p>検索結果を扱うためには、`ODPAIR' 型の構造体を用いる。</p>
+
+<dl>
+<dt><kbd>typedef struct { int id; int score; } ODPAIR;</kbd></dt>
+<dd>`id' は文書のID番号である。`score' は文書に含まれる検索語の数を元に算出されるスコアである。</dd>
+</dl>
+
+<p>データベースのハンドルを作成するには、関数 `odopen' を用いる。</p>
+
+<dl>
+<dt><kbd>ODEUM *odopen(const char *<var>name</var>, int <var>omode</var>);</kbd></dt>
+<dd>`name' はデータベースディレクトリの名前を指定する。`omode' は接続モードを指定し、`OD_OREADER' ならリーダ、`OD_OWRITER' ならライタとなる。`OD_OWRITER' の場合、`OD_OCREAT' または `OD_OTRUNC' とのビット論理和にすることができる。`OD_OCREAT' はファイルが無い場合に新規作成することを指示し、`OD_OTRUNC' はファイルが存在しても作り直すことを指示する。`OD_OREADER' と `OD_OWRITER' の両方で `OD_ONOLCK' か `OD_OLCKNB' とのビット論理和にすることができるが、前者はファイルロックをかけずにデータベースを開くことを指示し、後者はブロックせずにロックをかけることを指示する。戻り値はデータベースハンドルであるか、エラーなら `NULL' である。ライタ(読み書き両用モード)でデータベースファイルを開く際にはそのファイルに対して排他ロãƒ
 ƒã‚¯ãŒã‹ã‘られ、リーダ(読み込み専用モード)で開く際には共有ロックがかけられる。その際には該当のロックがかけられるまで制御がブロックする。`OD_ONOLCK' を使う場合、アプリケーションが排他制御の責任を負う。</dd>
+</dl>
+
+<p>データベースとの接続を閉じてハンドルを破棄するには、関数 `odclose' を用いる。</p>
+
+<dl>
+<dt><kbd>int odclose(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。データベースの更新内容は、接続を閉じた時点で初めてファイルと同期される。ライタでデータベースを開いた場合、適切に接続を閉じないとデータベースが破壊される。閉じたハンドルの領域は解放されるので、以後は利用できなくなる。</dd>
+</dl>
+
+<p>文書を追加するには、関数 `odput' を用いる。</p>
+
+<dl>
+<dt><kbd>int odput(ODEUM *<var>odeum</var>, const ODDOC *<var>doc</var>, int <var>wmax</var>, int <var>over</var>);</kbd></dt>
+<dd>`odeum' はライタで接続したデータベースハンドルを指定する。`doc' は文書ハンドルを指定する。`wmax' は文書データベースに格納する語の最大数を指定するが、負数なら無制限となる。`over' は重複した文書の上書きを行うか否かを指定する。それが偽で文書のURIが重複した場合はエラーとなる。戻り値は正常なら真であり、エラーなら偽である。</dd>
+</dl>
+
+<p>URIで指定した文書を削除するには、関数 `odout' を用いる。</p>
+
+<dl>
+<dt><kbd>int odout(ODEUM *<var>odeum</var>, const char *<var>uri</var>);</kbd></dt>
+<dd>`odeum' はライタで接続したデータベースハンドルを指定する。`uri' は文書のURIの文字列を指定する。戻り値は正常なら真であり、エラーなら偽である。該当する文書がない場合も偽を返す。</dd>
+</dl>
+
+<p>ID番号で指定した文書を削除するには、関数 `odoutbyid' を用いる。</p>
+
+<dl>
+<dt><kbd>int odoutbyid(ODEUM *<var>odeum</var>, int <var>id</var>);</kbd></dt>
+<dd>`odeum' はライタで接続したデータベースハンドルを指定する。`id' は文書のID番号を指定する。戻り値は正常なら真であり、エラーなら偽である。該当する文書がない場合も偽を返す。</dd>
+</dl>
+
+<p>URIで指定した文書を取得するには、関数 `odget' を用いる。</p>
+
+<dl>
+<dt><kbd>ODDOC *odget(ODEUM *<var>odeum</var>, const char *<var>uri</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。`uri' は文書のURIの文字列を指定する。戻り値は正常なら該当の文書のハンドルであり、エラーなら `NULL' である。該当の文書がない場合も `NULL' を返す。戻り値のハンドルは、関数 `oddocopen' で開かれるので、不要になったら `oddocclose' で閉じるべきである。</dd>
+</dl>
+
+<p>ID番号で指定した文書を取得するには、関数 `odgetbyid' を用いる。</p>
+
+<dl>
+<dt><kbd>ODDOC *odgetbyid(ODEUM *<var>odeum</var>, int <var>id</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。`id' は文書のID番号を指定する。戻り値は正常なら該当の文書のハンドルであり、エラーなら `NULL' である。該当の文書がない場合も `NULL' を返す。戻り値のハンドルは、関数 `oddocopen' で開かれるので、不要になったら `oddocclose' で閉じるべきである。</dd>
+</dl>
+
+<p>URIで指定した文書のIDを取得するには、関数 `odgetidbyuri' を用いる。</p>
+
+<dl>
+<dt><kbd>int odgetidbyuri(ODEUM *<var>odeum</var>, const char *<var>uri</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。`uri' は文書のURIの文字列を指定する。戻り値は正常なら該当の文書のIDであり、エラーなら -1 である。該当の文書がない場合も -1 を返す。</dd>
+</dl>
+
+<p>ID番号で指定した文書が存在しているか調べるには、関数 `odcheck' を用いる。</p>
+
+<dl>
+<dt><kbd>int odcheck(ODEUM *<var>odeum</var>, int <var>id</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。`id' は文書のID番号を指定する。戻り値は文書が存在すれば真、そうでなければ偽である。</dd>
+</dl>
+
+<p>転置インデックスを検索して特定の語を含む文書群を知るには、関数 `odsearch' を用いる。</p>
+
+<dl>
+<dt><kbd>ODPAIR *odsearch(ODEUM *<var>odeum</var>, const char *<var>word</var>, int <var>max</var>, int *<var>np</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。`word' は検索語を指定する。`max' は取り出す文書の最大数を指定する。`np' の参照先には、戻り値の配列の要素数が格納される。戻り値は正常なら配列へのポインタであり、エラーなら `NULL' である。その配列の各要素は文書のID番号とスコアのペアであり、スコアの降順で並べられる。検索語に該当する文書が一つもなかったとしてもエラーにはならずに、空の配列を返す。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。配列の各要素には既に削除された文書のデータも含まれることに注意すべきである。</dd>
+</dl>
+
+<p>特定の語を含む文書の数を知るには、関数 `odsearchdnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int odsearchdnum(ODEUM *<var>odeum</var>, const char *<var>word</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。`word' は検索語を指定する。戻り値は正常なら検索語を含む文書の数であり、該当がない場合やエラーの場合は -1 である。この関数は転置インデックスの実データを読み込まないので効率がよい。</dd>
+</dl>
+
+<p>データベースのイテレータを初期化するには、関数 `oditerinit' を用いる。</p>
+
+<dl>
+<dt><kbd>int oditerinit(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。イテレータは、データベースに格納された全ての文書を参照するために用いられる。</dd>
+</dl>
+
+<p>データベースのイテレータから次の文書を取り出すには、関数 `oditernext' を用いる。</p>
+
+<dl>
+<dt><kbd>ODDOC *oditernext(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値は正常なら文書ハンドルであり、エラーなら `NULL' である。イテレータが最後まできて該当の文書がない場合も `NULL' を返す。この関数を繰り返して呼ぶことによって全ての文書を一度ずつ参照することができる。ただし、繰り返しの間にデータベースの更新があった場合はその限りではない。なお、取り出すレコードの順序は制御できず、格納した順番でレコードを取り出せるとは限らない。戻り値のハンドルは、関数 `oddocopen' で開かれるので、不要になったら `oddocclose' で閉じるべきである。</dd>
+</dl>
+
+<p>データベースを更新した内容をファイルとデバイスに同期させるには、関数 `odsync' を用いる。</p>
+
+<dl>
+<dt><kbd>int odsync(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。この関数はデータベースを閉じないうちに別プロセスにデータベースディレクトリを利用させる場合に役立つ。</dd>
+</dl>
+
+<p>データベースを最適化するには、関数 `odoptimize' を用いる。</p>
+
+<dl>
+<dt><kbd>int odoptimize(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はライタで接続したデータベースハンドルを指定する。戻り値は正常なら真であり、エラーなら偽である。転置インデックスにおける削除された文書の要素は削除される。</dd>
+</dl>
+
+<p>データベースの名前を得るには、関数 `odname' を用いる。</p>
+
+<dl>
+<dt><kbd>char *odname(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値は正常なら名前を格納した領域へのポインタであり、エラーなら `NULL' である。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>データベースファイルのサイズの合計を得るには、関数 `odfsiz' を用いる。</p>
+
+<dl>
+<dt><kbd>double odfsiz(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値は正常ならデータベースファイルのサイズの合計であり、エラーなら -1.0 である。</dd>
+</dl>
+
+<p>転置インデックス内のバケット配列の要素数の合計を得るには、関数 `odbnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int odbnum(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値は正常ならバケット配列の要素数の合計であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>転置インデックス内のバケット配列の利用済みの要素数の合計を得るには、関数 `odbusenum' を用いる。</p>
+
+<dl>
+<dt><kbd>int odbusenum(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値は正常ならバケット配列の利用済みの要素数の合計であり、エラーなら -1 である。</dd>
+</dl>
+
+
+<p>データベースに格納された文書数を得るには、関数 `oddnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int oddnum(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値は正常ならデータベースに格納された文書の数であり、エラーなら -1 である。</dd>
+</dl>
+
+<p>データベースに格納された単語数を得るには、関数 `odwnum' を用いる。</p>
+
+<dl>
+<dt><kbd>int odwnum(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値は正常ならデータベースに格納された語の数であり、エラーなら -1 である。I/Oバッファにより、戻り値は実際のサイズより小さくなる場合がある。</dd>
+</dl>
+
+<p>データベースハンドルがライタかどうかを調べるには、関数 `odwritable' を用いる。</p>
+
+<dl>
+<dt><kbd>int odwritable(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値はライタなら真であり、そうでなければ偽である。</dd>
+</dl>
+
+<p>データベースに致命的エラーが起きたかどうかを調べるには、関数 `odfatalerror' を用いる。</p>
+
+<dl>
+<dt><kbd>int odfatalerror(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値は致命的エラーがあれば真であり、そうでなければ偽である。</dd>
+</dl>
+
+<p>データベースディレクトリのinode番号を得るには、関数 `odinode' を用いる。</p>
+
+<dl>
+<dt><kbd>int odinode(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値はデータベースディレクトリのinode番号である。</dd>
+</dl>
+
+<p>データベースの最終更新時刻を得るには、関数 `odmtime' を用いる。</p>
+
+<dl>
+<dt><kbd>time_t odmtime(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。戻り値はデータベースの最終更新時刻である。</dd>
+</dl>
+
+<p>複数のデータベースをマージするには、関数 `odmerge' を用いる。</p>
+
+<dl>
+<dt><kbd>int odmerge(const char *<var>name</var>, const CBLIST *<var>elemnames</var>);</kbd></dt>
+<dd>`name' は作成するデータベースディレクトリの名前を指定する。`elemnames' は要素データベースの名前のリストを指定する。戻り値は正常なら真であり、エラーなら偽である。同じURLを持つ複数の文書が現れた場合、最初に現れたものが採用され、残りは無視される。</dd>
+</dl>
+
+<p>データベースディレクトリを削除するには、関数 `odremove' を用いる。</p>
+
+<dl>
+<dt><kbd>int odremove(const char *<var>name</var>);</kbd></dt>
+<dd>`name' はデータベースディレクトリの名前を指定する。戻り値は正常なら真であり、エラーなら偽である。データベースディレクトリの中にはQDBMの他のAPIによるデータベースを置くことができるが、それらもこの関数によって削除される。</dd>
+</dl>
+
+<p>文書ハンドルを作成するには、関数 `oddocopen' を用いる。</p>
+
+<dl>
+<dt><kbd>ODDOC *oddocopen(const char *<var>uri</var>);</kbd></dt>
+<dd>`uri' は文書のURIを指定する。戻り値は文書ハンドルである。新しい文書のID番号は定義されない。それはデータベースに格納した際に定義される。</dd>
+</dl>
+
+<p>文書ハンドルを破棄するには、関数 `oddocclose' を用いる。</p>
+
+<dl>
+<dt><kbd>void oddocclose(ODDOC *<var>doc</var>);</kbd></dt>
+<dd>`doc' は文書ハンドルを指定する。閉じたハンドルの領域は解放されるので、以後は利用できなくなる。</dd>
+</dl>
+
+<p>文書に属性を追加するには、関数 `oddocaddattr' を用いる。</p>
+
+<dl>
+<dt><kbd>void oddocaddattr(ODDOC *<var>doc</var>, const char *<var>name</var>, const char *<var>value</var>);</kbd></dt>
+<dd>`doc' は文書ハンドルを指定する。`name' は属性名の文字列を指定する。`value' は属性値の文字列を指定する。</dd>
+</dl>
+
+<p>文書に語を追加するには、関数 `oddocaddword' を用いる。</p>
+
+<dl>
+<dt><kbd>void oddocaddword(ODDOC *<var>doc</var>, const char *<var>normal</var>, const char *<var>asis</var>);</kbd></dt>
+<dd>`doc' は文書ハンドルを指定する。`normal' は語の正規形の文字列を指定する。正規形は転置インデックスのキーとして扱われる。正規形が空文字列の場合、その語は転置インデックスに反映されない。`asis' は語の出現形の文字列を指定する。出現形はアプリケーションが文書を取得した際に利用される。</dd>
+</dl>
+
+<p>文書のIDを得るには、関数 `oddocid' を用いる。</p>
+
+<dl>
+<dt><kbd>int oddocid(const ODDOC *<var>doc</var>);</kbd></dt>
+<dd>`doc' は文書ハンドルを指定する。戻り値は文書のID番号である。</dd>
+</dl>
+
+<p>文書のURIを得るには、関数 `oddocuri' を用いる。</p>
+
+<dl>
+<dt><kbd>const char *oddocuri(const ODDOC *<var>doc</var>);</kbd></dt>
+<dd>`doc' は文書ハンドルを指定する。戻り値は文書のURIの文字列である。</dd>
+</dl>
+
+<p>文書の属性値を得るには、関数 `oddocgetattr' を用いる。</p>
+
+<dl>
+<dt><kbd>const char *oddocgetattr(const ODDOC *<var>doc</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`doc' は文書ハンドルを指定する。`name' は属性名の文字列を指定する。戻り値は属性値の文字列であるか、該当がなければ `NULL' である。</dd>
+</dl>
+
+<p>文書内の語群の正規形のリストを得るには、関数 `oddocnwords' を用いる。</p>
+
+<dl>
+<dt><kbd>const CBLIST *oddocnwords(const ODDOC *<var>doc</var>);</kbd></dt>
+<dd>`doc' は文書ハンドルを指定する。戻り値は正規形の語群を格納したリストハンドルである。</dd>
+</dl>
+
+<p>文書内の語群の出現形のリストを得るには、関数 `oddocawords' を用いる。</p>
+
+<dl>
+<dt><kbd>const CBLIST *oddocawords(const ODDOC *<var>doc</var>);</kbd></dt>
+<dd>`doc' は文書ハンドルを指定する。戻り値は出現形の語群を格納したリストハンドルである。</dd>
+</dl>
+
+<p>文書のキーワードの正規形とそのスコアのマップを得るには、関数 `oddocscores' を用いる。</p>
+
+<dl>
+<dt><kbd>CBMAP *oddocscores(const ODDOC *<var>doc</var>, int <var>max</var>, ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`doc' は文書ハンドルを指定する。`max' は取得するキーワードの最大数を指定する。`odeum' が `NULL' でなければ、それを用いて重みづけのためのIDFが算出される。戻り値はキーワードとそのスコアを格納したマップハンドルである。スコアは10進数の文字列で表現される。戻り値のハンドルは関数 `cbmapopen' で開かれるので、不要になったら `cbmapclose' で閉じるべきである。</dd>
+</dl>
+
+<p>テキストを分解して語群の出現形のリストを得るには、関数 `odbreaktext' を用いる。</p>
+
+<dl>
+<dt><kbd>CBLIST *odbreaktext(const char *<var>text</var>);</kbd></dt>
+<dd>`text' はテキストの文字列を指定する。戻り値は語群の出現形のリストハンドルである。語群は空白文字とピリオドやコンマ等の区切り文字で分割される。戻り値のハンドルは関数 `cblistopen' で開かれるので、不要になったら `cblistclose' で閉じるべきである。</dd>
+</dl>
+
+<p>語の正規形を取得するには、関数 `odnormalizeword' を用いる。</p>
+
+<dl>
+<dt><kbd>char *odnormalizeword(const char *<var>asis</var>);</kbd></dt>
+<dd>`asis' は語の出現形の文字列を指定する。戻り値は語の正規形の文字列である。ASCIIコードのアルファベットは小文字に統一される。区切り文字のみからなる文字は空文字列として扱われる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>二つの文書集合からその共通集合を得るには、関数 `odpairsand' を用いる。</p>
+
+<dl>
+<dt><kbd>ODPAIR *odpairsand(ODPAIR *<var>apairs</var>, int <var>anum</var>, ODPAIR *<var>bpairs</var>, int <var>bnum</var>, int *<var>np</var>);</kbd></dt>
+<dd>`apairs' は前者の文書集合の配列を指定する。`anum' は前者の配列の要素数を指定する。`apairs' は後者の文書集合の配列を指定する。`anum' は後者の配列の要素数を指定する。`np' の参照先には、戻り値の配列の要素数が格納される。戻り値は新しい文書集合の配列へのポインタであり、各要素は二つの集合に共通して属するものである。各要素はスコアの降順で並べられる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>二つの文書集合からその和集合を得るには、関数 `odpairsor' を用いる。</p>
+
+<dl>
+<dt><kbd>ODPAIR *odpairsor(ODPAIR *<var>apairs</var>, int <var>anum</var>, ODPAIR *<var>bpairs</var>, int <var>bnum</var>, int *<var>np</var>);</kbd></dt>
+<dd>`apairs' は前者の文書集合の配列を指定する。`anum' は前者の配列の要素数を指定する。`apairs' は後者の文書集合の配列を指定する。`anum' は後者の配列の要素数を指定する。`np' の参照先には、戻り値の配列の要素数が格納される。戻り値は新しい文書集合の配列へのポインタであり、各要素は二つの集合の両方あるいはどちらか一方に属するものである。各要素はスコアの降順で並べられる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>二つの文書集合からその差集合を得るには、関数 `odpairsnotand' を用いる。</p>
+
+<dl>
+<dt><kbd>ODPAIR *odpairsnotand(ODPAIR *<var>apairs</var>, int <var>anum</var>, ODPAIR *<var>bpairs</var>, int <var>bnum</var>, int *<var>np</var>);</kbd></dt>
+<dd>`apairs' は前者の文書集合の配列を指定する。`anum' は前者の配列の要素数を指定する。`apairs' は後者の文書集合の配列を指定する。`anum' は後者の配列の要素数を指定する。`np' の参照先には、戻り値の配列の要素数が格納される。戻り値は新しい文書集合の配列へのポインタであり、各要素は前者の集合には属するが後者の集合には属さないものである。各要素はスコアの降順で並べられる。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。</dd>
+</dl>
+
+<p>文書集合をスコアの降順で並べるには、関数 `odpairssort' を用いる。</p>
+
+<dl>
+<dt><kbd>void odpairssort(ODPAIR *<var>pairs</var>, int <var>pnum</var>);</kbd></dt>
+<dd>`pairs' は文書集合の配列を指定する。`pnum' はその配列の要素数を指定する。</dd>
+</dl>
+
+<p>ある数の自然対数を得るには、関数 `odlogarithm' を用いる。</p>
+
+<dl>
+<dt><kbd>double odlogarithm(double <var>x</var>);</kbd></dt>
+<dd>`x' はある数を指定する。戻り値はその数の自然対数である。もしその数が 1.0 以下であれば、戻り値は 0.0 となる。この関数はアプリケーションが検索結果のIDFを算出する際に便利である。</dd>
+</dl>
+
+<p>二つのベクトルのなす角の余弦を得るには、関数 `odvectorcosine' を用いる。</p>
+
+<dl>
+<dt><kbd>double odvectorcosine(const int *<var>avec</var>, const int *<var>bvec</var>, int <var>vnum</var>);</kbd></dt>
+<dd>`avec' は前者の数値配列を指定する。`bvec' は後者の数値配列を指定する。`vnum' は各々の配列の要素数を指定する。戻り値は二つのベクトルのなす角の余弦である。この関数はアプリケーションが文書の類似度を算出する際に便利である。</dd>
+</dl>
+
+<p>性能を調整する大域的なパラメータを指定するには、関数 `odsettuning' を用いる。</p>
+
+<dl>
+<dt><kbd>void odsettuning(int <var>ibnum</var>, int <var>idnum</var>, int <var>cbnum</var>, int <var>csiz</var>);</kbd></dt>
+<dd>`ibnum' は転置インデックスのバケット数を指定する。`idnum' は転置インデックスの分割数を指定する。`cbnum' はダーティバッファのバケット数を指定する。`csiz' はダーティバッファに使うメモリの最大バイト数を指定する。デフォルトの設定は `odsettuning(32749, 7, 262139, 8388608)' に相当する。この関数はハンドルを開く前に呼び出すべきである。</dd>
+</dl>
+
+<p>テキストを分解して出現形と正規形を別々のリストに格納するには、関数 `odanalyzetext' を用いる。</p>
+
+<dl>
+<dt><kbd>void odanalyzetext(ODEUM *<var>odeum</var>, const char *<var>text</var>, CBLIST *<var>awords</var>, CBLIST *<var>nwords</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。`text' はテキストの文字列を指定する。`awords' は出現型を格納するリストハンドルを指定する。`nwords' は正規型を格納するリストハンドルを指定するが、`NULL' なら無視される。語群は空白文字とピリオドやコンマ等の区切り文字で分割される。</dd>
+</dl>
+
+<p>関数 `odanalyzetext' で使われる文字の分類を設定するには、関数 `odsetcharclass' を用いる。</p>
+
+<dl>
+<dt><kbd>void odsetcharclass(ODEUM *<var>odeum</var>, const char *<var>spacechars</var>, const char *<var>delimchars</var>, const char *<var>gluechars</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。`spachechars' は空白文字を含む文字列を指定する。`delimchars' は区切り文字を含む文字列を指定する。`gluechars' は接着文字を含む文字列を指定する。</dd>
+</dl>
+
+<p>小さな問い合わせ言語を使って検索を行うには、関数 `odquery' を用いる。</p>
+
+<dl>
+<dt><kbd>ODPAIR *odquery(ODEUM *<var>odeum</var>, const char *<var>query</var>, int *<var>np</var>, CBLIST *<var>errors</var>);</kbd></dt>
+<dd>`odeum' はデータベースハンドルを指定する。`query' は問い合わせ言語の文字列を指定する。`np' の参照先には、戻り値の配列の要素数が格納される。`error' はエラーメッセージを格納するリストハンドルを指定する。戻り値は正常なら配列へのポインタであり、エラーなら `NULL' である。その配列の各要素は文書のID番号とスコアのペアであり、スコアの降順で並べられる。検索語に該当する文書が一つもなかったとしてもエラーにはならずに、空の配列を返す。戻り値の領域は `malloc' で確保されるので、不要になったら `free' で解放するべきである。配列の各要素には既に削除された文書のデータも含まれることに注意すべきである。</dd>
+</dl>
+
+<h3>サンプルコード</h3>
+
+<p>文書をデータベースに格納するサンプルコードを以下に示す。</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <odeum.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define DBNAME   "index"
+
+int main(int argc, char **argv){
+  ODEUM *odeum;
+  ODDOC *doc;
+  CBLIST *awords;
+  const char *asis;
+  char *normal;
+  int i;
+
+  /* データベースを開く */
+  if(!(odeum = odopen(DBNAME, OD_OWRITER | OD_OCREAT))){
+    fprintf(stderr, "odopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* 文書ハンドルを取得する */
+  doc = oddocopen("http://www.foo.bar/baz.txt");
+
+  /* 文書の属性を設定する */
+  oddocaddattr(doc, "title", "Balcony Scene");
+  oddocaddattr(doc, "author", "Shakespeare");
+
+  /* テキストを分解して語のリストを得る */
+  awords = odbreaktext("Parting is such sweet sorrow.");
+
+  /* 各語を文書ハンドルに設定する */
+  for(i = 0; i < cblistnum(awords); i++){
+    /* 語のリストから一語を取り出す */
+    asis = cblistval(awords, i, NULL);
+    /* 出現形から正規形を生成する */
+    normal = odnormalizeword(asis);
+    /* 語を文書ハンドルに設定する */
+    oddocaddword(doc, normal, asis);
+    /* 正規形の領域を解放する */
+    free(normal);
+  }
+
+  /* 文書をデータベースに登録する */
+  if(!odput(odeum, doc, -1, 1)){
+    fprintf(stderr, "odput: %s\n", dperrmsg(dpecode));
+  }
+
+  /* 語のリストを解放する */
+  cblistclose(awords);
+
+  /* 文書ハンドルを解放する */
+  oddocclose(doc);
+
+  /* データベースを閉じる */
+  if(!odclose(odeum)){
+    fprintf(stderr, "odclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<p>データベース内の文書を検索するサンプルコードを以下に示す。</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <odeum.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define DBNAME   "index"
+
+int main(int argc, char **argv){
+  ODEUM *odeum;
+  ODPAIR *pairs;
+  ODDOC *doc;
+  const CBLIST *words;
+  const char *title, *author, *asis;
+  int i, j, pnum;
+
+  /* データベースを取得する */
+  if(!(odeum = odopen(DBNAME, OD_OREADER))){
+    fprintf(stderr, "odopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* 文書の検索を行う */
+  if((pairs = odsearch(odeum, "sorrow", -1, &pnum)) != NULL){
+
+    /* 文書の配列を走査する */
+    for(i = 0; i < pnum; i++){
+      /* 文書ハンドルを取得する */
+      if(!(doc = odgetbyid(odeum, pairs[i].id))) continue;
+      /* 文書の属性を表示する */
+      printf("URI: %s\n", oddocuri(doc));
+      title = oddocgetattr(doc, "title");
+      if(title) printf("TITLE: %s\n", title);
+      author = oddocgetattr(doc, "author");
+      if(author) printf("AUTHOR: %s\n", author);
+      /* 文書内の語を出現形で表示する */
+      printf("WORDS:");
+      words = oddocawords(doc);
+      for(j = 0; j < cblistnum(words); j++){
+        asis = cblistval(words, j, NULL);
+        printf(" %s", asis);
+      }
+      putchar('\n');
+      /* 文書ハンドルを解放する */
+      oddocclose(doc);
+    }
+
+    /* 文書の配列を解放する */
+    free(pairs);
+
+  } else {
+    fprintf(stderr, "odsearch: %s\n", dperrmsg(dpecode));
+  }
+
+  /* データベースを閉じる */
+  if(!odclose(odeum)){
+    fprintf(stderr, "odclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<h3>注記</h3>
+
+<p>Odeumを利用したプログラムをビルドする方法は、Depotの場合と全く同じである。</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>POSIXスレッドを有効にしてQDBMをビルドした場合、外部変数 `dpecode' はスレッド固有データへの参照として扱われ、Odeumの各関数はリエントラントになる。その場合、スレッド間で同時に同じハンドルにアクセスしない限りは、各関数はスレッドセーフである。ただし、`errno' や `malloc' 等がスレッドセーフな処理系であることが前提となる。</p>
+
+<p>ZLIBを有効にしてQDBMをビルドした場合、文書属性用データベース内のレコードは圧縮されて保存される。その場合、サイズが30%以下になる。したがって、Odeumを利用する場合はZLIBを有効にした方がよい。ZLIBを有効にして作成したOdeumのデータベースを、ZLIBを有効にしていない環境で利用することはできず、またその逆も同様である。ZLIBが有効でなくLZOが有効な場合は、ZLIBの変わりにLZOが用いられる。</p>
+
+<h3>問い合わせ言語</h3>
+
+<p>関数 `odquery' の問い合わせ言語は以下の文法に基づく。</p>
+
+<pre>expr ::= subexpr ( op subexpr )*
+subexpr ::= WORD
+subexpr ::= LPAREN expr RPAREN
+</pre>
+
+<p>演算子としては "&"(AND)と "|"(OR)と "!"(NOTAND)が利用できる。また、括弧 "()" を使うことで演算子の評価順序を制御することができる。問い合わせの文字列は関数 `odanalyzetext' を用いて分解されるので、"&"、"|"、"!"、"("、")" は区切り文字として設定されている必要がある。また、空白で単語を区切っても "&" で区切ったのと同じことになる。つまり "joe blow" は "joe & blow" と同じである。</p>
+
+<p>問い合わせ文字列の文字コードは対象文書の文字コードと一致している必要がある。また、空白文字と区切り文字と接着文字に指定できるのは1バイト文字だけである。</p>
+
+<hr />
+
+<h2><a name="odeumcli" id="odeumcli" class="head">Odeum用コマンド</a></h2>
+
+<p>Odeumに対応するコマンドラインインタフェースは以下のものである。</p>
+
+<p>コマンド `odmgr' はOdeumやそのアプリケーションのデバッグに役立つツールである。データベースを更新したり、データベースの状態を調べたりする機能を持つ。シェルスクリプトで全文検索システムを作るのにも利用できる。以下の書式で用いる。`name' はデータベース名、`file' はファイル名、`expr' は文書のURIかID番号、`words' は検索語、`elems' は要素データベースを指定する。</p>
+
+<dl>
+<dt><kbd>odmgr create <var>name</var></kbd></dt>
+<dd>データベースファイルを作成する。</dd>
+<dt><kbd>odmgr put [-uri <var>str</var>] [-title <var>str</var>] [-author <var>str</var>] [-date <var>str</var>] [-wmax <var>num</var>] [-keep] <var>name</var> [<var>file</var>]</kbd></dt>
+<dd>ファイルを読み込んで文書を追加する。`file' を省略すると標準入力を読み込むが、その場合はURIの指定が必須となる。</dd>
+<dt><kbd>odmgr out [-id] <var>name</var> <var>expr</var></kbd></dt>
+<dd>URIに対応する文書を削除する。</dd>
+<dt><kbd>odmgr get [-id] [-t|-h] <var>name</var> <var>expr</var></kbd></dt>
+<dd>URIに対応する文書を表示する。出力は文書のID番号とURIとスコアをタブで区切ったものである。</dd>
+<dt><kbd>odmgr search [-max <var>num</var>] [-or] [-idf] [-t|-h|-n] <var>name</var> <var>words</var>...</kbd></dt>
+<dd>指定した語を含む文書を検索する。出力の第1行は、検索語全体の該当数と各検索語およびその該当数をタブで区切ったものである。第2行以降は、該当の各文書のID番号とURIとスコアをタブで区切ったものである。</dd>
+<dt><kbd>odmgr list [-t|-h] <var>name</var></kbd></dt>
+<dd>データベース内の全ての文書を表示する。出力の各行は文書のID番号とURIとスコアをタブで区切ったものである。</dd>
+<dt><kbd>odmgr optimize <var>name</var></kbd></dt>
+<dd>データベースを最適化する。</dd>
+<dt><kbd>odmgr inform <var>name</var></kbd></dt>
+<dd>データベースの雑多な情報を出力する。</dd>
+<dt><kbd>odmgr merge <var>name</var> <var>elems</var>...</kbd></dt>
+<dd>複数のデータベースをマージする。</dd>
+<dt><kbd>odmgr remove <var>name</var></kbd></dt>
+<dd>データベースディレクトリを削除する。</dd>
+<dt><kbd>odmgr break [-h|-k|-s] [<var>file</var>]</kbd></dt>
+<dd>ファイルを読み込んで、テキストを語に分解して出力する。出力の各行は各語の正規形と出現形をタブで区切ったものである。</dd>
+<dt><kbd>odmgr version</kbd></dt>
+<dd>QDBMのバージョン情報を出力する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-uri <var>str</var></kbd> : 文書のURIを明示的に指定する。</li>
+<li><kbd>-title <var>str</var></kbd> : 文書のタイトルを指定する。</li>
+<li><kbd>-author <var>str</var></kbd> : 文書の著者名を指定する。</li>
+<li><kbd>-date <var>str</var></kbd> : 文書の更新日時を指定する。</li>
+<li><kbd>-wmax <var>num</var></kbd> : 格納する語の最大数を指定する。</li>
+<li><kbd>-keep</kbd> : 同じURIの文書が既存であれば上書きを行わない。</li>
+<li><kbd>-id</kbd> : URIでなくID番号で文書を指定する。</li>
+<li><kbd>-t</kbd> : 文書の詳細情報をタブ区切りで出力する。</li>
+<li><kbd>-h</kbd> : 文書の詳細情報を人間が読みやすい形式で出力する。</li>
+<li><kbd>-k</kbd> : 文書のキーワードのみを出力する。</li>
+<li><kbd>-s</kbd> : 文書の要約のみを出力する。</li>
+<li><kbd>-max <var>num</var></kbd> : 出力する文書の最大数を指定する。</li>
+<li><kbd>-or</kbd> : AND検索でなくOR検索を行う。</li>
+<li><kbd>-idf</kbd> : IDFでスコアを重みづけする。</li>
+<li><kbd>-n</kbd> : 文書のIDとスコアのみを表示する。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。環境変数 `QDBMDBGFD' の値として、変数 `dpecode' の変更履歴を出力するファイルディスクリプタを指定ことができる。</p>
+
+<p>コマンド `odtest' はOdeumの機能テストや性能テストに用いるツールである。このコマンドによって生成されたデータベースディレクトリを `odmgr' によって解析したり、`time' コマンドによってこのコマンドの実行時間を計ったりするとよい。以下の書式で用いる。`name' はデータベース名、`dnum' は文書数、`wnum' は文書毎の語数、`pnum' は語のパターン数を指定する。</p>
+
+<dl>
+<dt><kbd>odtest write [-tune <var>ibnum</var> <var>idnum</var> <var>cbnum</var> <var>csiz</var>] <var>name</var> <var>dnum</var> <var>wnum</var> <var>pnum</var></kbd></dt>
+<dd>無作為な属性と語を持つ文書を連続してデータベースに追加する。</dd>
+<dt><kbd>odtest read <var>name</var></kbd></dt>
+<dd>上記で生成したデータベースの全文書を検索する。</dd>
+<dt><kbd>odtest combo <var>name</var></kbd></dt>
+<dd>各種操作の組み合わせテストを行う。</dd>
+<dt><kbd>odtest wicked <var>name</var> <var>dnum</var></kbd></dt>
+<dd>各種更新操作を無作為に選択して実行する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-tune <var>ibnum</var> <var>idnum</var> <var>cbnum</var> <var>csiz</var></kbd> : 性能パラメータを指定する。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。環境変数 `QDBMDBGFD' の値として、変数 `dpecode' の変更履歴を出力するファイルディスクリプタを指定ことができる。</p>
+
+<p>コマンド `odidx' はローカルファイルシステム上のファイルを読み込んでOdeumのデータベースに登録するユーティリティである。このコマンドはWebサイトの全文検索システムを構築する際に役立つ。サポートされるファイルフォーマットはプレーンテキストとHTMLである。サポートされる文字コードはUS-ASCIIとISO-8859-1である。各文書のURIにはファイルのパスが指定される。各文書には、`title' と `date' という属性が付与される。既にデータベース登録してあるファイルを登録しようとした場合、更新時刻が新しければ登録され、そうでなければ無視される。更新時刻はデータベースディレクトリの中の '_mtime' というサブデータベースに記録される。スコア情報はデータベースディレクトリの中の `_score' というサブデータベースに記録さã‚
 Œã‚‹ã€‚以下の書式で用いる。`name' はデータベース名、`dir' はディレクトリ名を指定する。</p>
+
+<dl>
+<dt><kbd>odidx register [-l <var>file</var>] [-wmax <var>num</var>] [-tsuf <var>sufs</var>] [-hsuf <var>sufs</var>] <var>name</var> [<var>dir</var>]</kbd></dt>
+<dd>特定のディレクトリ以下のファイル群をデータベース登録する。`dir' が省略された場合、カレントディレクトリが指定される。</dd>
+<dt><kbd>odidx relate <var>name</var></kbd></dt>
+<dd>データベースの各文書に関連文書検索のためのスコア情報を付加する。</dd>
+<dt><kbd>odidx purge <var>name</var></kbd></dt>
+<dd>ファイルシステムに存在しない文書をデータベースから削除する。</dd>
+</dl>
+
+<p>各オプションは以下の機能を持つ。</p>
+
+<ul class="lines">
+<li><kbd>-l <var>file</var></kbd> : 登録すべきファイルのパスのリストをファイルから読み込む。`-' を指定した場合、標準入力が読み込まれる。</li>
+<li><kbd>-wmax <var>num</var></kbd> : データベースに格納する語の最大数を指定する。</li>
+<li><kbd>-tsuf <var>sufs</var></kbd> : プレーンテキストファイルの拡張子をカンマ区切りで指定する。デフォルトは `-tsuf .txt,.text' と同意である。</li>
+<li><kbd>-hsuf <var>sufs</var></kbd> : HTMLファイルの拡張子をカンマ区切りで指定する。デフォルトは `-hsuf .html,.htm' と同意である。</li>
+</ul>
+
+<p>このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了する。</p>
+
+<p>Odeumのコマンド群を駆使すると、全文検索システムを簡単に実現することができる。例えば `/home/mikio' 以下にあり、かつ `.txt' か `.c' か `.h' という接尾辞を持つファイル群を `casket' という名前のインデックスに登録するなら、以下のようにする。</p>
+
+<pre>odidx register -tsuf ".txt,.c,.h" -hsuf "" casket /home/mikio
+</pre>
+
+<p>そして、`unix' および `posix' という語を含む文書を検索し、上位8件を表示するには、以下のようにする。</p>
+
+<pre>odmgr search -max 8 -h casket "unix posix"
+</pre>
+
+<p>`odidx' で生成したデータベースは、QDBMに付録される全文検索のためのCGIスクリプト `qfts.cgi' でそのまま利用することができる。</p>
+
+<hr />
+
+<h2><a name="fileformat" id="fileformat" class="head">ファイルフォーマット</a></h2>
+
+<h3>Depotのファイルフォーマット</h3>
+
+<p>Depotが管理するデータベースファイルの内容は、ヘッダ部、バケット部、レコード部の三つに大別される。</p>
+
+<p>ヘッダ部はファイルの先頭から 48 バイトの固定長でとられ、以下の情報が記録される。</p>
+
+<ol>
+<li>マジックナンバ : オフセット 0 から始まる。ビッグエンディアン用なら文字列 "[DEPOT]\n\f" を内容とし、リトルエンディアン用なら文字列 "[depot]\n\f" を内容とする。</li>
+<li>バージョン番号 : オフセット 12 から始まる。ライブラリのバージョン番号を10進数で表現した文字列を内容とする。</li>
+<li>ラッパー用フラグ : オフセット 16 から始まる。`int' 型の整数である。</li>
+<li>ファイルサイズ : オフセット 24 から始まる。`int' 型の整数である。</li>
+<li>バケット配列の要素数 : オフセット 32 から始まる。`int' 型の整数である。</li>
+<li>レコード数 : オフセット 40 から始まる。`int' 型の整数である。</li>
+</ol>
+
+<p>バケット部はヘッダ部の直後にバケット配列の要素数に応じた大きさでとられ、チェーンの先頭要素のオフセットが各要素に記録される。</p>
+
+<p>レコード部はバケット部の直後からファイルの末尾までを占め、各レコードの以下の情報を持つ要素が記録される。</p>
+
+<ol>
+<li>フラグ : `int' 型の整数である。</li>
+<li>キーの第二ハッシュ値 : `int' 型の整数である。</li>
+<li>キーのサイズ : `int' 型の整数である。</li>
+<li>値のサイズ : `int' 型の整数である。</li>
+<li>パディングのサイズ : `int' 型の整数である。</li>
+<li>左の子の位置 : `int' 型の整数である。</li>
+<li>右の子の位置 : `int' 型の整数である。</li>
+<li>キーの実データ : キーのサイズで定義される長さを持つ一連のバイトである。</li>
+<li>値の実データ : 値のサイズで定義される長さを持つ一連のバイトである。</li>
+<li>パディング : 値のサイズとアラインメントにより算出される長さを持つ一連のバイトである。</li>
+</ol>
+
+<h3>Villaのファイルフォーマット</h3>
+
+<p>Villaの扱う全てのデータはDepotのデータベースに記録される。記録されるデータは、メタデータと論理ページに分類される。論理ページはリーフノードと非リーフノードに分類される。メタデータはレコード数等の管理情報を記録するもので、キーと値ともに `int' 型である。リーフノードはレコードを保持する。非リーフノードはページを参照する疎インデックスを保持する。</p>
+
+<p>Villaは、小さい自然数を直列化して扱う際に記憶領域を節約するために、可変長整数フォーマット(BER圧縮)を用いる。可変長整数のオブジェクトは、領域の先頭から解析し、値が正のバイトを読んだらそこで終端とする。各バイトは絶対値で評価され、リトルエンディアンの128進数として算出される。</p>
+
+<p>レコードはユーザデータの論理的な単位である。キーが重複する論理レコードは物理的には単一のレコードにまとめられる。物理レコードは以下の形式で直列化される。</p>
+
+<ol>
+<li>キーのサイズ : 可変長整数型である。</li>
+<li>キーの実データ : キーのサイズで定義される長さを持つ一連のバイトである。</li>
+<li>値の数 : 可変長整数型である。</li>
+<li>値のリスト : 以下の表現を値の数だけ繰り返した一連のバイトである。<ol>
+<li>サイズ : 可変長整数型である。</li>
+<li>実データ : サイズで定義される長さを持つ一連のバイトである。</li>
+</ol></li>
+</ol>
+
+<p>リーフノードはレコードの集合を格納するための物理的な単位である。リーフノードは `int' 型のIDをキーとし、以下の値を持つレコードとしてDepotのデータベースに格納される。レコードは常にキーの昇順に整列した状態で保持される。</p>
+
+<ol>
+<li>前のリーフのID : 可変長整数型である。</li>
+<li>次のリーフのID : 可変長整数型である。</li>
+<li>レコードのリスト : 直列化したレコードを連結したもの。</li>
+</ol>
+
+<p>インデックスはページを探索するためのポインタの論理的な単位である。インデックスは以下の形式で直列化される。</p>
+
+<ol>
+<li>参照先のページのID : 可変長整数型である。</li>
+<li>キーのサイズ : 可変長整数型である。</li>
+<li>キーの実データ : キーのサイズで定義される長さを持つ一連のバイトである。</li>
+</ol>
+
+<p>非リーフノードはインデックスの集合を格納するための物理的な単位である。非リーフノードは `int' 型のIDをキーとし、以下の値を持つレコードとしてDepotのデータベースに格納される。インデックスは常にキーの昇順に整列した状態で保持される。</p>
+
+<ol>
+<li>最初の子ノードのID : 可変長整数型である。</li>
+<li>インデックスのリスト : 直列化したインデックスを連結したもの。</li>
+</ol>
+
+<h3>注記</h3>
+
+<p>データベースファイルはスパースではないので、通常のファイルと同様に複製等の操作を行うことができる。Depotはバイトオーダの調整をしないでファイルの読み書きを行っているので、バイトオーダの異なる環境にデータベースファイルを移設してもそのままでは利用できない。</p>
+
+<p>DepotやVillaのデータベースファイルをネットワークで配布する際には、MIMEタイプを `application/x-qdbm' にしてほしい。ファイル名の接尾辞は `.qdb' にしてほしい。Curiaのデータベースディレクトリをネットワークで配布する際には、TAR形式等を用いたアーカイブに変換して行うことができる。</p>
+
+<p>データベースファイルのマジックナンバを `file' コマンドに識別させたい場合は、`magic' ファイルに以下の行を追記するとよい。</p>
+
+<pre>0       string          [DEPOT]\n\f     QDBM, big endian
+>12     string          x               \b, version=%s
+>19     byte            ^1              \b, Hash
+>19     byte            &1              \b, B+ tree
+>19     byte            &2              \b (deflated:ZLIB)
+>19     byte            &4              \b (deflated:LZO)
+>19     byte            &8              \b (deflated:BZIP2)
+>24     belong          x               \b, filesize=%d
+>32     belong          x               \b, buckets=%d
+>40     belong          x               \b, records=%d
+0       string          [depot]\n\f     QDBM, little endian
+>12     string          x               \b, version=%s
+>16     byte            ^1              \b, Hash
+>16     byte            &1              \b, B+ tree
+>16     byte            &2              \b (deflated:ZLIB)
+>16     byte            &4              \b (deflated:LZO)
+>16     byte            &8              \b (deflated:BZIP2)
+>24     lelong          x               \b, filesize=%d
+>32     lelong          x               \b, buckets=%d
+>40     lelong          x               \b, records=%d
+</pre>
+
+<hr />
+
+<h2><a name="porting" id="porting" class="head">移植方法</a></h2>
+
+<p>QDBMはPOSIX互換の全てのプラットフォームで動作することを目標としている。ただし、いくつかのAPIが実装されていないプラットフォームでも動作することが望ましい。また、GCC以外のコンパイラを利用してもビルドができることが望ましい。様々なプラットフォームへの移植作業は、新しい `Makefile' を追加したりソースファイルの一部を修正したりすることによってなされる。C言語のAPIであれば、おそらく以下のファイルのいくつかを修正することになる。もしくはそれらを基に新しいファイルを作ってもよい。</p>
+
+<ul class="lines">
+<li><kbd>Makefile.in</kbd> : `./configure' に利用され、`Makefile' のベースとなる。</li>
+<li><kbd>myconf.h</kbd> : システム依存の設定ファイル。</li>
+<li><kbd>depot.h</kbd> : 基本APIのヘッダ。</li>
+<li><kbd>curia.h</kbd> : 拡張APIのヘッダ。</li>
+<li><kbd>relic.h</kbd> : NDBM互換APIのヘッダ。</li>
+<li><kbd>hovel.h</kbd> : GDBM互換APIのヘッダ。</li>
+<li><kbd>cabin.h</kbd> : ユーティリティAPIのヘッダ。</li>
+<li><kbd>villa.h</kbd> : 上級APIのヘッダ。</li>
+<li><kbd>vista.h</kbd> : 拡張上級APIのヘッダ。</li>
+<li><kbd>odeum.h</kbd> : 転置APIのヘッダ。</li>
+<li><kbd>myconf.c</kbd> : システム依存の実装。</li>
+<li><kbd>depot.c</kbd> : 基本APIの実装。</li>
+<li><kbd>curia.c</kbd> : 拡張APIの実装。</li>
+<li><kbd>relic.c</kbd> : NDBM互換APIの実装。</li>
+<li><kbd>hovel.c</kbd> : GDBM互換APIの実装。</li>
+<li><kbd>cabin.c</kbd> : ユーティリティAPIの実装。</li>
+<li><kbd>villa.c</kbd> : 上級APIの実装。</li>
+<li><kbd>vista.c</kbd> : 拡張上級APIの実装。</li>
+<li><kbd>odeum.c</kbd> : 転置APIの実装。</li>
+</ul>
+
+<p>`fcntl' コールによるファイルロックがサポートされていないプラットフォームでは、`Makefile' で定義される `CFLAGS' マクロに `-DMYNOLOCK' を追加するとよい。その際にはプロセス間の排他制御を行う別の方法を考える必要がある。同様に、`mmap' コールがないプラットフォームでは、`CFLAGS' に `-DMYNOMMAP' を追加するとよい。`mmap' に関しては `malloc' 等を用いたエミュレーションが用意されている。その他のシステムコールが実装されていない場合は、`myconf.h' と `myconf.c' を修正して該当のシステムコールのエミュレーションを行えばよい。</p>
+
+<p>C++用のAPIではPOSIXスレッドを使っているので、そのパッケージが実装されていない環境にはC++用APIは移植できない。Java用のAPIではJNIを使っているので、そのヘッダやライブラリの場所に注意すべきである。また、`long long' や `int64' といった型定義にも注意すべきである。PerlやRuby用のAPIでは各々の言語処理系で用意されたビルドコマンドを用いているので、その仕様に精通すべきである。</p>
+
+<hr />
+
+<h2><a name="bugs" id="bugs" class="head">バグ</a></h2>
+
+<p>QDBMの各文書は英語を母国語とする人達によって校正されるべきである。</p>
+
+<p>segmentation faultによるクラッシュ、予期せぬデータの消失等の不整合、メモリリーク、その他諸々のバグに関して、既知のもので未修正のものはない。</p>
+
+<p>バグを発見したら、是非とも作者にフィードバックしてほしい。その際、QDBMのバージョンと、利用環境のOSとコンパイラのバージョンも教えてほしい。</p>
+
+<p>1.7.13より前のバージョンのQDBMで作成したデータベースは、それ以後のバージョンと互換性がない。</p>
+
+<hr />
+
+<h2><a name="faq" id="faq" class="head">よく聞かれる質問</a></h2>
+
+<dl>
+<dt>Q. : QDBMはSQLをサポートするか。</dt>
+<dd>A. : QDBMはSQLをサポートしない。QDBMはRDBMS(関係データベース管理システム)ではない。組み込みのRDBMSを求めるなら、SQLiteなどを利用するとよい。</dd>
+<dt>Q. : 結局のところ、GDBM(NDBM、SDBM、Berkeley DB)とどう違うのか。</dt>
+<dd>A. : 処理が速い。データベースファイルが小さい。APIが簡潔である。特筆すべきは、レコードの上書きを繰り返す場合の時間的および空間的効率がとてもよく、実用上のスケーラビリティが高いことである。また、レコード数が100万を越えるような大規模なデータベースを構築する際にも、処理が極端に遅くなったり、ファイルのサイズが極端に大きくなったりしない。とはいえ、用途によっては他のDBMやDBMSを使う方が適切かもしれないので、各自で性能や機能の比較をしてみてほしい。</dd>
+<dt>Q. : 参考文献は何か。</dt>
+<dd>A. : QDBMの各種アルゴリズムは、主にAho他の `Data Structures and Algorithms'(邦題は「データ構造とアルゴリズム」)およびSedgewickの `Algorithms in C'(邦題は「アルゴリズムC」)の記述に基礎を置いている。</dd>
+<dt>Q. : どのAPIを使えばよいのか。</dt>
+<dd>A. : レコードの検索が完全一致だけで済むのなら、Depotを試すとよい。その規模が大きいなら、Curiaを試すとよい。レコードを順序に基づいて参照したいなら、Villaを試すとよい。その規模が大きいなら、Vistaを試すとよい。最大のレコード数を追求するなら、ZLIBかLZOを有効にしてQDBMをビルドし、その上でVistaを用いるのがよい。</dd>
+<dt>Q. : アプリケーションの良いサンプルコードはあるか。</dt>
+<dd>A. : 各APIのコマンドのソースコードを参考にしてほしい。`dptsv.c' と `crtsv.c' と `vltsv.c' が最も簡潔である。</dd>
+<dt>Q. : データベースが壊れたのだが、どうしてか。</dt>
+<dd>A. : 大抵の場合、あなたのアプリケーションがきちんとデータベースを閉じていないのが原因である。デーモンプロセスであろうが、CGIスクリプトであろうが、アプリケーションが終了する際には必ずデータベースを閉じなければならない。なお、CGIのプロセスはSIGPIPEやSIGTERMによって殺されることがあることにも留意すべきである。</dd>
+<dt>Q. : QDBMのデータベースはどのくらい堅牢なのか。</dt>
+<dd>A. : QDBMは絶対的な堅牢性は保証しない。オペレーティングシステムが暴走した際にはデータベースが壊れる可能性がある。Villaのトランザクションはアプリケーションの暴走からデータベースを保護することができるが、オペレーティングシステムの暴走には対処できない。したがって、ミッションクリティカルな用途にQDBMを利用する場合は、データベースの多重化を考慮すべきである。</dd>
+<dt>Q. : DepotとCuriaのアラインメントの使い方がよくわからないが。</dt>
+<dd>A. : 上書きモードや連結モードでの書き込みを繰り返す場合に、アラインメントはデータベースファイルのサイズが急激に大きくなるのを防ぐ。アラインメントの適切なサイズはアプリケーションによって異なるので、各自で実験してみてほしい。さしあたりは32くらいにしておくとよい。</dd>
+<dt>Q. : Villaの性能パラメータの調整がよくわからないが。</dt>
+<dd>A. : レコードを順番に参照することが多いならば、`lrecmax' と `nidxmax' をより大きくした方がよい。レコードを無作為に参照することが多いならば、それらは小さくした方がよい。RAMに余裕があるならば、`lcnum' と `ncnum' を増やすと性能がかなり向上する。ZLIBやLZOやBZIP2を有効化した場合、`lrecmax' を大きくした方が圧縮効率がよくなる。</dd>
+<dt>Q. : Villaの圧縮方式としてはZLIBとLZOとBZIP2のどれがよいのか。</dt>
+<dd>A. : 圧縮率が最も良いのはBZIP2で、処理速度が最も高いのはLZOで、ZLIBはその中間的な特性を持つ。特に理由がない限りはZLIBを使うとよい。ただし、更新が頻繁なデータベースにはLZOが適切で、更新がほとんどないならばBZIP2が適切である。圧縮しないという選択肢よりはLZOを使う方がよい。LZOのライセンスはGNU GPLであることに注意すること。</dd>
+<dt>Q. : スパースファイルとは何か。</dt>
+<dd>A. : ホール(一度もデータが書き込まれていないブロック)があるファイルのことである。ファイルシステムがスパースファイルをサポートしている場合、ホールは物理的な記憶装置に割り当てられない。QDBMでは、DP_OSPARSEなどのフラグを用いると、ハッシュのバケット配列は初期化されずにホールとなる。このことを利用して、非常に巨大なハッシュ表を実現することができる。ただし、その性能はファイルシステムの設定に強く依存する。</dd>
+<dt>Q. : なぜDepotとCuriaにはトランザクション機能がないのか。</dt>
+<dd>A. : アプリケーションが独自のトランザクション機能を実装している場合には、データベース内部のトランザクションは邪魔になるからである。トランザクションはCabinのマップを使えば簡単に実装できる。</dd>
+<dt>Q. : 性能を引き出すシステムの設定はどうであるか。</dt>
+<dd>A. : データベースのサイズと同等以上のRAMをマシンに搭載することが望ましい。そして、I/Oバッファのサイズを大きくし、ダーティバッファをフラッシュする頻度が少なくするように設定するとよい。ファイルシステムの選択も重要である。Linux上では、通常はEXT2が最高速であるが、EXT3の `writeback' モードの方が速いこともある。ReiserFSはそれなりである。EXT3のその他のモードはかなり遅い。他のファイルシステムに関しては各自で実験してみてほしい。</dd>
+<dt>Q. : `gcc' の代わりに `cc' を使ってビルドできるか。</dt>
+<dd>A. : `LTmakefile' を使えばできる。</dd>
+<dt>Q. : Visual C++を使ってビルドできるか。</dt>
+<dd>A. : できる。`Makefile' の代わりに `VCmakefile' を使うこと。</dd>
+<dt>Q. : 他にQDBMを利用できる言語はあるか。</dt>
+<dd>A. : PHP、Scheme(Gauche)、OCaml用のインタフェースが既に公開されているようである。それ以外の言語については、必要なら自分で作ってほしい。</dd>
+<dt>Q. : 「QDBM」とはどういう意味なのか。</dt>
+<dd>A. : 「QDBM」は「Quick Database Manager」の略である。高速に動作するという意味と、アプリケーションの開発が迅速にできるという意味が込められている。</dd>
+<dt>Q. : 各APIの名前はどういう意味なのか。どう発音するのか。</dt>
+<dd>A. : 5文字の英単語から適当に選択しただけで、深い意味はない。なお、「depot」は、空港、倉庫、補給所など、物質が集まる場所を意味するらしい。発音を片仮名で表現するなら「ディーポゥ」が妥当だろう。「curia」は、宮廷、法廷など、権威が集まる場所を意味するらしい。発音を片仮名で表現するなら「キュリア」が妥当だろう。「relic」は、遺物、遺跡など、過去の残骸を意味するらしい。発音を片仮名で表現するなら「レリック」が妥当だろう。「hovel」は、小屋、物置、離れ家など、粗末な建物を意味するらしい。発音を片仮名で表現するなら「ハヴル」が妥当だろう。「cabin」は、機室、客室、小屋など、簡易的な居住空間を意味するらしい。発音を片仮名で表現するなら「キャビン」が妥当だろう。「villa」は、別荘ã
 €éƒŠå¤–住宅など、都会風でない住居を意味するらしい。発音を片仮名で表現するなら「ヴィラ」が妥当だろう。「vista」は、予想、展望など、遠くを見渡すことを意味するらしい。発音を片仮名で表現するなら「ヴィスタ」が妥当だろう。「odeum」は、音楽堂、劇場など、音楽や詩吟を行う建物を意味するらしい。発音を片仮名で表現するなら「オディアム」が妥当だろう。</dd>
+</dl>
+
+<hr />
+
+<h2><a name="copying" id="copying" class="head">ライセンス</a></h2>
+
+<p>QDBMはフリーソフトウェアである。あなたは、Free Software Foundationが公表したGNU Lesser General Public Licenseのバージョン2.1あるいはそれ以降の各バージョンの中からいずれかを選択し、そのバージョンが定める条項に従ってQDBMを再頒布または変更することができる。</p>
+
+<p>QDBMは有用であると思われるが、頒布にあたっては、市場性及び特定目的適合性についての暗黙の保証を含めて、いかなる保証も行なわない。詳細についてはGNU Lesser General Public Licenseを読んでほしい。</p>
+
+<p>あなたは、QDBMと一緒にGNU Lesser General Public Licenseの写しを受け取っているはずである(`COPYING' ファイルを参照)。そうでない場合は、Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA へ連絡してほしい。</p>
+
+<p>QDBMは平林幹雄が作成した。作者と連絡をとるには、`mikio at users.sourceforge.net' 宛に電子メールを送ってほしい。ただし、質問やバグレポートなど、他のユーザと共有できる話題はメーリングリストに送ってほしい。メーリングリストの参加方法については、`http://lists.sourceforge.net/lists/listinfo/qdbm-users' を参照すること。</p>
+
+<hr />
+
+</body>
+
+</html>
+
+<!-- END OF FILE -->

Added: box/trunk/qdbm/spex.html
===================================================================
--- box/trunk/qdbm/spex.html	                        (rev 0)
+++ box/trunk/qdbm/spex.html	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,4343 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta http-equiv="Content-Style-Type" content="text/css" />
+<meta name="author" content="Mikio Hirabayashi" />
+<meta name="keywords" content="QDBM, DBM, database, hash, B+ tree" />
+<meta name="description" content="fundamental specifications of QDBM" />
+<link rel="contents" href="./" />
+<link rel="alternate" href="spex-ja.html" hreflang="ja" title="the Japanese version" />
+<link rev="made" href="mailto:mikio at users.sourceforge.net" />
+<title>Specifications of QDBM Version 1</title>
+<style type="text/css">html { margin: 0em 0em; padding: 0em 0em; background: #eeeeee none; }
+body { margin: 2em 2em; padding: 0em 0em;
+  background: #eeeeee none; color: #111111;
+  font-style: normal; font-weight: normal; }
+h1 { margin-top: 1.8em; margin-bottom: 1.3em; font-weight: bold; }
+h2 { margin-top: 1.8em; margin-bottom: 1.1em; font-weight: bold;
+  border-left: solid 0.6em #445555; border-bottom: solid 1pt #bbbbbb;
+  padding: 0.5em 0.5em; width: 60%; }
+h3 { margin-top: 1.8em; margin-bottom: 0.8em; font-weight: bold; }
+hr { margin-top: 2.5em; margin-bottom: 1.5em; height: 1pt;
+  color: #999999; background-color: #999999; border: none; }
+div.note,div.navi { text-align: right; }
+div.logo { text-align: center; margin: 3em 0em; }
+div.logo img { border: inset 2pt #ccccdd; }
+p { margin: 0.8em 0em; line-height: 140%; }
+p,dd { text-indent: 0.8em; }
+div,pre { margin-left: 1.7em; margin-right: 1.7em; }
+pre { background-color: #ddddee; padding: 0.2em; border: 1pt solid #bbbbcc; font-size: smaller; }
+kbd { color: #111111; font-style: normal; font-weight: bold; }
+a { color: #0022aa; text-decoration: none; }
+a:hover,a:focus { color: #0033ee; text-decoration: underline; }
+a.head { color: #111111; text-decoration: none; }
+table { padding: 1pt 2pt 1pt 2pt; border: none; margin-left: 1.7em; border-collapse: collapse; }
+th { padding: 1pt 4pt 1pt 4pt; border-style: none;
+  text-align: left; vertical-align: bottom; }
+td { padding: 1pt 4pt 1pt 4pt; border: 1pt solid #333333;
+  text-align: left; vertical-align: top; }
+ul,ol,dl { line-height: 140%; }
+dt { margin-left: 1.2em; }
+dd { margin-left: 2.0em; }
+ul.lines { list-style-type: none; }
+ at media print {
+  html,body { margin: 0em 0em; background-color: #ffffff; color: #000000; }
+  h1 { padding: 8em 0em 0.5em 0em; text-align: center; }
+  h2 { page-break-before: always; }
+  div.note { text-align: center; }
+  div.navi,div.logo { display: none }
+  hr { display: none; }
+  pre { margin: 0.8em 0.8em; background-color: #ffffff;
+    border: 1pt solid #aaaaaa; font-size: smaller; }
+  a,kbd { color: #000000; text-decoration: none; }
+  h1,h2,h3 { font-family: sans-serif; }
+  p,div,li,dt,dd { font-family: serif; }
+  pre,kbd { font-family: monospace; }
+  dd { font-size: smaller; }
+}
+</style>
+</head>
+
+<body>
+
+<h1>Fundamental Specifications of QDBM Version 1</h1>
+
+<div class="note">Copyright (C) 2000-2007 Mikio Hirabayashi</div>
+<div class="note">Last Update: Thu, 26 Oct 2006 15:00:20 +0900</div>
+<div class="navi">[<a href="spex-ja.html" hreflang="ja">Japanese</a>] [<a href="http://qdbm.sourceforge.net/">Home</a>]</div>
+
+<hr />
+
+<h2>Table of Contents</h2>
+
+<ol>
+<li><a href="#overview">Overview</a></li>
+<li><a href="#features">Features</a></li>
+<li><a href="#installation">Installation</a></li>
+<li><a href="#depotapi">Depot: Basic API</a></li>
+<li><a href="#depotcli">Commands for Depot</a></li>
+<li><a href="#curiaapi">Curia: Extended API</a></li>
+<li><a href="#curiacli">Commands for Curia</a></li>
+<li><a href="#relicapi">Relic: NDBM-compatible API</a></li>
+<li><a href="#reliccli">Commands for Relic</a></li>
+<li><a href="#hovelapi">Hovel: GDBM-compatible API</a></li>
+<li><a href="#hovelcli">Commands for Hovel</a></li>
+<li><a href="#cabinapi">Cabin: Utility API</a></li>
+<li><a href="#cabincli">Commands for Cabin</a></li>
+<li><a href="#villaapi">Villa: Advanced API</a></li>
+<li><a href="#villacli">Commands for Villa</a></li>
+<li><a href="#odeumapi">Odeum: Inverted API</a></li>
+<li><a href="#odeumcli">Commands for Odeum</a></li>
+<li><a href="#fileformat">File Format</a></li>
+<li><a href="#porting">Porting</a></li>
+<li><a href="#bugs">Bugs</a></li>
+<li><a href="#faq">Frequently Asked Questions</a></li>
+<li><a href="#copying">Copying</a></li>
+</ol>
+
+<hr />
+
+<h2><a name="overview" id="overview" class="head">Overview</a></h2>
+
+<p>QDBM is a library of routines for managing a database.  The database is a simple data file containing records, each is a pair of a key and a value.  Every key and value is serial bytes with variable length.  Both binary data and character string can be used as a key and a value.  There is neither concept of data tables nor data types.  Records are organized in hash table or B+ tree.</p>
+
+<p>As for database of hash table, each key must be unique within a database, so it is impossible to store two or more records with a key overlaps.  The following access methods are provided to the database: storing a record with a key and a value, deleting a record by a key, retrieving a record by a key.  Moreover, traversal access to every key are provided, although the order is arbitrary.  These access methods are similar to ones of DBM (or its followers: NDBM and GDBM) library defined in the UNIX standard.  QDBM is an alternative for DBM because of its higher performance.</p>
+
+<p>As for database of B+ tree, records whose keys are duplicated can be stored.  Access methods of storing, deleting, and retrieving are provided as with the database of hash table.  Records are stored in order by a comparing function assigned by a user.  It is possible to access each record with the cursor in ascending or descending order.  According to this mechanism, forward matching search for strings and range search for integers are realized.  Moreover, transaction is available in database of B+ tree.</p>
+
+<p>QDBM is written in C, and provided as APIs of C, C++, Java, Perl, and Ruby.  QDBM is available on platforms which have API conforming to POSIX.  QDBM is a free software licensed under the GNU Lesser General Public License.</p>
+
+<hr />
+
+<h2><a name="features" id="features" class="head">Features</a></h2>
+
+<h3>Effective Implementation of Hash Database</h3>
+
+<p>QDBM is developed referring to GDBM for the purpose of the following three points: higher processing speed, smaller size of a database file, and simpler API.  They have been achieved.  Moreover, as with GDBM, the following three restrictions of traditional DBM: a process can handle only one database, the size of a key and a value is bounded, a database file is sparse, are cleared.</p>
+
+<p>QDBM uses hash algorithm to retrieve records.  If a bucket array has sufficient number of elements, the time complexity of retrieval is `O(1)'.  That is, time required for retrieving a record is constant, regardless of the scale of a database.  It is also the same about storing and deleting.  Collision of hash values is managed by separate chaining.  Data structure of the chains is binary search tree.  Even if a bucket array has unusually scarce elements, the time complexity of retrieval is `O(log n)'.</p>
+
+<p>QDBM attains improvement in retrieval by loading RAM with the whole of a bucket array.  If a bucket array is on RAM, it is possible to access a region of a target record by about one path of file operations.  A bucket array saved in a file is not read into RAM with the `read' call but directly mapped to RAM with the `mmap' call.  Therefore, preparation time on connecting to a database is very short, and two or more processes can share the same memory map.</p>
+
+<p>If the number of elements of a bucket array is about half of records stored within a database, although it depends on characteristic of the input, the probability of collision of hash values is about 56.7% (36.8% if the same, 21.3% if twice, 11.5% if four times, 6.0% if eight times).  In such case, it is possible to retrieve a record by two or less paths of file operations.  If it is made into a performance index, in order to handle a database containing one million of records, a bucket array with half a million of elements is needed.  The size of each element is 4 bytes.  That is, if 2M bytes of RAM is available, a database containing one million records can be handled.</p>
+
+<p>QDBM provides two modes to connect to a database: `reader' and `writer'.  A reader can perform retrieving but neither storing nor deleting.  A writer can perform all access methods.  Exclusion control between processes is performed when connecting to a database by file locking.  While a writer is connected to a database, neither readers nor writers can be connected.  While a reader is connected to a database, other readers can be connect, but writers can not.  According to this mechanism, data consistency is guaranteed with simultaneous connections in multitasking environment.</p>
+
+<p>Traditional DBM provides two modes of the storing operations: `insert' and `replace'.  In the case a key overlaps an existing record, the insert mode keeps the existing value, while the replace mode transposes it to the specified value.  In addition to the two modes, QDBM provides `concatenate' mode.  In the mode, the specified value is concatenated at the end of the existing value and stored.  This feature is useful when adding a element to a value as an array.  Moreover, although DBM has a method to fetch out a value from a database only by reading the whole of a region of a record, QDBM has a method to fetch out a part of a region of a value.  When a value is treated as an array, this feature is also useful.</p>
+
+<p>Generally speaking, while succession of updating, fragmentation of available regions occurs, and the size of a database grows rapidly.  QDBM deal with this problem by coalescence of dispensable regions and reuse of them, and featuring of optimization of a database.  When overwriting a record with a value whose size is greater than the existing one, it is necessary to remove the region to another position of the file.  Because the time complexity of the operation depends on the size of the region of a record, extending values successively is inefficient.  However, QDBM deal with this problem by alignment.  If increment can be put in padding, it is not necessary to remove the region.</p>
+
+<p>As for many file systems, it is impossible to handle a file whose size is more than 2GB.  To deal with this problem, QDBM provides a directory database containing multiple database files.  Due to this feature, it is possible to handle a database whose total size is up to 1TB in theory.  Moreover, because database files can be deployed on multiple disks, the speed of updating operations can be improved as with RAID-0 (striping).  It is also possible for the database files to deploy on multiple file servers using NFS and so on.</p>
+
+<h3>Useful Implementation of B+ Tree Database</h3>
+
+<p>Although B+ tree database is slower than hash database, it features ordering access to each record.  The order can be assigned by users.  Records of B+ tree are sorted and arranged in logical pages.  Sparse index organized in B tree that is multiway balanced tree are maintained for each page.  Thus, the time complexity of retrieval and so on is `O(log n)'.  Cursor is provided to access each record in order.  The cursor can jump to a position specified by a key and can step forward or backward from the current position.  Because each page is arranged as double linked list, the time complexity of stepping cursor is `O(1)'.</p>
+
+<p>B+ tree database is implemented, based on above hash database.  Because each page of B+ tree is stored as each record of hash database, B+ tree database inherits efficiency of storage management of hash database.  Because the header of each record is smaller and alignment of each page is adjusted according to the page size, in most cases, the size of database file is cut by half compared to one of hash database.  Although operation of many pages are required to update B+ tree, QDBM expedites the process by caching pages and reducing file operations.  In most cases, because whole of the sparse index is cached on memory, it is possible to retrieve a record by one or less path of file operations.</p>
+
+<p>B+ tree database features transaction mechanism.  It is possible to commit a series of operations between the beginning and the end of the transaction in a lump, or to abort the transaction and perform rollback to the state before the transaction.  Even if the process of an application is crashed while the transaction, the database file is not broken.</p>
+
+<p>In case that QDBM was built with ZLIB, LZO, or BZIP2 enabled, a lossless data-compression library, the content of each page of B+ tree is compressed and stored in a file.  Because each record in a page has similar patterns, high efficiency of compression is expected due to the Lempel-Ziv algorithm and the like.  In case handling text data, the size of a database is reduced to about 25%.  If the scale of a database is large and disk I/O is the bottleneck, featuring compression makes the processing speed improved to a large extent.</p>
+
+<h3>Simple but Various Interfaces</h3>
+
+<p>QDBM provides very simple APIs.  You can perform database I/O as usual file I/O with `FILE' pointer defined in ANSI C.  In the basic API of QDBM, entity of a database is recorded as one file.  In the extended API, entity of a database is recorded as several files in one directory.  Because the two APIs are very similar with each other, porting an application from one to the other is easy.</p>
+
+<p>APIs which are compatible with NDBM and GDBM are also provided.  As there are a lot of applications using NDBM or GDBM, it is easy to port them onto QDBM.  In most cases, it is completed only by replacement of header including (#include) and re-compiling.  However, QDBM can not handle database files made by the original NDBM or GDBM.</p>
+
+<p>In order to handle records on memory easily, the utility API is provided.  It implements memory allocating functions, sorting functions, extensible datum, array list, hash map, and so on.  Using them, you can handle records in C language cheaply as in such script languages as Perl or Ruby.</p>
+
+<p>B+ tree database is used with the advanced API.  The advanced API is implemented using the basic API and the utility API.  Because the advanced API is also similar to the basic API and the extended API, it is easy to learn how to use it.</p>
+
+<p>In order to handle an inverted index which is used by full-text search systems, the inverted API is provided.  If it is easy to handle an inverted index of documents, an application can focus on text processing and natural language processing.  Because this API does not depend on character codes nor languages, it is possible to implement a full-text search system which can respond to various requests from users.</p>
+
+<p>Along with APIs for C, QDBM provides APIs for C++, Java, Perl, and Ruby.  APIs for C are composed of seven kinds: the basic API, the extended API, the NDBM-compatible API, the GDBM-compatible API, the utility API, the advanced API, and the inverted API.  Command line interfaces corresponding to each API are also provided.  They are useful for prototyping, testing, debugging, and so on.  The C++ API encapsulates database handling functions of the basic API, the extended API, and the advanced API with class mechanism of C++.  The Java API has native methods calling the basic API, the extended API, and the advanced API with Java Native Interface.  The Perl API has methods calling the basic API, the extended API, and the advanced API with XS language.  The Ruby API has method calling the basic API, the extended API, and the advanced API as modules of Ruby.  Moreover, CGI scripts for administration of databases, file uploading, and full-text search are provided.</p>
+
+<h3>Wide Portability</h3>
+
+<p>QDBM is implemented being based on syntax of ANSI C (C89) and using only APIs defined in ANSI C or POSIX.  Thus, QDBM works on most UNIX and its compatible OSs.  As for C API, checking operations have been done at least on the following platforms.</p>
+
+<ul>
+<li>Linux (2.2, 2.4, 2.6) (IA32, IA64, AMD64, PA-RISC, Alpha, PowerPC, M68000, ARM)</li>
+<li>FreeBSD (4.9, 5.0, 5.1, 5.2, 5.3) (IA32, IA64, SPARC, Alpha)</li>
+<li>NetBSD (1.6) (IA32)</li>
+<li>OpenBSD (3.4) (IA32)</li>
+<li>SunOS (5.6, 5.7, 5.8, 5.9, 5.10) (IA32, SPARC)</li>
+<li>HP-UX (11.11, 11.23) (IA64, PA-RISC)</li>
+<li>AIX (5.2) (POWER)</li>
+<li>Windows (2000, XP) (IA32, IA64, AMD64) (Cygwin, MinGW, Visual C++)</li>
+<li>Mac OS X (10.2, 10.3, 10.4) (IA32, PowerPC)</li>
+<li>Tru64 (5.1) (Alpha)</li>
+<li>RISC OS (5.03) (ARM)</li>
+</ul>
+
+<p>Although a database file created by QDBM depends on byte order of the processor, to do with it, utilities to dump data in format which is independent to byte orders are provided.</p>
+
+<hr />
+
+<h2><a name="installation" id="installation" class="head">Installation</a></h2>
+
+<h3>Preparation</h3>
+
+<p>To install QDBM from a source package, GCC of 2.8 or later version and `make' are required.</p>
+
+<p>When an archive file of QDBM is extracted, change the current working directory to the generated directory and perform installation.</p>
+
+<h3>Usual Steps</h3>
+
+<p>Follow the procedures below on Linux, BSD, or SunOS.</p>
+
+<p>Run the configuration script.</p>
+
+<pre>./configure
+</pre>
+
+<p>Build programs.</p>
+
+<pre>make
+</pre>
+
+<p>Perform self-diagnostic test.</p>
+
+<pre>make check
+</pre>
+
+<p>Install programs.  This operation must be carried out by the root user.</p>
+
+<pre>make install
+</pre>
+
+<h3>Using GNU Libtool</h3>
+
+<p>If above steps do not work, try the following steps.  This way needs GNU Libtool of 1.5 or later version.</p>
+
+<p>Run the configuration script.</p>
+
+<pre>./configure
+</pre>
+
+<p>Build programs.</p>
+
+<pre>make -f LTmakefile
+</pre>
+
+<p>Perform self-diagnostic test.</p>
+
+<pre>make -f LTmakefile check
+</pre>
+
+<p>Install programs.  This operation must be carried out by the root user.</p>
+
+<pre>make -f LTmakefile install
+</pre>
+
+<h3>Result</h3>
+
+<p>When a series of work finishes, the following files will be installed.  As for the rest, manuals will be installed under `/usr/local/man/man1' and '/usr/local/man/man3', other documents will be installed under `/usr/local/share/qdbm'.  A configuration file for `pkg-config' will be installed under `/usr/local/lib/pkgconfig'.</p>
+
+<pre>/usr/local/include/depot.h
+/usr/local/include/curia.h
+/usr/local/include/relic.h
+/usr/local/include/hovel.h
+/usr/local/include/cabin.h
+/usr/local/include/villa.h
+/usr/local/include/vista.h
+/usr/local/include/odeum.h
+/usr/local/lib/libqdbm.a
+/usr/local/lib/libqdbm.so.14.13.0
+/usr/local/lib/libqdbm.so.14
+/usr/local/lib/libqdbm.so
+/usr/local/bin/dpmgr
+/usr/local/bin/dptest
+/usr/local/bin/dptsv
+/usr/local/bin/crmgr
+/usr/local/bin/crtest
+/usr/local/bin/crtsv
+/usr/local/bin/rlmgr
+/usr/local/bin/rltest
+/usr/local/bin/hvmgr
+/usr/local/bin/hvtest
+/usr/local/bin/cbtest
+/usr/local/bin/cbcodec
+/usr/local/bin/vlmgr
+/usr/local/bin/vltest
+/usr/local/bin/vltsv
+/usr/local/bin/odmgr
+/usr/local/bin/odtest
+/usr/local/bin/odidx
+/usr/local/bin/qmttest
+</pre>
+
+<p>When you run a program linked dynamically to `libqdbm.so', the library search path should include `/usr/local/lib'.  You can set the library search path with the environment variable `LD_LIBRARY_PATH'.</p>
+
+<p>To uninstall QDBM, execute the following command after `./configure'.  This operation must be carried out by the root user.</p>
+
+<pre>make uninstall
+</pre>
+
+<p>If an old version of QDBM is installed on your system, uninstall it before installation of a new one.</p>
+
+<p>The other APIs except for C nor CGI scripts are not installed by default.  Refer to `plus/xspex.html' to know how to install the C++ API.  Refer to `java/jspex.html' to know how to install the Java API.  Refer to `perl/plspex.html' to know how to install the Perl API.  Refer to `ruby/rbspex.html' to know how to install the Ruby API.  Refer to `cgi/cgispex.html' to know how to install the CGI script.</p>
+
+<p>To install QDBM from such a binary package as RPM, refer to the manual of the package manager.  For example, if you use RPM, execute like the following command by the root user.</p>
+
+<pre>rpm -ivh qdbm-1.x.x-x.i386.rpm
+</pre>
+
+<h3>For Windows</h3>
+
+<p>On Windows (Cygwin), you should follow the procedures below for installation.</p>
+
+<p>Run the configuration script.</p>
+
+<pre>./configure
+</pre>
+
+<p>Build programs.</p>
+
+<pre>make win
+</pre>
+
+<p>Perform self-diagnostic test.</p>
+
+<pre>make check-win
+</pre>
+
+<p>Install programs.  As well, perform `make uninstall-win' to uninstall them.</p>
+
+<pre>make install-win
+</pre>
+
+<p>On Windows, the import library `libqdbm.dll.a' is created as well as the static library `libqdbm.a', and the dynamic linking library `qdbm.dll' is created instead of such shared libraries as `libqdbm.so'.  `qdbm.dll' is installed into `/usr/local/bin'.</p>
+
+<p>In order to build QDBM using MinGW on Cygwin, you should perform `make mingw' instead of `make win'.  With the UNIX emulation layer of Cygwin, generated programs depend on `cygwin1.dll' (they come under GNU GPL).  This problem is solved by linking them to the Win32 native DLL with MinGW.</p>
+
+<p>In order to build QDBM using Visual C++, you should edit `VCmakefile' and set the search paths for libraries and headers.  And perform `nmake /f VCMakefile'.  Applications linking to `qdbm.dll' should link to `msvcrt.dll' by `/MD' or `/MDd' option of the compiler.  Refer to `VCmakefile' for detail configurations.</p>
+
+<h3>For Mac OS X</h3>
+
+<p>On Mac OS X (Darwin), you should follow the procedures below for installation.</p>
+
+<p>Run the configuration script.</p>
+
+<pre>./configure
+</pre>
+
+<p>Build programs.</p>
+
+<pre>make mac
+</pre>
+
+<p>Perform self-diagnostic test.</p>
+
+<pre>make check-mac
+</pre>
+
+<p>Install programs.  As well, perform `make uninstall-mac' to uninstall them.</p>
+
+<pre>make install-mac
+</pre>
+
+<p>On Mac OS X, `libqdbm.dylib' and so on are created instead of `libqdbm.so' and so on.  You can set the library search path with the environment variable `DYLD_LIBRARY_PATH'.</p>
+
+<h3>For HP-UX</h3>
+
+<p>On HP-UX, you should follow the procedures below for installation.</p>
+
+<p>Run the configuration script.</p>
+
+<pre>./configure
+</pre>
+
+<p>Build programs.</p>
+
+<pre>make hpux
+</pre>
+
+<p>Perform self-diagnostic test.</p>
+
+<pre>make check-hpux
+</pre>
+
+<p>Install programs.  As well, perform `make uninstall-hpux' to uninstall them.</p>
+
+<pre>make install-hpux
+</pre>
+
+<p>On HP-UX, `libqdbm.sl' is created instead of `libqdbm.so' and so on.  You can set the library search path with the environment variable `SHLIB_PATH'.</p>
+
+<h3>For RISC OS</h3>
+
+<p>On RISC OS, you should follow the procedures below for installation.</p>
+
+<p>Build programs.  As `cc' is used for compilation by default, if you want to use `gcc', add the argument `CC=gcc'.</p>
+
+<pre>make -f RISCmakefile
+</pre>
+
+<p>When a series of work finishes, the library file `libqdbm' and such commands as `dpmgr' are generated.  Because how to install them is not defined, copy them manually for installation.  As with it, such header files as `depot.h' should be installed manually.</p>
+
+<h3>Detail Configurations</h3>
+
+<p>You can configure building processes by the following optional arguments of `./configure'.</p>
+
+<ul class="lines">
+<li><kbd>--enable-debug</kbd> : build for debugging.  Enable debugging symbols, do not perform optimization, and perform static linking.</li>
+<li><kbd>--enable-devel</kbd> : build for development.  Enable debugging symbols, perform optimization, and perform dynamic linking.</li>
+<li><kbd>--enable-stable</kbd> : build for stable release.  Perform conservative optimization, and perform dynamic linking.</li>
+<li><kbd>--enable-pthread</kbd> : feature POSIX thread and treat global variables as thread specific data.</li>
+<li><kbd>--disable-lock</kbd> : build for environments without file locking.</li>
+<li><kbd>--disable-mmap</kbd> : build for environments without memory mapping.</li>
+<li><kbd>--enable-zlib</kbd> : feature ZLIB compression for B+ tree and inverted index.</li>
+<li><kbd>--enable-lzo</kbd> : feature LZO compression for B+ tree and inverted index.</li>
+<li><kbd>--enable-bzip</kbd> : feature BZIP2 compression for B+ tree and inverted index.</li>
+<li><kbd>--enable-iconv</kbd> : feature ICONV utilities for conversion of character encodings.</li>
+</ul>
+
+<p>Usually, QDBM and its applications can be built without any dependency on non-standard libraries except for `libqdbm.*'.   However, they depend on `libpthread.*' if POSIX thread is enabled, and they depend on `libz.*' if ZLIB is enabled, and they depend on `liblzo2.*' if LZO is enabled, and they depend on `libbz2.*' if BZIP2 is enabled, and they depend on `libiconv.*' if ICONV is enabled.</p>
+
+<p>Because the license of LZO is GNU GPL, note that applications linking to `liblzo2.*' should meet commitments of GNU GPL.</p>
+
+<hr />
+
+<h2><a name="depotapi" id="depotapi" class="head">Depot: Basic API</a></h2>
+
+<h3>Overview</h3>
+
+<p>Depot is the basic API of QDBM.  Almost all features for managing a database provided by QDBM are implemented by Depot.  Other APIs are no more than wrappers of Depot.  Depot is the fastest in all APIs of QDBM.</p>
+
+<p>In order to use Depot, you should include `depot.h' and `stdlib.h' in the source files.  Usually, the following description will be near the beginning of a source file.</p>
+
+<dl>
+<dt><kbd>#include <depot.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+</dl>
+
+<p>A pointer to `DEPOT' is used as a database handle.  It is like that some file I/O routines of `stdio.h' use a pointer to `FILE'.  A database handle is opened with the function `dpopen' and closed with `dpclose'.  You should not refer directly to any member of the handle.  If a fatal error occurs in a database, any access method via the handle except `dpclose' will not work and return error status.  Although a process is allowed to use multiple database handles at the same time, handles of the same database file should not be used.</p>
+
+<h3>API</h3>
+
+<p>The external variable `dpversion' is the string containing the version information.</p>
+
+<dl>
+<dt><kbd>extern const char *dpversion;</kbd></dt>
+</dl>
+
+<p>The external variable `dpecode' is assigned with the last happened error code.  Refer to `depot.h' for details of the error codes.</p>
+
+<dl>
+<dt><kbd>extern int dpecode;</kbd></dt>
+<dd>The initial value of this variable is `DP_ENOERR'.  The other values are `DP_EFATAL', `DP_EMODE', `DP_EBROKEN', `DP_EKEEP', `DP_ENOITEM', `DP_EALLOC', `DP_EMAP', `DP_EOPEN', `DP_ECLOSE', `DP_ETRUNC', `DP_ESYNC', `DP_ESTAT', `DP_ESEEK', `DP_EREAD', `DP_EWRITE', `DP_ELOCK', `DP_EUNLINK', `DP_EMKDIR', `DP_ERMDIR', and `DP_EMISC'.</dd>
+</dl>
+
+<p>The function `dperrmsg' is used in order to get a message string corresponding to an error code.</p>
+
+<dl>
+<dt><kbd>const char *dperrmsg(int <var>ecode</var>);</kbd></dt>
+<dd>`ecode' specifies an error code.  The return value is the message string of the error code.  The region of the return value is not writable.</dd>
+</dl>
+
+<p>The function `dpopen' is used in order to get a database handle.</p>
+
+<dl>
+<dt><kbd>DEPOT *dpopen(const char *<var>name</var>, int <var>omode</var>, int <var>bnum</var>);</kbd></dt>
+<dd>`name' specifies the name of a database file.  `omode' specifies the connection mode: `DP_OWRITER' as a writer, `DP_OREADER' as a reader.  If the mode is `DP_OWRITER', the following may be added by bitwise or: `DP_OCREAT', which means it creates a new database if not exist, `DP_OTRUNC', which means it creates a new database regardless if one exists.  Both of `DP_OREADER' and `DP_OWRITER' can be added to by bitwise or: `DP_ONOLCK', which means it opens a database file without file locking, or `DP_OLCKNB', which means locking is performed without blocking.  `DP_OCREAT' can be added to by bitwise or: `DP_OSPARSE', which means it creates a database file as a sparse file.  `bnum' specifies the number of elements of the bucket array.  If it is not more than 0, the default value is specified.  The size of a bucket array is determined on creating, and can not be changed except for by optimization of the database.  Suggested size of a bucket array is about from 0.5 to 4 times of 
 the number of all records to store.  The return value is the database handle or `NULL' if it is not successful.  While connecting as a writer, an exclusive lock is invoked to the database file.  While connecting as a reader, a shared lock is invoked to the database file.  The thread blocks until the lock is achieved.  If `DP_ONOLCK' is used, the application is responsible for exclusion control.</dd>
+</dl>
+
+<p>The function `dpclose' is used in order to close a database handle.</p>
+
+<dl>
+<dt><kbd>int dpclose(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  If successful, the return value is true, else, it is false.  Because the region of a closed handle is released, it becomes impossible to use the handle.  Updating a database is assured to be written when the handle is closed.  If a writer opens a database but does not close it appropriately, the database will be broken.</dd>
+</dl>
+
+<p>The function `dpput' is used in order to store a record.</p>
+
+<dl>
+<dt><kbd>int dpput(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>dmode</var>);</kbd></dt>
+<dd>`depot' specifies a database handle connected as a writer.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `vbuf' specifies the pointer to the region of a value.  `vsiz' specifies the size of the region of the value.  If it is negative, the size is assigned with `strlen(vbuf)'.  `dmode' specifies behavior when the key overlaps, by the following values: `DP_DOVER', which means the specified value overwrites the existing one, `DP_DKEEP', which means the existing value is kept, `DP_DCAT', which means the specified value is concatenated at the end of the existing value.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<p>The function `dpout' is used in order to delete a record.</p>
+
+<dl>
+<dt><kbd>int dpout(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`depot' specifies a database handle connected as a writer.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is true, else, it is false.  False is returned when no record corresponds to the specified key.</dd>
+</dl>
+
+<p>The function `dpget' is used in order to retrieve a record.</p>
+
+<dl>
+<dt><kbd>char *dpget(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `start' specifies the offset address of the beginning of the region of the value to be read.  `max' specifies the max size to be read.  If it is negative, the size to read is unlimited.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  If successful, the return value is the pointer to the region of the value of the corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the specified key or the size of the value of the corresponding record is less than `start'.  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 valu
 e is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `dpgetwb' is used in order to retrieve a record and write the value into a buffer.</p>
+
+<dl>
+<dt><kbd>int dpgetwb(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, char *<var>vbuf</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `start' specifies the offset address of the beginning of the region of the value to be read.  `max' specifies the max size to be read.  It shuld be equal to or less than the size of the writing buffer.  `vbuf' specifies the pointer to a buffer into which the value of the corresponding record is written.  If successful, the return value is the size of the written data, else, it is -1.  -1 is returned when no record corresponds to the specified key or the size of the value of the corresponding record is less than `start'.  Note that no additional zero code is appended at the end of the region of the writing buffer.</dd>
+</dl>
+
+<p>The function `dpvsiz' is used in order to get the size of the value of a record.</p>
+
+<dl>
+<dt><kbd>int dpvsiz(DEPOT *<var>depot</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is the size of the value of the corresponding record, else, it is -1.  Because this function does not read the entity of a record, it is faster than `dpget'.</dd>
+</dl>
+
+<p>The function `dpiterinit' is used in order to initialize the iterator of a database handle.</p>
+
+<dl>
+<dt><kbd>int dpiterinit(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  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.</dd>
+</dl>
+
+<p>The function `dpiternext' is used in order to get the next key of the iterator.</p>
+
+<dl>
+<dt><kbd>char *dpiternext(DEPOT *<var>depot</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  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 if 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.</dd>
+</dl>
+
+<p>The function `dpsetalign' is used in order to set alignment of a database handle.</p>
+
+<dl>
+<dt><kbd>int dpsetalign(DEPOT *<var>depot</var>, int <var>align</var>);</kbd></dt>
+<dd>`depot' specifies a database handle connected as a writer.  `align' specifies the size of alignment.  If successful, the return value is true, else, it is false.  If alignment is set to a database, the efficiency of overwriting values is improved.  The size of alignment is suggested to be average size of the values of the records to be stored.  If alignment is positive, padding whose size is multiple number of the alignment is placed.  If alignment is negative, as `vsiz' is the size of a value, the size of padding is calculated with `(vsiz / pow(2, abs(align) - 1))'.  Because alignment setting is not saved in a database, you should specify alignment every opening a database.</dd>
+</dl>
+
+<p>The function `dpsetfbpsiz' is used in order to set the size of the free block pool of a database handle.</p>
+
+<dl>
+<dt><kbd>int dpsetfbpsiz(DEPOT *<var>depot</var>, int <var>size</var>);</kbd></dt>
+<dd>`depot' specifies a database handle connected as a writer.  `size' specifies the size of the free block pool of a database.  If successful, the return value is true, else, it is false.  The default size of the free block pool is 16.  If the size is greater, the space efficiency of overwriting values is improved with the time efficiency sacrificed.</dd>
+</dl>
+
+<p>The function `dpsync' is used in order to synchronize updating contents with the file and the device.</p>
+
+<dl>
+<dt><kbd>int dpsync(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle connected as a writer.  If successful, the return value is true, else, it is false.  This function is useful when another process uses the connected database file.</dd>
+</dl>
+
+<p>The function `dpoptimize' is used in order to optimize a database.</p>
+
+<dl>
+<dt><kbd>int dpoptimize(DEPOT *<var>depot</var>, int <var>bnum</var>);</kbd></dt>
+<dd>`depot' specifies a database handle connected as a writer.  `bnum' specifies the number of the elements of the bucket array.  If it is not more than 0, the default value is specified.  If successful, the return value is true, else, it is false.  In an alternating succession of deleting and storing with overwrite or concatenate, dispensable regions accumulate.  This function is useful to do away with them.</dd>
+</dl>
+
+<p>The function `dpname' is used in order to get the name of a database.</p>
+
+<dl>
+<dt><kbd>char *dpname(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  If successful, the return value is the pointer to the region of the name of the database, 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `dpfsiz' is used in order to get the size of a database file.</p>
+
+<dl>
+<dt><kbd>int dpfsiz(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  If successful, the return value is the size of the database file, else, it is -1.</dd>
+</dl>
+
+<p>The function `dpbnum' is used in order to get the number of the elements of the bucket array.</p>
+
+<dl>
+<dt><kbd>int dpbnum(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  If successful, the return value is the number of the elements of the bucket array, else, it is -1.</dd>
+</dl>
+
+<p>The function `dpbusenum' is used in order to get the number of the used elements of the bucket array.</p>
+
+<dl>
+<dt><kbd>int dpbusenum(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  If successful, the return value is the number of the used elements of the bucket array, else, it is -1.  This function is inefficient because it accesses all elements of the bucket array.</dd>
+</dl>
+
+<p>The function `dprnum' is used in order to get the number of the records stored in a database.</p>
+
+<dl>
+<dt><kbd>int dprnum(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  If successful, the return value is the number of the records stored in the database, else, it is -1.</dd>
+</dl>
+
+<p>The function `dpwritable' is used in order to check whether a database handle is a writer or not.</p>
+
+<dl>
+<dt><kbd>int dpwritable(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  The return value is true if the handle is a writer, false if not.</dd>
+</dl>
+
+<p>The function `dpfatalerror' is used in order to check whether a database has a fatal error or not.</p>
+
+<dl>
+<dt><kbd>int dpfatalerror(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  The return value is true if the database has a fatal error, false if not.</dd>
+</dl>
+
+<p>The function `dpinode' is used in order to get the inode number of a database file.</p>
+
+<dl>
+<dt><kbd>int dpinode(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  The return value is the inode number of the database file.</dd>
+</dl>
+
+<p>The function `dpmtime' is used in order to get the last modified time of a database.</p>
+
+<dl>
+<dt><kbd>time_t dpmtime(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  The return value is the last modified time of the database.</dd>
+</dl>
+
+<p>The function `dpfdesc' is used in order to get the file descriptor of a database file.</p>
+
+<dl>
+<dt><kbd>int dpfdesc(DEPOT *<var>depot</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  The return value is the file descriptor of the database file.  Handling the file descriptor of a database file directly is not suggested.</dd>
+</dl>
+
+<p>The function `dpremove' is used in order to remove a database file.</p>
+
+<dl>
+<dt><kbd>int dpremove(const char *<var>name</var>);</kbd></dt>
+<dd>`name' specifies the name of a database file.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<p>The function `dprepair' is used in order to repair a broken database file.</p>
+
+<dl>
+<dt><kbd>int dprepair(const char *<var>name</var>);</kbd></dt>
+<dd>`name' specifies the name of a database file.  If successful, the return value is true, else, it is false.  There is no guarantee that all records in a repaired database file correspond to the original or expected state.</dd>
+</dl>
+
+<p>The function `dpexportdb' is used in order to dump all records as endian independent data.</p>
+
+<dl>
+<dt><kbd>int dpexportdb(DEPOT *<var>depot</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`depot' specifies a database handle.  `name' specifies the name of an output file.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<p>The function `dpimportdb' is used in order to load all records from endian independent data.</p>
+
+<dl>
+<dt><kbd>int dpimportdb(DEPOT *<var>depot</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`depot' specifies a database handle connected as a writer.  The database of the handle must be empty.  `name' specifies the name of an input file.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<p>The function `dpsnaffle' is used in order to retrieve a record directly from a database file.</p>
+
+<dl>
+<dt><kbd>char *dpsnaffle(const char *<var>name</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`name' specifies the name of a database file.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  If successful, the return value is the pointer to the region of the value of the corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the specified key.  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 if it is no longer in use.  Although this function can be used even while the database file is locked by another process, it is not assured that recent updated is reflect
 ed.</dd>
+</dl>
+
+<p>The function `dpinnerhash' is a hash function used inside Depot.</p>
+
+<dl>
+<dt><kbd>int dpinnerhash(const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  The return value is the hash value of 31 bits length computed from the key.  This function is useful when an application calculates the state of the inside bucket array.</dd>
+</dl>
+
+<p>The function `dpouterhash' is a hash function which is independent from the hash functions used inside Depot.</p>
+
+<dl>
+<dt><kbd>int dpouterhash(const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  The return value is the hash value of 31 bits length computed from the key.  This function is useful when an application uses its own hash algorithm outside Depot.</dd>
+</dl>
+
+<p>The function `dpprimenum' is used in order to get a natural prime number not less than a number.</p>
+
+<dl>
+<dt><kbd>int dpprimenum(int <var>num</var>);</kbd></dt>
+<dd>`num' specified a natural number.  The return value is a natural prime number not less than the specified number.  This function is useful when an application determines the size of a bucket array of its own hash algorithm.</dd>
+</dl>
+
+<h3>Examples</h3>
+
+<p>The following example stores and retrieves a phone number, using the name as the key.</p>
+
+<pre>#include <depot.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NAME     "mikio"
+#define NUMBER   "000-1234-5678"
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  DEPOT *depot;
+  char *val;
+
+  /* open the database */
+  if(!(depot = dpopen(DBNAME, DP_OWRITER | DP_OCREAT, -1))){
+    fprintf(stderr, "dpopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* store the record */
+  if(!dpput(depot, NAME, -1, NUMBER, -1, DP_DOVER)){
+    fprintf(stderr, "dpput: %s\n", dperrmsg(dpecode));
+  }
+
+  /* retrieve the record */
+  if(!(val = dpget(depot, NAME, -1, 0, -1, NULL))){
+    fprintf(stderr, "dpget: %s\n", dperrmsg(dpecode));
+  } else {
+    printf("Name: %s\n", NAME);
+    printf("Number: %s\n", val);
+    free(val);
+  }
+
+  /* close the database */
+  if(!dpclose(depot)){
+    fprintf(stderr, "dpclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<p>The following example shows all records of the database.</p>
+
+<pre>#include <depot.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  DEPOT *depot;
+  char *key, *val;
+
+  /* open the database */
+  if(!(depot = dpopen(DBNAME, DP_OREADER, -1))){
+    fprintf(stderr, "dpopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* initialize the iterator */
+  if(!dpiterinit(depot)){
+    fprintf(stderr, "dpiterinit: %s\n", dperrmsg(dpecode));
+  }
+
+  /* scan with the iterator */
+  while((key = dpiternext(depot, NULL)) != NULL){
+    if(!(val = dpget(depot, key, -1, 0, -1, NULL))){
+      fprintf(stderr, "dpget: %s\n", dperrmsg(dpecode));
+      free(key);
+      break;
+    }
+    printf("%s: %s\n", key, val);
+    free(val);
+    free(key);
+  }
+
+  /* close the database */
+  if(!dpclose(depot)){
+    fprintf(stderr, "dpclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<h3>Notes</h3>
+
+<p>For building a program using Depot, the program should be linked with a library file `libqdbm.a' or `libqdbm.so'.  For example, the following command is executed to build `sample' from `sample.c'.</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>If QDBM was built with POSIX thread enabled, the global variable `dpecode' is treated as thread specific data, and functions of Depot are reentrant.  In that case, they are thread-safe as long as a handle is not accessed by threads at the same time, on the assumption that `errno', `malloc', and so on are thread-safe.</p>
+
+<hr />
+
+<h2><a name="depotcli" id="depotcli" class="head">Commands for Depot</a></h2>
+
+<p>Depot has the following command line interfaces.</p>
+
+<p>The command `dpmgr' is a utility for debugging Depot and its applications.  It features editing and checking of a database.  It can be used for database applications with shell scripts.  This command is used in the following format.  `name' specifies a database name.  `key' specifies the key of a record.  `val' specifies the value of a record.</p>
+
+<dl>
+<dt><kbd>dpmgr create [-s] [-bnum <var>num</var>] <var>name</var></kbd></dt>
+<dd>Create a database file.</dd>
+<dt><kbd>dpmgr put [-kx|-ki] [-vx|-vi|-vf] [-keep|-cat] [-na] <var>name</var> <var>key</var> <var>val</var></kbd></dt>
+<dd>Store a record with a key and a value.</dd>
+<dt><kbd>dpmgr out [-kx|-ki] <var>name</var> <var>key</var></kbd></dt>
+<dd>Delete a record with a key.</dd>
+<dt><kbd>dpmgr get [-nl] [-kx|-ki] [-start <var>num</var>] [-max <var>num</var>] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>Retrieve a record with a key and output it to the standard output.</dd>
+<dt><kbd>dpmgr list [-nl] [-k|-v] [-ox] <var>name</var></kbd></dt>
+<dd>List all keys and values delimited with tab and line-feed to the standard output.</dd>
+<dt><kbd>dpmgr optimize [-bnum <var>num</var>] [-na] <var>name</var></kbd></dt>
+<dd>Optimize a database.</dd>
+<dt><kbd>dpmgr inform [-nl] <var>name</var></kbd></dt>
+<dd>Output miscellaneous information to the standard output.</dd>
+<dt><kbd>dpmgr remove <var>name</var></kbd></dt>
+<dd>Remove a database file.</dd>
+<dt><kbd>dpmgr repair <var>name</var></kbd></dt>
+<dd>Repair a broken database file.</dd>
+<dt><kbd>dpmgr exportdb <var>name</var> <var>file</var></kbd></dt>
+<dd>Dump all records as endian independent data.</dd>
+<dt><kbd>dpmgr importdb [-bnum <var>num</var>] <var>name</var> <var>file</var></kbd></dt>
+<dd>Load all records from endian independent data.</dd>
+<dt><kbd>dpmgr snaffle [-kx|-ki] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>Retrieve a record from a locked database with a key and output it to the standard output.</dd>
+<dt><kbd>dpmgr version</kbd></dt>
+<dd>Output version information of QDBM to the standard output.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-s</kbd> : make the file sparse.</li>
+<li><kbd>-bnum <var>num</var></kbd> : specify the number of the elements of the bucket array.</li>
+<li><kbd>-kx</kbd> : treat `key' as a binary expression of hexadecimal notation.</li>
+<li><kbd>-ki</kbd> : treat `key' as an integer expression of decimal notation.</li>
+<li><kbd>-vx</kbd> : treat `val' as a binary expression of hexadecimal notation.</li>
+<li><kbd>-vi</kbd> : treat `val' as an integer expression of decimal notation.</li>
+<li><kbd>-vf</kbd> : read the value from a file specified with `val'.</li>
+<li><kbd>-keep</kbd> : specify the storing mode for `DP_DKEEP'.</li>
+<li><kbd>-cat</kbd> : specify the storing mode for `DP_DCAT'.</li>
+<li><kbd>-na</kbd> : do not set alignment.</li>
+<li><kbd>-nl</kbd> : open the database without file locking.</li>
+<li><kbd>-start</kbd> : specify the beginning offset of a value to fetch.</li>
+<li><kbd>-max</kbd> : specify the max size of a value to fetch.</li>
+<li><kbd>-ox</kbd> : treat the output as a binary expression of hexadecimal notation.</li>
+<li><kbd>-n</kbd> : do not output the tailing newline.</li>
+<li><kbd>-k</kbd> : output keys only.</li>
+<li><kbd>-v</kbd> : output values only.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.  The environment variable `QDBMDBGFD' specifies the file descriptor to output the history of updating the variable `dpecode'.</p>
+
+<p>The command `dptest' is a utility for facility test and performance test.  Check a database generated by the command or measure the execution time of the command.  This command is used in the following format.  `name' specifies a database name.  `rnum' specifies the number of the records.  `bnum' specifies the number of the elements of the bucket array.  `pnum' specifies the number of patterns of the keys.  `align' specifies the basic size of alignment.  `fbpsiz' specifies the size of the free block pool.</p>
+
+<dl>
+<dt><kbd>dptest write [-s] <var>name</var> <var>rnum</var> <var>bnum</var></kbd></dt>
+<dd>Store records with keys of 8 bytes.  They change as `00000001', `00000002'...</dd>
+<dt><kbd>dptest read [-wb] <var>name</var></kbd></dt>
+<dd>Retrieve all records of the database above.</dd>
+<dt><kbd>dptest rcat [-c] <var>name</var> <var>rnum</var> <var>bnum</var> <var>pnum</var> <var>align</var> <var>fbpsiz</var></kbd></dt>
+<dd>Store records with partway duplicated keys using concatenate mode.</dd>
+<dt><kbd>dptest combo <var>name</var></kbd></dt>
+<dd>Perform combination test of various operations.</dd>
+<dt><kbd>dptest wicked [-c] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>Perform updating operations selected at random.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-s</kbd> : make the file sparse.</li>
+<li><kbd>-wb</kbd> : use the function `dpgetwb' instead of the function `dpget'.</li>
+<li><kbd>-c</kbd> : perform comparison test with map of Cabin.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.  The environment variable `QDBMDBGFD' specifies the file descriptor to output the history of updating the variable `dpecode'.</p>
+
+<p>The command `dptsv' features mutual conversion between a database of Depot and a TSV text.  This command is useful when data exchange with another version of QDBM or another DBM, or when data exchange between systems which have different byte orders.  This command is used in the following format.  `name' specifies a database name.  The subcommand `export' reads TSV data from the standard input.  If a key overlaps, the latter is adopted.  `-bnum' specifies the number of the elements of the bucket array.  The subcommand `import' writes TSV data to the standard output.</p>
+
+<dl>
+<dt><kbd>dptsv import [-bnum <var>num</var>] [-bin] <var>name</var></kbd></dt>
+<dd>Create a database from TSV.</dd>
+<dt><kbd>dptsv export [-bin] <var>name</var></kbd></dt>
+<dd>Write TSV data of a database.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-bnum <var>num</var></kbd> : specify the number of the elements of the bucket array.</li>
+<li><kbd>-bin</kbd> : treat records as Base64 format.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<p>Commands of Depot realize a simple database system.  For example, to make a database to search `/etc/password' by a user name, perform the following command.</p>
+
+<pre>cat /etc/passwd | tr ':' '\t' | dptsv import casket
+</pre>
+
+<p>Thus, to retrieve the information of a user `mikio', perform the following command.</p>
+
+<pre>dpmgr get casket mikio
+</pre>
+
+<p>It is easy to implement functions upsides with these commands, using the API of Depot.</p>
+
+<hr />
+
+<h2><a name="curiaapi" id="curiaapi" class="head">Curia: Extended API</a></h2>
+
+<h3>Overview</h3>
+
+<p>Curia is the extended API of QDBM.  It provides routines for managing multiple database files in a directory.  Restrictions of some file systems that the size of each file is limited are escaped by dividing a database file into two or more.  If the database files deploy on multiple devices, the scalability is improved.</p>
+
+<p>Although Depot creates a database with a file name, Curia creates a database with a directory name.  A database file named as `depot' is placed in the specified directory.  Although it keeps the attribute of the database, it does not keep the entities of the records.  Besides, sub directories are created by the number of division of the database, named with 4 digits.  The database files are placed in the subdirectories.  The entities of the records are stored in the database file.  For example, in the case that a database directory named as `casket' and the number of division is 3, `casket/depot', `casket/0001/depot', `casket/0002/depot' and `casket/0003/depot' are created.  No error occurs even if the namesake directory exists when creating a database.  So, if sub directories exists and some devices are mounted on the sub directories, the database files deploy on the multiple devices.</p>
+
+<p>Curia features managing large objects.  Although usual records are stored in some database files, records of large objects are stored in individual files.  Because the files of large objects are deployed in different directories named with the hash values, the access speed is part-way robust although it is slower than the speed of usual records.  Large and not often accessed data should be secluded as large objects.  By doing this, the access speed of usual records is improved.  The directory hierarchies of large objects are placed in the directory named as `lob' in the sub directories of the database.  Because the key spaces of the usual records and the large objects are different, the operations keep out of each other.</p>
+
+<p>In order to use Curia, you should include `depot.h', `curia.h' and `stdlib.h' in the source files.  Usually, the following description will be near the beginning of a source file.</p>
+
+<dl>
+<dt><kbd>#include <depot.h></kbd></dt>
+<dt><kbd>#include <curia.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+</dl>
+
+<p>A pointer to `CURIA' is used as a database handle.  It is like that some file I/O routines of `stdio.h' use a pointer to `FILE'.  A database handle is opened with the function `cropen' and closed with `crclose'.  You should not refer directly to any member of the handle.  If a fatal error occurs in a database, any access method via the handle except `crclose' will not work and return error status.  Although a process is allowed to use multiple database handles at the same time, handles of the same database directory should not be used.</p>
+
+<p>Curia also assign the external variable `dpecode' with the error code.  The function `dperrmsg' is used in order to get the message of the error code.</p>
+
+<h3>API</h3>
+
+<p>The function `cropen' is used in order to get a database handle.</p>
+
+<dl>
+<dt><kbd>CURIA *cropen(const char *<var>name</var>, int <var>omode</var>, int <var>bnum</var>, int <var>dnum</var>);</kbd></dt>
+<dd>`name' specifies the name of a database directory.  `omode' specifies the connection mode: `CR_OWRITER' as a writer, `CR_OREADER' as a reader.  If the mode is `CR_OWRITER', the following may be added by bitwise or: `CR_OCREAT', which means it creates a new database if not exist, `CR_OTRUNC', which means it creates a new database regardless if one exists.  Both of `CR_OREADER' and `CR_OWRITER' can be added to by bitwise or: `CR_ONOLCK', which means it opens a database directory without file locking, or `CR_OLCKNB', which means locking is performed without blocking.  `CR_OCREAT' can be added to by bitwise or: `CR_OSPARSE', which means it creates database files as sparse files.  `bnum' specifies the number of elements of each bucket array.  If it is not more than 0, the default value is specified.  The size of each bucket array is determined on creating, and can not be changed except for by optimization of the database.  Suggested size of each bucket array is about from 0.5
  to 4 times of the number of all records to store.  `dnum' specifies the number of division of the database.  If it is not more than 0, the default value is specified.  The number of division can not be changed from the initial value.  The max number of division is 512.  The return value is the database handle or `NULL' if it is not successful.  While connecting as a writer, an exclusive lock is invoked to the database directory.  While connecting as a reader, a shared lock is invoked to the database directory.  The thread blocks until the lock is achieved.  If `CR_ONOLCK' is used, the application is responsible for exclusion control.</dd>
+</dl>
+
+<p>The function `crclose' is used in order to close a database handle.</p>
+
+<dl>
+<dt><kbd>int crclose(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  If successful, the return value is true, else, it is false.  Because the region of a closed handle is released, it becomes impossible to use the handle.  Updating a database is assured to be written when the handle is closed.  If a writer opens a database but does not close it appropriately, the database will be broken.</dd>
+</dl>
+
+<p>The function `crput' is used in order to store a record.</p>
+
+<dl>
+<dt><kbd>int crput(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>dmode</var>);</kbd></dt>
+<dd>`curia' specifies a database handle connected as a writer.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `vbuf' specifies the pointer to the region of a value.  `vsiz' specifies the size of the region of the value.  If it is negative, the size is assigned with `strlen(vbuf)'.  `dmode' specifies behavior when the key overlaps, by the following values: `CR_DOVER', which means the specified value overwrites the existing one, `CR_DKEEP', which means the existing value is kept, `CR_DCAT', which means the specified value is concatenated at the end of the existing value.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<p>The function `crout' is used in order to delete a record.</p>
+
+<dl>
+<dt><kbd>int crout(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`curia' specifies a database handle connected as a writer.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is true, else, it is false.  False is returned when no record corresponds to the specified key.</dd>
+</dl>
+
+<p>The function `crget' is used in order to retrieve a record.</p>
+
+<dl>
+<dt><kbd>char *crget(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `start' specifies the offset address of the beginning of the region of the value to be read.  `max' specifies the max size to be read.  If it is negative, the size to read is unlimited.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  If successful, the return value is the pointer to the region of the value of the corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the specified key or the size of the value of the corresponding record is less than `start'.  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 valu
 e is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `crgetwb' is used in order to retrieve a record and write the value into a buffer.</p>
+
+<dl>
+<dt><kbd>int crgetwb(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, char *<var>vbuf</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `start' specifies the offset address of the beginning of the region of the value to be read.  `max' specifies the max size to be read.  It shuld be equal to or less than the size of the writing buffer.  `vbuf' specifies the pointer to a buffer into which the value of the corresponding record is written.  If successful, the return value is the size of the written data, else, it is -1.  -1 is returned when no record corresponds to the specified key or the size of the value of the corresponding record is less than `start'.  Note that no additional zero code is appended at the end of the region of the writing buffer.</dd>
+</dl>
+
+<p>The function `crvsiz' is used in order to get the size of the value of a record.</p>
+
+<dl>
+<dt><kbd>int crvsiz(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is the size of the value of the corresponding record, else, it is -1.  Because this function does not read the entity of a record, it is faster than `crget'.</dd>
+</dl>
+
+<p>The function `criterinit' is used in order to initialize the iterator of a database handle.</p>
+
+<dl>
+<dt><kbd>int criterinit(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  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.</dd>
+</dl>
+
+<p>The function `criternext' is used in order to get the next key of the iterator.</p>
+
+<dl>
+<dt><kbd>char *criternext(CURIA *<var>curia</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  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 if 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.</dd>
+</dl>
+
+<p>The function `crsetalign' is used in order to set alignment of a database handle.</p>
+
+<dl>
+<dt><kbd>int crsetalign(CURIA *<var>curia</var>, int <var>align</var>);</kbd></dt>
+<dd>`curia' specifies a database handle connected as a writer.  `align' specifies the size of alignment.  If successful, the return value is true, else, it is false.  If alignment is set to a database, the efficiency of overwriting values is improved.  The size of alignment is suggested to be average size of the values of the records to be stored.  If alignment is positive, padding whose size is multiple number of the alignment is placed.  If alignment is negative, as `vsiz' is the size of a value, the size of padding is calculated with `(vsiz / pow(2, abs(align) - 1))'.  Because alignment setting is not saved in a database, you should specify alignment every opening a database.</dd>
+</dl>
+
+<p>The function `crsetfbpsiz' is used in order to set the size of the free block pool of a database handle.</p>
+
+<dl>
+<dt><kbd>int crsetfbpsiz(CURIA *<var>curia</var>, int <var>size</var>);</kbd></dt>
+<dd>`curia' specifies a database handle connected as a writer.  `size' specifies the size of the free block pool of a database.  If successful, the return value is true, else, it is false.  The default size of the free block pool is 16.  If the size is greater, the space efficiency of overwriting values is improved with the time efficiency sacrificed.</dd>
+</dl>
+
+<p>The function `crsync' is used in order to synchronize updating contents with the files and the devices.</p>
+
+<dl>
+<dt><kbd>int crsync(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle connected as a writer.  If successful, the return value is true, else, it is false.  This function is useful when another process uses the connected database directory.</dd>
+</dl>
+
+<p>The function `croptimize' is used in order to optimize a database.</p>
+
+<dl>
+<dt><kbd>int croptimize(CURIA *<var>curia</var>, int <var>bnum</var>);</kbd></dt>
+<dd>`curia' specifies a database handle connected as a writer.  `bnum' specifies the number of the elements of each bucket array.  If it is not more than 0, the default value is specified.  In an alternating succession of deleting and storing with overwrite or concatenate, dispensable regions accumulate.  This function is useful to do away with them.</dd>
+</dl>
+
+<p>The function `crname' is used in order to get the name of a database.</p>
+
+<dl>
+<dt><kbd>char *crname(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  If successful, the return value is the pointer to the region of the name of the database, 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `crfsiz' is used in order to get the total size of database files.</p>
+
+<dl>
+<dt><kbd>int crfsiz(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  If successful, the return value is the total size of the database files, else, it is -1.  If the total size is more than 2GB, the return value overflows.</dd>
+</dl>
+
+<p>The function `crfsizd' is used in order to get the total size of database files as double-precision floating-point number.</p>
+
+<dl>
+<dt><kbd>double crfsizd(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  If successful, the return value is the total size of the database files, else, it is -1.0.</dd>
+</dl>
+
+<p>The function `crbnum' is used in order to get the total number of the elements of each bucket array.</p>
+
+<dl>
+<dt><kbd>int crbnum(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  If successful, the return value is the total number of the elements of each bucket array, else, it is -1.</dd>
+</dl>
+
+<p>The function `crbusenum' is used in order to get the total number of the used elements of each bucket array.</p>
+
+<dl>
+<dt><kbd>int crbusenum(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  If successful, the return value is the total number of the used elements of each bucket array, else, it is -1.  This function is inefficient because it accesses all elements of each bucket array.</dd>
+</dl>
+
+<p>The function `crrnum' is used in order to get the number of the records stored in a database.</p>
+
+<dl>
+<dt><kbd>int crrnum(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  If successful, the return value is the number of the records stored in the database, else, it is -1.</dd>
+</dl>
+
+<p>The function `crwritable' is used in order to check whether a database handle is a writer or not.</p>
+
+<dl>
+<dt><kbd>int crwritable(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  The return value is true if the handle is a writer, false if not.</dd>
+</dl>
+
+<p>The function `crfatalerror' is used in order to check whether a database has a fatal error or not.</p>
+
+<dl>
+<dt><kbd>int crfatalerror(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  The return value is true if the database has a fatal error, false if not.</dd>
+</dl>
+
+<p>The function `crinode' is used in order to get the inode number of a database directory.</p>
+
+<dl>
+<dt><kbd>int crinode(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  The return value is the inode number of the database directory.</dd>
+</dl>
+
+<p>The function `crmtime' is used in order to get the last modified time of a database.</p>
+
+<dl>
+<dt><kbd>time_t crmtime(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  The return value is the last modified time of the database.</dd>
+</dl>
+
+<p>The function `crremove' is used in order to remove a database directory.</p>
+
+<dl>
+<dt><kbd>int crremove(const char *<var>name</var>);</kbd></dt>
+<dd>`name' specifies the name of a database directory.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<p>The function `crrepair' is used in order to repair a broken database directory.</p>
+
+<dl>
+<dt><kbd>int crrepair(const char *<var>name</var>);</kbd></dt>
+<dd>`name' specifies the name of a database directory.  If successful, the return value is true, else, it is false.  There is no guarantee that all records in a repaired database directory correspond to the original or expected state.</dd>
+</dl>
+
+<p>The function `crexportdb' is used in order to dump all records as endian independent data.</p>
+
+<dl>
+<dt><kbd>int crexportdb(CURIA *<var>curia</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  `name' specifies the name of an output directory.  If successful, the return value is true, else, it is false.  Note that large objects are ignored.</dd>
+</dl>
+
+<p>The function `crimportdb' is used in order to load all records from endian independent data.</p>
+
+<dl>
+<dt><kbd>int crimportdb(CURIA *<var>curia</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`curia' specifies a database handle connected as a writer.  The database of the handle must be empty.  `name' specifies the name of an input directory.  If successful, the return value is true, else, it is false.  Note that large objects are ignored.</dd>
+</dl>
+
+<p>The function `crsnaffle' is used in order to retrieve a record directly from a database directory.</p>
+
+<dl>
+<dt><kbd>char *crsnaffle(const char *<var>name</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`name' specifies the name of a database directory.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  If successful, the return value is the pointer to the region of the value of the corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the specified key.  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 if it is no longer in use.  Although this function can be used even while the database directory is locked by another process, it is not assured that recent updated 
 is reflected.</dd>
+</dl>
+
+<p>The function `crputlob' is used in order to store a large object.</p>
+
+<dl>
+<dt><kbd>int crputlob(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>dmode</var>);</kbd></dt>
+<dd>`curia' specifies a database handle connected as a writer.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `vbuf' specifies the pointer to the region of a value.  `vsiz' specifies the size of the region of the value.  If it is negative, the size is assigned with `strlen(vbuf)'.  `dmode' specifies behavior when the key overlaps, by the following values: `CR_DOVER', which means the specified value overwrites the existing one, `CR_DKEEP', which means the existing value is kept, `CR_DCAT', which means the specified value is concatenated at the end of the existing value.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<p>The function `croutlob' is used in order to delete a large object.</p>
+
+<dl>
+<dt><kbd>int croutlob(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`curia' specifies a database handle connected as a writer.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is true, else, it is false.  false is returned when no large object corresponds to the specified key.</dd>
+</dl>
+
+<p>The function `crgetlob' is used in order to retrieve a large object.</p>
+
+<dl>
+<dt><kbd>char *crgetlob(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>start</var>, int <var>max</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `start' specifies the offset address of the beginning of the region of the value to be read.  `max' specifies the max size to be read.  If it is negative, the size to read is unlimited.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  If successful, the return value is the pointer to the region of the value of the corresponding large object, else, it is `NULL'.  `NULL' is returned when no large object corresponds to the specified key or the size of the value of the corresponding large object is less than `start'.  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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `crgetlobfd' is used in order to get the file descriptor of a large object.</p>
+
+<dl>
+<dt><kbd>int crgetlobfd(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is the file descriptor of the corresponding large object, else, it is -1.  -1 is returned when no large object corresponds to the specified key.  The returned file descriptor is opened with the `open' call.  If the database handle was opened as a writer, the descriptor is writable (O_RDWR), else, it is not writable (O_RDONLY).  The descriptor should be closed with the `close' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `crvsizlob' is used in order to get the size of the value of a large object.</p>
+
+<dl>
+<dt><kbd>int crvsizlob(CURIA *<var>curia</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is the size of the value of the corresponding large object, else, it is -1.  Because this function does not read the entity of a large object, it is faster than `crgetlob'.</dd>
+</dl>
+
+<p>The function `crrnumlob' is used in order to get the number of the large objects stored in a database.</p>
+
+<dl>
+<dt><kbd>int crrnumlob(CURIA *<var>curia</var>);</kbd></dt>
+<dd>`curia' specifies a database handle.  If successful, the return value is the number of the large objects stored in the database, else, it is -1.</dd>
+</dl>
+
+<h3>Examples</h3>
+
+<p>The following example stores and retrieves a phone number, using the name as the key.</p>
+
+<pre>#include <depot.h>
+#include <curia.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NAME     "mikio"
+#define NUMBER   "000-1234-5678"
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  CURIA *curia;
+  char *val;
+
+  /* open the database */
+  if(!(curia = cropen(DBNAME, CR_OWRITER | CR_OCREAT, -1, -1))){
+    fprintf(stderr, "cropen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* store the record */
+  if(!crput(curia, NAME, -1, NUMBER, -1, CR_DOVER)){
+    fprintf(stderr, "crput: %s\n", dperrmsg(dpecode));
+  }
+
+  /* retrieve the record */
+  if(!(val = crget(curia, NAME, -1, 0, -1, NULL))){
+    fprintf(stderr, "crget: %s\n", dperrmsg(dpecode));
+  } else {
+    printf("Name: %s\n", NAME);
+    printf("Number: %s\n", val);
+    free(val);
+  }
+
+  /* close the database */
+  if(!crclose(curia)){
+    fprintf(stderr, "crclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<p>The following example shows all records of the database.</p>
+
+<pre>#include <depot.h>
+#include <curia.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  CURIA *curia;
+  char *key, *val;
+
+  /* open the database */
+  if(!(curia = cropen(DBNAME, CR_OREADER, -1, -1))){
+    fprintf(stderr, "cropen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* initialize the iterator */
+  if(!criterinit(curia)){
+    fprintf(stderr, "criterinit: %s\n", dperrmsg(dpecode));
+  }
+
+  /* scan with the iterator */
+  while((key = criternext(curia, NULL)) != NULL){
+    if(!(val = crget(curia, key, -1, 0, -1, NULL))){
+      fprintf(stderr, "crget: %s\n", dperrmsg(dpecode));
+      free(key);
+      break;
+    }
+    printf("%s: %s\n", key, val);
+    free(val);
+    free(key);
+  }
+
+  /* close the iterator */
+  if(!crclose(curia)){
+    fprintf(stderr, "crclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<h3>Notes</h3>
+
+<p>How to build programs using Curia is the same as the case of Depot.</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>If QDBM was built with POSIX thread enabled, the global variable `dpecode' is treated as thread specific data, and functions of Curia are reentrant.  In that case, they are thread-safe as long as a handle is not accessed by threads at the same time, on the assumption that `errno', `malloc', and so on are thread-safe.</p>
+
+<hr />
+
+<h2><a name="curiacli" id="curiacli" class="head">Commands for Curia</a></h2>
+
+<p>Curia has the following command line interfaces.</p>
+
+<p>The command `crmgr' is a utility for debugging Curia and its applications.  It features editing and checking of a database.  It can be used for the database applications with shell scripts.  This command is used in the following format.  `name' specifies a database name.  `key' specifies the key of a record.  `val' specifies the value of a record.</p>
+
+<dl>
+<dt><kbd>crmgr create [-s] [-bnum <var>num</var>] [-dnum <var>num</var>] <var>name</var></kbd></dt>
+<dd>Create a database file.</dd>
+<dt><kbd>crmgr put [-kx|-ki] [-vx|-vi|-vf] [-keep|-cat] [-lob] [-na] <var>name</var> <var>key</var> <var>val</var></kbd></dt>
+<dd>Store a record with a key and a value.</dd>
+<dt><kbd>crmgr out [-kx|-ki] [-lob] <var>name</var> <var>key</var></kbd></dt>
+<dd>Delete a record with a key.</dd>
+<dt><kbd>crmgr get [-nl] [-kx|-ki] [-start <var>num</var>] [-max <var>num</var>] [-ox] [-lob] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>Retrieve a record with a key and output it to the standard output.</dd>
+<dt><kbd>crmgr list [-nl] [-k|-v] [-ox] <var>name</var></kbd></dt>
+<dd>List all keys and values delimited with tab and line-feed to the standard output.</dd>
+<dt><kbd>crmgr optimize [-bnum <var>num</var>] [-na] <var>name</var></kbd></dt>
+<dd>Optimize a database.</dd>
+<dt><kbd>crmgr inform [-nl] <var>name</var></kbd></dt>
+<dd>Output miscellaneous information to the standard output.</dd>
+<dt><kbd>crmgr remove <var>name</var></kbd></dt>
+<dd>Remove a database directory.</dd>
+<dt><kbd>crmgr repair <var>name</var></kbd></dt>
+<dd>Repair a broken database directory.</dd>
+<dt><kbd>crmgr exportdb <var>name</var> <var>dir</var></kbd></dt>
+<dd>Dump all records as endian independent data.</dd>
+<dt><kbd>crmgr importdb [-bnum <var>num</var>] [-dnum <var>num</var>] <var>name</var> <var>dir</var></kbd></dt>
+<dd>Load all records from endian independent data.</dd>
+<dt><kbd>crmgr snaffle [-kx|-ki] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>Retrieve a record from a locked database with a key and output it to the standard output.</dd>
+<dt><kbd>crmgr version</kbd></dt>
+<dd>Output version information of QDBM to the standard output.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-s</kbd> : make the files sparse.</li>
+<li><kbd>-bnum <var>num</var></kbd> : specify the number of elements of each bucket array.</li>
+<li><kbd>-dnum <var>num</var></kbd> : specify the number of division of the database.</li>
+<li><kbd>-kx</kbd> : treat `key' as a binary expression of hexadecimal notation.</li>
+<li><kbd>-ki</kbd> : treat `key' as an integer expression of decimal notation.</li>
+<li><kbd>-vx</kbd> : treat `val' as a binary expression of hexadecimal notation.</li>
+<li><kbd>-vi</kbd> : treat `val' as an integer expression of decimal notation.</li>
+<li><kbd>-vf</kbd> : read the value from a file specified with `val'.</li>
+<li><kbd>-keep</kbd> : specify the storing mode for `CR_DKEEP'.</li>
+<li><kbd>-cat</kbd> : specify the storing mode for `CR_DCAT'.</li>
+<li><kbd>-na</kbd> : do not set alignment.</li>
+<li><kbd>-nl</kbd> : open the database without file locking.</li>
+<li><kbd>-start</kbd> : specify the beginning offset of a value to fetch.</li>
+<li><kbd>-max</kbd> : specify the max size of a value to fetch.</li>
+<li><kbd>-ox</kbd> : treat the output as a binary expression of hexadecimal notation.</li>
+<li><kbd>-lob</kbd> : handle large objects.</li>
+<li><kbd>-n</kbd> : do not output the tailing newline.</li>
+<li><kbd>-k</kbd> : output keys only.</li>
+<li><kbd>-v</kbd> : output values only.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.  The environment variable `QDBMDBGFD' specifies the file descriptor to output the history of updating the variable `dpecode'.</p>
+
+<p>The command `crtest' is a utility for facility test and performance test.  Check a database generated by the command or measure the execution time of the command.  This command is used in the following format.  `name' specifies a database name.  `rnum' specifies the number of records.  `bnum' specifies the number of elements of a bucket array.  `dnum' specifies the number of division of a database.  `pnum' specifies the number of patterns of the keys.  `align' specifies the basic size of alignment.  `fbpsiz' specifies the size of the free block pool.</p>
+
+<dl>
+<dt><kbd>crtest write [-s] [-lob] <var>name</var> <var>rnum</var> <var>bnum</var> <var>dnum</var></kbd></dt>
+<dd>Store records with keys of 8 bytes.  They change as `00000001', `00000002'...</dd>
+<dt><kbd>crtest read [-wb] [-lob] <var>name</var></kbd></dt>
+<dd>Retrieve all records of the database above.</dd>
+<dt><kbd>crtest rcat [-c] <var>name</var> <var>rnum</var> <var>bnum</var> <var>dnum</var> <var>pnum</var> <var>align</var> <var>fbpsiz</var></kbd></dt>
+<dd>Store records with partway duplicated keys using concatenate mode.</dd>
+<dt><kbd>crtest combo <var>name</var></kbd></dt>
+<dd>Perform combination test of various operations.</dd>
+<dt><kbd>crtest wicked [-c] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>Perform updating operations selected at random.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-s</kbd> : make the files sparse.</li>
+<li><kbd>-lob</kbd> : handle large objects.</li>
+<li><kbd>-wb</kbd> : use the function `crgetwb' instead of the function `crget'.</li>
+<li><kbd>-c</kbd> : perform comparison test with map of Cabin.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.  The environment variable `QDBMDBGFD' specifies the file descriptor to output the history of updating the variable `dpecode'.</p>
+
+<p>The command `crtsv' features mutual conversion between a database of Curia and a TSV text.  This command is useful when data exchange with another version of QDBM or another DBM, or when data exchange between systems which have different byte orders.  This command is used in the following format.  `name' specifies a database name.  The subcommand `export' reads TSV data from the standard input.  If a key overlaps, the latter is adopted.  `-bnum' specifies the number of the elements of the bucket array.  `-dnum' specifies the number of division of the database.  The subcommand `import' writes TSV data to the standard output.</p>
+
+<dl>
+<dt><kbd>crtsv import [-bnum <var>num</var>] [-dnum <var>num</var>] [-bin] <var>name</var></kbd></dt>
+<dd>Create a database from TSV.</dd>
+<dt><kbd>crtsv export [-bin] <var>name</var></kbd></dt>
+<dd>Write TSV data of a database.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-bnum <var>num</var></kbd> : specify the number of the elements of the bucket array.</li>
+<li><kbd>-dnum <var>num</var></kbd> : specify the number of division of the database.</li>
+<li><kbd>-bin</kbd> : treat records as Base64 format.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<p>Commands of Curia realize a simple database system.  For example, to make a database to search `/etc/password' by a user name, perform the following command.</p>
+
+<pre>cat /etc/passwd | tr ':' '\t' | crtsv import casket
+</pre>
+
+<p>Thus, to retrieve the information of a user `mikio', perform the following command.</p>
+
+<pre>crmgr get casket mikio
+</pre>
+
+<p>It is easy to implement functions upsides with these commands, using the API of Curia.</p>
+
+<hr />
+
+<h2><a name="relicapi" id="relicapi" class="head">Relic: NDBM-compatible API</a></h2>
+
+<h3>Overview</h3>
+
+<p>Relic is the API which is compatible with NDBM.  So, Relic wraps functions of Depot as API of NDBM.  It is easy to port an application from NDBM to QDBM.  In most cases, you should only replace the includings of `ndbm.h' with `relic.h' and replace the linking option `-lndbm' with `-lqdbm'.</p>
+
+<p>The original NDBM treats a database as a pair of files.  One, `a directory file', has a name with suffix `.dir' and stores a bit map of keys.  The other, `a data file', has a name with suffix `.pag' and stores entities of each records.  Relic creates the directory file as a mere dummy file and creates the data file as a database.  Relic has no restriction about the size of each record.  Relic can not handle database files made by the original NDBM.</p>
+
+<p>In order to use Relic, you should include `relic.h', `stdlib.h', `sys/types.h', `sys/stat.h' and `fcntl.h' in the source files.  Usually, the following description will be near the beginning of a source file.</p>
+
+<dl>
+<dt><kbd>#include <relic.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+<dt><kbd>#include <sys/types.h></kbd></dt>
+<dt><kbd>#include <sys/stat.h></kbd></dt>
+<dt><kbd>#include <fcntl.h></kbd></dt>
+</dl>
+
+<p>A pointer to `DBM' is used as a database handle.  A database handle is opened with the function `dbm_open' and closed with `dbm_close'.  You should not refer directly to any member of a handle.</p>
+
+<h3>API</h3>
+
+<p>Structures of `datum' type is used in order to give and receive data of keys and values with functions of Relic.</p>
+
+<dl>
+<dt><kbd>typedef struct { void *dptr; size_t dsize; } datum;</kbd></dt>
+<dd>`dptr' specifies the pointer to the region of a key or a value. `dsize' specifies the size of the region.</dd>
+</dl>
+
+<p>The function `dbm_open' is used in order to get a database handle.</p>
+
+<dl>
+<dt><kbd>DBM *dbm_open(char *<var>name</var>, int <var>flags</var>, int <var>mode</var>);</kbd></dt>
+<dd>`name' specifies the name of a database.  The file names are concatenated with suffixes.  `flags' is the same as the one of `open' call, although `O_WRONLY' is treated as `O_RDWR' and additional flags except for `O_CREAT' and `O_TRUNC' have no effect.  `mode' specifies the mode of the database file as the one of `open' call does.  The return value is the database handle or `NULL' if it is not successful.</dd>
+</dl>
+
+<p>The function `dbm_close' is used in order to close a database handle.</p>
+
+<dl>
+<dt><kbd>void dbm_close(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  Because the region of the closed handle is released, it becomes impossible to use the handle.</dd>
+</dl>
+
+<p>The function `dbm_store' is used in order to store a record.</p>
+
+<dl>
+<dt><kbd>int dbm_store(DBM *<var>db</var>, datum <var>key</var>, datum <var>content</var>, int <var>flags</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  `key' specifies a structure of a key.  `content' specifies a structure of a value.  `flags' specifies behavior when the key overlaps, by the following values: `DBM_REPLACE', which means the specified value overwrites the existing one, `DBM_INSERT', which means the existing value is kept.  The return value is 0 if it is successful, 1 if it gives up because of overlaps of the key, -1 if other error occurs.</dd>
+</dl>
+
+<p>The function `dbm_delete' is used in order to delete a record.</p>
+
+<dl>
+<dt><kbd>int dbm_delete(DBM *<var>db</var>, datum <var>key</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  `key' specifies a structure of a key.  The return value is 0 if it is successful, -1 if some errors occur.</dd>
+</dl>
+
+<p>The function `dbm_fetch' is used in order to retrieve a record.</p>
+
+<dl>
+<dt><kbd>datum dbm_fetch(DBM *<var>db</var>, datum <var>key</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  `key' specifies a structure of a key.  The return value is a structure of the result.  If a record corresponds, the member `dptr' of the structure is the pointer to the region of the value.  If no record corresponds or some errors occur, `dptr' is `NULL'.  `dptr' points to the region related with the handle.  The region is available until the next time of calling this function with the same handle.</dd>
+</dl>
+
+<p>The function `dbm_firstkey' is used in order to get the first key of a database.</p>
+
+<dl>
+<dt><kbd>datum dbm_firstkey(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  The return value is a structure of the result.  If a record corresponds, the member `dptr' of the structure is the pointer to the region of the first key.  If no record corresponds or some errors occur, `dptr' is `NULL'.  `dptr' points to the region related with the handle.  The region is available until the next time of calling this function or the function `dbm_nextkey' with the same handle.</dd>
+</dl>
+
+<p>The function `dbm_nextkey' is used in order to get the next key of a database.</p>
+
+<dl>
+<dt><kbd>datum dbm_nextkey(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  The return value is a structure of the result.  If a record corresponds, the member `dptr' of the structure is the pointer to the region of the next key.  If no record corresponds or some errors occur, `dptr' is `NULL'.  `dptr' points to the region related with the handle.  The region is available until the next time of calling this function or the function `dbm_firstkey' with the same handle.</dd>
+</dl>
+
+<p>The function `dbm_error' is used in order to check whether a database has a fatal error or not.</p>
+
+<dl>
+<dt><kbd>int dbm_error(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  The return value is true if the database has a fatal error, false if not.</dd>
+</dl>
+
+<p>The function `dbm_clearerr' has no effect.</p>
+
+<dl>
+<dt><kbd>int dbm_clearerr(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  The return value is 0.  The function is only for compatibility.</dd>
+</dl>
+
+<p>The function `dbm_rdonly' is used in order to check whether a handle is read-only or not.</p>
+
+<dl>
+<dt><kbd>int dbm_rdonly(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  The return value is true if the handle is read-only, or false if not read-only.</dd>
+</dl>
+
+<p>The function `dbm_dirfno' is used in order to get the file descriptor of a directory file.</p>
+
+<dl>
+<dt><kbd>int dbm_dirfno(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  The return value is the file descriptor of the directory file.</dd>
+</dl>
+
+<p>The function `dbm_pagfno' is used in order to get the file descriptor of a data file.</p>
+
+<dl>
+<dt><kbd>int dbm_pagfno(DBM *<var>db</var>);</kbd></dt>
+<dd>`db' specifies a database handle.  The return value is the file descriptor of the data file.</dd>
+</dl>
+
+<h3>Examples</h3>
+
+<p>The following example stores and retrieves a phone number, using the name as the key.</p>
+
+<pre>#include <relic.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+
+#define NAME     "mikio"
+#define NUMBER   "000-1234-5678"
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  DBM *db;
+  datum key, val;
+  int i;
+
+  /* open the database */
+  if(!(db = dbm_open(DBNAME, O_RDWR | O_CREAT, 00644))){
+    perror("dbm_open");
+    return 1;
+  }
+
+  /* prepare the record */
+  key.dptr = NAME;
+  key.dsize = strlen(NAME);
+  val.dptr = NUMBER;
+  val.dsize = strlen(NUMBER);
+
+  /* store the record */
+  if(dbm_store(db, key, val, DBM_REPLACE) != 0){
+    perror("dbm_store");
+  }
+
+  /* retrieve the record */
+  val = dbm_fetch(db, key);
+  if(val.dptr){
+    printf("Name: %s\n", NAME);
+    printf("Number: ");
+    for(i = 0; i < val.dsize; i++){
+      putchar(((char *)val.dptr)[i]);
+    }
+    putchar('\n');
+  } else {
+    perror("dbm_fetch");
+  }
+
+  /* close the database */
+  dbm_close(db);
+
+  return 0;
+}
+</pre>
+
+<h3>Notes</h3>
+
+<p>How to build programs using Relic is the same as the case of Depot.  Note that an option to be given to a linker is not `-lndbm', but `-lqdbm'.</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>Functions of Relic are thread-safe as long as a handle is not accessed by threads at the same time, on the assumption that `errno', `malloc', and so on are thread-safe.</p>
+
+<hr />
+
+<h2><a name="reliccli" id="reliccli" class="head">Commands for Relic</a></h2>
+
+<p>Relic has the following command line interfaces.</p>
+
+<p>The command `rlmgr' is a utility for debugging Relic and its applications.  It features editing and checking of a database.  It can be used for database applications with shell scripts.  This command is used in the following format.  `name' specifies a database name.  `key' specifies the key of a record.  `val' specifies the value of a record.</p>
+
+<dl>
+<dt><kbd>rlmgr create <var>name</var></kbd></dt>
+<dd>Create a database file.</dd>
+<dt><kbd>rlmgr store [-kx] [-vx|-vf] [-insert] <var>name</var> <var>key</var> <var>val</var></kbd></dt>
+<dd>Store a record with a key and a value.</dd>
+<dt><kbd>rlmgr delete [-kx] <var>name</var> <var>key</var></kbd></dt>
+<dd>Delete a record with a key.</dd>
+<dt><kbd>rlmgr fetch [-kx] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>Retrieve a record with a key and output to the standard output.</dd>
+<dt><kbd>rlmgr list [-ox] <var>name</var></kbd></dt>
+<dd>List all keys and values delimited with tab and line-feed to the standard output.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-kx</kbd> : treat `key' as a binary expression of hexadecimal notation.</li>
+<li><kbd>-vx</kbd> : treat `val' as a binary expression of hexadecimal notation.</li>
+<li><kbd>-vf</kbd> : read the value from a file specified with `val'.</li>
+<li><kbd>-insert</kbd> : specify the storing mode for `DBM_INSERT'.</li>
+<li><kbd>-ox</kbd> : treat the output as a binary expression of hexadecimal notation.</li>
+<li><kbd>-n</kbd> : do not output the tailing newline.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<p>The command `rltest' is a utility for facility test and performance test.  Check a database generated by the command or measure the execution time of the command.  This command is used in the following format.  `name' specifies a database name.  `rnum' specifies the number of records.</p>
+
+<dl>
+<dt><kbd>rltest write <var>name</var> <var>rnum</var></kbd></dt>
+<dd>Store records with keys of 8 bytes.  They change as `00000001', `00000002'...</dd>
+<dt><kbd>rltest read <var>name</var> <var>rnum</var></kbd></dt>
+<dd>Retrieve records of the database above.</dd>
+</dl>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<hr />
+
+<h2><a name="hovelapi" id="hovelapi" class="head">Hovel: GDBM-compatible API</a></h2>
+
+<h3>Overview</h3>
+
+<p>Hovel is the API which is compatible with GDBM.  So, Hovel wraps functions of Depot and Curia as API of GDBM.  It is easy to port an application from GDBM to QDBM.  In most cases, you should only replace the includings of `gdbm.h' with `hovel.h' and replace the linking option `-lgdbm' with `-lqdbm'.  Hovel can not handle database files made by the original GDBM.</p>
+
+<p>In order to use Hovel, you should include `hovel.h', `stdlib.h', `sys/types.h' and `sys/stat.h' in the source files.  Usually, the following description will be near the beginning of a source file.</p>
+
+<dl>
+<dt><kbd>#include <hovel.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+<dt><kbd>#include <sys/types.h></kbd></dt>
+<dt><kbd>#include <sys/stat.h></kbd></dt>
+</dl>
+
+<p>An object of `GDBM_FILE' is used as a database handle.  A database handle is opened with the function `gdbm_open' and closed with `gdbm_close'.  You should not refer directly to any member of a handle.  Although Hovel works as a wrapper of Depot and handles a database file usually, if you use the function `gdbm_open2' to open the handle, it is possible to make behavior of a handle as a wrapper of Curia and treat a database directory.</p>
+
+<h3>API</h3>
+
+<p>Structures of `datum' type is used in order to give and receive data of keys and values with functions of Hovel.</p>
+
+<dl>
+<dt><kbd>typedef struct { char *dptr; size_t dsize; } datum;</kbd></dt>
+<dd>`dptr' specifies the pointer to the region of a key or a value.  `dsize' specifies the size of the region.</dd>
+</dl>
+
+<p>The external variable `gdbm_version' is the string containing the version information.</p>
+
+<dl>
+<dt><kbd>extern char *gdbm_version;</kbd></dt>
+</dl>
+
+<p>The external variable `gdbm_errno' is assigned with the last happened error code.  Refer to `hovel.h' for details of the error codes.</p>
+
+<dl>
+<dt><kbd>extern gdbm_error gdbm_errno;</kbd></dt>
+<dd>The initial value of this variable is `GDBM_NO_ERROR'.  The other values are `GDBM_MALLOC_ERROR', `GDBM_BLOCK_SIZE_ERROR', `GDBM_FILE_OPEN_ERROR', `GDBM_FILE_WRITE_ERROR', `GDBM_FILE_SEEK_ERROR', `GDBM_FILE_READ_ERROR', `GDBM_BAD_MAGIC_NUMBER', `GDBM_EMPTY_DATABASE', `GDBM_CANT_BE_READER', `GDBM_CANT_BE_WRITER', `GDBM_READER_CANT_DELETE', `GDBM_READER_CANT_STORE', `GDBM_READER_CANT_REORGANIZE', `GDBM_UNKNOWN_UPDATE', `GDBM_ITEM_NOT_FOUND', `GDBM_REORGANIZE_FAILED', `GDBM_CANNOT_REPLACE', `GDBM_ILLEGAL_DATA', `GDBM_OPT_ALREADY_SET', and `GDBM_OPT_ILLEGAL'.</dd>
+</dl>
+
+<p>The function `gdbm_strerror' is used in order to get a message string corresponding to an error code.</p>
+
+<dl>
+<dt><kbd>char *gdbm_strerror(gdbm_error <var>gdbmerrno</var>);</kbd></dt>
+<dd>`gdbmerrno' specifies an error code.  The return value is the message string of the error code.  The region of the return value is not writable.</dd>
+</dl>
+
+<p>The function `gdbm_open' is used in order to get a database handle after the fashion of GDBM.</p>
+
+<dl>
+<dt><kbd>GDBM_FILE gdbm_open(char *<var>name</var>, int <var>block_size</var>, int <var>read_write</var>, int <var>mode</var>, void (*<var>fatal_func</var>)(void));</kbd></dt>
+<dd>`name' specifies the name of a database.  `block_size' is ignored.  `read_write' specifies the connection mode: `GDBM_READER' as a reader, `GDBM_WRITER', `GDBM_WRCREAT' and `GDBM_NEWDB' as a writer.  `GDBM_WRCREAT' makes a database file or directory if it does not exist.  `GDBM_NEWDB' makes a new database even if it exists.  You can add the following to writer modes by bitwise or: `GDBM_SYNC', `GDBM_NOLOCK', `GDBM_LOCKNB', `GDBM_FAST', and `GDBM_SPARSE'.  `GDBM_SYNC' means a database is synchronized after every updating method.  `GDBM_NOLOCK' means a database is opened without file locking.  `GDBM_LOCKNB' means file locking is performed without blocking.  `GDBM_FAST' is ignored.  `GDBM_SPARSE' is an original mode of QDBM and makes database a sparse file.  `mode' specifies mode of a database file as the one of `open' call does.  `fatal_func' is ignored.  The return value is the database handle or `NULL' if it is not successful.</dd>
+</dl>
+
+<p>The function `gdbm_open2' is used in order to get a database handle after the fashion of QDBM.</p>
+
+<dl>
+<dt><kbd>GDBM_FILE gdbm_open2(char *<var>name</var>, int <var>read_write</var>, int <var>mode</var>, int <var>bnum</var>, int <var>dnum</var>, int <var>align</var>);</kbd></dt>
+<dd>`name' specifies the name of a database.  `read_write' specifies the connection mode: `GDBM_READER' as a reader, `GDBM_WRITER', `GDBM_WRCREAT' and `GDBM_NEWDB' as a writer.  `GDBM_WRCREAT' makes a database file or directory if it does not exist.  `GDBM_NEWDB' makes a new database even if it exists.  You can add the following to writer modes by bitwise or: `GDBM_SYNC', `GDBM_NOLOCK', 'GDBM_LOCKNB', `GDBM_FAST', and `GDBM_SPARSE'.  `GDBM_SYNC' means a database is synchronized after every updating method.  `GDBM_NOLOCK' means a database is opened without file locking.  `GDBM_LOCKNB' means file locking is performed without blocking.  `GDBM_FAST' is ignored.  `GDBM_SPARSE' is an original mode of QDBM and makes database sparse files.  `mode' specifies a mode of a database file or a database directory as the one of `open' or `mkdir' call does.  `bnum' specifies the number of elements of each bucket array.  If it is not more than 0, the default value is specified.  `dnum' specif
 ies the number of division of the database.  If it is not more than 0, the returning handle is created as a wrapper of Depot, else, it is as a wrapper of Curia.  `align' specifies the basic size of alignment.  The return value is the database handle or `NULL' if it is not successful.  If the database already exists, whether it is one of Depot or Curia is measured automatically.</dd>
+</dl>
+
+<p>The function `gdbm_close' is used in order to close a database handle.</p>
+
+<dl>
+<dt><kbd>void gdbm_close(GDBM_FILE <var>dbf</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle.  Because the region of the closed handle is released, it becomes impossible to use the handle.</dd>
+</dl>
+
+<p>The function `gdbm_store' is used in order to store a record.</p>
+
+<dl>
+<dt><kbd>int gdbm_store(GDBM_FILE <var>dbf</var>, datum <var>key</var>, datum <var>content</var>, int <var>flag</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle connected as a writer.  `key' specifies a structure of a key.  `content' specifies a structure of a value.  `flag' specifies behavior when the key overlaps, by the following values: `GDBM_REPLACE', which means the specified value overwrites the existing one, `GDBM_INSERT', which means the existing value is kept.  The return value is 0 if it is successful, 1 if it gives up because of overlaps of the key, -1 if other error occurs.</dd>
+</dl>
+
+<p>The function `gdbm_delete' is used in order to delete a record.</p>
+
+<dl>
+<dt><kbd>int gdbm_delete(GDBM_FILE <var>dbf</var>, datum <var>key</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle connected as a writer.  `key' specifies a structure of a key.  The return value is 0 if it is successful, -1 if some errors occur.</dd>
+</dl>
+
+<p>The function `gdbm_fetch' is used in order to retrieve a record.</p>
+
+<dl>
+<dt><kbd>datum gdbm_fetch(GDBM_FILE <var>dbf</var>, datum <var>key</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle.  `key' specifies a structure of a key.  The return value is a structure of the result.  If a record corresponds, the member `dptr' of the structure is the pointer to the region of the value.  If no record corresponds or some errors occur, `dptr' is `NULL'.  Because the region pointed to by `dptr' is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `gdbm_exists' is used in order to check whether a record exists or not.</p>
+
+<dl>
+<dt><kbd>int gdbm_exists(GDBM_FILE <var>dbf</var>, datum <var>key</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle.  `key' specifies a structure of a key.  The return value is true if a record corresponds and no error occurs, or false, else, it is false.</dd>
+</dl>
+
+<p>The function `gdbm_firstkey' is used in order to get the first key of a database.</p>
+
+<dl>
+<dt><kbd>datum gdbm_firstkey(GDBM_FILE <var>dbf</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle.  The return value is a structure of the result.  If a record corresponds, the member `dptr' of the structure is the pointer to the region of the first key.  If no record corresponds or some errors occur, `dptr' is `NULL'.  Because the region pointed to by `dptr' is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `gdbm_nextkey' is used in order to get the next key of a database.</p>
+
+<dl>
+<dt><kbd>datum gdbm_nextkey(GDBM_FILE <var>dbf</var>, datum <var>key</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle.  The return value is a structure of the result.  If a record corresponds, the member `dptr' of the structure is the pointer to the region of the next key.  If no record corresponds or some errors occur, `dptr' is `NULL'.  Because the region pointed to by `dptr' is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `gdbm_sync' is used in order to synchronize updating contents with the file and the device.</p>
+
+<dl>
+<dt><kbd>void gdbm_sync(GDBM_FILE <var>dbf</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle connected as a writer.</dd>
+</dl>
+
+<p>The function `gdbm_reorganize' is used in order to reorganize a database.</p>
+
+<dl>
+<dt><kbd>int gdbm_reorganize(GDBM_FILE <var>dbf</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle connected as a writer.  If successful, the return value is 0, else -1.</dd>
+</dl>
+
+<p>The function `gdbm_fdesc' is used in order to get the file descriptor of a database file.</p>
+
+<dl>
+<dt><kbd>int gdbm_fdesc(GDBM_FILE <var>dbf</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle connected as a writer.  The return value is the file descriptor of the database file.  If the database is a directory the return value is -1.</dd>
+</dl>
+
+<p>The function `gdbm_setopt' has no effect.</p>
+
+<dl>
+<dt><kbd>int gdbm_setopt(GDBM_FILE <var>dbf</var>, int <var>option</var>, int *<var>value</var>, int <var>size</var>);</kbd></dt>
+<dd>`dbf' specifies a database handle.  `option' is ignored.  `size' is ignored.  The return value is 0.  The function is only for compatibility.</dd>
+</dl>
+
+<h3>Examples</h3>
+
+<p>The following example stores and retrieves a phone number, using the name as the key.</p>
+
+<pre>#include <hovel.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+
+#define NAME     "mikio"
+#define NUMBER   "000-1234-5678"
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  GDBM_FILE dbf;
+  datum key, val;
+  int i;
+
+  /* open the database */
+  if(!(dbf = gdbm_open(DBNAME, 0, GDBM_WRCREAT, 00644, NULL))){
+    fprintf(stderr, "gdbm_open: %s\n", gdbm_strerror(gdbm_errno));
+    return 1;
+  }
+
+  /* prepare the record */
+  key.dptr = NAME;
+  key.dsize = strlen(NAME);
+  val.dptr = NUMBER;
+  val.dsize = strlen(NUMBER);
+
+  /* store the record */
+  if(gdbm_store(dbf, key, val, GDBM_REPLACE) != 0){
+    fprintf(stderr, "gdbm_store: %s\n", gdbm_strerror(gdbm_errno));
+  }
+
+  /* retrieve the record */
+  val = gdbm_fetch(dbf, key);
+  if(val.dptr){
+    printf("Name: %s\n", NAME);
+    printf("Number: ");
+    for(i = 0; i < val.dsize; i++){
+      putchar(val.dptr[i]);
+    }
+    putchar('\n');
+    free(val.dptr);
+  } else {
+    fprintf(stderr, "gdbm_fetch: %s\n", gdbm_strerror(gdbm_errno));
+  }
+
+  /* close the database */
+  gdbm_close(dbf);
+
+  return 0;
+}
+</pre>
+
+<h3>Notes</h3>
+
+<p>How to build programs using Hovel is the same as the case of Depot.  Note that an option to be given to a linker is not `-lgdbm', but `-lqdbm'.</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>If QDBM was built with POSIX thread enabled, the global variable `gdbm_errno' is treated as thread specific data, and functions of Hovel are reentrant.  In that case, they are thread-safe as long as a handle is not accessed by threads at the same time, on the assumption that `errno', `malloc', and so on are thread-safe.</p>
+
+<hr />
+
+<h2><a name="hovelcli" id="hovelcli" class="head">Commands for Hovel</a></h2>
+
+<p>Hovel has the following command line interfaces.</p>
+
+<p>The command `hvmgr' is a utility for debugging Hovel and its applications.  It features editing and checking of a database.  It can be used for database applications with shell scripts.  This command is used in the following format.  `name' specifies a database name.  `key' specifies the key of a record.  `val' specifies the value of a record.</p>
+
+<dl>
+<dt><kbd>hvmgr create [-qdbm bnum dnum] [-s] <var>name</var></kbd></dt>
+<dd>Create a database file.</dd>
+<dt><kbd>hvmgr store [-qdbm] [-kx] [-vx|-vf] [-insert] <var>name</var> <var>key</var> <var>val</var></kbd></dt>
+<dd>Store a record with a key and a value.</dd>
+<dt><kbd>hvmgr delete [-qdbm] [-kx] <var>name</var> <var>key</var></kbd></dt>
+<dd>Delete a record with a key.</dd>
+<dt><kbd>hvmgr fetch [-qdbm] [-kx] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>Retrieve a record with a key and output to the standard output.</dd>
+<dt><kbd>hvmgr list [-qdbm] [-ox] <var>name</var></kbd></dt>
+<dd>List all keys and values delimited with tab and line-feed to the standard output.</dd>
+<dt><kbd>hvmgr optimize [-qdbm] <var>name</var></kbd></dt>
+<dd>Optimize a database.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-qdbm [<var>bnum</var> <var>dnum</var>]</kbd> : use `gdbm_open2' to open the database.  `bnum' specifies the number of the elements of the bucket array.  `dnum' specifies the number of division of the database.</li>
+<li><kbd>-s</kbd> : make the file sparse.</li>
+<li><kbd>-kx</kbd> : treat `key' as a binary expression of hexadecimal notation.</li>
+<li><kbd>-vx</kbd> : treat `val' as a binary expression of hexadecimal notation.</li>
+<li><kbd>-vf</kbd> : read the value from a file specified with `val'.</li>
+<li><kbd>-insert</kbd> : specify the storing mode for `GDBM_INSERT'.</li>
+<li><kbd>-ox</kbd> : treat the output as a binary expression of hexadecimal notation.</li>
+<li><kbd>-n</kbd> : do not output the trailing newline.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<p>The command `hvtest' is a utility for facility test and performance test.  Check a database generated by the command or measure the execution time of the command.  This command is used in the following format.  `name' specifies a database name.  `rnum' specifies the number of records.</p>
+
+<dl>
+<dt><kbd>hvtest write [-qdbm] [-s] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>Store records with keys of 8 bytes.  They changes as `00000001', `00000002'...</dd>
+<dt><kbd>hvtest read [-qdbm] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>Retrieve records of the database above.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-qdbm</kbd> : use `gdbm_open2' and open the handle as Curia.</li>
+<li><kbd>-s</kbd> : make the file sparse.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<hr />
+
+<h2><a name="cabinapi" id="cabinapi" class="head">Cabin: Utility API</a></h2>
+
+<h3>Overview</h3>
+
+<p>Cabin is the utility API which provides memory allocating functions, sorting functions, extensible datum, array list, hash map, heap array, and so on for handling records easily on memory.  This API features also parsing MIME, CSV, and XML, and features various types of encoding and decoding.</p>
+
+<p>In order to use Cabin, you should include `cabin.h' and `stdlib.h' in the source files.  Usually, the following description will be near the beginning of a source file.</p>
+
+<dl>
+<dt><kbd>#include <cabin.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+</dl>
+
+<p>A pointer to `CBDATUM' is used as a handle of an extensible datum.  A datum handle is opened with the function `cbdatumopen' and closed with `cbdatumclose'.  A pointer to `CBLIST' is used as a handle of an array list.  A list handle is opened with the function `cblistopen' and closed with `cblistclose'.  A pointer to `CBMAP' is used as a handle of a hash map.  A map handle is opened with the function `cbmapopen' and closed with `cbmapclose'.  A pointer to `CBHEAP' is used as a handle of a heap array.  A heap handle is opened with the function `cbheapopen' and closed with `cbheapclose'.  You should not refer directly to any member of each handles.</p>
+
+<h3>API</h3>
+
+<p>The external variable `cbfatalfunc' is the pointer to call back function for handling a fatal error.</p>
+
+<dl>
+<dt><kbd>extern void (*cbfatalfunc)(const char *);</kbd></dt>
+<dd>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.</dd>
+</dl>
+
+<p>The function `cbmalloc' is used in order to allocate a region on memory.</p>
+
+<dl>
+<dt><kbd>void *cbmalloc(size_t <var>size</var>);</kbd></dt>
+<dd>`size' specifies the size of the region.  The return value is the pointer to the allocated region.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbrealloc' is used in order to re-allocate a region on memory.</p>
+
+<dl>
+<dt><kbd>void *cbrealloc(void *<var>ptr</var>, size_t <var>size</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  The return value is the pointer to the re-allocated region.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbmemdup' is used in order to duplicate a region on memory.</p>
+
+<dl>
+<dt><kbd>char *cbmemdup(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbfree' is used in order to free a region on memory.</p>
+
+<dl>
+<dt><kbd>void cbfree(void *<var>ptr</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a 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.</dd>
+</dl>
+
+<p>The function `cbglobalgc' is used in order to register the pointer or handle of an object to the global garbage collector.</p>
+
+<dl>
+<dt><kbd>void cbglobalgc(void *<var>ptr</var>, void (*<var>func</var>)(void *));</kbd></dt>
+<dd>`ptr' specifies the pointer or handle of an object.  `func' specifies the pointer to a function to release resources of the object.  Its argument is the pointer or handle of the object to release.  This function assures that resources of an object are released when the process exits normally by returning from the `main' function or calling the `exit' function.</dd>
+</dl>
+
+<p>The function `cbggcsweep' is used in order to exercise the global garbage collector explicitly.</p>
+
+<dl>
+<dt><kbd>void cbggcsweep(void);</kbd></dt>
+<dd>Note that you should not use objects registered to the global garbage collector any longer after calling this function.  Because the global garbage collector is initialized and you can register new objects into it.</dd>
+</dl>
+
+<p>The function `cbvmemavail' is used in order to check availability of allocation of the virtual memory.</p>
+
+<dl>
+<dt><kbd>int cbvmemavail(size_t <var>size</var>);</kbd></dt>
+<dd>`size' specifies the size of region to be allocated newly.  The return value is true if allocation should be success, or false if not.</dd>
+</dl>
+
+<p>The function `cbisort' is used in order to sort an array using insert sort.</p>
+
+<dl>
+<dt><kbd>void cbisort(void *<var>base</var>, int <var>nmemb</var>, int <var>size</var>, int(*<var>compar</var>)(const void *, const void *));</kbd></dt>
+<dd>`base' specifies the pointer to an array.  `nmemb' specifies the number of elements of the array.  `size' specifies the size of each element.  `compar' specifies the pointer to comparing function.  The two arguments specify the pointers of elements.  The comparing function should returns positive if the former is big, negative if the latter is big, 0 if both are equal.  Insert sort is useful only if most elements have been sorted already.</dd>
+</dl>
+
+<p>The function `cbssort' is used in order to sort an array using shell sort.</p>
+
+<dl>
+<dt><kbd>void cbssort(void *<var>base</var>, int <var>nmemb</var>, int <var>size</var>, int(*<var>compar</var>)(const void *, const void *));</kbd></dt>
+<dd>`base' specifies the pointer to an array.  `nmemb' specifies the number of elements of the array.  `size' specifies the size of each element.  `compar' specifies the pointer to comparing function.  The two arguments specify the pointers of elements.  The comparing function should returns positive if the former is big, negative if the latter is big, 0 if both are equal.  If most elements have been sorted, shell sort may be faster than heap sort or quick sort.</dd>
+</dl>
+
+<p>The function `cbhsort' is used in order to sort an array using heap sort.</p>
+
+<dl>
+<dt><kbd>void cbhsort(void *<var>base</var>, int <var>nmemb</var>, int <var>size</var>, int(*<var>compar</var>)(const void *, const void *));</kbd></dt>
+<dd>`base' specifies the pointer to an array.  `nmemb' specifies the number of elements of the array.  `size' specifies the size of each element. `compar' specifies the pointer to comparing function.  The two arguments specify the pointers of elements.  The comparing function should returns positive if the former is big, negative if the latter is big, 0 if both are equal.  Although heap sort is robust against bias of input, quick sort is faster in most cases.</dd>
+</dl>
+
+<p>The function `cbqsort' is used in order to sort an array using quick sort.</p>
+
+<dl>
+<dt><kbd>void cbqsort(void *<var>base</var>, int <var>nmemb</var>, int <var>size</var>, int(*<var>compar</var>)(const void *, const void *));</kbd></dt>
+<dd>`base' specifies the pointer to an array.  `nmemb' specifies the number of elements of the array.  `size' specifies the size of each element. `compar' specifies the pointer to comparing function.  The two arguments specify the pointers of elements.  The comparing function should returns positive if the former is big, negative if the latter is big, 0 if both are equal.  Being sensitive to bias of input, quick sort is the fastest sorting algorithm.</dd>
+</dl>
+
+<p>The function `cbstricmp' is used in order to compare two strings with case insensitive evaluation.</p>
+
+<dl>
+<dt><kbd>int cbstricmp(const char *<var>astr</var>, const char *<var>bstr</var>);</kbd></dt>
+<dd>`astr' specifies the pointer of one string.  `astr' specifies the pointer 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.  Upper cases and lower cases of alphabets in ASCII code are not distinguished.</dd>
+</dl>
+
+<p>The function `cbstrfwmatch' is used in order to check whether a string begins with a key.</p>
+
+<dl>
+<dt><kbd>int cbstrfwmatch(const char *<var>str</var>, const char *<var>key</var>);</kbd></dt>
+<dd>`str' specifies the pointer of a target string.  `key' specifies the pointer of a forward matching key string.  The return value is true if the target string begins with the key, else, it is false.</dd>
+</dl>
+
+<p>The function `cbstrfwimatch' is used in order to check whether a string begins with a key, with case insensitive evaluation.</p>
+
+<dl>
+<dt><kbd>int cbstrfwimatch(const char *<var>str</var>, const char *<var>key</var>);</kbd></dt>
+<dd>`str' specifies the pointer of a target string.  `key' specifies the pointer of a forward matching key string.  The return value is true if the target string begins with the key, else, it is false.  Upper cases and lower cases of alphabets in ASCII code are not distinguished.</dd>
+</dl>
+
+<p>The function `cbstrbwmatch' is used in order to check whether a string ends with a key.</p>
+
+<dl>
+<dt><kbd>int cbstrbwmatch(const char *<var>str</var>, const char *<var>key</var>);</kbd></dt>
+<dd>`str' specifies the pointer of a target string.  `key' specifies the pointer of a backward matching key string.  The return value is true if the target string ends with the key, else, it is false.</dd>
+</dl>
+
+<p>The function `cbstrbwimatch' is used in order to check whether a string ends with a key, with case insensitive evaluation.</p>
+
+<dl>
+<dt><kbd>int cbstrbwimatch(const char *<var>str</var>, const char *<var>key</var>);</kbd></dt>
+<dd>`str' specifies the pointer of a target string.  `key' specifies the pointer of a backward matching key string.  The return value is true if the target string ends with the key, else, it is false.  Upper cases and lower cases of alphabets in ASCII code are not distinguished.</dd>
+</dl>
+
+<p>The function `cbstrstrkmp' is used in order to locate a substring in a string using KMP method.</p>
+
+<dl>
+<dt><kbd>char *cbstrstrkmp(const char *<var>haystack</var>, const char *<var>needle</var>);</kbd></dt>
+<dd>`haystack' specifies the pointer of a target string.  `needle' specifies the pointer of a substring to be found.  The return value is the pointer to the beginning of the substring or `NULL' if the substring is not found.  In most cases, `strstr' as a built-in function of the compiler is faster than this function.</dd>
+</dl>
+
+<p>The function `cbstrstrkmp' is used in order to locate a substring in a string using BM method.</p>
+
+<dl>
+<dt><kbd>char *cbstrstrbm(const char *<var>haystack</var>, const char *<var>needle</var>);</kbd></dt>
+<dd>`haystack' specifies the pointer of a target string.  `needle' specifies the pointer of a substring to be found.  The return value is the pointer to the beginning of the substring or `NULL' if the substring is not found.  In most cases, `strstr' as a built-in function of the compiler is faster than this function.</dd>
+</dl>
+
+<p>The function `cbstrtoupper' is used in order to convert the letters of a string to upper case.</p>
+
+<dl>
+<dt><kbd>char *cbstrtoupper(char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer of a string to convert.  The return value is the pointer to the string.</dd>
+</dl>
+
+<p>The function `cbstrtolower' is used in order to convert the letters of a string to lower case.</p>
+
+<dl>
+<dt><kbd>char *cbstrtolower(char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer of a string to convert.  The return value is the pointer to the string.</dd>
+</dl>
+
+<p>The function `cbstrtrim' is used in order to cut space characters at head or tail of a string.</p>
+
+<dl>
+<dt><kbd>char *cbstrtrim(char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer of a string to convert.  The return value is the pointer to the string.</dd>
+</dl>
+
+<p>The function `cbstrsqzspc' is used in order to squeeze space characters in a string and trim it.</p>
+
+<dl>
+<dt><kbd>char *cbstrsqzspc(char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer of a string to convert.  The return value is the pointer to the string.</dd>
+</dl>
+
+<p>The function `cbstrcountutf' is used in order to count the number of characters in a string of UTF-8.</p>
+
+<dl>
+<dt><kbd>int cbstrcountutf(const char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer of a string of UTF-8.  The return value is the number of characters in the string.</dd>
+</dl>
+
+<p>The function `cbstrcututf' is used in order to cut a string of UTF-8 at the specified number of characters.</p>
+
+<dl>
+<dt><kbd>char *cbstrcututf(char *<var>str</var>, int <var>num</var>);</kbd></dt>
+<dd>`str' specifies the pointer of a string of UTF-8.  `num' specifies the number of characters to be kept.  The return value is the pointer to the string.</dd>
+</dl>
+
+<p>The function `cbdatumopen' is used in order to get a datum handle.</p>
+
+<dl>
+<dt><kbd>CBDATUM *cbdatumopen(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to the region of the initial content.  If it is `NULL', an empty datum is created.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  The return value is a datum handle.</dd>
+</dl>
+
+<p>The function `cbdatumdup' is used in order to copy a datum.</p>
+
+<dl>
+<dt><kbd>CBDATUM *cbdatumdup(const CBDATUM *<var>datum</var>);</kbd></dt>
+<dd>`datum' specifies a datum handle.  The return value is a new datum handle.</dd>
+</dl>
+
+<p>The function `cbdatumclose' is used in order to free a datum handle.</p>
+
+<dl>
+<dt><kbd>void cbdatumclose(CBDATUM *<var>datum</var>);</kbd></dt>
+<dd>`datum' specifies a datum handle.  Because the region of a closed handle is released, it becomes impossible to use the handle.</dd>
+</dl>
+
+<p>The function `cbdatumcat' is used in order to concatenate a datum and a region.</p>
+
+<dl>
+<dt><kbd>void cbdatumcat(CBDATUM *<var>datum</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`datum' specifies a datum handle.  `ptr' specifies the pointer to the region to be appended.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.</dd>
+</dl>
+
+<p>The function `cbdatumptr' is used in order to get the pointer of the region of a datum.</p>
+
+<dl>
+<dt><kbd>const char *cbdatumptr(const CBDATUM *<var>datum</var>);</kbd></dt>
+<dd>`datum' specifies a datum handle.  The return value is the pointer of the region of a datum.  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.</dd>
+</dl>
+
+<p>The function `cbdatumsize' is used in order to get the size of the region of a datum.</p>
+
+<dl>
+<dt><kbd>int cbdatumsize(const CBDATUM *<var>datum</var>);</kbd></dt>
+<dd>`datum' specifies a datum handle.  The return value is the size of the region of a datum.</dd>
+</dl>
+
+<p>The function `cbdatumsetsize' is used in order to change the size of the region of a datum.</p>
+
+<dl>
+<dt><kbd>void cbdatumsetsize(CBDATUM *<var>datum</var>, int <var>size</var>);</kbd></dt>
+<dd>`datum' specifies a datum handle.  `size' specifies the new size of the region.  If the new size is bigger than the one of old, the surplus region is filled with zero codes.</dd>
+</dl>
+
+<p>The function `cbdatumprintf' is used in order to perform formatted output into a datum.</p>
+
+<dl>
+<dt><kbd>void cbdatumprintf(CBDATUM *<var>datum</var>, const char *<var>format</var>, ...);</kbd></dt>
+<dd>`format' specifies a 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.  `:' works as with `s' but performs MIME encoding as UTF-8.  The other conversion character work as with each original.</dd>
+</dl>
+
+<p>The function `cbdatumtomalloc' is used in order to convert a datum to an allocated region.</p>
+
+<dl>
+<dt><kbd>char *cbdatumtomalloc(CBDATUM *<var>datum</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`datum' specifies a datum handle.  `sp' specifies the pointer to a variable to 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 region of the datum.  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 if it is no longer in use.  Because the region of the original datum is released, it should not be released again.</dd>
+</dl>
+
+<p>The function `cblistopen' is used in order to get a list handle.</p>
+
+<dl>
+<dt><kbd>CBLIST *cblistopen(void);</kbd></dt>
+<dd>The return value is a list handle.</dd>
+</dl>
+
+<p>The function `cblistdup' is used in order to copy a list.</p>
+
+<dl>
+<dt><kbd>CBLIST *cblistdup(const CBLIST *<var>list</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  The return value is a new list handle.</dd>
+</dl>
+
+<p>The function `cblistclose' is used in order to close a list handle.</p>
+
+<dl>
+<dt><kbd>void cblistclose(CBLIST *<var>list</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  Because the region of a closed handle is released, it becomes impossible to use the handle.</dd>
+</dl>
+
+<p>The function `cblistnum' is used in order to get the number of elements of a list.</p>
+
+<dl>
+<dt><kbd>int cblistnum(const CBLIST *<var>list</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  The return value is the number of elements of the list.</dd>
+</dl>
+
+<p>The function `cblistval' is used in order to get the pointer to the region of an element of a list.</p>
+
+<dl>
+<dt><kbd>const char *cblistval(const CBLIST *<var>list</var>, int <var>index</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  `index' specifies the index of an element.  `sp' specifies the pointer to a variable to 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 region of the 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.  If `index' is equal to or more than the number of elements, the return value is `NULL'.</dd>
+</dl>
+
+<p>The function `cblistpush' is used in order to add an element at the end of a list.</p>
+
+<dl>
+<dt><kbd>void cblistpush(CBLIST *<var>list</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  `ptr' specifies the pointer to the region of an element.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.</dd>
+</dl>
+
+<p>The function `cblistpop' is used in order to remove an element of the end of a list.</p>
+
+<dl>
+<dt><kbd>char *cblistpop(CBLIST *<var>list</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  `sp' specifies the pointer to a variable to 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 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.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.  If the list is empty, the return value is `NULL'.</dd>
+</dl>
+
+<p>The function `cblistunshift' is used in order to add an element at the top of a list.</p>
+
+<dl>
+<dt><kbd>void cblistunshift(CBLIST *<var>list</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  `ptr' specifies the pointer to the region of an element.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.</dd>
+</dl>
+
+<p>The function `cblistshift' is used in order to remove an element of the top of a list.</p>
+
+<dl>
+<dt><kbd>char *cblistshift(CBLIST *<var>list</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  `sp' specifies the pointer to a variable to 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 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.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.  If the list is empty, the return value is `NULL'.</dd>
+</dl>
+
+<p>The function `cblistinsert' is used in order to add an element at the specified location of a list.</p>
+
+<dl>
+<dt><kbd>void cblistinsert(CBLIST *<var>list</var>, int <var>index</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  `index' specifies the index of an element.  `ptr' specifies the pointer to the region of the element.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.</dd>
+</dl>
+
+<p>The function `cblistremove' is used in order to remove an element at the specified location of a list.</p>
+
+<dl>
+<dt><kbd>char *cblistremove(CBLIST *<var>list</var>, int <var>index</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  `index' specifies the index of an element.  `sp' specifies the pointer to a variable to 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 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.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if 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'.</dd>
+</dl>
+
+<p>The function `cblistover' is used in order to overwrite an element at the specified location of a list.</p>
+
+<dl>
+<dt><kbd>void cblistover(CBLIST *<var>list</var>, int <var>index</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  `index' specifies the index of an element.  `ptr' specifies the pointer to the region of the new content.  `size' specifies the size of the new content.  If it is negative, the size is assigned with `strlen(ptr)'.  If `index' is equal to or more than the number of elements, this function has no effect.</dd>
+</dl>
+
+<p>The function `cblistsort' is used in order to sort elements of a list in lexical order.</p>
+
+<dl>
+<dt><kbd>void cblistsort(CBLIST *<var>list</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  Quick sort is used for sorting.</dd>
+</dl>
+
+<p>The function `cblistlsearch' is used in order to search a list for an element using liner search.</p>
+
+<dl>
+<dt><kbd>int cblistlsearch(const CBLIST *<var>list</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  `ptr' specifies the pointer to the region of a key.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  The return value is the index of a corresponding element or -1 if there is no corresponding element.  If two or more elements corresponds, the former returns.</dd>
+</dl>
+
+<p>The function `cblistbsearch' is used in order to search a list for an element using binary search.</p>
+
+<dl>
+<dt><kbd>int cblistbsearch(const CBLIST *<var>list</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  It should be sorted in lexical order.  `ptr' specifies the pointer to the region of a key.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  The return value is the index of a corresponding element or -1 if there is no corresponding element.  If two or more elements corresponds, which returns is not defined.</dd>
+</dl>
+
+<p>The function `cblistdump' is used in order to serialize a list into a byte array.</p>
+
+<dl>
+<dt><kbd>char *cblistdump(const CBLIST *<var>list</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`list' specifies a list handle.  `sp' specifies the pointer to a variable to 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cblistload' is used in order to redintegrate a serialized list.</p>
+
+<dl>
+<dt><kbd>CBLIST *cblistload(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a byte array.  `size' specifies the size of the region.  The return value is a new list handle.</dd>
+</dl>
+
+<p>The function `cbmapopen' is used in order to get a map handle.</p>
+
+<dl>
+<dt><kbd>CBMAP *cbmapopen(void);</kbd></dt>
+<dd>The return value is a map handle.</dd>
+</dl>
+
+<p>The function `cbmapdup' is used in order to copy a map.</p>
+
+<dl>
+<dt><kbd>CBMAP *cbmapdup(CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  The return value is a new map handle.  The iterator of the source map is initialized.</dd>
+</dl>
+
+<p>The function `cbmapclose' is used in order to close a map handle.</p>
+
+<dl>
+<dt><kbd>void cbmapclose(CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  Because the region of a closed handle is released, it becomes impossible to use the handle.</dd>
+</dl>
+
+<p>The function `cbmapput' is used in order to store a record into a map.</p>
+
+<dl>
+<dt><kbd>int cbmapput(CBMAP *<var>map</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>over</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `vbuf' specifies the pointer to the region of a value.  `vsiz' specifies the size of the region of the value.  If it is negative, the size is assigned with `strlen(vbuf)'.  `over' specifies whether the value of the duplicated record is overwritten or not.  If `over' is false and the key duplicated, the return value is false, else, it is true.</dd>
+</dl>
+
+<p>The function `cbmapputcat' is used in order to concatenate a value at the end of the value of the existing record.</p>
+
+<dl>
+<dt><kbd>void cbmapputcat(CBMAP *<var>map</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `vbuf' specifies the pointer to the region of a value.  `vsiz' specifies the size of the region of the value.  If it is negative, the size is assigned with `strlen(vbuf)'.  If there is no corresponding record, a new record is created.</dd>
+</dl>
+
+<p>The function `cbmapout' is used in order to delete a record of a map.</p>
+
+<dl>
+<dt><kbd>int cbmapout(CBMAP *<var>map</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is true.  False is returned when no record corresponds to the specified key.</dd>
+</dl>
+
+<p>The function `cbmapget' is used in order to retrieve a record of a map.</p>
+
+<dl>
+<dt><kbd>const char *cbmapget(const CBMAP *<var>map</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  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.</dd>
+</dl>
+
+<p>The function `cbmapmove' is used in order to move a record to the edge of a map.</p>
+
+<dl>
+<dt><kbd>int cbmapmove(CBMAP *<var>map</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>head</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `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.</dd>
+</dl>
+
+<p>The function `cbmapiterinit' is used in order to initialize the iterator of a map.</p>
+
+<dl>
+<dt><kbd>void cbmapiterinit(CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  The iterator is used in order to access the key of every record stored in a map.</dd>
+</dl>
+
+<p>The function `cbmapiternext' is used in order to get the next key of the iterator of a map.</p>
+
+<dl>
+<dt><kbd>const char *cbmapiternext(CBMAP *<var>map</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  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.  The order of iteration is assured to be the same of the one of storing.</dd>
+</dl>
+
+<p>The function `cbmapiterval' is used in order to get the value binded to the key fetched from the iterator of a map.</p>
+
+<dl>
+<dt><kbd>const char *cbmapiterval(const char *<var>kbuf</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`kbuf' specifies the pointer to the region of a iteration key.  `sp' specifies the pointer to a variable to 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 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.</dd>
+</dl>
+
+<p>The function `cbmaprnum' is used in order to get the number of the records stored in a map.</p>
+
+<dl>
+<dt><kbd>int cbmaprnum(const CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  The return value is the number of the records stored in the map.</dd>
+</dl>
+
+<p>The function `cbmapkeys' is used in order to get the list handle contains all keys in a map.</p>
+
+<dl>
+<dt><kbd>CBLIST *cbmapkeys(CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  The return value is the list handle contains all keys in the map.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbmapvals' is used in order to get the list handle contains all values in a map.</p>
+
+<dl>
+<dt><kbd>CBLIST *cbmapvals(CBMAP *<var>map</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  The return value is the list handle contains all values in the map.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbmapdump' is used in order to serialize a map into a byte array.</p>
+
+<dl>
+<dt><kbd>char *cbmapdump(const CBMAP *<var>map</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`map' specifies a map handle.  `sp' specifies the pointer to a variable to 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbmapload' is used in order to redintegrate a serialized map.</p>
+
+<dl>
+<dt><kbd>CBMAP *cbmapload(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a byte array.  `size' specifies the size of the region.  The return value is a new map handle.</dd>
+</dl>
+
+<p>The function `cbmaploadone' is used in order to extract a record from a serialized map.</p>
+
+<dl>
+<dt><kbd>char *cbmaploadone(const char *<var>ptr</var>, int <var>size</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a byte array.  `size' specifies the size of the region.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  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.</dd>
+</dl>
+
+
+<p>The function `cbheapopen' is used in order to get a heap handle.</p>
+
+<dl>
+<dt><kbd>CBHEAP *cbheapopen(int <var>size</var>, int <var>max</var>, int(*<var>compar</var>)(const void *, const void *));</kbd></dt>
+<dd>`size' specifies the size of each record.  `max' specifies the maximum number of records in the heap.  `compar' specifies the pointer to comparing function.  The two arguments specify the pointers of records.  The comparing function should returns positive if the former is big, negative if the latter is big, 0 if both are equal.  The return value is a heap handle.</dd>
+</dl>
+
+<p>The function `cbheapdup' is used in order to copy a heap.</p>
+
+<dl>
+<dt><kbd>CBHEAP *cbheapdup(CBHEAP *<var>heap</var>);</kbd></dt>
+<dd>`heap' specifies a heap handle.  The return value is a new heap handle.</dd>
+</dl>
+
+<p>The function `cbheapclose' is used in order to close a heap handle.</p>
+
+<dl>
+<dt><kbd>void cbheapclose(CBHEAP *<var>heap</var>);</kbd></dt>
+<dd>`heap' specifies a heap handle.  Because the region of a closed handle is released, it becomes impossible to use the handle.</dd>
+</dl>
+
+<p>The function `cbheapnum' is used in order to get the number of the records stored in a heap.</p>
+
+<dl>
+<dt><kbd>int cbheapnum(CBHEAP *<var>heap</var>);</kbd></dt>
+<dd>`heap' specifies a heap handle.  The return value is the number of the records stored in the heap.</dd>
+</dl>
+
+<p>The function `cbheapinsert' is used in order to insert a record into a heap.</p>
+
+<dl>
+<dt><kbd>int cbheapinsert(CBHEAP *<var>heap</var>, const void *<var>ptr</var>);</kbd></dt>
+<dd>`heap' specifies a heap handle.  `ptr' specifies the pointer to the region of a record.  The return value is true if the record is added, else false.  If the new record is bigger than the biggest existing regord, the new record is not added.  If the new record is added and the number of records exceeds the maximum number, the biggest existing record is removed.</dd>
+</dl>
+
+<p>The function `cbheapval' is used in order to get the pointer to the region of a record in a heap.</p>
+
+<dl>
+<dt><kbd>void *cbheapval(CBHEAP *<var>heap</var>, int <var>index</var>);</kbd></dt>
+<dd>`heap' specifies a heap handle.  `index' specifies the index of a record.  The return value is the pointer to the region of the record.  If `index' is equal to or more than the number of records, the return value is `NULL'.  Note that records are organized by the nagative order the comparing function.</dd>
+</dl>
+
+<p>The function `cbheaptomalloc' is used in order to convert a heap to an allocated region.</p>
+
+<dl>
+<dt><kbd>void *cbheaptomalloc(CBHEAP *<var>heap</var>, int *<var>np</var>);</kbd></dt>
+<dd>`heap' specifies a heap handle.  `np' specifies the pointer to a variable to which the number of records of the return value is assigned.  If it is `NULL', it is not used.  The return value is the pointer to the region of the heap.  Records are sorted.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.  Because the region of the original heap is released, it should not be released again.</dd>
+</dl>
+
+<p>The function `cbsprintf' is used in order to allocate a formatted string on memory.</p>
+
+<dl>
+<dt><kbd>char *cbsprintf(const char *<var>format</var>, ...);</kbd></dt>
+<dd>`format' specifies a printf-like format string.  The conversion character `%' can be used with such flag characters as `d', `o', `u', `x', `X', `e', `E', `f', `g', `G', `c', `s', and `%'.  Specifiers of the field length and the precision can be put between the conversion characters and the flag characters.  The specifiers consist of decimal characters, `.', `+', `-', and the space character.  The other arguments are used according to the format string.  The return value is the pointer to the allocated region of 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 it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbreplace' is used in order to replace some patterns in a string.</p>
+
+<dl>
+<dt><kbd>char *cbreplace(const char *<var>str</var>, CBMAP *<var>pairs</var>);</kbd></dt>
+<dd>`str' specifies the pointer to a source string.  `pairs' specifies the handle of a map composed of pairs of replacement.  The key of each pair specifies a pattern before replacement and its value specifies the pattern after replacement.  The return value is the pointer to the allocated region of 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 it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbsplit' is used in order to make a list by splitting a serial datum.</p>
+
+<dl>
+<dt><kbd>CBLIST *cbsplit(const char *<var>ptr</var>, int <var>size</var>, const char *<var>delim</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to the region of the source content.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  `delim' specifies a string containing delimiting characters.  If it is `NULL', zero code is used as a delimiter.  The return value is a list handle.  If two delimiters are successive, it is assumed that an empty element is between the two.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose'.</dd>
+</dl>
+
+<p>The function `cbreadfile' is used in order to read whole data of a file.</p>
+
+<dl>
+<dt><kbd>char *cbreadfile(const char *<var>name</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`name' specifies the name of a file.    If it is `NULL', the standard input is specified.  `sp' specifies the pointer to a variable to 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.  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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbwritefile' is used in order to write a serial datum into a file.</p>
+
+<dl>
+<dt><kbd>int cbwritefile(const char *<var>name</var>, const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`name specifies the name of a file.  If it is `NULL', the standard output is specified.  `ptr' specifies the pointer to the region of the source content.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  If successful, the return value is true, else, it is false.  If the file exists, it is overwritten.  Else, a new file is created.</dd>
+</dl>
+
+<p>The function `cbreadlines' is used in order to read every line of a file.</p>
+
+<dl>
+<dt><kbd>CBLIST *cbreadlines(const char *<var>name</var>);</kbd></dt>
+<dd>`name' specifies the name of a file.  If it is `NULL', the standard input is specified.  The return value is a list handle of the lines if successful, else it is NULL.  Line separators are cut out.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbdirlist' is used in order to read names of files in a directory.</p>
+
+<dl>
+<dt><kbd>CBLIST *cbdirlist(const char *<var>name</var>);</kbd></dt>
+<dd>`name' specifies the name of a directory.  The return value is a list handle of names if successful, else it is NULL.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbfilestat' is used in order to get the status of a file or a directory.</p>
+
+<dl>
+<dt><kbd>int cbfilestat(const char *<var>name</var>, int *<var>isdirp</var>, int *<var>sizep</var>, int *<var>mtimep</var>);</kbd></dt>
+<dd>`name' specifies the name of a file or a directory.  `dirp' specifies the pointer to a variable to which whether the file is a directory is assigned.  If it is `NULL', it is not used.  `sizep' specifies the pointer to a variable to which the size of the file is assigned.  If it is `NULL', it is not used.  `mtimep' specifies the pointer to a variable to which the last modified time of the file is assigned.  If it is `NULL', it is not used.  If successful, the return value is true, else, false.  False is returned when the file does not exist or the permission is denied.</dd>
+</dl>
+
+<p>The function `cbremove' is used in order to remove a file or a directory and its sub ones recursively.</p>
+
+<dl>
+<dt><kbd>int cbremove(const char *<var>name</var>);</kbd></dt>
+<dd>`name' specifies the name of a file or a directory.  If successful, the return value is true, else, false.  False is returned when the file does not exist or the permission is denied.</dd>
+</dl>
+
+<p>The function `cburlbreak' is used in order to break up a URL into elements.</p>
+
+<dl>
+<dt><kbd>CBMAP *cburlbreak(const char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer to a string of URL.  The return value is a map handle.  Each key of the map is the name of an element.  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 handle of the return value is opened with the function `cbmapopen', it should be closed with the function `cbmapclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The runction `cburlresolve' is used in order to resolve a relative URL with another absolute URL.</p>
+
+<dl>
+<dt><kbd>char *cburlresolve(const char *<var>base</var>, const char *<var>target</var>);</kbd></dt>
+<dd>`base' specifies an absolute URL of a base location.  `target' specifies a URL to be resolved.  The return value is a 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cburlencode' is used in order to encode a serial object with URL encoding.</p>
+
+<dl>
+<dt><kbd>char *cburlencode(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  The return value is the pointer to 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 it is no longer in use.</dd>
+</dl>
+
+<p>The function `cburldecode' is used in order to decode a string encoded with URL encoding.</p>
+
+<dl>
+<dt><kbd>char *cburldecode(const char *<var>str</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`str' specifies the pointer to a source string.  `sp' specifies the pointer to a variable to 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 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbbaseencode' is used in order to encode a serial object with Base64 encoding.</p>
+
+<dl>
+<dt><kbd>char *cbbaseencode(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  The return value is the pointer to 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 it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbbasedecode' is used in order to decode a string encoded with Base64 encoding.</p>
+
+<dl>
+<dt><kbd>char *cbbasedecode(const char *<var>str</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`str' specifies the pointer to a source string.  `sp' specifies the pointer to a variable to 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 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbquoteencode' is used in order to encode a serial object with quoted-printable encoding.</p>
+
+<dl>
+<dt><kbd>char *cbquoteencode(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  The return value is the pointer to 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 it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbquotedecode' is used in order to decode a string encoded with quoted-printable encoding.</p>
+
+<dl>
+<dt><kbd>char *cbquotedecode(const char *<var>str</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`str' specifies the pointer to a source string.  `sp' specifies the pointer to a variable to 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 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 if it is no longer in use.</dd>
+</dl>
+
+
+<p>The function `cbmimebreak' is used in order to split a string of MIME into headers and the body.</p>
+
+<dl>
+<dt><kbd>char *cbmimebreak(const char *<var>ptr</var>, int <var>size</var>, CBMAP *<var>attrs</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to the region of MIME data.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  `attrs' specifies a map handle to store attributes.  If it is `NULL', it is not used.  Each key of the map is an attribute name uncapitalized.  `sp' specifies the pointer to a variable to 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 of the body data.  If the content type is defined, the attribute map has the key "TYPE" specifying the type.  If the character encoding is defined, the key "CHARSET" specifies the encoding name.  If the boundary string of multipart is defined, the key "BOUNDARY" specifies the string.  If the content disposition is defined, the key "DISPOSITION" specifies the direction.  If the file name is defined, the key "FILENAME" specifies the name.  If the attribute name is defined, the key "NAME" specifies the nam
 e.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbmimeparts' is used in order to split multipart data of MIME into its parts.</p>
+
+<dl>
+<dt><kbd>CBLIST *cbmimeparts(const char *<var>ptr</var>, int <var>size</var>, const char *<var>boundary</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to the region of multipart data of MIME.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  `boundary' specifies the pointer to the region of the boundary string.  The return value is a list handle.  Each element of the list is the string of a part.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbmimeencode' is used in order to encode a string with MIME encoding.</p>
+
+<dl>
+<dt><kbd>char *cbmimeencode(const char *<var>str</var>, const char *<var>encname</var>, int <var>base</var>);</kbd></dt>
+<dd>`str' specifies the pointer to a string.  `encname' specifies a string of the name of the character encoding.  The return value is the pointer to the result string.  `base' specifies whether to use Base64 encoding.  If it is false, quoted-printable is used.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbmimedecode' is used in order to decode a string encoded with MIME encoding.</p>
+
+<dl>
+<dt><kbd>char *cbmimedecode(const char *<var>str</var>, char *<var>enp</var>);</kbd></dt>
+<dd>`str' specifies the pointer to an encoded string.  `enp' specifies the pointer to a 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 pointer to 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 it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbcsvrows' is used in order to split a string of CSV into rows.</p>
+
+<dl>
+<dt><kbd>CBLIST *cbcsvrows(const char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer to the region of an CSV string.  The return value is a list handle.  Each element of the list is a string of a row.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use.  The character encoding of the input string should be US-ASCII, UTF-8, ISO-8859-*, EUC-*, or Shift_JIS.  Being compatible with MS-Excel, these functions for CSV can handle cells including such meta characters as comma, between double quotation marks.</dd>
+</dl>
+
+<p>The function `cbcsvcells' is used in order to split the string of a row of CSV into cells.</p>
+
+<dl>
+<dt><kbd>CBLIST *cbcsvcells(const char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer to the region of a row of CSV.  The return value is a list handle.  Each element of the list is the unescaped string of a cell of the row.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbcsvescape' is used in order to escape a string with the meta characters of CSV.</p>
+
+<dl>
+<dt><kbd>char *cbcsvescape(const char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer to the region of a string.  The return value is the pointer to the escaped string sanitized of meta characters.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbcsvunescape' is used in order to unescape a string with the escaped meta characters of CSV.</p>
+
+<dl>
+<dt><kbd>char *cbcsvunescape(const char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer to the region of a string with meta characters.  The return value is the pointer to the unescaped string.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbxmlbreak' is used in order to split a string of XML into tags and text sections.</p>
+
+<dl>
+<dt><kbd>CBLIST *cbxmlbreak(const char *<var>str</var>, int <var>cr</var>);</kbd></dt>
+<dd>`str' specifies the pointer to the region of an XML string.  `cr' specifies whether to remove comments.  The return value is a list handle.  Each element of the list is the string of a tag or a text section.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use.  The character encoding of the input string should be US-ASCII, UTF-8, ISO-8859-*, EUC-*, or Shift_JIS.  Because these functions for XML are not XML parser with validation check, it can handle also HTML and SGML.</dd>
+</dl>
+
+<p>The function `cbxmlattrs' is used in order to get the map of attributes of an XML tag.</p>
+
+<dl>
+<dt><kbd>CBMAP *cbxmlattrs(const char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer to the region of a tag string.  The return value is a map handle.  Each key of the map is the name of an attribute.  Each value is unescaped.  You can get the name of the tag with the key of an empty string.  Because the handle of the return value is opened with the function `cbmapopen', it should be closed with the function `cbmapclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbxmlescape' is used in order to escape a string with the meta characters of XML.</p>
+
+<dl>
+<dt><kbd>char *cbxmlescape(const char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer to the region of a string.  The return value is the pointer to the escaped string sanitized of meta characters.  This function converts only `&', `<', `>', and `"'.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbxmlunescape' is used in order to unescape a string with the entity references of XML.</p>
+
+<dl>
+<dt><kbd>char *cbxmlunescape(const char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies the pointer to the region of a string.  The return value is the pointer to 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbdeflate' is used in order to compress a serial object with ZLIB.</p>
+
+<dl>
+<dt><kbd>char *cbdeflate(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  `sp' specifies the pointer to a variable to 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 if it is no longer in use.  This function is available only if QDBM was built with ZLIB enabled.</dd>
+</dl>
+
+<p>The function `cbinflate' is used in order to decompress a serial object compressed with ZLIB.</p>
+
+<dl>
+<dt><kbd>char *cbinflate(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  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 if it is no longer in use.  This function is available only if QDBM was built with ZLIB enabled.</dd>
+</dl>
+
+<p>The function `cbgzencode' is used in order to compress a serial object with GZIP.</p>
+
+<dl>
+<dt><kbd>char *cbgzencode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  `sp' specifies the pointer to a variable to 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 if it is no longer in use.  This function is available only if QDBM was built with ZLIB enabled.</dd>
+</dl>
+
+<p>The function `cbgzdecode' is used in order to decompress a serial object compressed with GZIP.</p>
+
+<dl>
+<dt><kbd>char *cbgzdecode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  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 if it is no longer in use.  This function is available only if QDBM was built with ZLIB enabled.</dd>
+</dl>
+
+<p>The function `cbgetcrc' is used in order to get the CRC32 checksum of a serial object.</p>
+
+<dl>
+<dt><kbd>unsigned int cbgetcrc(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  The return value is the CRC32 checksum of the object.  This function is available only if QDBM was built with ZLIB enabled.</dd>
+</dl>
+
+<p>The function `cblzoencode' is used in order to compress a serial object with LZO.</p>
+
+<dl>
+<dt><kbd>char *cblzoencode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  `sp' specifies the pointer to a variable to 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 if it is no longer in use.  This function is available only if QDBM was built with LZO enabled.</dd>
+</dl>
+
+<p>The function `cblzodecode' is used in order to decompress a serial object compressed with LZO.</p>
+
+<dl>
+<dt><kbd>char *cblzodecode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  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 if it is no longer in use.  This function is available only if QDBM was built with LZO enabled.</dd>
+</dl>
+
+<p>The function `cbbzencode' is used in order to compress a serial object with BZIP2.</p>
+
+<dl>
+<dt><kbd>char *cbbzencode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  `sp' specifies the pointer to a variable to 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 if it is no longer in use.  This function is available only if QDBM was built with BZIP2 enabled.</dd>
+</dl>
+
+<p>The function `cbbzdecode' is used in order to decompress a serial object compressed with BZIP2.</p>
+
+<dl>
+<dt><kbd>char *cbbzdecode(const char *<var>ptr</var>, int <var>size</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  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 if it is no longer in use.  This function is available only if QDBM was built with BZIP2 enabled.</dd>
+</dl>
+
+<p>The function `cbiconv' is used in order to convert the character encoding of a string.</p>
+
+<dl>
+<dt><kbd>char *cbiconv(const char *<var>ptr</var>, int <var>size</var>, const char *<var>icode</var>, const char *<var>ocode</var>, int *<var>sp</var>, int *<var>mp</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  `icode' specifies the name of encoding of the input string.  `ocode' specifies the name of encoding of the output string.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  `mp' specifies the pointer to a variable to which the number of missing characters by failure of conversion is assigned.  If it is `NULL', it is not used.  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 if it is no longer in use.  This function is available only if QD
 BM was built with ICONV enabled.</dd>
+</dl>
+
+<p>The function `cbencname' is used in order to detect the encoding of a string automatically.</p>
+
+<dl>
+<dt><kbd>const char *cbencname(const char *<var>ptr</var>, int <var>size</var>);</kbd></dt>
+<dd>`ptr' specifies the pointer to a region.  `size' specifies the size of the region.  If it is negative, the size is assigned with `strlen(ptr)'.  The return value is the string of the encoding name of the string.  As it stands, US-ASCII, ISO-2022-JP, Shift_JIS, CP932, EUC-JP, UTF-8, UTF-16, UTF-16BE, and UTF-16LE are supported.  If none of them matches, ISO-8859-1 is selected.  This function is available only if QDBM was built with ICONV enabled.</dd>
+</dl>
+
+<p>The function `cbjetlag' is used in order to get the jet lag of the local time in seconds.</p>
+
+<dl>
+<dt><kbd>int cbjetlag(void);</kbd></dt>
+<dd>The return value is the jet lag of the local time in seconds.</dd>
+</dl>
+
+<p>The function `cbcalendar' is used in order to get the Gregorian calendar of a time.</p>
+
+<dl>
+<dt><kbd>void cbcalendar(time_t <var>t</var>, int <var>jl</var>, int *<var>yearp</var>, int *<var>monp</var>, int *<var>dayp</var>, int *<var>hourp</var>, int *<var>minp</var>, int *<var>secp</var>);</kbd></dt>
+<dd>`t' specifies a source time.  If it is negative, the current time is specified.  `jl' specifies the jet lag of a location in seconds.  `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.</dd>
+</dl>
+
+<p>The function `cbdayofweek' is used in order to get the day of week of a date.</p>
+
+<dl>
+<dt><kbd>int cbdayofweek(int <var>year</var>, int <var>mon</var>, int <var>day</var>);</kbd></dt>
+<dd>`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.</dd>
+</dl>
+
+<p>The function `cbdatestrwww' is used in order to get the string for a date in W3CDTF.</p>
+
+<dl>
+<dt><kbd>char *cbdatestrwww(time_t <var>t</var>, int <var>jl</var>);</kbd></dt>
+<dd>`t' specifies a source time.  If it is negative, the current time is specified.  `jl' specifies the jet lag of a location in seconds.  The return value is the string of the date in W3CDTF (YYYY-MM-DDThh:mm:ddTZD).  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbdatestrhttp' is used in order to get the string for a date in RFC 1123 format.</p>
+
+<dl>
+<dt><kbd>char *cbdatestrhttp(time_t <var>t</var>, int <var>jl</var>);</kbd></dt>
+<dd>`t' specifies a source time.  If it is negative, the current time is specified.  `jl' specifies the jet lag of a location in seconds.  The return value is the string of the date in RFC 1123 format (Wdy, DD-Mon-YYYY hh:mm:dd TZD).  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `cbstrmktime' is used in order to get the time value of a date string in decimal, hexadecimal, W3CDTF, or RFC 822 (1123).</p>
+
+<dl>
+<dt><kbd>time_t cbstrmktime(const char *<var>str</var>);</kbd></dt>
+<dd>`str' specifies a date string in decimal, hexadecimal, W3CDTF, or RFC 822 (1123).  The return value is the time value of the date or -1 if the format is invalid.  Decimal can be trailed by "s" for in seconds, "m" for in minutes, "h" for in hours, and "d" for in days.</dd>
+</dl>
+
+<p>The function `cbproctime' is used in order to get user and system processing times.</p>
+
+<dl>
+<dt><kbd>void cbproctime(double *<var>usrp</var>, double *<var>sysp</var>);</kbd></dt>
+<dd>`usrp' specifies the pointer to a variable to which the user processing time is assigned.  If it is `NULL', it is not used.  The unit of time is seconds.  `sysp' specifies the pointer to a variable to which the system processing time is assigned.  If it is `NULL', it is not used.  The unit of time is seconds.</dd>
+</dl>
+
+<p>The function `cbstdiobin' is used in order to ensure that the standard I/O is binary mode.</p>
+
+<dl>
+<dt><kbd>void cbstdiobin(void);</kbd></dt>
+<dd>This function is useful for applications on dosish file systems.</dd>
+</dl>
+
+<h3>Examples</h3>
+
+<p>The following example is typical use.</p>
+
+<pre>#include <cabin.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(int argc, char **argv){
+  CBDATUM *datum;
+  CBLIST *list;
+  CBMAP *map;
+  char *buf1, *buf2;
+  int i;
+
+  /* open the datum handle */
+  datum = cbdatumopen("123", -1);
+  /* concatenate the data */
+  cbdatumcat(datum, "abc", -1);
+  /* print the datum */
+  printf("%s\n", cbdatumptr(datum));
+  /* close the datum handle */
+  cbdatumclose(datum);
+
+  /* open the list handle */
+  list = cblistopen();
+  /* add elements into the list */
+  cblistpush(list, "apple", -1);
+  cblistpush(list, "orange", -1);
+  /* print all elements */
+  for(i = 0; i < cblistnum(list); i++){
+    printf("%s\n", cblistval(list, i, NULL));
+  }
+  /* close the list handle */
+  cblistclose(list);
+
+  /* open the map handle */
+  map = cbmapopen();
+  /* add records into the map */
+  cbmapput(map, "dog", -1, "bowwow", -1, 1);
+  cbmapput(map, "cat", -1, "meow", -1, 1);
+  /* search for values and print them */
+  printf("%s\n", cbmapget(map, "dog", -1, NULL));
+  printf("%s\n", cbmapget(map, "cat", -1, NULL));
+  /* close the map */
+  cbmapclose(map);
+
+  /* Base64 encoding */
+  buf1 = cbbaseencode("I miss you.", -1);
+  printf("%s\n", buf1);
+  /* Base64 decoding */
+  buf2 = cbbasedecode(buf1, NULL);
+  printf("%s\n", buf2);
+  /* release the resources */
+  free(buf2);
+  free(buf1);
+
+  /* register a plain pointer to the global garbage collector */
+  buf1 = cbmemdup("Take it easy.", -1);
+  cbglobalgc(buf1, free);
+  /* the pointer is available but you don't have to release it */
+  printf("%s\n", buf1);
+  
+  /* register a list to the global garbage collector */
+  list = cblistopen();
+  cbglobalgc(list, (void (*)(void *))cblistclose);
+  /* the handle is available but you don't have to close it */
+  cblistpush(list, "Don't hesitate.", -1);
+  for(i = 0; i < cblistnum(list); i++){
+    printf("%s\n", cblistval(list, i, NULL));    
+  }
+
+  return 0;
+}
+</pre>
+
+<h3>Notes</h3>
+
+<p>How to build programs using Cabin is the same as the case of Depot.</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>Functions of Cabin except for `cbglobalgc' are thread-safe as long as a handle is not accessed by threads at the same time, on the assumption that `errno', `malloc', and so on are thread-safe.</p>
+
+<hr />
+
+<h2><a name="cabincli" id="cabincli" class="head">Commands for Cabin</a></h2>
+
+<p>Cabin has the following command line interfaces.</p>
+
+<p>The command `cbtest' is a utility for facility test and performance test.  Measure the execution time of the command.  This command is used in the following format.  `rnum' specifies the number of records.</p>
+
+<dl>
+<dt><kbd>cbtest sort [-d] <var>rnum</var></kbd></dt>
+<dd>Perform test of sorting algorithms.</dd>
+<dt><kbd>cbtest strstr [-d] <var>rnum</var></kbd></dt>
+<dd>Perform test of string locating algorithms.</dd>
+<dt><kbd>cbtest list [-d] <var>rnum</var></kbd></dt>
+<dd>Perform writing test of list.</dd>
+<dt><kbd>cbtest map [-d] <var>rnum</var></kbd></dt>
+<dd>Perform writing test of map.</dd>
+<dt><kbd>cbtest wicked <var>rnum</var></kbd></dt>
+<dd>Perform updating operations of list and map selected at random.</dd>
+<dt><kbd>cbtest misc</kbd></dt>
+<dd>Perform test of miscellaneous routines.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-d</kbd> : read and show data of the result.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<p>The command `cbcodec' is a tool to use encoding and decoding features provided by Cabin.  This command is used in the following format.  `file' specifies a input file.  If it is omitted, the standard input is read.</p>
+
+<dl>
+<dt><kbd>cbcodec url [-d] [-br] [-rs <var>base</var> <var>target</var>] [-l] [-e <var>expr</var>] [<var>file</var>]</kbd></dt>
+<dd>Perform URL encoding and its decoding.</dd>
+<dt><kbd>cbcodec base [-d] [-l] [-c <var>num</var>] [-e <var>expr</var>] [<var>file</var>]</kbd></dt>
+<dd>Perform Base64 encoding and its decoding.</dd>
+<dt><kbd>cbcodec quote [-d] [-l] [-c <var>num</var>] [-e <var>expr</var>] [<var>file</var>]</kbd></dt>
+<dd>Perform quoted-printable encoding and its decoding.</dd>
+<dt><kbd>cbcodec mime [-d] [-hd] [-bd] [-part <var>num</var>] [-l] [-ec <var>code</var>] [-qp] [-dc] [-e <var>expr</var>] [<var>file</var>]</kbd></dt>
+<dd>Perform MIME encoding and its decoding.</dd>
+<dt><kbd>cbcodec csv [-d] [-t] [-l] [-e <var>expr</var>] [-html] [<var>file</var>]</kbd></dt>
+<dd>Process CSV.  By default, escape meta characters.</dd>
+<dt><kbd>cbcodec xml [-d] [-p] [-l] [-e <var>expr</var>] [-tsv] [<var>file</var>]</kbd></dt>
+<dd>Process XML.  By default, escape meta characters.</dd>
+<dt><kbd>cbcodec zlib [-d] [-gz] [-crc] [<var>file</var>]</kbd></dt>
+<dd>Perform deflation and inflation with ZLIB.  It is available only if QDBM was built with ZLIB enabled.</dd>
+<dt><kbd>cbcodec lzo [-d] [<var>file</var>]</kbd></dt>
+<dd>Perform compression and decompression with LZO.  It is available only if QDBM was built with LZO enabled.</dd>
+<dt><kbd>cbcodec bzip [-d] [<var>file</var>]</kbd></dt>
+<dd>Perform compression and decompression with BZIP2.  It is available only if QDBM was built with BZIP2 enabled.</dd>
+<dt><kbd>cbcodec iconv [-ic <var>code</var>] [-oc <var>code</var>] [-ol <var>ltype</var>] [-cn] [-um] [-wc] [<var>file</var>]</kbd></dt>
+<dd>Convert character encoding with ICONV.  It is available only if QDBM was built with ICONV enabled.</dd>
+<dt><kbd>cbcodec date [-wf] [-rf] [-utc] [<var>str</var>]</kbd></dt>
+<dd>Convert a date string specified `str'.  By default, UNIX time is output.  If `str' is omitted, the current date is dealt.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-d</kbd> : perform decoding (unescaping), not encoding (escaping).</li>
+<li><kbd>-br</kbd> : break up URL into elements.</li>
+<li><kbd>-rs</kbd> : resolve relative URL.</li>
+<li><kbd>-l</kbd> : output the tailing newline.</li>
+<li><kbd>-e <var>expr</var></kbd> : specify input data directly.</li>
+<li><kbd>-c <var>num</var></kbd> : limit the number of columns of the encoded data.</li>
+<li><kbd>-hd</kbd> : parse MIME and extract headers in TSV format.</li>
+<li><kbd>-bd</kbd> : parse MIME and extract the body.</li>
+<li><kbd>-part <var>num</var></kbd> : parse MIME and extract a part.</li>
+<li><kbd>-ec <var>code</var></kbd> : specify the input encoding, which is UTF-8 by default.</li>
+<li><kbd>-qp</kbd> : use quoted-printable encoding, which is Base64 by default.</li>
+<li><kbd>-dc</kbd> : output the encoding name instead of the result string when decoding.</li>
+<li><kbd>-t</kbd> : parse CSV.  Convert the data into TSV.  Tab and new-line in a cell are deleted.</li>
+<li><kbd>-html</kbd> : parse CSV.  Convert the data into HTML.</li>
+<li><kbd>-p</kbd> : parse XML.  Show tags and text sections with dividing headers.</li>
+<li><kbd>-tsv</kbd> : parse XML.  Show the result in TSV format.  Characters of tabs and new-lines are URL-encoded.</li>
+<li><kbd>-gz</kbd> : use GZIP format.</li>
+<li><kbd>-crc</kbd> : output the CRC32 checksum as hexadecimal and big endian.</li>
+<li><kbd>-ic <var>code</var></kbd> : specify the input encoding, which is detected automatically by default.</li>
+<li><kbd>-oc <var>code</var></kbd> : specify the output encoding, which is UTF-8 by default.</li>
+<li><kbd>-ol <var>ltype</var></kbd> : convert line feed characters, with `unix'(LF), `dos'(CRLF), and `mac'(CR).</li>
+<li><kbd>-cn</kbd> : detect the input encoding and show its name.</li>
+<li><kbd>-wc</kbd> : count the number of characters of the input string of UTF-8.</li>
+<li><kbd>-um</kbd> : output mappings of UCS-2 characters and C strings of UTF-16BE and UTF-8.</li>
+<li><kbd>-wf</kbd> : output in W3CDTF format.</li>
+<li><kbd>-rf</kbd> : output in RFC 1123 format.</li>
+<li><kbd>-utc</kbd> : output the coordinate universal time.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<hr />
+
+<h2><a name="villaapi" id="villaapi" class="head">Villa: Advanced API</a></h2>
+
+<h3>Overview</h3>
+
+<p>Villa is the advanced API of QDBM.  It provides routines for managing a database file of B+ tree.  Each record is stored being sorted in order defined by a user.  As for hash databases, retrieving method is provided only as complete accord.  However, with Villa, it is possible to retrieve records specified by range.  Cursor is used in order to access each record in order.  It is possible to store records duplicating keys in a database.  Moreover, according to the transaction mechanism, you can commit or abort operations of a database in a lump.</p>
+
+<p>Villa is implemented, based on Depot and Cabin.  A database file of Villa is actual one of Depot.  Although processing speed of retrieving and storing is slower than Depot, the size of a database is smaller.</p>
+
+<p>In order to use Villa, you should include `depot.h', `cabin.h', `villa.h' and `stdlib.h' in the source files.  Usually, the following description will be near the beginning of a source file.</p>
+
+<dl>
+<dt><kbd>#include <depot.h></kbd></dt>
+<dt><kbd>#include <cabin.h></kbd></dt>
+<dt><kbd>#include <villa.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+</dl>
+
+<p>A pointer to `VILLA' is used as a database handle. It is like that some file I/O routines of `stdio.h' use a pointer to `FILE'.  A database handle is opened with the function `vlopen' and closed with `vlclose'.  You should not refer directly to any member of the handle.  If a fatal error occurs in a database, any access method via the handle except `vlclose' will not work and return error status.  Although a process is allowed to use multiple database handles at the same time, handles of the same database file should not be used.  Before the cursor is used, it should be initialized by one of `vlcurfirst', `vlcurlast' or `vlcurjump'.  Also after storing or deleting a record with functions except for `vlcurput' and `vlcurout', the cursor should be initialized.</p>
+
+<p>Villa also assign the external variable `dpecode' with the error code. The function `dperrmsg' is used in order to get the message of the error code.</p>
+
+<h3>API</h3>
+
+<p>You can define a comparing function to specify the order of records.  The function should be the following type.</p>
+
+<dl>
+<dt><kbd>typedef int(*VLCFUNC)(const char *<var>aptr</var>, int <var>asiz</var>, const char *<var>bptr</var>, int <var>bsiz</var>);</kbd></dt>
+<dd>`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.  The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent.</dd>
+</dl>
+
+<p>The function `vlopen' is used in order to get a database handle.</p>
+
+<dl>
+<dt><kbd>VILLA *vlopen(const char *<var>name</var>, int <var>omode</var>, VLCFUNC <var>cmp</var>);</kbd></dt>
+<dd>`name' specifies the name of a database file.  `omode' specifies the connection mode: `VL_OWRITER' as a writer, `VL_OREADER' as a reader.  If the mode is `VL_OWRITER', the following may be added by bitwise or: `VL_OCREAT', which means it creates a new database if not exist, `VL_OTRUNC', which means it creates a new database regardless if one exists, `VL_OZCOMP', which means leaves in the database are compressed with ZLIB, `VL_OYCOMP', which means leaves in the database are compressed with LZO, `VL_OXCOMP', which means leaves in the database are compressed with BZIP2.  Both of `VL_OREADER' and `VL_OWRITER' can be added to by bitwise or: `VL_ONOLCK', which means it opens a database file without file locking, or `VL_OLCKNB', which means locking is performed without blocking.  `cmp' specifies the comparing function: `VL_CMPLEX' comparing keys in lexical order, `VL_CMPINT' comparing keys as objects of `int' in native byte order, `VL_CMPNUM' comparing keys as numbers of big en
 dian, `VL_CMPDEC' comparing keys as decimal strings.  Any function based on the declaration of the type `VLCFUNC' can be assigned to the comparing function.  The comparing function should be kept same in the life of a database.  The return value is the database handle or `NULL' if it is not successful.  While connecting as a writer, an exclusive lock is invoked to the database file.  While connecting as a reader, a shared lock is invoked to the database file.  The thread blocks until the lock is achieved.  `VL_OZCOMP', `VL_OYCOMP', and `VL_OXCOMP' are available only if QDBM was built each with ZLIB, LZO, and BZIP2 enabled.  If `VL_ONOLCK' is used, the application is responsible for exclusion control.</dd>
+</dl>
+
+<p>The function `vlclose' is used in order to close a database handle.</p>
+
+<dl>
+<dt><kbd>int vlclose(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  If successful, the return value is true, else, it is false.  Because the region of a closed handle is released, it becomes impossible to use the handle.  Updating a database is assured to be written when the handle is closed.  If a writer opens a database but does not close it appropriately, the database will be broken.  If the transaction is activated and not committed, it is aborted.</dd>
+</dl>
+
+<p>The function `vlput' is used in order to store a record.</p>
+
+<dl>
+<dt><kbd>int vlput(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>dmode</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `vbuf' specifies the pointer to the region of a value.  `vsiz' specifies the size of the region of the value.  If it is negative, the size is assigned with `strlen(vbuf)'.  `dmode' specifies behavior when the key overlaps, by the following values: `VL_DOVER', which means the specified value overwrites the existing one, `VL_DKEEP', which means the existing value is kept, `VL_DCAT', which means the specified value is concatenated at the end of the existing value, `VL_DDUP', which means duplication of keys is allowed and the specified value is added as the last one, `VL_DDUPR', which means duplication of keys is allowed and the specified value is added as the first one.  If successful, the return value is true, else, it is false.  The cursor becomes
  unavailable due to updating database.</dd>
+</dl>
+
+<p>The function `vlout' is used in order to delete a record.</p>
+
+<dl>
+<dt><kbd>int vlout(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is true, else, it is false.  False is returned when no record corresponds to the specified key.  When the key of duplicated records is specified, the first record of the same key is deleted.  The cursor becomes unavailable due to updating database.</dd>
+</dl>
+
+<p>The function `vlget' is used in order to retrieve a record.</p>
+
+<dl>
+<dt><kbd>char *vlget(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  If successful, the return value is the pointer to the region of the value of the corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the specified key.  When the key of duplicated records is specified, the value of the first record of the same key 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `vlvsiz' is used in order to get the size of the value of a record.</p>
+
+<dl>
+<dt><kbd>int vlvsiz(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is the size of the value of the corresponding record, else, it is -1.  If multiple records correspond, the size of the first is returned.</dd>
+</dl>
+
+<p>The function `vlvnum' is used in order to get the number of records corresponding a key.</p>
+
+<dl>
+<dt><kbd>int vlvnum(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  The return value is the number of corresponding records.  If no record corresponds, 0 is returned.</dd>
+</dl>
+
+<p>The function `vlputlist' is used in order to store plural records corresponding a key.</p>
+
+<dl>
+<dt><kbd>int vlputlist(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, const CBLIST *<var>vals</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `vals' specifies a list handle of values.  The list should not be empty.  If successful, the return value is true, else, it is false.  The cursor becomes unavailable due to updating database.</dd>
+</dl>
+
+<p>The function `vloutlist' is used in order to delete all records corresponding a key.</p>
+
+<dl>
+<dt><kbd>int vloutlist(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is true, else, it is false.  False is returned when no record corresponds to the specified key.  The cursor becomes unavailable due to updating database.</dd>
+</dl>
+
+<p>The function `vlgetlist' is used in order to retrieve values of all records corresponding a key.</p>
+
+<dl>
+<dt><kbd>CBLIST *vlgetlist(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  If successful, the return value is a list handle of the values of the corresponding records, else, it is `NULL'.  `NULL' is returned when no record corresponds to the specified key.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The function `vlgetcat' is used in order to retrieve concatenated values of all records corresponding a key.</p>
+
+<dl>
+<dt><kbd>char *vlgetcat(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  If successful, the return value is the pointer to the region of the concatenated values of the corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the specified key.  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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `vlcurfirst' is used in order to move the cursor to the first record.</p>
+
+<dl>
+<dt><kbd>int vlcurfirst(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  If successful, the return value is true, else, it is false.  False is returned if there is no record in the database.</dd>
+</dl>
+
+<p>The function `vlcurlast' is used in order to move the cursor to the last record.</p>
+
+<dl>
+<dt><kbd>int vlcurlast(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  If successful, the return value is true, else, it is false.  False is returned if there is no record in the database.</dd>
+</dl>
+
+<p>The function `vlcurprev' is used in order to move the cursor to the previous record.</p>
+
+<dl>
+<dt><kbd>int vlcurprev(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  If successful, the return value is true, else, it is false.  False is returned if there is no previous record.</dd>
+</dl>
+
+<p>The function `vlcurnext' is used in order to move the cursor to the next record.</p>
+
+<dl>
+<dt><kbd>int vlcurnext(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  If successful, the return value is true, else, it is false.  False is returned if there is no next record.</dd>
+</dl>
+
+<p>The function `vlcurjump' is used in order to move the cursor to a position around a record.</p>
+
+<dl>
+<dt><kbd>int vlcurjump(VILLA *<var>villa</var>, const char *<var>kbuf</var>, int <var>ksiz</var>, int <var>jmode</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  `kbuf' specifies the pointer to the region of a key.  `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned with `strlen(kbuf)'.  `jmode' specifies detail adjustment: `VL_JFORWARD', which means that the cursor is set to the first record of the same key and that the cursor is set to the next substitute if completely matching record does not exist, `VL_JBACKWARD', which means that the cursor is set to the last record of the same key and that the cursor is set to the previous substitute if completely matching record does not exist.  If successful, the return value is true, else, it is false.  False is returned if there is no record corresponding the condition.</dd>
+</dl>
+
+<p>The function `vlcurkey' is used in order to get the key of the record where the cursor is.</p>
+
+<dl>
+<dt><kbd>char *vlcurkey(VILLA *<var>villa</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  If successful, the return value is the pointer to the region of the key of the corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the cursor.  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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `vlcurval' is used in order to get the value of the record where the cursor is.</p>
+
+<dl>
+<dt><kbd>char *vlcurval(VILLA *<var>villa</var>, int *<var>sp</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  `sp' specifies the pointer to a variable to which the size of the region of the return value is assigned.  If it is `NULL', it is not used.  If successful, the return value is the pointer to the region of the value of the corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the cursor.  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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `vlcurput' is used in order to insert a record around the cursor.</p>
+
+<dl>
+<dt><kbd>int vlcurput(VILLA *<var>villa</var>, const char *<var>vbuf</var>, int <var>vsiz</var>, int <var>cpmode</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  `vbuf' specifies the pointer to the region of a value.  `vsiz' specifies the size of the region of the value.  If it is negative, the size is assigned with `strlen(vbuf)'.  `cpmode' specifies detail adjustment: `VL_CPCURRENT', which means that the value of the current record is overwritten, `VL_CPBEFORE', which means that a new record is inserted before the current record, `VL_CPAFTER', which means that a new record is inserted after the current record.  If successful, the return value is true, else, it is false.  False is returned when no record corresponds to the cursor.  After insertion, the cursor is moved to the inserted record.</dd>
+</dl>
+
+<p>The function `vlcurout' is used in order to delete the record where the cursor is.</p>
+
+<dl>
+<dt><kbd>int vlcurout(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  If successful, the return value is true, else, it is false.  False is returned when no record corresponds to the cursor.  After deletion, the cursor is moved to the next record if possible.</dd>
+</dl>
+
+<p>The function `vlsettuning' is used in order to set the tuning parameters for performance.</p>
+
+<dl>
+<dt><kbd>void vlsettuning(VILLA *<var>villa</var>, int <var>lrecmax</var>, int <var>nidxmax</var>, int <var>lcnum</var>, int <var>ncnum</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  `lrecmax' specifies the max number of records in a leaf node of B+ tree.  If it is not more than 0, the default value is specified.  `nidxmax' specifies the max number of indexes in a non-leaf node of B+ tree.  If it is not more than 0, the default value is specified.  `lcnum' specifies the max number of caching leaf nodes.  If it is not more than 0, the default value is specified.  `ncnum' specifies the max number of caching non-leaf nodes.  If it is not more than 0, the default value is specified.  The default setting is equivalent to `vlsettuning(49, 192, 1024, 512)'.  Because tuning parameters are not saved in a database, you should specify them every opening a database.</dd>
+</dl>
+
+<p>The function `vlsetfbpsiz' is used in order to set the size of the free block pool of a database handle.</p>
+
+<dl>
+<dt><kbd>int vlsetfbpsiz(VILLA *<var>villa</var>, int <var>size</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  `size' specifies the size of the free block pool of a database.  If successful, the return value is true, else, it is false.  The default size of the free block pool is 256.  If the size is greater, the space efficiency of overwriting values is improved with the time efficiency sacrificed.</dd>
+</dl>
+
+<p>The function `vlsync' is used in order to synchronize updating contents with the file and the device.</p>
+
+<dl>
+<dt><kbd>int vlsync(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  If successful, the return value is true, else, it is false.  This function is useful when another process uses the connected database file.  This function should not be used while the transaction is activated.</dd>
+</dl>
+
+<p>The function `vloptimize' is used in order to optimize a database.</p>
+
+<dl>
+<dt><kbd>int vloptimize(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  If successful, the return value is true, else, it is false.  In an alternating succession of deleting and storing with overwrite or concatenate, dispensable regions accumulate.  This function is useful to do away with them.  This function should not be used while the transaction is activated.</dd>
+</dl>
+
+<p>The function `vlname' is used in order to get the name of a database.</p>
+
+<dl>
+<dt><kbd>char *vlname(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  If successful, the return value is the pointer to the region of the name of the database, 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `vlfsiz' is used in order to get the size of a database file.</p>
+
+<dl>
+<dt><kbd>int vlfsiz(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  If successful, the return value is the size of the database file, else, it is -1.  Because of the I/O buffer, the return value may be less than the hard size.</dd>
+</dl>
+
+<p>The function `vllnum' is used in order to get the number of the leaf nodes of B+ tree.</p>
+
+<dl>
+<dt><kbd>int vllnum(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  If successful, the return value is the number of the leaf nodes, else, it is -1.</dd>
+</dl>
+
+<p>The function `vlnnum' is used in order to get the number of the non-leaf nodes of B+ tree.</p>
+
+<dl>
+<dt><kbd>int vlnnum(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  If successful, the return value is the number of the non-leaf nodes, else, it is -1.</dd>
+</dl>
+
+<p>The function `vlrnum' is used in order to get the number of the records stored in a database.</p>
+
+<dl>
+<dt><kbd>int vlrnum(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  If successful, the return value is the number of the records stored in the database, else, it is -1.</dd>
+</dl>
+
+<p>The function `vlwritable' is used in order to check whether a database handle is a writer or not.</p>
+
+<dl>
+<dt><kbd>int vlwritable(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  The return value is true if the handle is a writer, false if not.</dd>
+</dl>
+
+<p>The function `vlfatalerror' is used in order to check whether a database has a fatal error or not.</p>
+
+<dl>
+<dt><kbd>int vlfatalerror(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  The return value is true if the database has a fatal error, false if not.</dd>
+</dl>
+
+<p>The function `vlinode' is used in order to get the inode number of a database file.</p>
+
+<dl>
+<dt><kbd>int vlinode(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  The return value is the inode number of the database file.</dd>
+</dl>
+
+<p>The function `vlmtime' is used in order to get the last modified time of a database.</p>
+
+<dl>
+<dt><kbd>time_t vlmtime(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  The return value is the last modified time of the database.</dd>
+</dl>
+
+<p>The function `vltranbegin' is used in order to begin the transaction.</p>
+
+<dl>
+<dt><kbd>int vltranbegin(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  If successful, the return value is true, else, it is false.  Because this function does not perform mutual exclusion control in multi-thread, the application is responsible for it.  Only one transaction can be activated with a database handle at the same time.</dd>
+</dl>
+
+<p>The function `vltrancommit' is used in order to commit the transaction.</p>
+
+<dl>
+<dt><kbd>int vltrancommit(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  If successful, the return value is true, else, it is false.  Updating a database in the transaction is fixed when it is committed successfully.</dd>
+</dl>
+
+<p>The function `vltranabort' is used in order to abort the transaction.</p>
+
+<dl>
+<dt><kbd>int vltranabort(VILLA *<var>villa</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  If successful, the return value is true, else, it is false.  Updating a database in the transaction is discarded when it is aborted.  The state of the database is rollbacked to before transaction.</dd>
+</dl>
+
+<p>The function `vlremove' is used in order to remove a database file.</p>
+
+<dl>
+<dt><kbd>int vlremove(const char *<var>name</var>);</kbd></dt>
+<dd>`name' specifies the name of a database file.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<p>The function `vlrepair' is used in order to repair a broken database file.</p>
+
+<dl>
+<dt><kbd>int vlrepair(const char *<var>name</var>, VLCFUNC <var>cmp</var>);</kbd></dt>
+<dd>`name' specifies the name of a database file.  `cmp' specifies the comparing function of the database file.  If successful, the return value is true, else, it is false.  There is no guarantee that all records in a repaired database file correspond to the original or expected state.</dd>
+</dl>
+
+<p>The function `vlexportdb' is used in order to dump all records as endian independent data.</p>
+
+<dl>
+<dt><kbd>int vlexportdb(VILLA *<var>villa</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`villa' specifies a database handle.  `name' specifies the name of an output file.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<p>The function `vlimportdb' is used in order to load all records from endian independent data.</p>
+
+<dl>
+<dt><kbd>int vlimportdb(VILLA *<var>villa</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`villa' specifies a database handle connected as a writer.  The database of the handle must be empty.  `name' specifies the name of an input file.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<h3>Examples</h3>
+
+<p>The following example stores and retrieves a phone number, using the name as the key.</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NAME     "mikio"
+#define NUMBER   "000-1234-5678"
+#define DBNAME   "book"
+
+int main(int argc, char **argv){
+  VILLA *villa;
+  char *val;
+
+  /* open the database */
+  if(!(villa = vlopen(DBNAME, VL_OWRITER | VL_OCREAT, VL_CMPLEX))){
+    fprintf(stderr, "vlopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* store the record */
+  if(!vlput(villa, NAME, -1, NUMBER, -1, VL_DOVER)){
+    fprintf(stderr, "vlput: %s\n", dperrmsg(dpecode));
+  }
+
+  /* retrieve the record */
+  if(!(val = vlget(villa, NAME, -1, NULL))){
+    fprintf(stderr, "vlget: %s\n", dperrmsg(dpecode));
+  } else {
+    printf("Name: %s\n", NAME);
+    printf("Number: %s\n", val);
+    free(val);
+  }
+
+  /* close the database */
+  if(!vlclose(villa)){
+    fprintf(stderr, "vlclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<p>The following example performs forward matching search for strings.</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define DBNAME   "words"
+#define PREFIX   "apple"
+
+int main(int argc, char **argv){
+  VILLA *villa;
+  char *key, *val;
+
+  /* open the database */
+  if(!(villa = vlopen(DBNAME, VL_OWRITER | VL_OCREAT, VL_CMPLEX))){
+    fprintf(stderr, "vlopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* store records */
+  if(!vlput(villa, "applet", -1, "little application", -1, VL_DDUP) ||
+     !vlput(villa, "aurora", -1, "polar wonderwork", -1, VL_DDUP) ||
+     !vlput(villa, "apple", -1, "delicious fruit", -1, VL_DDUP) ||
+     !vlput(villa, "amigo", -1, "good friend", -1, VL_DDUP) ||
+     !vlput(villa, "apple", -1, "big city", -1, VL_DDUP)){
+    fprintf(stderr, "vlput: %s\n", dperrmsg(dpecode));
+  }
+
+  /* set the cursor at the top of candidates */
+  vlcurjump(villa, PREFIX, -1, VL_JFORWARD);
+
+  /* scan with the cursor */
+  while((key = vlcurkey(villa, NULL)) != NULL){
+    if(strstr(key, PREFIX) != key){
+      free(key);
+      break;
+    }
+    if(!(val = vlcurval(villa, NULL))){
+      fprintf(stderr, "vlcurval: %s\n", dperrmsg(dpecode));
+      free(key);
+      break;
+    }
+    printf("%s: %s\n", key, val);
+    free(val);
+    free(key);
+    vlcurnext(villa);
+  }
+
+  /* close the database */
+  if(!vlclose(villa)){
+    fprintf(stderr, "vlclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<h3>Notes</h3>
+
+<p>How to build programs using Villa is the same as the case of Depot.</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>If QDBM was built with POSIX thread enabled, the global variable `dpecode' is treated as thread specific data, and functions of Villa are reentrant.  In that case, they are thread-safe as long as a handle is not accessed by threads at the same time, on the assumption that `errno', `malloc', and so on are thread-safe.</p>
+
+<h3>Vista: Extended Advanced API</h3>
+
+<p>Vista is the extended API of Villa.  To compensate for the defect that Villa can not handle a file whose size is more than 2GB, Vista does not use Depot but Curia for handling its internal database.  While Vista provides data structure and operations of B+ tree as with Villa, its database is realized as a directory.</p>
+
+<p>In order to use Vista, you should include `vista.h' instead of `villa.h'.  Because Vista is implemented by overriding symbols of Villa, it can be used as with Villa.  That is, Signatures of Villa and Vista is all the same.  However, as its adverse effect, modules (compilation unit) using Vista can not use Villa (do not include `villa.h').</p>
+
+<hr />
+
+<h2><a name="villacli" id="villacli" class="head">Commands for Villa</a></h2>
+
+<p>Villa has the following command line interfaces.</p>
+
+<p>The command `vlmgr' is a utility for debugging Villa and its applications.  It features editing and checking of a database.  It can be used for database applications with shell scripts.  This command is used in the following format.  `name' specifies a database name.  `key' specifies the key of a record.  `val' specifies the value of a record.</p>
+
+<dl>
+<dt><kbd>vlmgr create [-cz|-cy|-cx] <var>name</var></kbd></dt>
+<dd>Create a database file.</dd>
+<dt><kbd>vlmgr put [-kx|-ki] [-vx|-vi|-vf] [-keep|-cat|-dup] <var>name</var> <var>key</var> <var>val</var></kbd></dt>
+<dd>Store a record with a key and a value.</dd>
+<dt><kbd>vlmgr out [-l] [-kx|-ki] <var>name</var> <var>key</var></kbd></dt>
+<dd>Delete a record with a key.</dd>
+<dt><kbd>vlmgr get [-nl] [-l] [-kx|-ki] [-ox] [-n] <var>name</var> <var>key</var></kbd></dt>
+<dd>Retrieve a record with a key and output it to the standard output.</dd>
+<dt><kbd>vlmgr list [-nl] [-k|-v] [-kx|-ki] [-ox] [-top <var>key</var>] [-bot <var>key</var>] [-gt] [-lt] [-max <var>num</var>] [-desc] <var>name</var></kbd></dt>
+<dd>List all keys and values delimited with tab and line-feed to the standard output.</dd>
+<dt><kbd>vlmgr optimize <var>name</var></kbd></dt>
+<dd>Optimize a database.</dd>
+<dt><kbd>vlmgr inform [-nl] <var>name</var></kbd></dt>
+<dd>Output miscellaneous information to the standard output.</dd>
+<dt><kbd>vlmgr remove <var>name</var></kbd></dt>
+<dd>Remove a database file.</dd>
+<dt><kbd>vlmgr repair [-ki] <var>name</var></kbd></dt>
+<dd>Repair a broken database file.</dd>
+<dt><kbd>vlmgr exportdb [-ki] <var>name</var> <var>file</var></kbd></dt>
+<dd>Dump all records as endian independent data.</dd>
+<dt><kbd>vlmgr importdb [-ki] <var>name</var> <var>file</var></kbd></dt>
+<dd>Load all records from endian independent data.</dd>
+<dt><kbd>vlmgr version</kbd></dt>
+<dd>Output version information of QDBM to the standard output.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-cz</kbd> : compress leaves in the database with ZLIB.</li>
+<li><kbd>-cy</kbd> : compress leaves in the database with LZO.</li>
+<li><kbd>-cx</kbd> : compress leaves in the database with BZIP2.</li>
+<li><kbd>-l</kbd> : all records corresponding the key are dealt.</li>
+<li><kbd>-kx</kbd> : treat `key' as a binary expression of hexadecimal notation.</li>
+<li><kbd>-ki</kbd> : treat `key' as an integer expression of decimal notation.</li>
+<li><kbd>-vx</kbd> : treat `val' as a binary expression of hexadecimal notation.</li>
+<li><kbd>-vi</kbd> : treat `val' as an integer expression of decimal notation.</li>
+<li><kbd>-vf</kbd> : read the value from a file specified with `val'.</li>
+<li><kbd>-keep</kbd> : specify the storing mode for `VL_DKEEP'.</li>
+<li><kbd>-cat</kbd> : specify the storing mode for `VL_DCAT'.</li>
+<li><kbd>-dup</kbd> : specify the storing mode for `VL_DDUP'.</li>
+<li><kbd>-nl</kbd> : open the database without file locking.</li>
+<li><kbd>-top <var>key</var></kbd> : specify the top key of listing.</li>
+<li><kbd>-bot <var>key</var></kbd> : specify the bottom key of listing.</li>
+<li><kbd>-gt</kbd> : do not include the top key of listing.</li>
+<li><kbd>-lt</kbd> : do not include the bottom key of listing.</li>
+<li><kbd>-max <var>num</var></kbd> : specify the max number of listing.</li>
+<li><kbd>-desc</kbd> : list in descending order.</li>
+<li><kbd>-ox</kbd> : treat the output as a binary expression of hexadecimal notation.</li>
+<li><kbd>-n</kbd> : do not output the tailing newline.</li>
+<li><kbd>-k</kbd> : output keys only.</li>
+<li><kbd>-v</kbd> : output values only.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.  The environment variable `QDBMDBGFD' specifies the file descriptor to output the history of updating the variable `dpecode'.</p>
+
+<p>The command `vltest' is a utility for facility test and performance test.  Check a database generated by the command or measure the execution time of the command.  This command is used in the following format.  `name' specifies a database name.  `rnum' specifies the number of the records.</p>
+
+<dl>
+<dt><kbd>vltest write [-int] [-cz|-cy|-cx] [-tune <var>lrecmax</var> <var>nidxmax</var> <var>lcnum</var> <var>ncnum</var>] [-fbp <var>num</var>] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>Store records with keys of 8 bytes.  They change as `00000001', `00000002'...</dd>
+<dt><kbd>vltest read [-int] [-vc] <var>name</var></kbd></dt>
+<dd>Retrieve all records of the database above.</dd>
+<dt><kbd>vltest rdup [-int] [-cz|-cy|-cx] [-cc] [-tune <var>lrecmax</var> <var>nidxmax</var> <var>lcnum</var> <var>ncnum</var>] [-fbp <var>num</var>] <var>name</var> <var>rnum</var> <var>pnum</var></kbd></dt>
+<dd>Store records with partway duplicated keys using duplicate mode.</dd>
+<dt><kbd>vltest combo [-cz|-cy|-cx] <var>name</var></kbd></dt>
+<dd>Perform combination test of various operations.</dd>
+<dt><kbd>vltest wicked [-cz|-cy|-cx] <var>name</var> <var>rnum</var></kbd></dt>
+<dd>Perform updating operations selected at random.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-int</kbd> : treat keys and values as objects of `int', and use comparing function `VL_CMPINT'.</li>
+<li><kbd>-cz</kbd> : compress leaves in the database with ZLIB.</li>
+<li><kbd>-cy</kbd> : compress leaves in the database with LZO.</li>
+<li><kbd>-cx</kbd> : compress leaves in the database with BZIP2.</li>
+<li><kbd>-vc</kbd> : refer to volatile cache.</li>
+<li><kbd>-cc</kbd> : select `VL_DCAT' or `VL_DDUP' at random.</li>
+<li><kbd>-tune <var>lrecmax</var> <var>nidxmax</var> <var>lcnum</var> <var>ncnum</var></kbd> : set tuning parameters.</li>
+<li><kbd>-fbp <var>num</var></kbd> : set the size of the free block pool.</li>
+<li><kbd>-c</kbd> : perform comparison test with map of Cabin.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.  The environment variable `QDBMDBGFD' specifies the file descriptor to output the history of updating the variable `dpecode'.</p>
+
+<p>The command `vltsv' features mutual conversion between a database of Villa and a TSV text.  This command is useful when data exchange with another version of QDBM or another DBM, or when data exchange between systems which have different byte orders.  This command is used in the following format.  `name' specifies a database name.  The subcommand `export' reads TSV data from the standard input.  The subcommand `import' writes TSV data to the standard output.</p>
+
+<dl>
+<dt><kbd>vltsv import [-bin] <var>name</var></kbd></dt>
+<dd>Create a database from TSV.</dd>
+<dt><kbd>vltsv export [-bin] <var>name</var></kbd></dt>
+<dd>Write TSV data of a database.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-bin</kbd> : treat records as Base64 format.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<p>Commands of Villa realize a simple database system.  For example, to make a database to search `/etc/password' by a user name, perform the following command.</p>
+
+<pre>cat /etc/passwd | tr ':' '\t' | vltsv import casket
+</pre>
+
+<p>Thus, to retrieve the information of a user `mikio', perform the following command.</p>
+
+<pre>vlmgr get casket mikio
+</pre>
+
+<p>It is easy to implement functions upsides with these commands, using the API of Villa.</p>
+
+<p>The command `qmttest' checks multi-thread safety of Depot, Curia, and Villa.  This command works with multi threads only if QDBM was built with POSIX thread.  This command is used in the following format.  `name' specifies the prefix of each database.  `rnum' specifies the number of records to be stored in each database.  `tnum' specifies the number of threads.</p>
+
+<dl>
+<dt><kbd>qmttest <var>name</var> <var>rnum</var> <var>tnum</var></kbd></dt>
+<dd>Check multi-thread safety.</dd>
+</dl>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<hr />
+
+<h2><a name="odeumapi" id="odeumapi" class="head">Odeum: Inverted API</a></h2>
+
+<h3>Overview</h3>
+
+<p>Odeum is the API which handles an inverted index.  An inverted index is a data structure to retrieve a list of some documents that include one of words which were extracted from a population of documents.  It is easy to realize a full-text search system with an inverted index.  Odeum provides an abstract data structure which consists of words and attributes of a document.  It is used when an application stores a document into a database and when an application retrieves some documents from a database.</p>
+
+<p>Odeum does not provide methods to extract the text from the original data of a document.  It should be implemented by applications.  Although Odeum provides utilities to extract words from a text, it is oriented to such languages whose words are separated with space characters as English.  If an application handles such languages which need morphological analysis or N-gram analysis as Japanese, or if an application perform more such rarefied analysis of natural languages as stemming, its own analyzing method can be adopted.  Result of search is expressed as an array contains elements which are structures composed of the ID number of documents and its score.  In order to search with two or more words, Odeum provides utilities of set operations.</p>
+
+<p>Odeum is implemented, based on Curia, Cabin, and Villa.  Odeum creates a database with a directory name.  Some databases of Curia and Villa are placed in the specified directory.  For example, `casket/docs', `casket/index', and `casket/rdocs' are created in the case that a database directory named as `casket'.  `docs' is a database directory of Curia.  The key of each record is the ID number of a document, and the value is such attributes as URI.  `index' is a database directory of Curia.  The key of each record is the normalized form of a word, and the value is an array whose element is a pair of the ID number of a document including the word and its score.  `rdocs' is a database file of Villa.  The key of each record is the URI of a document, and the value is its ID number.</p>
+
+<p>In order to use Odeum, you should include `depot.h', `cabin.h', `odeum.h' and `stdlib.h' in the source files.  Usually, the following description will be near the beginning of a source file.</p>
+
+<dl>
+<dt><kbd>#include <depot.h></kbd></dt>
+<dt><kbd>#include <cabin.h></kbd></dt>
+<dt><kbd>#include <odeum.h></kbd></dt>
+<dt><kbd>#include <stdlib.h></kbd></dt>
+</dl>
+
+<p>A pointer to `ODEUM' is used as a database handle.  A database handle is opened with the function `odopen' and closed with `odclose'.  You should not refer directly to any member of the handle.  If a fatal error occurs in a database, any access method via the handle except `odclose' will not work and return error status.  Although a process is allowed to use multiple database handles at the same time, handles of the same database file should not be used.</p>
+
+<p>A pointer to `ODDOC' is used as a document handle.  A document handle is opened with the function `oddocopen' and closed with `oddocclose'.  You should not refer directly to any member of the handle.  A document consists of attributes and words.  Each word is expressed as a pair of a normalized form and a appearance form.</p>
+
+<p>Odeum also assign the external variable `dpecode' with the error code. The function `dperrmsg' is used in order to get the message of the error code.</p>
+
+<h3>API</h3>
+
+<p>Structures of `ODPAIR' type is used in order to handle results of search.</p>
+
+<dl>
+<dt><kbd>typedef struct { int id; int score; } ODPAIR;</kbd></dt>
+<dd>`id' specifies the ID number of a document.  `score' specifies the score calculated from the number of searching words in the document.</dd>
+</dl>
+
+<p>The function `odopen' is used in order to get a database handle.</p>
+
+<dl>
+<dt><kbd>ODEUM *odopen(const char *<var>name</var>, int <var>omode</var>);</kbd></dt>
+<dd>`name' specifies the name of a database directory.  `omode' specifies the connection mode: `OD_OWRITER' as a writer, `OD_OREADER' as a reader.  If the mode is `OD_OWRITER', the following may be added by bitwise or: `OD_OCREAT', which means it creates a new database if not exist, `OD_OTRUNC', which means it creates a new database regardless if one exists.  Both of `OD_OREADER' and `OD_OWRITER' can be added to by bitwise or: `OD_ONOLCK', which means it opens a database directory without file locking, or `OD_OLCKNB', which means locking is performed without blocking.  The return value is the database handle or `NULL' if it is not successful.  While connecting as a writer, an exclusive lock is invoked to the database directory.  While connecting as a reader, a shared lock is invoked to the database directory.  The thread blocks until the lock is achieved.  If `OD_ONOLCK' is used, the application is responsible for exclusion control.</dd>
+</dl>
+
+<p>The function `odclose' is used in order to close a database handle.</p>
+
+<dl>
+<dt><kbd>int odclose(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  If successful, the return value is true, else, it is false.  Because the region of a closed handle is released, it becomes impossible to use the handle.  Updating a database is assured to be written when the handle is closed.  If a writer opens a database but does not close it appropriately, the database will be broken.</dd>
+</dl>
+
+<p>The function `odput' is used in order to store a document.</p>
+
+<dl>
+<dt><kbd>int odput(ODEUM *<var>odeum</var>, const ODDOC *<var>doc</var>, int <var>wmax</var>, int <var>over</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle connected as a writer.  `doc' specifies a document handle.  `wmax' specifies the max number of words to be stored in the document database.  If it is negative, the number is unlimited.  `over' specifies whether the data of the duplicated document is overwritten or not.  If it is false and the URI of the document is duplicated, the function returns as an error.  If successful, the return value is true, else, it is false.</dd>
+</dl>
+
+<p>The function `odout' is used in order to delete a document specified by a URI.</p>
+
+<dl>
+<dt><kbd>int odout(ODEUM *<var>odeum</var>, const char *<var>uri</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle connected as a writer.  `uri' specifies the string of the URI of a document.  If successful, the return value is true, else, it is false.  False is returned when no document corresponds to the specified URI.</dd>
+</dl>
+
+<p>The function `odoutbyid' is used in order to delete a document specified by an ID number.</p>
+
+<dl>
+<dt><kbd>int odoutbyid(ODEUM *<var>odeum</var>, int <var>id</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle connected as a writer.  `id' specifies the ID number of a document.  If successful, the return value is true, else, it is false.  False is returned when no document corresponds to the specified ID number.</dd>
+</dl>
+
+<p>The function `odget' is used in order to retrieve a document specified by a URI.</p>
+
+<dl>
+<dt><kbd>ODDOC *odget(ODEUM *<var>odeum</var>, const char *<var>uri</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  `uri' specifies the string of the URI of a document.  If successful, the return value is the handle of the corresponding document, else, it is `NULL'.  `NULL' is returned when no document corresponds to the specified URI.  Because the handle of the return value is opened with the function `oddocopen', it should be closed with the function `oddocclose'.</dd>
+</dl>
+
+<p>The function `odgetbyid' is used in order to retrieve a document by an ID number.</p>
+
+<dl>
+<dt><kbd>ODDOC *odgetbyid(ODEUM *<var>odeum</var>, int <var>id</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  `id' specifies the ID number of a document.  If successful, the return value is the handle of the corresponding document, else, it is `NULL'.  `NULL' is returned when no document corresponds to the specified ID number.  Because the handle of the return value is opened with the function `oddocopen', it should be closed with the function `oddocclose'.</dd>
+</dl>
+
+<p>The function `odgetidbyuri' is used in order to retrieve the ID of the document specified by a URI.</p>
+
+<dl>
+<dt><kbd>int odgetidbyuri(ODEUM *<var>odeum</var>, const char *<var>uri</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  `uri' specifies the string the URI of a document.  If successful, the return value is the ID number of the document, else, it is -1.  -1 is returned when no document corresponds to the specified URI.</dd>
+</dl>
+
+<p>The function `odcheck' is used in order to check whether the document specified by an ID number exists.</p>
+
+<dl>
+<dt><kbd>int odcheck(ODEUM *<var>odeum</var>, int <var>id</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  `id' specifies the ID number of a document.  The return value is true if the document exists, else, it is false.</dd>
+</dl>
+
+<p>The function `odsearch' is used in order to search the inverted index for documents including a particular word.</p>
+
+<dl>
+<dt><kbd>ODPAIR *odsearch(ODEUM *<var>odeum</var>, const char *<var>word</var>, int <var>max</var>, int *<var>np</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  `word' specifies a searching word.  `max' specifies the max number of documents to be retrieve.  `np' specifies the pointer to a variable to which the number of the elements of the return value is assigned.  If successful, the return value is the pointer to an array, else, it is `NULL'.  Each element of the array is a pair of the ID number and the score of a document, and sorted in descending order of their scores.  Even if no document corresponds to the specified word, it is not error but returns an dummy array.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.  Note that each element of the array of the return value can be data of a deleted document.</dd>
+</dl>
+
+<p>The function `odsearchnum' is used in order to get the number of documents including a word.</p>
+
+<dl>
+<dt><kbd>int odsearchdnum(ODEUM *<var>odeum</var>, const char *<var>word</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  `word' specifies a searching word.  If successful, the return value is the number of documents including the word, else, it is -1.  Because this function does not read the entity of the inverted index, it is faster than `odsearch'.</dd>
+</dl>
+
+<p>The function `oditerinit' is used in order to initialize the iterator of a database handle.</p>
+
+<dl>
+<dt><kbd>int oditerinit(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  If successful, the return value is true, else, it is false.  The iterator is used in order to access every document stored in a database.</dd>
+</dl>
+
+<p>The function `oditernext' is used in order to get the next key of the iterator.</p>
+
+<dl>
+<dt><kbd>ODDOC *oditernext(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  If successful, the return value is the handle of the next document, else, it is `NULL'.  `NULL' is returned when no document is to be get out of the iterator.  It is possible to access every document 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 string matches the one of the traversal access.  Because the handle of the return value is opened with the function `oddocopen', it should be closed with the function `oddocclose'.</dd>
+</dl>
+
+<p>The function `odsync' is used in order to synchronize updating contents with the files and the devices.</p>
+
+<dl>
+<dt><kbd>int odsync(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle connected as a writer.  If successful, the return value is true, else, it is false.  This function is useful when another process uses the connected database directory.</dd>
+</dl>
+
+<p>The function `odoptimize' is used in order to optimize a database.</p>
+
+<dl>
+<dt><kbd>int odoptimize(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle connected as a writer.  If successful, the return value is true, else, it is false.  Elements of the deleted documents in the inverted index are purged.</dd>
+</dl>
+
+<p>The function `odname' is used in order to get the name of a database.</p>
+
+<dl>
+<dt><kbd>char *odname(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  If successful, the return value is the pointer to the region of the name of the database, 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 if it is no longer in use.</dd>
+</dl>
+
+<p>The function `odfsiz' is used in order to get the total size of database files.</p>
+
+<dl>
+<dt><kbd>double odfsiz(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  If successful, the return value is the total size of the database files, else, it is -1.0.</dd>
+</dl>
+
+<p>The function `odbnum' is used in order to get the total number of the elements of the bucket arrays in the inverted index.</p>
+
+<dl>
+<dt><kbd>int odbnum(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  If successful, the return value is the total number of the elements of the bucket arrays, else, it is -1.</dd>
+</dl>
+
+<p>The function `odbusenum' is used in order to get the total number of the used elements of the bucket arrays in the inverted index.</p>
+
+<dl>
+<dt><kbd>int odbusenum(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  If successful, the return value is the total number of the used elements of the bucket arrays, else, it is -1.</dd>
+</dl>
+
+<p>The function `oddnum' is used in order to get the number of the documents stored in a database.</p>
+
+<dl>
+<dt><kbd>int oddnum(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  If successful, the return value is the number of the documents stored in the database, else, it is -1.</dd>
+</dl>
+
+<p>The function `odwnum' is used in order to get the number of the words stored in a database.</p>
+
+<dl>
+<dt><kbd>int odwnum(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  If successful, the return value is the number of the words stored in the database, else, it is -1.  Because of the I/O buffer, the return value may be less than the hard number.</dd>
+</dl>
+
+<p>The function `odwritable' is used in order to check whether a database handle is a writer or not.</p>
+
+<dl>
+<dt><kbd>int odwritable(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  The return value is true if the handle is a writer, false if not.</dd>
+</dl>
+
+<p>The function `odfatalerror' is used in order to check whether a database has a fatal error or not.</p>
+
+<dl>
+<dt><kbd>int odfatalerror(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  The return value is true if the database has a fatal error, false if not.</dd>
+</dl>
+
+<p>The function `odinode' is used in order to get the inode number of a database directory.</p>
+
+<dl>
+<dt><kbd>int odinode(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  The return value is the inode number of the database directory.</dd>
+</dl>
+
+<p>The function `odmtime' is used in order to get the last modified time of a database.</p>
+
+<dl>
+<dt><kbd>time_t odmtime(ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  The return value is the last modified time of the database.</dd>
+</dl>
+
+<p>The function `odmerge' is used in order to merge plural database directories.</p>
+
+<dl>
+<dt><kbd>int odmerge(const char *<var>name</var>, const CBLIST *<var>elemnames</var>);</kbd></dt>
+<dd>`name' specifies the name of a database directory to create.  `elemnames' specifies a list of names of element databases.  If successful, the return value is true, else, it is false.  If two or more documents which have the same URL come in, the first one is adopted and the others are ignored.</dd>
+</dl>
+
+<p>The function `odremove' is used in order to remove a database directory.</p>
+
+<dl>
+<dt><kbd>int odremove(const char *<var>name</var>);</kbd></dt>
+<dd>`name' specifies the name of a database directory.  If successful, the return value is true, else, it is false.  A database directory can contain databases of other APIs of QDBM, they are also removed by this function.</dd>
+</dl>
+
+<p>The function `oddocopen' is used in order to get a document handle.</p>
+
+<dl>
+<dt><kbd>ODDOC *oddocopen(const char *<var>uri</var>);</kbd></dt>
+<dd>`uri' specifies the URI of a document.  The return value is a document handle.  The ID number of a new document is not defined.  It is defined when the document is stored in a database.</dd>
+</dl>
+
+<p>The function `oddocclose' is used in order to close a document handle.</p>
+
+<dl>
+<dt><kbd>void oddocclose(ODDOC *<var>doc</var>);</kbd></dt>
+<dd>`doc' specifies a document handle.  Because the region of a closed handle is released, it becomes impossible to use the handle.</dd>
+</dl>
+
+<p>The function `oddocaddattr' is used in order to add an attribute to a document.</p>
+
+<dl>
+<dt><kbd>void oddocaddattr(ODDOC *<var>doc</var>, const char *<var>name</var>, const char *<var>value</var>);</kbd></dt>
+<dd>`doc' specifies a document handle.  `name' specifies the string of the name of an attribute.  `value' specifies the string of the value of the attribute.</dd>
+</dl>
+
+<p>The function `oddocaddword' is used in order to add a word to a document.</p>
+
+<dl>
+<dt><kbd>void oddocaddword(ODDOC *<var>doc</var>, const char *<var>normal</var>, const char *<var>asis</var>);</kbd></dt>
+<dd>`doc' specifies a document handle.  `normal' specifies the string of the normalized form of a word.  Normalized forms are treated as keys of the inverted index.  If the normalized form of a word is an empty string, the word is not reflected in the inverted index.  `asis' specifies the string of the appearance form of the word.  Appearance forms are used after the document is retrieved by an application.</dd>
+</dl>
+
+<p>The function `oddocid' is used in order to get the ID number of a document.</p>
+
+<dl>
+<dt><kbd>int oddocid(const ODDOC *<var>doc</var>);</kbd></dt>
+<dd>`doc' specifies a document handle.  The return value is the ID number of a document.</dd>
+</dl>
+
+<p>The function `oddocuri' is used in order to get the URI of a document.</p>
+
+<dl>
+<dt><kbd>const char *oddocuri(const ODDOC *<var>doc</var>);</kbd></dt>
+<dd>`doc' specifies a document handle.  The return value is the string of the URI of a document.</dd>
+</dl>
+
+<p>The function `oddocgetattr' is used in order to get the value of an attribute of a document.</p>
+
+<dl>
+<dt><kbd>const char *oddocgetattr(const ODDOC *<var>doc</var>, const char *<var>name</var>);</kbd></dt>
+<dd>`doc' specifies a document handle.  `name' specifies the string of the name of an attribute.  The return value is the string of the value of the attribute, or `NULL' if no attribute corresponds.</dd>
+</dl>
+
+<p>The function `oddocnwords' is used in order to get the list handle contains words in normalized form of a document.</p>
+
+<dl>
+<dt><kbd>const CBLIST *oddocnwords(const ODDOC *<var>doc</var>);</kbd></dt>
+<dd>`doc' specifies a document handle.  The return value is the list handle contains words in normalized form.</dd>
+</dl>
+
+<p>The function `oddocawords' is used in order to get the list handle contains words in appearance form of a document.</p>
+
+<dl>
+<dt><kbd>const CBLIST *oddocawords(const ODDOC *<var>doc</var>);</kbd></dt>
+<dd>`doc' specifies a document handle.  The return value is the list handle contains words in appearance form.</dd>
+</dl>
+
+<p>The function `oddocscores' is used in order to get the map handle contains keywords in normalized form and their scores.</p>
+
+<dl>
+<dt><kbd>CBMAP *oddocscores(const ODDOC *<var>doc</var>, int <var>max</var>, ODEUM *<var>odeum</var>);</kbd></dt>
+<dd>`doc' specifies a document handle.  `max' specifies the max number of keywords to get.  `odeum' specifies a database handle with which the IDF for weighting is calculate.  If it is `NULL', it is not used.  The return value is the map handle contains keywords and their scores.  Scores are expressed as decimal strings.  Because the handle of the return value is opened with the function `cbmapopen', it should be closed with the function `cbmapclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The function `odbreaktext' is used in order to break a text into words in appearance form.</p>
+
+<dl>
+<dt><kbd>CBLIST *odbreaktext(const char *<var>text</var>);</kbd></dt>
+<dd>`text' specifies the string of a text.  The return value is the list handle contains words in appearance form.  Words are separated with space characters and such delimiters as period, comma and so on.  Because the handle of the return value is opened with the function `cblistopen', it should be closed with the function `cblistclose' if it is no longer in use.</dd>
+</dl>
+
+<p>The function `odnormalizeword' is used in order to make the normalized form of a word.</p>
+
+<dl>
+<dt><kbd>char *odnormalizeword(const char *<var>asis</var>);</kbd></dt>
+<dd>`asis' specifies the string of the appearance form of a word.  The return value is is the string of the normalized form of the word.  Alphabets of the ASCII code are unified into lower cases.  Words composed of only delimiters are treated as empty strings.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `odpairsand' is used in order to get the common elements of two sets of documents.</p>
+
+<dl>
+<dt><kbd>ODPAIR *odpairsand(ODPAIR *<var>apairs</var>, int <var>anum</var>, ODPAIR *<var>bpairs</var>, int <var>bnum</var>, int *<var>np</var>);</kbd></dt>
+<dd>`apairs' specifies the pointer to the former document array.  `anum' specifies the number of the elements of the former document array.  `bpairs' specifies the pointer to the latter document array.  `bnum' specifies the number of the elements of the latter document array.  `np' specifies the pointer to a variable to which the number of the elements of the return value is assigned.  The return value is the pointer to a new document array whose elements commonly belong to the specified two sets.  Elements of the array are sorted in descending order of their scores.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `odpairsor' is used in order to get the sum of elements of two sets of documents.</p>
+
+<dl>
+<dt><kbd>ODPAIR *odpairsor(ODPAIR *<var>apairs</var>, int <var>anum</var>, ODPAIR *<var>bpairs</var>, int <var>bnum</var>, int *<var>np</var>);</kbd></dt>
+<dd>`apairs' specifies the pointer to the former document array.  `anum' specifies the number of the elements of the former document array.  `bpairs' specifies the pointer to the latter document array.  `bnum' specifies the number of the elements of the latter document array.  `np' specifies the pointer to a variable to which the number of the elements of the return value is assigned.  The return value is the pointer to a new document array whose elements belong to both or either of the specified two sets.  Elements of the array are sorted in descending order of their scores.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `odpairsnotand' is used in order to get the difference set of documents.</p>
+
+<dl>
+<dt><kbd>ODPAIR *odpairsnotand(ODPAIR *<var>apairs</var>, int <var>anum</var>, ODPAIR *<var>bpairs</var>, int <var>bnum</var>, int *<var>np</var>);</kbd></dt>
+<dd>`apairs' specifies the pointer to the former document array.  `anum' specifies the number of the elements of the former document array.  `bpairs' specifies the pointer to the latter document array of the sum of elements.  `bnum' specifies the number of the elements of the latter document array.  `np' specifies the pointer to a variable to which the number of the elements of the return value is assigned.  The return value is the pointer to a new document array whose elements belong to the former set but not to the latter set.  Elements of the array are sorted in descending order of their scores.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.</dd>
+</dl>
+
+<p>The function `odpairssort' is used in order to sort a set of documents in descending order of scores.</p>
+
+<dl>
+<dt><kbd>void odpairssort(ODPAIR *<var>pairs</var>, int <var>pnum</var>);</kbd></dt>
+<dd>`pairs' specifies the pointer to a document array.  `pnum' specifies the number of the elements of the document array.</dd>
+</dl>
+
+<p>The function `odlogarithm' is used in order to get the natural logarithm of a number.</p>
+
+<dl>
+<dt><kbd>double odlogarithm(double <var>x</var>);</kbd></dt>
+<dd>`x' specifies a number.  The return value is the natural logarithm of the number.  If the number is equal to or less than 1.0, the return value is 0.0.  This function is useful when an application calculates the IDF of search results.</dd>
+</dl>
+
+<p>The function `odvectorcosine' is used in order to get the cosine of the angle of two vectors.</p>
+
+<dl>
+<dt><kbd>double odvectorcosine(const int *<var>avec</var>, const int *<var>bvec</var>, int <var>vnum</var>);</kbd></dt>
+<dd>`avec' specifies the pointer to one array of numbers.  `bvec' specifies the pointer to the other array of numbers.  `vnum' specifies the number of elements of each array.  The return value is the cosine of the angle of two vectors.  This function is useful when an application calculates similarity of documents.</dd>
+</dl>
+
+<p>The function `odsettuning' is used in order to set the global tuning parameters.</p>
+
+<dl>
+<dt><kbd>void odsettuning(int <var>ibnum</var>, int <var>idnum</var>, int <var>cbnum</var>, int <var>csiz</var>);</kbd></dt>
+<dd>`ibnum' specifies the number of buckets for inverted indexes.  `idnum' specifies the division number of inverted index.  `cbnum' specifies the number of buckets for dirty buffers.  `csiz' specifies the maximum bytes to use memory for dirty buffers.  The default setting is equivalent to `odsettuning(32749, 7, 262139, 8388608)'.  This function should be called before opening a handle.</dd>
+</dl>
+
+<p>The function `odanalyzetext' is used in order to break a text into words and store appearance forms and normalized form into lists.</p>
+
+<dl>
+<dt><kbd>void odanalyzetext(ODEUM *<var>odeum</var>, const char *<var>text</var>, CBLIST *<var>awords</var>, CBLIST *<var>nwords</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  `text' specifies the string of a text.  `awords' specifies a list handle into which appearance form is store.  `nwords' specifies a list handle into which normalized form is store.  If it is `NULL', it is ignored.  Words are separated with space characters and such delimiters as period, comma and so on.</dd>
+</dl>
+
+<p>The function `odsetcharclass' is used in order to set the classes of characters used by `odanalyzetext'.</p>
+
+<dl>
+<dt><kbd>void odsetcharclass(ODEUM *<var>odeum</var>, const char *<var>spacechars</var>, const char *<var>delimchars</var>, const char *<var>gluechars</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  `spacechars' spacifies a string contains space characters.  `delimchars' spacifies a string contains delimiter characters.  `gluechars' spacifies a string contains glue characters.</dd>
+</dl>
+
+<p>The function `odquery' is used in order to query a database using a small boolean query language.</p>
+
+<dl>
+<dt><kbd>ODPAIR *odquery(ODEUM *<var>odeum</var>, const char *<var>query</var>, int *<var>np</var>, CBLIST *<var>errors</var>);</kbd></dt>
+<dd>`odeum' specifies a database handle.  'query' specifies the text of the query.  `np' specifies the pointer to a variable to which the number of the elements of the return value is assigned.  `errors' specifies a list handle into which error messages are stored.  If it is `NULL', it is ignored.  If successful, the return value is the pointer to an array, else, it is `NULL'.  Each element of the array is a pair of the ID number and the score of a document, and sorted in descending order of their scores.  Even if no document corresponds to the specified condition, it is not error but returns an dummy array.  Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if it is no longer in use.  Note that each element of the array of the return value can be data of a deleted document.</dd>
+</dl>
+
+<h3>Examples</h3>
+
+<p>The following example stores a document into the database.</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <odeum.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define DBNAME   "index"
+
+int main(int argc, char **argv){
+  ODEUM *odeum;
+  ODDOC *doc;
+  CBLIST *awords;
+  const char *asis;
+  char *normal;
+  int i;
+
+  /* open the database */
+  if(!(odeum = odopen(DBNAME, OD_OWRITER | OD_OCREAT))){
+    fprintf(stderr, "odopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* get the document handle */
+  doc = oddocopen("http://www.foo.bar/baz.txt");
+
+  /* set attributes of the document */
+  oddocaddattr(doc, "title", "Balcony Scene");
+  oddocaddattr(doc, "author", "Shakespeare");
+
+  /* break the text and get the word list */
+  awords = odbreaktext("Parting is such sweet sorrow.");
+
+  /* set each word into the document handle */
+  for(i = 0; i < cblistnum(awords); i++){
+    /* get one word of the list */
+    asis = cblistval(awords, i, NULL);
+    /* get the normalized form from the appearance form */
+    normal = odnormalizeword(asis);
+    /* add the word into the document handle */
+    oddocaddword(doc, normal, asis);
+    /* release the region of the normalized form */
+    free(normal);
+  }
+
+  /* store the document into the database */
+  if(!odput(odeum, doc, -1, 1)){
+    fprintf(stderr, "odput: %s\n", dperrmsg(dpecode));
+  }
+
+  /* release the word list */
+  cblistclose(awords);
+
+  /* release the document handle */
+  oddocclose(doc);
+
+  /* close the database */
+  if(!odclose(odeum)){
+    fprintf(stderr, "odclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<p>The following example retrieves documents.</p>
+
+<pre>#include <depot.h>
+#include <cabin.h>
+#include <odeum.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define DBNAME   "index"
+
+int main(int argc, char **argv){
+  ODEUM *odeum;
+  ODPAIR *pairs;
+  ODDOC *doc;
+  const CBLIST *words;
+  const char *title, *author, *asis;
+  int i, j, pnum;
+
+  /* open the database */
+  if(!(odeum = odopen(DBNAME, OD_OREADER))){
+    fprintf(stderr, "odopen: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  /* retrieve documents */
+  if((pairs = odsearch(odeum, "sorrow", -1, &pnum)) != NULL){
+
+    /* scan each element of the document array */
+    for(i = 0; i < pnum; i++){
+      /* get the document handle */
+      if(!(doc = odgetbyid(odeum, pairs[i].id))) continue;
+      /* show the attributes */
+      printf("URI: %s\n", oddocuri(doc));
+      title = oddocgetattr(doc, "title");
+      if(title) printf("TITLE: %s\n", title);
+      author = oddocgetattr(doc, "author");
+      if(author) printf("AUTHOR: %s\n", author);
+      /* show words in appearance form */
+      printf("WORDS:");
+      words = oddocawords(doc);
+      for(j = 0; j < cblistnum(words); j++){
+        asis = cblistval(words, j, NULL);
+        printf(" %s", asis);
+      }
+      putchar('\n');
+      /* release the document handle */
+      oddocclose(doc);
+    }
+
+    /* release the document array */
+    free(pairs);
+
+  } else {
+    fprintf(stderr, "odsearch: %s\n", dperrmsg(dpecode));
+  }
+
+  /* close the database */
+  if(!odclose(odeum)){
+    fprintf(stderr, "odclose: %s\n", dperrmsg(dpecode));
+    return 1;
+  }
+
+  return 0;
+}
+</pre>
+
+<h3>Notes</h3>
+
+<p>How to build programs using Odeum is the same as the case of Depot.</p>
+
+<pre>gcc -I/usr/local/include -o sample sample.c -L/usr/local/lib -lqdbm
+</pre>
+
+<p>If QDBM was built with POSIX thread enabled, the global variable `dpecode' is treated as thread specific data, and functions of Odeum are reentrant.  In that case, they are thread-safe as long as a handle is not accessed by threads at the same time, on the assumption that `errno', `malloc', and so on are thread-safe.</p>
+
+<p>If QDBM was built with ZLIB enabled, records in the database for document attributes are compressed.  In that case, the size of the database is reduced to 30% or less.  Thus, you should enable ZLIB if you use Odeum.  A database of Odeum created without ZLIB enabled is not available on environment with ZLIB enabled, and vice versa.  If ZLIB was not enabled but LZO, LZO is used instead.</p>
+
+<h3>Query Language</h3>
+
+<p>The query language of the function `odquery' is a basic language following this grammar:</p>
+
+<pre>expr ::= subexpr ( op subexpr )*
+subexpr ::= WORD
+subexpr ::= LPAREN expr RPAREN
+</pre>
+
+<p>Operators are "&" (AND), "|" (OR), and "!" (NOTAND).  You can use parenthesis to group sub-expressions together in order to change order of operations.  The given query is broken up using the function `odanalyzetext', so if you want to specify different text breaking rules, then make sure that you at least set "&", "|", "!", "(", and ")" to be delimiter characters.  Consecutive words are treated as having an implicit "&" operator between them, so "zed shaw" is actually "zed & shaw".</p>
+
+<p>The encoding of the query text should be the same with the encoding of target documents.  Moreover, each of space characters, delimiter characters, and glue characters should be single byte.</p>
+
+<hr />
+
+<h2><a name="odeumcli" id="odeumcli" class="head">Commands for Odeum</a></h2>
+
+<p>Odeum has the following command line interfaces.</p>
+
+<p>The command `odmgr' is a utility for debugging Odeum and its applications.  It features editing and checking of a database.  It can be used for full-text search systems with shell scripts.  This command is used in the following format.  `name' specifies a database name.  `file' specifies a file name, `expr' specifies the URI or the ID number of a document, `words' specifies searching words.  `elems' specifies element databases.</p>
+
+<dl>
+<dt><kbd>odmgr create <var>name</var></kbd></dt>
+<dd>Create a database file.</dd>
+<dt><kbd>odmgr put [-uri <var>str</var>] [-title <var>str</var>] [-author <var>str</var>] [-date <var>str</var>] [-wmax <var>num</var>] [-keep] <var>name</var> [<var>file</var>]</kbd></dt>
+<dd>Add a document by reading a file.  If `file' is omitted, the standard input is read and URI is needed.</dd>
+<dt><kbd>odmgr out [-id] <var>name</var> <var>expr</var></kbd></dt>
+<dd>Delete a document specified by a URI.</dd>
+<dt><kbd>odmgr get [-id] [-t|-h] <var>name</var> <var>expr</var></kbd></dt>
+<dd>Show a document specified by a URI.  The output is the ID number and the URI of a document, in tab separated format.</dd>
+<dt><kbd>odmgr search [-max <var>num</var>] [-or] [-idf] [-t|-h|-n] <var>name</var> <var>words</var>...</kbd></dt>
+<dd>Retrieve documents including specified words.  The first line of the output is the total number of hits and each word with its number of hits, in tab separated format.  The second line and below are the ID numbers and the scores of documents, in tab separated format.</dd>
+<dt><kbd>odmgr list [-t|-h] <var>name</var></kbd></dt>
+<dd>Show all documents in a database.  Each line of the output is the ID number and the score of a document, in tab separated format.</dd>
+<dt><kbd>odmgr optimize <var>name</var></kbd></dt>
+<dd>Optimize a database.</dd>
+<dt><kbd>odmgr inform <var>name</var></kbd></dt>
+<dd>Output miscellaneous information.</dd>
+<dt><kbd>odmgr merge <var>name</var> <var>elems</var>...</kbd></dt>
+<dd>Merge plural databases.</dd>
+<dt><kbd>odmgr remove <var>name</var></kbd></dt>
+<dd>Remove a database directory.</dd>
+<dt><kbd>odmgr break [-h|-k|-s] [<var>file</var>]</kbd></dt>
+<dd>Read a file and output words in the text.  Each line of the output is the appearance form and the normalized form of a word, in tab separated format.</dd>
+<dt><kbd>odmgr version</kbd></dt>
+<dd>Output version information of QDBM.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-uri <var>str</var></kbd> : specify the URI of the document explicitly.</li>
+<li><kbd>-title <var>str</var></kbd> : specify the title of the document.</li>
+<li><kbd>-author <var>str</var></kbd> : specify the author of the document.</li>
+<li><kbd>-date <var>str</var></kbd> : specify the modified date of the document.</li>
+<li><kbd>-wmax <var>num</var></kbd> : specify the max number of words to be stored.</li>
+<li><kbd>-keep</kbd> : the storing mode is not to be overwrite.</li>
+<li><kbd>-id</kbd> : specify a document not by a URI but by an ID number.</li>
+<li><kbd>-t</kbd> : output the details of a document in tab separated format.</li>
+<li><kbd>-h</kbd> : output the details of a document in human-readable format.</li>
+<li><kbd>-k</kbd> : output keywords of a document.</li>
+<li><kbd>-s</kbd> : output summary of a document.</li>
+<li><kbd>-max <var>num</var></kbd> : specify the max number of documents of the output.</li>
+<li><kbd>-or</kbd> : perform OR search, nut AND search.</li>
+<li><kbd>-idf</kbd> : tune scores with IDF.</li>
+<li><kbd>-n</kbd> : show ID numbers and scores only.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.  The environment variable `QDBMDBGFD' specifies the file descriptor to output the history of updating the variable `dpecode'.</p>
+
+<p>The command `odtest' is a utility for facility test and performance test.  Check a database generated by the command or measure the execution time of the command.  This command is used in the following format.  `name' specifies a database name.  `dnum' specifies the number of the documents.  `wnum' specifies the number of words per document.  `pnum' specifies the number of patterns of words.</p>
+
+<dl>
+<dt><kbd>odtest write [-tune <var>ibnum</var> <var>idnum</var> <var>cbnum</var> <var>csiz</var>] <var>name</var> <var>dnum</var> <var>wnum</var> <var>pnum</var></kbd></dt>
+<dd>Store documents with random attributes and random words.</dd>
+<dt><kbd>odtest read <var>name</var></kbd></dt>
+<dd>Retrieve all documents of the database above.</dd>
+<dt><kbd>odtest combo <var>name</var></kbd></dt>
+<dd>Perform combination test of various operations.</dd>
+<dt><kbd>odtest wicked <var>name</var> <var>dnum</var></kbd></dt>
+<dd>Perform updating operations selected at random.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-tune <var>ibnum</var> <var>idnum</var> <var>cbnum</var> <var>csiz</var></kbd> : set tuning parameters.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.  The environment variable `QDBMDBGFD' specifies the file descriptor to output the history of updating the variable `dpecode'.</p>
+
+<p>The command `odidx' is a utility for indexing files on the local file system.  This command is useful for a full-text search system of a Web site.  Supported file format are plain text and HTML.  Supported character encoding are US-ASCII and ISO-8859-1.  The URI of each document is specified with the path of its file.  Attributes named as `title' and `date' are given to each documents.  When a document is already existing in the database, it is registered if its last modification time is newer, or it is ignored.  Modification times are stored in the sub database `_mtime' in the main database directory.  Score information are stored in the sub database `_score' in the main database directory.  This command is used in the following format.  `name' specifies a database name.  `dir' specifies a directory name.</p>
+
+<dl>
+<dt><kbd>odidx register [-l <var>file</var>] [-wmax <var>num</var>] [-tsuf <var>sufs</var>] [-hsuf <var>sufs</var>] <var>name</var> [<var>dir</var>]</kbd></dt>
+<dd>Register files in the specified directory.  If `dir' is omitted, the current directory is specified.</dd>
+<dt><kbd>odidx relate <var>name</var></kbd></dt>
+<dd>Add score information for relational document search to each documents in the database.</dd>
+<dt><kbd>odidx purge <var>name</var></kbd></dt>
+<dd>Purge documents which are not existing on the local files system.</dd>
+</dl>
+
+<p>Options feature the following.</p>
+
+<ul class="lines">
+<li><kbd>-l <var>file</var></kbd> : read a file and get list of paths of files to register.  If `-' is specified, the standard input is read.</li>
+<li><kbd>-wmax <var>num</var></kbd> : specify the max number of words to be stored in the document database.</li>
+<li><kbd>-tsuf <var>sufs</var></kbd> : specify suffixes of plain text files in comma separated format.  The default is `-tsuf .txt,.text'.</li>
+<li><kbd>-hsuf <var>sufs</var></kbd> : specify suffixes of HTML files in comma separated format.  The default is `-hsuf .html,.htm'.</li>
+</ul>
+
+<p>This command returns 0 on success, another on failure.</p>
+
+<p>Commands of Odeum make it easy to realize a full-text search system.  For example, to register files which are under `/home/mikio' and whose suffix are `.txt', `.c', or `.h', perform the following command.</p>
+
+<pre>odidx register -tsuf ".txt,.c,.h" -hsuf "" casket /home/mikio
+</pre>
+
+<p>Thus, to retrieve documents which include `unix' and `posix' and show the top 8 terms, perform the following command.</p>
+
+<pre>odmgr search -max 8 -h casket "unix posix"
+</pre>
+
+<p>A database generated by `odidx' is available with the CGI script which is included in QDBM for full-text search.</p>
+
+<hr />
+
+<h2><a name="fileformat" id="fileformat" class="head">File Format</a></h2>
+
+<h3>File Format of Depot</h3>
+
+<p>The contents of a database file managed by Depot is divided roughly into the following three sections: the header section, the bucket section and the record section.</p>
+
+<p>The header section places at the beginning of the file and its length is constant 48 bytes.  The following information are stored in the header section.</p>
+
+<ol>
+<li>magic number: from offset 0, contains "[DEPOT]\n\f" for big endian or "[depot]\n\f" for little endian.</li>
+<li>version number: decimal string of the version number of the library.</li>
+<li>flags for wrappers: from offset 16, type of `int'.</li>
+<li>file size: from offset 24, type of `int'.</li>
+<li>number of the bucket: from offset 32, type of `int'.</li>
+<li>number of records: from offset 40, type of `int'.</li>
+</ol>
+
+<p>The bucket section places after the header section and its length is determined according to the number of the bucket.  Each element of the bucket stores an offset of the root node of each separate chain.</p>
+
+<p>The record section places after the bucket section and occupies to the end of the file.  The element of the record section contains the following information.</p>
+
+<ol>
+<li>flags: type of `int'.</li>
+<li>second hash value: type of `int'.</li>
+<li>size of the key: type of `int'.</li>
+<li>size of the value: type of `int'.</li>
+<li>size of the padding: type of `int'.</li>
+<li>offset of the left child: type of `int'.</li>
+<li>offset of the right child: type of `int'.</li>
+<li>entity of the key: serial bytes with variable length.</li>
+<li>entity of the value: serial bytes with variable length.</li>
+<li>padding data: void serial bytes with variable length.</li>
+</ol>
+
+<h3>File Format of Villa</h3>
+
+<p>Every data handled by Villa is stored in a database of Depot.  Storing data is divided into meta data and logical pages.  Logical pages can be classified into leaf nodes and non-leaf nodes.  Meta data are such managing information as the number of records.  Both of its key and its value are type of `int'.  Leaf nodes hold records.  Non-leaf nodes hold sparse index referring to pages.</p>
+
+<p>Villa uses variable length numeric format (BER compression) to handle small natural number with frugal storage.  A variable length numeric object is parsed from the top of the region and parsing ends at the byte of positive value.  Each byte are evaluated as absolute value and calculated as little endian number based on the radix 128.</p>
+
+<p>Record is logical unit of user data.  Some records overlapping keys are shaped into one physical record.  A Physical record is serialized in the following format.</p>
+
+<ol>
+<li>size of the key: type of variable length number</li>
+<li>entity of the key: serial bytes with variable length</li>
+<li>number of values: type of variable length number</li>
+<li>list of values: serial bytes repeating the following expressions<ol>
+<li>size: type of variable length number</li>
+<li>entity of the key: serial bytes with variable length</li>
+</ol></li>
+</ol>
+
+<p>Leaf node is physical unit to store a set of records.  The key of a leaf node is its ID whose type is `int'.  A leaf node is stored in a database of Depot with the following values.  Its records are sorted in ascending order of each key.</p>
+
+<ol>
+<li>ID of the previous leaf: type of variable length number</li>
+<li>ID of the next leaf: type of variable length number</li>
+<li>list of records: concatenation of serialized records</li>
+</ol>
+
+<p>Index is logical unit of a pointer to search for pages.  An index is serialized int the following format.</p>
+
+<ol>
+<li>ID of the referring page: type of variable length number</li>
+<li>size of the key: type of variable length number</li>
+<li>entity of the key: serial bytes with variable length</li>
+</ol>
+
+<p>Non-leaf node is physical unit to store a set of indexes.  The key of a non-leaf node is its ID whose type is `int'.  A non-leaf node is stored in a database of Depot with the following values.  Its indexes are sorted in ascending order of each key.</p>
+
+<ol>
+<li>ID of the first child node: type of variable length number</li>
+<li>list of indexes: concatenation of serialized indexes</li>
+</ol>
+
+<h3>Notes</h3>
+
+<p>Because the database file is not sparse, move, copy, unlink, ftp, and so on with the file are possible.  Because Depot reads and writes data without normalization of byte order, it is impossible to share the same file between the environment with different byte order.</p>
+
+<p>When you distribute a database file of Depot or Villa via network, the MIME type suggested to be `application/x-qdbm'.  Suffix of the file name is suggested to be `.qdb'.  When you distribute a database directory of Curia, you may convert the directory tree to an archive of such type as TAR.</p>
+
+<p>For the command `file' to recognize database files, append the following expressions into `magic' file.</p>
+
+<pre>0       string          [DEPOT]\n\f     QDBM, big endian
+>12     string          x               \b, version=%s
+>19     byte            ^1              \b, Hash
+>19     byte            &1              \b, B+ tree
+>19     byte            &2              \b (deflated:ZLIB)
+>19     byte            &4              \b (deflated:LZO)
+>19     byte            &8              \b (deflated:BZIP2)
+>24     belong          x               \b, filesize=%d
+>32     belong          x               \b, buckets=%d
+>40     belong          x               \b, records=%d
+0       string          [depot]\n\f     QDBM, little endian
+>12     string          x               \b, version=%s
+>16     byte            ^1              \b, Hash
+>16     byte            &1              \b, B+ tree
+>16     byte            &2              \b (deflated:ZLIB)
+>16     byte            &4              \b (deflated:LZO)
+>16     byte            &8              \b (deflated:BZIP2)
+>24     lelong          x               \b, filesize=%d
+>32     lelong          x               \b, buckets=%d
+>40     lelong          x               \b, records=%d
+</pre>
+
+<hr />
+
+<h2><a name="porting" id="porting" class="head">Porting</a></h2>
+
+<p>One of the goal of QDBM is to work on all platforms which conform to POSIX. Even if some APIs are not implemented, QDBM should work.  Moreover, it should be possible to build QDBM using compilers other than GCC.  Porting to various platforms is performed to add a new `Makefile' or modify some parts of source files.  As for APIs of C, some of the following files should be modified.  Otherwise, you can create new files based on them.</p>
+
+<ul class="lines">
+<li><kbd>Makefile.in</kbd> : base of `Makefile', used by `./configure'.</li>
+<li><kbd>myconf.h</kbd> : configuration of system dependency.</li>
+<li><kbd>depot.h</kbd> : header of the basic API.</li>
+<li><kbd>curia.h</kbd> : header of the extended API.</li>
+<li><kbd>relic.h</kbd> : header of the NDBM-compatible API.</li>
+<li><kbd>hovel.h</kbd> : header of the GDBM-compatible API.</li>
+<li><kbd>cabin.h</kbd> : header of the utility API.</li>
+<li><kbd>villa.h</kbd> : header of the advanced API.</li>
+<li><kbd>vista.h</kbd> : header of the extended advanced API.</li>
+<li><kbd>odeum.h</kbd> : header of the inverted API.</li>
+<li><kbd>myconf.c</kbd> : implementation of system dependency.</li>
+<li><kbd>depot.c</kbd> : implementation of the basic API.</li>
+<li><kbd>curia.c</kbd> : implementation of the extended API.</li>
+<li><kbd>relic.c</kbd> : implementation of the NDBM-compatible API.</li>
+<li><kbd>hovel.c</kbd> : implementation of the GDBM-compatible API.</li>
+<li><kbd>cabin.c</kbd> : implementation of the utility API.</li>
+<li><kbd>villa.c</kbd> : implementation of the advanced API.</li>
+<li><kbd>vista.c</kbd> : implementation of the extended advanced API.</li>
+<li><kbd>odeum.c</kbd> : implementation of the inverted API.</li>
+</ul>
+
+<p>On platforms which do not support file locking with `fcntl' call, you should append `-DMYNOLOCK' to the macro `CFLAGS' defined in `Makefile'.  In that case, you should consider another exclusion control.  As with it, on platforms without `mmap' call, you should append `-DMYNOMMAP' to `CFLAGS'.  As for `mmap', its emulation using `malloc' and so on is provided.  If other system calls are not implemented, you should define emulation by modification of `myconf.h' and `myconf.c'.</p>
+
+<p>Because POSIX thread is used in C++ API, it is impossible to port C++ API to platforms without the package.  Because JNI is used in Java API, you should pay attention to location of the headers and libraries.  Moreover, you should consider such type definitions as `long long' or `int64'.  Because APIs of Perl and Ruby use building commands provided with each language system, you should be knowledgeable about their specifications.</p>
+
+<hr />
+
+<h2><a name="bugs" id="bugs" class="head">Bugs</a></h2>
+
+<p>Each document of QDBM should be calibrated by native English speakers.</p>
+
+<p>There is no such bug which are found but not fixed, as crash by segmentation fault, unexpected data vanishing, memory leak and so on.</p>
+
+<p>If you find any bug, report it to the author, with the information of the version of QDBM, the operating system and the compiler.</p>
+
+<p>Databases created with QDBM version 1.7.13 or earlier are not compatible to ones of the later versions.</p>
+
+<hr />
+
+<h2><a name="faq" id="faq" class="head">Frequently Asked Questions</a></h2>
+
+<dl>
+<dt>Q. : Does QDBM support SQL?</dt>
+<dd>A. : No, it does not.  QDBM is not a RDBMS (Relational Database Management System).  If you want an embedded RDBMS, use SQLite and so on.</dd>
+<dt>Q. : After all, how different from GDBM (NDBM, SDBM, Berkeley DB)?</dt>
+<dd>A. : Processing speed is higher, a database file is smaller, API is simpler.  A highly important thing is that efficiency in time and space is very good when records are frequently overwritten, so, scalability in practical use is high.  Moreover, even when constructing such a large database that the number of storing record is more than one million, processing speed does not slowdown deathly, filesize does not grow extremely.   However, because other DBM or DBMS may be more suitable in some cases, comparing performance and functionality by yourself is suggested.</dd>
+<dt>Q. : Which API should I use?</dt>
+<dd>A. : If you search for records as complete accord, try Depot.  If the scale is large, try Curia.  If you access records in some order, try Villa.  If the scale is large, try Vista.  If you pursue the greatest number of records, build QDBM with ZLIB or LZO enabled and use Vista.</dd>
+<dt>Q. : What is bibliography?</dt>
+<dd>A. : Algorithms of QDBM are mainly based on the descriptions in `Data Structures and Algorithms' by Aho et al and `Algorithms in C' by Sedgewick.</dd>
+<dt>Q. : Are there good sample codes for applications?</dt>
+<dd>A. : Refer to the source code of commands of each API.  `dptsv.c', `crtsv.c' and `vltsv.c' are simplest.</dd>
+<dt>Q. : My database file has been broken.  Why?</dt>
+<dd>A. : In most cases, the reason is that your application did not close the database on exit.  No matter whether it is a demon process or a CGI script, any application should close handling databases when it exits.  Moreover, we should remember that a process of CGI may be killed by SIGPIPE or SIGTERM.</dd>
+<dt>Q. : How robust are databases of QDBM?</dt>
+<dd>A. : QDBM does not assure absolute robustness.  A database may be broken if your operating system crashes.  Although transaction of Villa can save a database from crashes of applications, it is inadequate to crashes of operating systems.  So, you should consider multiplexing of a database or backup system if you use QDBM for mission critical applications.</dd>
+<dt>Q: How should I use alignment of Depot and Curia?</dt>
+<dd>A: If your application repeats writing with overwrite or concatenate mode.  Alignment saves the rapid growth of the size of the database file.  Because the best suited size of alignment of each application is different, you should learn it by experiment.  For the meantime, about 32 is suitable.</dd>
+<dt>Q. : How should I tune performance parameters of Villa?</dt>
+<dd>A. : If you perform mainly ordering access, `lrecmax' and `nidxmax' should be larger.  If you perform mainly random access, they should be less.  If RAM of your system is abundant, `lcnum' and `ncnum' should be increased in order to improve performance.  If ZLIB, LZO, or BZIP2 is enabled, increase `lrecmax' and compression efficiency is improved.</dd>
+<dt>Q. : Which is the most preferable of ZLIB, LZO or BZIP2 for Villa?</dt>
+<dd>A. : BZIP2 has the best compression retio.  LZO has the best compression speed.  ZLIB takes a mean position of them.  If you don't have any special reason, using ZLIB is suggested.  However, if updating of the database is frequent, LZO is more preferable.  If updating of the database is very infrequently, BZIP2 is more preferable.  Note that the license of LZO is the GNU LGPL.</dd>
+<dt>Q. : What is `sparse file'?</dt>
+<dd>A. : It is a file where some holes are.  `Hole' means a block where any data has never written in.  If a file system supports sparse file, holes are not allocated into any physical storage.  As for QDBM, if a database is created with such flags as DP_OSPARSE, the bucket array is not initialized and its blocks become holes.  According to that mechanism, you can use greatly huge hash tables.  However, its performance is strongly depends on the setting of the file system.</dd>
+<dt>Q. : Why Depot and Curia do not feature transaction?</dt>
+<dd>A. : If an application implements its own transaction, inner transaction of database is superfluous.  You can implement transaction for application easily with hash map provided by Cabin.</dd>
+<dt>Q. : How should I tune the system for performance?</dt>
+<dd>A. : Install more RAM on your machine than the size of a database.  Then, enlarge I/O buffer and cut down on flushing dirty buffers.  File system is also important.  On Linux, although EXT2 is usually fastest, EXT3 is faster in some cases.  ReiserFS is okey.  The other modes of EXT3 are very slow.  About other file systems, you should learn them by experiment.</dd>
+<dt>Q. : Can I build QDBM using `cc' instead of `gcc'?</dt>
+<dd>A. : Yes.  Try to build QDBM with `LTmakefile'.</dd>
+<dt>Q. : Can I build QDBM using Visual C++?</dt>
+<dd>A. : Yes.  Use `VCmakefile' instead of `Makefile'.</dd>
+<dt>Q. : Can I use QDBM in other languages?</dt>
+<dd>A. : As for PHP, Scheme (Gauche), and OCaml, interfaces of QDBM have been released.  If you need it for another language, try to turn it out.</dd>
+<dt>Q. : What does `QDBM' mean?</dt>
+<dd>A. : `QDBM' stands for `Quick Database Manager'.  It means that processing speed is high, and that you can write applications quickly.</dd>
+</dl>
+
+<hr />
+
+<h2><a name="copying" id="copying" class="head">Copying</a></h2>
+
+<p>QDBM 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.</p>
+
+<p>QDBM 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.</p>
+
+<p>You should have received a copy of the GNU Lesser General Public License along with QDBM (See the file `COPYING'); if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.</p>
+
+<p>QDBM was written by Mikio Hirabayashi.  You can contact the author by e-mail to `mikio at users.sourceforge.net'.  However, as for topics which can be shared among other users, please send it to the mailing list.  To join the mailing list, refer to `http://lists.sourceforge.net/lists/listinfo/qdbm-users'.</p>
+
+<hr />
+
+</body>
+
+</html>
+
+<!-- END OF FILE -->

Added: box/trunk/qdbm/villa.c
===================================================================
--- box/trunk/qdbm/villa.c	                        (rev 0)
+++ box/trunk/qdbm/villa.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,2666 @@
+/*************************************************************************************************
+ * Implementation of Villa
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#define QDBM_INTERNAL  1
+
+#include "villa.h"
+#include "myconf.h"
+
+#define VL_LEAFIDMIN   1                 /* minimum number of leaf ID */
+#define VL_NODEIDMIN   100000000         /* minimum number of node ID */
+#define VL_VNUMBUFSIZ  8                 /* size of a buffer for variable length number */
+#define VL_NUMBUFSIZ   32                /* size of a buffer for a number */
+#define VL_PAGEBUFSIZ  32768             /* size of a buffer to read each page */
+#define VL_MAXLEAFSIZ  49152             /* maximum size of each leaf */
+#define VL_DEFLRECMAX  49                /* default number of records in each leaf */
+#define VL_DEFNIDXMAX  192               /* default number of indexes in each node */
+#define VL_DEFLCNUM    1024              /* default number of leaf cache */
+#define VL_DEFNCNUM    512               /* default number of node cache */
+#define VL_CACHEOUT    8                 /* number of pages in a process of cacheout */
+#define VL_INITBNUM    32749             /* initial bucket number */
+#define VL_PAGEALIGN   -3                /* alignment for pages */
+#define VL_FBPOOLSIZ   128               /* size of free block pool */
+#define VL_PATHBUFSIZ  1024              /* size of a path buffer */
+#define VL_TMPFSUF     MYEXTSTR "vltmp"  /* suffix of a temporary file */
+#define VL_ROOTKEY     -1                /* key of the root key */
+#define VL_LASTKEY     -2                /* key of the last key */
+#define VL_LNUMKEY     -3                /* key of the number of leaves */
+#define VL_NNUMKEY     -4                /* key of the number of nodes */
+#define VL_RNUMKEY     -5                /* key of the number of records */
+#define VL_CRDNUM      7                 /* default division number for Vista */
+
+/* set a buffer for a variable length number */
+#define VL_SETVNUMBUF(VL_len, VL_buf, VL_num) \
+  do { \
+    int _VL_num; \
+    _VL_num = VL_num; \
+    if(_VL_num == 0){ \
+      ((signed char *)(VL_buf))[0] = 0; \
+      (VL_len) = 1; \
+    } else { \
+      (VL_len) = 0; \
+      while(_VL_num > 0){ \
+        int _VL_rem = _VL_num & 0x7f; \
+        _VL_num >>= 7; \
+        if(_VL_num > 0){ \
+          ((signed char *)(VL_buf))[(VL_len)] = -_VL_rem - 1; \
+        } else { \
+          ((signed char *)(VL_buf))[(VL_len)] = _VL_rem; \
+        } \
+        (VL_len)++; \
+      } \
+    } \
+  } while(FALSE)
+
+/* read a variable length buffer */
+#define VL_READVNUMBUF(VL_buf, VL_size, VL_num, VL_step) \
+  do { \
+    int _VL_i, _VL_base; \
+    (VL_num) = 0; \
+    _VL_base = 1; \
+    if((VL_size) < 2){ \
+      (VL_num) = ((signed char *)(VL_buf))[0]; \
+      (VL_step) = 1; \
+    } else { \
+      for(_VL_i = 0; _VL_i < (VL_size); _VL_i++){ \
+        if(((signed char *)(VL_buf))[_VL_i] >= 0){ \
+          (VL_num) += ((signed char *)(VL_buf))[_VL_i] * _VL_base; \
+          break; \
+        } \
+        (VL_num) += _VL_base * (((signed char *)(VL_buf))[_VL_i] + 1) * -1; \
+        _VL_base *= 128; \
+      } \
+      (VL_step) = _VL_i + 1; \
+    } \
+  } while(FALSE)
+
+enum {                                   /* enumeration for flags */
+  VL_FLISVILLA = 1 << 0,                 /* whether for Villa */
+  VL_FLISZLIB = 1 << 1,                  /* whether with ZLIB */
+  VL_FLISLZO = 1 << 2,                   /* whether with LZO */
+  VL_FLISBZIP = 1 << 3                   /* whether with BZIP2 */
+};
+
+
+/* private function prototypes */
+static int vllexcompare(const char *aptr, int asiz, const char *bptr, int bsiz);
+static int vlintcompare(const char *aptr, int asiz, const char *bptr, int bsiz);
+static int vlnumcompare(const char *aptr, int asiz, const char *bptr, int bsiz);
+static int vldeccompare(const char *aptr, int asiz, const char *bptr, int bsiz);
+static int vldpputnum(DEPOT *depot, int knum, int vnum);
+static int vldpgetnum(DEPOT *depot, int knum, int *vnp);
+static VLLEAF *vlleafnew(VILLA *villa, int prev, int next);
+static int vlleafcacheout(VILLA *villa, int id);
+static int vlleafsave(VILLA *villa, VLLEAF *leaf);
+static VLLEAF *vlleafload(VILLA *villa, int id);
+static VLLEAF *vlgethistleaf(VILLA *villa, const char *kbuf, int ksiz);
+static int vlleafaddrec(VILLA *villa, VLLEAF *leaf, int dmode,
+                        const char *kbuf, int ksiz, const char *vbuf, int vsiz);
+static int vlleafdatasize(VLLEAF *leaf);
+static VLLEAF *vlleafdivide(VILLA *villa, VLLEAF *leaf);
+static VLNODE *vlnodenew(VILLA *villa, int heir);
+static int vlnodecacheout(VILLA *villa, int id);
+static int vlnodesave(VILLA *villa, VLNODE *node);
+static VLNODE *vlnodeload(VILLA *villa, int id);
+static void vlnodeaddidx(VILLA *villa, VLNODE *node, int order,
+                         int pid, const char *kbuf, int ksiz);
+static int vlsearchleaf(VILLA *villa, const char *kbuf, int ksiz);
+static int vlcacheadjust(VILLA *villa);
+static VLREC *vlrecsearch(VILLA *villa, VLLEAF *leaf, const char *kbuf, int ksiz, int *ip);
+
+
+
+/*************************************************************************************************
+ * public objects
+ *************************************************************************************************/
+
+
+/* Comparing functions. */
+VLCFUNC VL_CMPLEX = vllexcompare;
+VLCFUNC VL_CMPINT = vlintcompare;
+VLCFUNC VL_CMPNUM = vlnumcompare;
+VLCFUNC VL_CMPDEC = vldeccompare;
+
+
+/* Get a database handle. */
+VILLA *vlopen(const char *name, int omode, VLCFUNC cmp){
+  DEPOT *depot;
+  int dpomode, flags, cmode, root, last, lnum, nnum, rnum;
+  VILLA *villa;
+  VLLEAF *leaf;
+  assert(name && cmp);
+  dpomode = DP_OREADER;
+  if(omode & VL_OWRITER){
+    dpomode = DP_OWRITER;
+    if(omode & VL_OCREAT) dpomode |= DP_OCREAT;
+    if(omode & VL_OTRUNC) dpomode |= DP_OTRUNC;
+  }
+  if(omode & VL_ONOLCK) dpomode |= DP_ONOLCK;
+  if(omode & VL_OLCKNB) dpomode |= DP_OLCKNB;
+  if(!(depot = dpopen(name, dpomode, VL_INITBNUM))) return NULL;
+  flags = dpgetflags(depot);
+  cmode = 0;
+  root = -1;
+  last = -1;
+  lnum = 0;
+  nnum = 0;
+  rnum = 0;
+  if(dprnum(depot) > 0){
+    if(!(flags & VL_FLISVILLA) ||
+       !vldpgetnum(depot, VL_ROOTKEY, &root) || !vldpgetnum(depot, VL_LASTKEY, &last) ||
+       !vldpgetnum(depot, VL_LNUMKEY, &lnum) || !vldpgetnum(depot, VL_NNUMKEY, &nnum) ||
+       !vldpgetnum(depot, VL_RNUMKEY, &rnum) || root < VL_LEAFIDMIN || last < VL_LEAFIDMIN ||
+       lnum < 0 || nnum < 0 || rnum < 0){
+      dpclose(depot);
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      return NULL;
+    }
+    if(flags & VL_FLISZLIB){
+      cmode = VL_OZCOMP;
+    } else if(flags & VL_FLISLZO){
+      cmode = VL_OYCOMP;
+    } else if(flags & VL_FLISBZIP){
+      cmode = VL_OXCOMP;
+    }
+  } else if(omode & VL_OWRITER){
+    if(omode & VL_OZCOMP){
+      cmode = VL_OZCOMP;
+    } else if(omode & VL_OYCOMP){
+      cmode = VL_OYCOMP;
+    } else if(omode & VL_OXCOMP){
+      cmode = VL_OXCOMP;
+    }
+  }
+  if(omode & VL_OWRITER){
+    flags |= VL_FLISVILLA;
+    if(_qdbm_deflate && cmode == VL_OZCOMP){
+      flags |= VL_FLISZLIB;
+    } else if(_qdbm_lzoencode && cmode == VL_OYCOMP){
+      flags |= VL_FLISLZO;
+    } else if(_qdbm_bzencode && cmode == VL_OXCOMP){
+      flags |= VL_FLISBZIP;
+    }
+    if(!dpsetflags(depot, flags) || !dpsetalign(depot, VL_PAGEALIGN) ||
+       !dpsetfbpsiz(depot, VL_FBPOOLSIZ)){
+      dpclose(depot);
+      return NULL;
+    }
+  }
+  CB_MALLOC(villa, sizeof(VILLA));
+  villa->depot = depot;
+  villa->cmp = cmp;
+  villa->wmode = (omode & VL_OWRITER);
+  villa->cmode = cmode;
+  villa->root = root;
+  villa->last = last;
+  villa->lnum = lnum;
+  villa->nnum = nnum;
+  villa->rnum = rnum;
+  villa->leafc = cbmapopen();
+  villa->nodec = cbmapopen();
+  villa->hnum = 0;
+  villa->hleaf = -1;
+  villa->lleaf = -1;
+  villa->curleaf = -1;
+  villa->curknum = -1;
+  villa->curvnum = -1;
+  villa->leafrecmax = VL_DEFLRECMAX;
+  villa->nodeidxmax = VL_DEFNIDXMAX;
+  villa->leafcnum = VL_DEFLCNUM;
+  villa->nodecnum = VL_DEFNCNUM;
+  villa->tran = FALSE;
+  villa->rbroot = -1;
+  villa->rblast = -1;
+  villa->rblnum = -1;
+  villa->rbnnum = -1;
+  villa->rbrnum = -1;
+  if(root == -1){
+    leaf = vlleafnew(villa, -1, -1);
+    villa->root = leaf->id;
+    villa->last = leaf->id;
+    if(!vltranbegin(villa) || !vltranabort(villa)){
+      vlclose(villa);
+      return NULL;
+    }
+  }
+  return villa;
+}
+
+
+/* Close a database handle. */
+int vlclose(VILLA *villa){
+  int err, pid;
+  const char *tmp;
+  assert(villa);
+  err = FALSE;
+  if(villa->tran){
+    if(!vltranabort(villa)) err = TRUE;
+  }
+  cbmapiterinit(villa->leafc);
+  while((tmp = cbmapiternext(villa->leafc, NULL)) != NULL){
+    pid = *(int *)tmp;
+    if(!vlleafcacheout(villa, pid)) err = TRUE;
+  }
+  cbmapiterinit(villa->nodec);
+  while((tmp = cbmapiternext(villa->nodec, NULL)) != NULL){
+    pid = *(int *)tmp;
+    if(!vlnodecacheout(villa, pid)) err = TRUE;
+  }
+  if(villa->wmode){
+    if(!dpsetalign(villa->depot, 0)) err = TRUE;
+    if(!vldpputnum(villa->depot, VL_ROOTKEY, villa->root)) err = TRUE;
+    if(!vldpputnum(villa->depot, VL_LASTKEY, villa->last)) err = TRUE;
+    if(!vldpputnum(villa->depot, VL_LNUMKEY, villa->lnum)) err = TRUE;
+    if(!vldpputnum(villa->depot, VL_NNUMKEY, villa->nnum)) err = TRUE;
+    if(!vldpputnum(villa->depot, VL_RNUMKEY, villa->rnum)) err = TRUE;
+  }
+  cbmapclose(villa->leafc);
+  cbmapclose(villa->nodec);
+  if(!dpclose(villa->depot)) err = TRUE;
+  free(villa);
+  return err ? FALSE : TRUE;
+}
+
+
+/* Store a record. */
+int vlput(VILLA *villa, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){
+  VLLEAF *leaf, *newleaf;
+  VLNODE *node, *newnode;
+  VLIDX *idxp;
+  CBDATUM *key;
+  int i, pid, todiv, heir, parent, mid;
+  assert(villa && kbuf && vbuf);
+  villa->curleaf = -1;
+  villa->curknum = -1;
+  villa->curvnum = -1;
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(vsiz < 0) vsiz = strlen(vbuf);
+  if(villa->hleaf < VL_LEAFIDMIN || !(leaf = vlgethistleaf(villa, kbuf, ksiz))){
+    if((pid = vlsearchleaf(villa, kbuf, ksiz)) == -1) return FALSE;
+    if(!(leaf = vlleafload(villa, pid))) return FALSE;
+  }
+  if(!vlleafaddrec(villa, leaf, dmode, kbuf, ksiz, vbuf, vsiz)){
+    dpecodeset(DP_EKEEP, __FILE__, __LINE__);
+    return FALSE;
+  }
+  todiv = FALSE;
+  switch(CB_LISTNUM(leaf->recs) % 4){
+  case 0:
+    if(CB_LISTNUM(leaf->recs) >= 4 &&
+       vlleafdatasize(leaf) > VL_MAXLEAFSIZ * (villa->cmode > 0 ? 2 : 1)){
+      todiv = TRUE;
+      break;
+    }
+  case 2:
+    if(CB_LISTNUM(leaf->recs) > villa->leafrecmax) todiv = TRUE;
+    break;
+  }
+  if(todiv){
+    if(!(newleaf = vlleafdivide(villa, leaf))) return FALSE;
+    if(leaf->id == villa->last) villa->last = newleaf->id;
+    heir = leaf->id;
+    pid = newleaf->id;
+    key = ((VLREC *)CB_LISTVAL(newleaf->recs, 0))->key;
+    key = cbdatumdup(key);
+    while(TRUE){
+      if(villa->hnum < 1){
+        node = vlnodenew(villa, heir);
+        vlnodeaddidx(villa, node, TRUE, pid, CB_DATUMPTR(key), CB_DATUMSIZE(key));
+        villa->root = node->id;
+        CB_DATUMCLOSE(key);
+        break;
+      }
+      parent = villa->hist[--villa->hnum];
+      if(!(node = vlnodeload(villa, parent))){
+        CB_DATUMCLOSE(key);
+        return FALSE;
+      }
+      vlnodeaddidx(villa, node, FALSE, pid, CB_DATUMPTR(key), CB_DATUMSIZE(key));
+      CB_DATUMCLOSE(key);
+      if(CB_LISTNUM(node->idxs) <= villa->nodeidxmax) break;
+      mid = CB_LISTNUM(node->idxs) / 2;
+      idxp = (VLIDX *)CB_LISTVAL(node->idxs, mid);
+      newnode = vlnodenew(villa, idxp->pid);
+      heir = node->id;
+      pid = newnode->id;
+      CB_DATUMOPEN2(key, CB_DATUMPTR(idxp->key), CB_DATUMSIZE(idxp->key));
+      for(i = mid + 1; i < CB_LISTNUM(node->idxs); i++){
+        idxp = (VLIDX *)CB_LISTVAL(node->idxs, i);
+        vlnodeaddidx(villa, newnode, TRUE, idxp->pid,
+                     CB_DATUMPTR(idxp->key), CB_DATUMSIZE(idxp->key));
+      }
+      for(i = 0; i < CB_LISTNUM(newnode->idxs); i++){
+        idxp = (VLIDX *)cblistpop(node->idxs, NULL);
+        CB_DATUMCLOSE(idxp->key);
+        free(idxp);
+      }
+      node->dirty = TRUE;
+    }
+  }
+  if(!villa->tran && !vlcacheadjust(villa)) return FALSE;
+  return TRUE;
+}
+
+
+/* Delete a record. */
+int vlout(VILLA *villa, const char *kbuf, int ksiz){
+  VLLEAF *leaf;
+  VLREC *recp;
+  int pid, ri, vsiz;
+  char *vbuf;
+  assert(villa && kbuf);
+  villa->curleaf = -1;
+  villa->curknum = -1;
+  villa->curvnum = -1;
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(villa->hleaf < VL_LEAFIDMIN || !(leaf = vlgethistleaf(villa, kbuf, ksiz))){
+    if((pid = vlsearchleaf(villa, kbuf, ksiz)) == -1) return FALSE;
+    if(!(leaf = vlleafload(villa, pid))) return FALSE;
+  }
+  if(!(recp = vlrecsearch(villa, leaf, kbuf, ksiz, &ri))){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(recp->rest){
+    CB_DATUMCLOSE(recp->first);
+    vbuf = cblistshift(recp->rest, &vsiz);
+    CB_DATUMOPEN2(recp->first, vbuf, vsiz);
+    free(vbuf);
+    if(CB_LISTNUM(recp->rest) < 1){
+      CB_LISTCLOSE(recp->rest);
+      recp->rest = NULL;
+    }
+  } else {
+    CB_DATUMCLOSE(recp->key);
+    CB_DATUMCLOSE(recp->first);
+    free(cblistremove(leaf->recs, ri, NULL));
+  }
+  leaf->dirty = TRUE;
+  villa->rnum--;
+  if(!villa->tran && !vlcacheadjust(villa)) return FALSE;
+  return TRUE;
+}
+
+
+/* Retrieve a record. */
+char *vlget(VILLA *villa, const char *kbuf, int ksiz, int *sp){
+  VLLEAF *leaf;
+  VLREC *recp;
+  char *rv;
+  int pid;
+  assert(villa && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(villa->hleaf < VL_LEAFIDMIN || !(leaf = vlgethistleaf(villa, kbuf, ksiz))){
+    if((pid = vlsearchleaf(villa, kbuf, ksiz)) == -1) return NULL;
+    if(!(leaf = vlleafload(villa, pid))) return NULL;
+  }
+  if(!(recp = vlrecsearch(villa, leaf, kbuf, ksiz, NULL))){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!villa->tran && !vlcacheadjust(villa)) return NULL;
+  if(sp) *sp = CB_DATUMSIZE(recp->first);
+  CB_MEMDUP(rv, CB_DATUMPTR(recp->first), CB_DATUMSIZE(recp->first));
+  return rv;
+}
+
+
+/* Get the size of the value of a record. */
+int vlvsiz(VILLA *villa, const char *kbuf, int ksiz){
+  VLLEAF *leaf;
+  VLREC *recp;
+  int pid;
+  assert(villa && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(villa->hleaf < VL_LEAFIDMIN || !(leaf = vlgethistleaf(villa, kbuf, ksiz))){
+    if((pid = vlsearchleaf(villa, kbuf, ksiz)) == -1) return -1;
+    if(!(leaf = vlleafload(villa, pid))) return -1;
+  }
+  if(!(recp = vlrecsearch(villa, leaf, kbuf, ksiz, NULL))){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return -1;
+  }
+  if(!villa->tran && !vlcacheadjust(villa)) return -1;
+  return CB_DATUMSIZE(recp->first);
+}
+
+
+/* Get the number of records corresponding a key. */
+int vlvnum(VILLA *villa, const char *kbuf, int ksiz){
+  VLLEAF *leaf;
+  VLREC *recp;
+  int pid;
+  assert(villa && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(villa->hleaf < VL_LEAFIDMIN || !(leaf = vlgethistleaf(villa, kbuf, ksiz))){
+    if((pid = vlsearchleaf(villa, kbuf, ksiz)) == -1) return 0;
+    if(!(leaf = vlleafload(villa, pid))) return 0;
+  }
+  if(!(recp = vlrecsearch(villa, leaf, kbuf, ksiz, NULL))){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return 0;
+  }
+  if(!villa->tran && !vlcacheadjust(villa)) return 0;
+  return 1 + (recp->rest ? CB_LISTNUM(recp->rest) : 0);
+}
+
+
+/* Store plural records corresponding a key. */
+int vlputlist(VILLA *villa, const char *kbuf, int ksiz, const CBLIST *vals){
+  int i, vsiz;
+  const char *vbuf;
+  assert(villa && kbuf && vals);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(CB_LISTNUM(vals) < 1){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  for(i = 0; i < CB_LISTNUM(vals); i++){
+    vbuf = CB_LISTVAL2(vals, i, vsiz);
+    if(!vlput(villa, kbuf, ksiz, vbuf, vsiz, VL_DDUP)) return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Delete all records corresponding a key. */
+int vloutlist(VILLA *villa, const char *kbuf, int ksiz){
+  int i, vnum;
+  assert(villa && kbuf);
+  if(!villa->wmode){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if((vnum = vlvnum(villa, kbuf, ksiz)) < 1) return FALSE;
+  for(i = 0; i < vnum; i++){
+    if(!vlout(villa, kbuf, ksiz)) return FALSE;
+  }
+  return TRUE;
+}
+
+
+/* Retrieve values of all records corresponding a key. */
+CBLIST *vlgetlist(VILLA *villa, const char *kbuf, int ksiz){
+  VLLEAF *leaf;
+  VLREC *recp;
+  int pid, i, vsiz;
+  CBLIST *vals;
+  const char *vbuf;
+  assert(villa && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(villa->hleaf < VL_LEAFIDMIN || !(leaf = vlgethistleaf(villa, kbuf, ksiz))){
+    if((pid = vlsearchleaf(villa, kbuf, ksiz)) == -1) return NULL;
+    if(!(leaf = vlleafload(villa, pid))) return NULL;
+  }
+  if(!(recp = vlrecsearch(villa, leaf, kbuf, ksiz, NULL))){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  CB_LISTOPEN(vals);
+  CB_LISTPUSH(vals, CB_DATUMPTR(recp->first), CB_DATUMSIZE(recp->first));
+  if(recp->rest){
+    for(i = 0; i < CB_LISTNUM(recp->rest); i++){
+      vbuf = CB_LISTVAL2(recp->rest, i, vsiz);
+      CB_LISTPUSH(vals, vbuf, vsiz);
+    }
+  }
+  if(!villa->tran && !vlcacheadjust(villa)){
+    CB_LISTCLOSE(vals);
+    return NULL;
+  }
+  return vals;
+}
+
+
+/* Retrieve concatenated values of all records corresponding a key. */
+char *vlgetcat(VILLA *villa, const char *kbuf, int ksiz, int *sp){
+  VLLEAF *leaf;
+  VLREC *recp;
+  int pid, i, vsiz, rsiz;
+  char *rbuf;
+  const char *vbuf;
+  assert(villa && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(villa->hleaf < VL_LEAFIDMIN || !(leaf = vlgethistleaf(villa, kbuf, ksiz))){
+    if((pid = vlsearchleaf(villa, kbuf, ksiz)) == -1) return NULL;
+    if(!(leaf = vlleafload(villa, pid))) return NULL;
+  }
+  if(!(recp = vlrecsearch(villa, leaf, kbuf, ksiz, NULL))){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  rsiz = CB_DATUMSIZE(recp->first);
+  CB_MALLOC(rbuf, rsiz + 1);
+  memcpy(rbuf, CB_DATUMPTR(recp->first), rsiz);
+  if(recp->rest){
+    for(i = 0; i < CB_LISTNUM(recp->rest); i++){
+      vbuf = CB_LISTVAL2(recp->rest, i, vsiz);
+      CB_REALLOC(rbuf, rsiz + vsiz + 1);
+      memcpy(rbuf + rsiz, vbuf, vsiz);
+      rsiz += vsiz;
+    }
+  }
+  rbuf[rsiz] = '\0';
+  if(!villa->tran && !vlcacheadjust(villa)){
+    free(rbuf);
+    return NULL;
+  }
+  if(sp) *sp = rsiz;
+  return rbuf;
+}
+
+
+/* Move the cursor to the first record. */
+int vlcurfirst(VILLA *villa){
+  VLLEAF *leaf;
+  assert(villa);
+  villa->curleaf = VL_LEAFIDMIN;
+  villa->curknum = 0;
+  villa->curvnum = 0;
+  if(!(leaf = vlleafload(villa, villa->curleaf))){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  while(CB_LISTNUM(leaf->recs) < 1){
+    villa->curleaf = leaf->next;
+    villa->curknum = 0;
+    villa->curvnum = 0;
+    if(villa->curleaf == -1){
+      dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(!(leaf = vlleafload(villa, villa->curleaf))){
+      villa->curleaf = -1;
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+
+/* Move the cursor to the last record. */
+int vlcurlast(VILLA *villa){
+  VLLEAF *leaf;
+  VLREC *recp;
+  assert(villa);
+  villa->curleaf = villa->last;
+  if(!(leaf = vlleafload(villa, villa->curleaf))){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  while(CB_LISTNUM(leaf->recs) < 1){
+    villa->curleaf = leaf->prev;
+    if(villa->curleaf == -1){
+      villa->curleaf = -1;
+      dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(!(leaf = vlleafload(villa, villa->curleaf))){
+      villa->curleaf = -1;
+      return FALSE;
+    }
+  }
+  villa->curknum = CB_LISTNUM(leaf->recs) - 1;
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+  villa->curvnum = recp->rest ? CB_LISTNUM(recp->rest) : 0;
+  return TRUE;
+}
+
+
+/* Move the cursor to the previous record. */
+int vlcurprev(VILLA *villa){
+  VLLEAF *leaf;
+  VLREC *recp;
+  assert(villa);
+  if(villa->curleaf == -1){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(leaf = vlleafload(villa, villa->curleaf)) || CB_LISTNUM(leaf->recs) < 1){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+  villa->curvnum--;
+  if(villa->curvnum < 0){
+    villa->curknum--;
+    if(villa->curknum < 0){
+      villa->curleaf = leaf->prev;
+      if(villa->curleaf == -1){
+        villa->curleaf = -1;
+        dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+        return FALSE;
+      }
+      if(!(leaf = vlleafload(villa, villa->curleaf))){
+        villa->curleaf = -1;
+        return FALSE;
+      }
+      while(CB_LISTNUM(leaf->recs) < 1){
+        villa->curleaf = leaf->prev;
+        if(villa->curleaf == -1){
+          dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+          return FALSE;
+        }
+        if(!(leaf = vlleafload(villa, villa->curleaf))){
+          villa->curleaf = -1;
+          return FALSE;
+        }
+      }
+      villa->curknum = CB_LISTNUM(leaf->recs) - 1;
+      recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+      villa->curvnum = recp->rest ? CB_LISTNUM(recp->rest) : 0;
+    }
+    recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+    villa->curvnum = recp->rest ? CB_LISTNUM(recp->rest) : 0;
+  }
+  if(!villa->tran && !vlcacheadjust(villa)) return FALSE;
+  return TRUE;
+}
+
+
+/* Move the cursor to the next record. */
+int vlcurnext(VILLA *villa){
+  VLLEAF *leaf;
+  VLREC *recp;
+  assert(villa);
+  if(villa->curleaf == -1){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(leaf = vlleafload(villa, villa->curleaf)) || CB_LISTNUM(leaf->recs) < 1){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+  villa->curvnum++;
+  if(villa->curvnum > (recp->rest ? CB_LISTNUM(recp->rest) : 0)){
+    villa->curknum++;
+    villa->curvnum = 0;
+  }
+  if(villa->curknum >= CB_LISTNUM(leaf->recs)){
+    villa->curleaf = leaf->next;
+    villa->curknum = 0;
+    villa->curvnum = 0;
+    if(villa->curleaf == -1){
+      dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(!(leaf = vlleafload(villa, villa->curleaf))){
+      villa->curleaf = -1;
+      return FALSE;
+    }
+    while(CB_LISTNUM(leaf->recs) < 1){
+      villa->curleaf = leaf->next;
+      villa->curknum = 0;
+      villa->curvnum = 0;
+      if(villa->curleaf == -1){
+        dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+        return FALSE;
+      }
+      if(!(leaf = vlleafload(villa, villa->curleaf))){
+        villa->curleaf = -1;
+        return FALSE;
+      }
+    }
+  }
+  if(!villa->tran && !vlcacheadjust(villa)) return FALSE;
+  return TRUE;
+}
+
+
+/* Move the cursor to a position around a record. */
+int vlcurjump(VILLA *villa, const char *kbuf, int ksiz, int jmode){
+  VLLEAF *leaf;
+  VLREC *recp;
+  int pid, index;
+  assert(villa && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if((pid = vlsearchleaf(villa, kbuf, ksiz)) == -1){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  if(!(leaf = vlleafload(villa, pid))){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  while(CB_LISTNUM(leaf->recs) < 1){
+    villa->curleaf = (jmode == VL_JFORWARD) ? leaf->next : leaf->prev;
+    if(villa->curleaf == -1){
+      dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(!(leaf = vlleafload(villa, villa->curleaf))){
+      villa->curleaf = -1;
+      return FALSE;
+    }
+  }
+  if(!(recp = vlrecsearch(villa, leaf, kbuf, ksiz, &index))){
+    if(jmode == VL_JFORWARD){
+      villa->curleaf = leaf->id;
+      if(index >= CB_LISTNUM(leaf->recs)) index--;
+      villa->curknum = index;
+      villa->curvnum = 0;
+      recp = (VLREC *)CB_LISTVAL(leaf->recs, index);
+      if(villa->cmp(kbuf, ksiz, CB_DATUMPTR(recp->key), CB_DATUMSIZE(recp->key)) < 0) return TRUE;
+      villa->curvnum = (recp->rest ? CB_LISTNUM(recp->rest) : 0);
+      return vlcurnext(villa);
+    } else {
+      villa->curleaf = leaf->id;
+      if(index >= CB_LISTNUM(leaf->recs)) index--;
+      villa->curknum = index;
+      recp = (VLREC *)CB_LISTVAL(leaf->recs, index);
+      villa->curvnum = (recp->rest ? CB_LISTNUM(recp->rest) : 0);
+      if(villa->cmp(kbuf, ksiz, CB_DATUMPTR(recp->key), CB_DATUMSIZE(recp->key)) > 0) return TRUE;
+      villa->curvnum = 0;
+      return vlcurprev(villa);
+    }
+  }
+  if(jmode == VL_JFORWARD){
+    villa->curleaf = pid;
+    villa->curknum = index;
+    villa->curvnum = 0;
+  } else {
+    villa->curleaf = pid;
+    villa->curknum = index;
+    villa->curvnum = (recp->rest ? CB_LISTNUM(recp->rest) : 0);
+  }
+  return TRUE;
+}
+
+
+/* Get the key of the record where the cursor is. */
+char *vlcurkey(VILLA *villa, int *sp){
+  VLLEAF *leaf;
+  VLREC *recp;
+  const char *kbuf;
+  char *rv;
+  int ksiz;
+  assert(villa);
+  if(villa->curleaf == -1){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(leaf = vlleafload(villa, villa->curleaf))){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+  kbuf = CB_DATUMPTR(recp->key);
+  ksiz = CB_DATUMSIZE(recp->key);
+  if(sp) *sp = ksiz;
+  CB_MEMDUP(rv, kbuf, ksiz);
+  return rv;
+}
+
+
+/* Get the value of the record where the cursor is. */
+char *vlcurval(VILLA *villa, int *sp){
+  VLLEAF *leaf;
+  VLREC *recp;
+  const char *vbuf;
+  char *rv;
+  int vsiz;
+  assert(villa);
+  if(villa->curleaf == -1){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(leaf = vlleafload(villa, villa->curleaf))){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+  if(villa->curvnum < 1){
+    vbuf = CB_DATUMPTR(recp->first);
+    vsiz = CB_DATUMSIZE(recp->first);
+  } else {
+    vbuf = CB_LISTVAL2(recp->rest, villa->curvnum - 1, vsiz);
+  }
+  if(sp) *sp = vsiz;
+  CB_MEMDUP(rv, vbuf, vsiz);
+  return rv;
+}
+
+
+/* Insert a record around the cursor. */
+int vlcurput(VILLA *villa, const char *vbuf, int vsiz, int cpmode){
+  VLLEAF *leaf;
+  VLREC *recp;
+  char *tbuf;
+  int tsiz;
+  assert(villa && vbuf);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(vsiz < 0) vsiz = strlen(vbuf);
+  if(villa->curleaf == -1){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(leaf = vlleafload(villa, villa->curleaf))){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+  switch(cpmode){
+  case VL_CPBEFORE:
+    if(villa->curvnum < 1){
+      if(!recp->rest){
+        CB_DATUMTOMALLOC(recp->first, tbuf, tsiz);
+        CB_DATUMOPEN2(recp->first, vbuf, vsiz);
+        CB_LISTOPEN(recp->rest);
+        CB_LISTPUSHBUF(recp->rest, tbuf, tsiz);
+      } else {
+        cblistunshift(recp->rest, CB_DATUMPTR(recp->first), CB_DATUMSIZE(recp->first));
+        CB_DATUMSETSIZE(recp->first, 0);
+        CB_DATUMCAT(recp->first, vbuf, vsiz);
+      }
+    } else {
+      CB_LISTINSERT(recp->rest, villa->curvnum - 1, vbuf, vsiz);
+    }
+    villa->rnum++;
+    break;
+  case VL_CPAFTER:
+    if(!recp->rest) CB_LISTOPEN(recp->rest);
+    CB_LISTINSERT(recp->rest, villa->curvnum, vbuf, vsiz);
+    villa->curvnum++;
+    villa->rnum++;
+    break;
+  default:
+    if(villa->curvnum < 1){
+      CB_DATUMSETSIZE(recp->first, 0);
+      CB_DATUMCAT(recp->first, vbuf, vsiz);
+    } else {
+      cblistover(recp->rest, villa->curvnum - 1, vbuf, vsiz);
+    }
+    break;
+  }
+  leaf->dirty = TRUE;
+  return TRUE;
+}
+
+
+/* Delete the record where the cursor is. */
+int vlcurout(VILLA *villa){
+  VLLEAF *leaf;
+  VLREC *recp;
+  char *vbuf;
+  int vsiz;
+  assert(villa);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(villa->curleaf == -1){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(leaf = vlleafload(villa, villa->curleaf))){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+  if(villa->curvnum < 1){
+    if(recp->rest){
+      vbuf = cblistshift(recp->rest, &vsiz);
+      CB_DATUMSETSIZE(recp->first, 0);
+      CB_DATUMCAT(recp->first, vbuf, vsiz);
+      free(vbuf);
+      if(CB_LISTNUM(recp->rest) < 1){
+        CB_LISTCLOSE(recp->rest);
+        recp->rest = NULL;
+      }
+    } else {
+      CB_DATUMCLOSE(recp->first);
+      CB_DATUMCLOSE(recp->key);
+      free(cblistremove(leaf->recs, villa->curknum, NULL));
+    }
+  } else {
+    free(cblistremove(recp->rest, villa->curvnum - 1, NULL));
+    if(villa->curvnum - 1 >= CB_LISTNUM(recp->rest)){
+      villa->curknum++;
+      villa->curvnum = 0;
+    }
+    if(CB_LISTNUM(recp->rest) < 1){
+      CB_LISTCLOSE(recp->rest);
+      recp->rest = NULL;
+    }
+  }
+  villa->rnum--;
+  leaf->dirty = TRUE;
+  if(villa->curknum >= CB_LISTNUM(leaf->recs)){
+    villa->curleaf = leaf->next;
+    villa->curknum = 0;
+    villa->curvnum = 0;
+    while(villa->curleaf != -1 && (leaf = vlleafload(villa, villa->curleaf)) != NULL &&
+          CB_LISTNUM(leaf->recs) < 1){
+      villa->curleaf = leaf->next;
+    }
+  }
+  return TRUE;
+}
+
+
+/* Set the tuning parameters for performance. */
+void vlsettuning(VILLA *villa, int lrecmax, int nidxmax, int lcnum, int ncnum){
+  assert(villa);
+  if(lrecmax < 1) lrecmax = VL_DEFLRECMAX;
+  if(lrecmax < 3) lrecmax = 3;
+  if(nidxmax < 1) nidxmax = VL_DEFNIDXMAX;
+  if(nidxmax < 4) nidxmax = 4;
+  if(lcnum < 1) lcnum = VL_DEFLCNUM;
+  if(lcnum < VL_CACHEOUT * 2) lcnum = VL_CACHEOUT * 2;
+  if(ncnum < 1) ncnum = VL_DEFNCNUM;
+  if(ncnum < VL_CACHEOUT * 2) ncnum = VL_CACHEOUT * 2;
+  villa->leafrecmax = lrecmax;
+  villa->nodeidxmax = nidxmax;
+  villa->leafcnum = lcnum;
+  villa->nodecnum = ncnum;
+}
+
+
+/* Set the size of the free block pool of a database handle. */
+int vlsetfbpsiz(VILLA *villa, int size){
+  assert(villa && size >= 0);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return dpsetfbpsiz(villa->depot, size);
+}
+
+
+/* Synchronize updating contents with the file and the device. */
+int vlsync(VILLA *villa){
+  int err;
+  err = FALSE;
+  if(!vlmemsync(villa)) err = TRUE;
+  if(!dpsync(villa->depot)) err = TRUE;
+  return err ? FALSE : TRUE;
+}
+
+
+/* Optimize a database. */
+int vloptimize(VILLA *villa){
+  int err;
+  assert(villa);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(villa->tran){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  if(!vlsync(villa)) return FALSE;
+  if(!dpoptimize(villa->depot, -1)) err = TRUE;
+  return err ? FALSE : TRUE;
+}
+
+
+/* Get the name of a database. */
+char *vlname(VILLA *villa){
+  assert(villa);
+  return dpname(villa->depot);
+}
+
+
+/* Get the size of a database file. */
+int vlfsiz(VILLA *villa){
+  return dpfsiz(villa->depot);
+}
+
+
+/* Get the number of the leaf nodes of B+ tree. */
+int vllnum(VILLA *villa){
+  assert(villa);
+  return villa->lnum;
+}
+
+
+/* Get the number of the non-leaf nodes of B+ tree. */
+int vlnnum(VILLA *villa){
+  assert(villa);
+  return villa->nnum;
+}
+
+
+/* Get the number of the records stored in a database. */
+int vlrnum(VILLA *villa){
+  assert(villa);
+  return villa->rnum;
+}
+
+
+/* Check whether a database handle is a writer or not. */
+int vlwritable(VILLA *villa){
+  assert(villa);
+  return villa->wmode;
+}
+
+
+/* Check whether a database has a fatal error or not. */
+int vlfatalerror(VILLA *villa){
+  assert(villa);
+  return dpfatalerror(villa->depot);
+}
+
+
+/* Get the inode number of a database file. */
+int vlinode(VILLA *villa){
+  assert(villa);
+  return dpinode(villa->depot);
+}
+
+
+/* Get the last modified time of a database. */
+time_t vlmtime(VILLA *villa){
+  assert(villa);
+  return dpmtime(villa->depot);
+}
+
+
+/* Begin the transaction. */
+int vltranbegin(VILLA *villa){
+  int err, pid;
+  const char *tmp;
+  VLLEAF *leaf;
+  VLNODE *node;
+  assert(villa);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(villa->tran){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  cbmapiterinit(villa->leafc);
+  while((tmp = cbmapiternext(villa->leafc, NULL)) != NULL){
+    pid = *(int *)tmp;
+    leaf = (VLLEAF *)cbmapget(villa->leafc, (char *)&pid, sizeof(int), NULL);
+    if(leaf->dirty && !vlleafsave(villa, leaf)) err = TRUE;
+  }
+  cbmapiterinit(villa->nodec);
+  while((tmp = cbmapiternext(villa->nodec, NULL)) != NULL){
+    pid = *(int *)tmp;
+    node = (VLNODE *)cbmapget(villa->nodec, (char *)&pid, sizeof(int), NULL);
+    if(node->dirty && !vlnodesave(villa, node)) err = TRUE;
+  }
+  if(!dpsetalign(villa->depot, 0)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_ROOTKEY, villa->root)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_LASTKEY, villa->last)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_LNUMKEY, villa->lnum)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_NNUMKEY, villa->nnum)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_RNUMKEY, villa->rnum)) err = TRUE;
+  if(!dpmemsync(villa->depot)) err = TRUE;
+  if(!dpsetalign(villa->depot, VL_PAGEALIGN)) err = TRUE;
+  villa->tran = TRUE;
+  villa->rbroot = villa->root;
+  villa->rblast = villa->last;
+  villa->rblnum = villa->lnum;
+  villa->rbnnum = villa->nnum;
+  villa->rbrnum = villa->rnum;
+  return err ? FALSE : TRUE;
+}
+
+
+/* Commit the transaction. */
+int vltrancommit(VILLA *villa){
+  int err, pid;
+  const char *tmp;
+  VLLEAF *leaf;
+  VLNODE *node;
+  assert(villa);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!villa->tran){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  cbmapiterinit(villa->leafc);
+  while((tmp = cbmapiternext(villa->leafc, NULL)) != NULL){
+    pid = *(int *)tmp;
+    leaf = (VLLEAF *)cbmapget(villa->leafc, (char *)&pid, sizeof(int), NULL);
+    if(leaf->dirty && !vlleafsave(villa, leaf)) err = TRUE;
+  }
+  cbmapiterinit(villa->nodec);
+  while((tmp = cbmapiternext(villa->nodec, NULL)) != NULL){
+    pid = *(int *)tmp;
+    node = (VLNODE *)cbmapget(villa->nodec, (char *)&pid, sizeof(int), NULL);
+    if(node->dirty && !vlnodesave(villa, node)) err = TRUE;
+  }
+  if(!dpsetalign(villa->depot, 0)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_ROOTKEY, villa->root)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_LASTKEY, villa->last)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_LNUMKEY, villa->lnum)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_NNUMKEY, villa->nnum)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_RNUMKEY, villa->rnum)) err = TRUE;
+  if(!dpmemsync(villa->depot)) err = TRUE;
+  if(!dpsetalign(villa->depot, VL_PAGEALIGN)) err = TRUE;
+  villa->tran = FALSE;
+  villa->rbroot = -1;
+  villa->rblast = -1;
+  villa->rblnum = -1;
+  villa->rbnnum = -1;
+  villa->rbrnum = -1;
+  while(cbmaprnum(villa->leafc) > villa->leafcnum || cbmaprnum(villa->nodec) > villa->nodecnum){
+    if(!vlcacheadjust(villa)){
+      err = TRUE;
+      break;
+    }
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Abort the transaction. */
+int vltranabort(VILLA *villa){
+  int err, pid;
+  const char *tmp;
+  VLLEAF *leaf;
+  VLNODE *node;
+  assert(villa);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!villa->tran){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  cbmapiterinit(villa->leafc);
+  while((tmp = cbmapiternext(villa->leafc, NULL)) != NULL){
+    pid = *(int *)tmp;
+    if(!(leaf = (VLLEAF *)cbmapget(villa->leafc, (char *)&pid, sizeof(int), NULL))){
+      err = TRUE;
+      continue;
+    }
+    if(leaf->dirty){
+      leaf->dirty = FALSE;
+      if(!vlleafcacheout(villa, pid)) err = TRUE;
+    }
+  }
+  cbmapiterinit(villa->nodec);
+  while((tmp = cbmapiternext(villa->nodec, NULL)) != NULL){
+    pid = *(int *)tmp;
+    if(!(node = (VLNODE *)cbmapget(villa->nodec, (char *)&pid, sizeof(int), NULL))){
+      err = TRUE;
+      continue;
+    }
+    if(node->dirty){
+      node->dirty = FALSE;
+      if(!vlnodecacheout(villa, pid)) err = TRUE;
+    }
+  }
+  villa->tran = FALSE;
+  villa->root = villa->rbroot;
+  villa->last = villa->rblast;
+  villa->lnum = villa->rblnum;
+  villa->nnum = villa->rbnnum;
+  villa->rnum = villa->rbrnum;
+  while(cbmaprnum(villa->leafc) > villa->leafcnum || cbmaprnum(villa->nodec) > villa->nodecnum){
+    if(!vlcacheadjust(villa)){
+      err = TRUE;
+      break;
+    }
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Remove a database file. */
+int vlremove(const char *name){
+  assert(name);
+  return dpremove(name);
+}
+
+
+/* Repair a broken database file. */
+int vlrepair(const char *name, VLCFUNC cmp){
+  DEPOT *depot;
+  VILLA *tvilla;
+  char path[VL_PATHBUFSIZ], *kbuf, *vbuf, *zbuf, *rp, *tkbuf, *tvbuf;
+  int i, err, flags, omode, ksiz, vsiz, zsiz, size, step, tksiz, tvsiz, vnum;
+  assert(name && cmp);
+  err = FALSE;
+  if(!dprepair(name)) err = TRUE;
+  if(!(depot = dpopen(name, DP_OREADER, -1))) return FALSE;
+  flags = dpgetflags(depot);
+  if(!(flags & VL_FLISVILLA)){
+    dpclose(depot);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  sprintf(path, "%s%s", name, VL_TMPFSUF);
+  omode = VL_OWRITER | VL_OCREAT | VL_OTRUNC;
+  if(flags & VL_FLISZLIB){
+    omode |= VL_OZCOMP;
+  } else if(flags & VL_FLISLZO){
+    omode |= VL_OXCOMP;
+  } else if(flags & VL_FLISBZIP){
+    omode |= VL_OYCOMP;
+  }
+  if(!(tvilla = vlopen(path, omode, cmp))){
+    dpclose(depot);
+    return FALSE;
+  }
+  if(!dpiterinit(depot)) err = TRUE;
+  while((kbuf =  dpiternext(depot, &ksiz)) != NULL){
+    if(ksiz == sizeof(int) && *(int *)kbuf < VL_NODEIDMIN && *(int *)kbuf > 0){
+      if((vbuf = dpget(depot, (char *)kbuf, sizeof(int), 0, -1, &vsiz)) != NULL){
+        if(_qdbm_inflate && (flags & VL_FLISZLIB) &&
+           (zbuf = _qdbm_inflate(vbuf, vsiz, &zsiz, _QDBM_ZMRAW)) != NULL){
+          free(vbuf);
+          vbuf = zbuf;
+          vsiz = zsiz;
+        } else if(_qdbm_lzodecode && (flags & VL_FLISLZO) &&
+                  (zbuf = _qdbm_lzodecode(vbuf, vsiz, &zsiz)) != NULL){
+          free(vbuf);
+          vbuf = zbuf;
+          vsiz = zsiz;
+        } else if(_qdbm_bzdecode && (flags & VL_FLISBZIP) &&
+                  (zbuf = _qdbm_bzdecode(vbuf, vsiz, &zsiz)) != NULL){
+          free(vbuf);
+          vbuf = zbuf;
+          vsiz = zsiz;
+        }
+        rp = vbuf;
+        size = vsiz;
+        if(size >= 1){
+          VL_READVNUMBUF(rp, size, vnum, step);
+          rp += step;
+          size -= step;
+        }
+        if(size >= 1){
+          VL_READVNUMBUF(rp, size, vnum, step);
+          rp += step;
+          size -= step;
+        }
+        while(size >= 1){
+          VL_READVNUMBUF(rp, size, tksiz, step);
+          rp += step;
+          size -= step;
+          if(size < tksiz) break;
+          tkbuf = rp;
+          rp += tksiz;
+          size -= tksiz;
+          if(size < 1) break;
+          VL_READVNUMBUF(rp, size, vnum, step);
+          rp += step;
+          size -= step;
+          if(vnum < 1 || size < 1) break;
+          for(i = 0; i < vnum && size >= 1; i++){
+            VL_READVNUMBUF(rp, size, tvsiz, step);
+            rp += step;
+            size -= step;
+            if(size < tvsiz) break;
+            tvbuf = rp;
+            rp += tvsiz;
+            size -= tvsiz;
+            if(!vlput(tvilla, tkbuf, tksiz, tvbuf, tvsiz, VL_DDUP)) err = TRUE;
+          }
+        }
+        free(vbuf);
+      }
+    }
+    free(kbuf);
+  }
+  if(!vlclose(tvilla)) err = TRUE;
+  if(!dpclose(depot)) err = TRUE;
+  if(!dpremove(name)) err = TRUE;
+  if(rename(path, name) == -1){
+    if(!err) dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    err = TRUE;
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Dump all records as endian independent data. */
+int vlexportdb(VILLA *villa, const char *name){
+  DEPOT *depot;
+  char path[VL_PATHBUFSIZ], *kbuf, *vbuf, *nkey;
+  int i, err, ksiz, vsiz, ki;
+  assert(villa && name);
+  sprintf(path, "%s%s", name, VL_TMPFSUF);
+  if(!(depot = dpopen(path, DP_OWRITER | DP_OCREAT | DP_OTRUNC, -1))) return FALSE;
+  err = FALSE;
+  vlcurfirst(villa);
+  for(i = 0; !err && (kbuf = vlcurkey(villa, &ksiz)) != NULL; i++){
+    if((vbuf = vlcurval(villa, &vsiz)) != NULL){
+      CB_MALLOC(nkey, ksiz + VL_NUMBUFSIZ);
+      ki = sprintf(nkey, "%X\t", i);
+      memcpy(nkey + ki, kbuf, ksiz);
+      if(!dpput(depot, nkey, ki + ksiz, vbuf, vsiz, DP_DKEEP)) err = TRUE;
+      free(nkey);
+      free(vbuf);
+    } else {
+      err = TRUE;
+    }
+    free(kbuf);
+    vlcurnext(villa);
+  }
+  if(!dpexportdb(depot, name)) err = TRUE;
+  if(!dpclose(depot)) err = TRUE;
+  if(!dpremove(path)) err = TRUE;
+  return !err && !vlfatalerror(villa);
+}
+
+
+/* Load all records from endian independent data. */
+int vlimportdb(VILLA *villa, const char *name){
+  DEPOT *depot;
+  char path[VL_PATHBUFSIZ], *kbuf, *vbuf, *rp;
+  int err, ksiz, vsiz;
+  assert(villa && name);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(vlrnum(villa) > 0){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  kbuf = dpname(villa->depot);
+  sprintf(path, "%s%s", kbuf, VL_TMPFSUF);
+  free(kbuf);
+  if(!(depot = dpopen(path, DP_OWRITER | DP_OCREAT | DP_OTRUNC, -1))) return FALSE;
+  err = FALSE;
+  if(!dpimportdb(depot, name)) err = TRUE;
+  dpiterinit(depot);
+  while(!err && (kbuf = dpiternext(depot, &ksiz)) != NULL){
+    if((vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz)) != NULL){
+      if((rp = strchr(kbuf, '\t')) != NULL){
+        rp++;
+        if(!vlput(villa, rp, ksiz - (rp - kbuf), vbuf, vsiz, VL_DDUP)) err = TRUE;
+      } else {
+        dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+        err = TRUE;
+      }
+      free(vbuf);
+    } else {
+      err = TRUE;
+    }
+    free(kbuf);
+  }
+  if(!dpclose(depot)) err = TRUE;
+  if(!dpremove(path)) err = TRUE;
+  return !err && !vlfatalerror(villa);
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Number of division of the database for Vista. */
+int *vlcrdnumptr(void){
+  static int defvlcrdnum = VL_CRDNUM;
+  void *ptr;
+  if(_qdbm_ptsafe){
+    if(!(ptr = _qdbm_settsd(&defvlcrdnum, sizeof(int), &defvlcrdnum))){
+      defvlcrdnum = DP_EMISC;
+      return &defvlcrdnum;
+    }
+    return (int *)ptr;
+  }
+  return &defvlcrdnum;
+}
+
+
+/* Synchronize updating contents on memory. */
+int vlmemsync(VILLA *villa){
+  int err, pid;
+  const char *tmp;
+  assert(villa);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(villa->tran){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  cbmapiterinit(villa->leafc);
+  while((tmp = cbmapiternext(villa->leafc, NULL)) != NULL){
+    pid = *(int *)tmp;
+    if(!vlleafcacheout(villa, pid)) err = TRUE;
+  }
+  cbmapiterinit(villa->nodec);
+  while((tmp = cbmapiternext(villa->nodec, NULL)) != NULL){
+    pid = *(int *)tmp;
+    if(!vlnodecacheout(villa, pid)) err = TRUE;
+  }
+  if(!dpsetalign(villa->depot, 0)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_ROOTKEY, villa->root)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_LASTKEY, villa->last)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_LNUMKEY, villa->lnum)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_NNUMKEY, villa->nnum)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_RNUMKEY, villa->rnum)) err = TRUE;
+  if(!dpsetalign(villa->depot, VL_PAGEALIGN)) err = TRUE;
+  if(!dpmemsync(villa->depot)) err = TRUE;
+  return err ? FALSE : TRUE;
+}
+
+
+/* Synchronize updating contents on memory, not physically. */
+int vlmemflush(VILLA *villa){
+  int err, pid;
+  const char *tmp;
+  assert(villa);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(villa->tran){
+    dpecodeset(DP_EMISC, __FILE__, __LINE__);
+    return FALSE;
+  }
+  err = FALSE;
+  cbmapiterinit(villa->leafc);
+  while((tmp = cbmapiternext(villa->leafc, NULL)) != NULL){
+    pid = *(int *)tmp;
+    if(!vlleafcacheout(villa, pid)) err = TRUE;
+  }
+  cbmapiterinit(villa->nodec);
+  while((tmp = cbmapiternext(villa->nodec, NULL)) != NULL){
+    pid = *(int *)tmp;
+    if(!vlnodecacheout(villa, pid)) err = TRUE;
+  }
+  if(!dpsetalign(villa->depot, 0)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_ROOTKEY, villa->root)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_LASTKEY, villa->last)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_LNUMKEY, villa->lnum)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_NNUMKEY, villa->nnum)) err = TRUE;
+  if(!vldpputnum(villa->depot, VL_RNUMKEY, villa->rnum)) err = TRUE;
+  if(!dpsetalign(villa->depot, VL_PAGEALIGN)) err = TRUE;
+  if(!dpmemflush(villa->depot)) err = TRUE;
+  return err ? FALSE : TRUE;
+}
+
+
+/* Refer to a volatile cache of a value of a record. */
+const char *vlgetcache(VILLA *villa, const char *kbuf, int ksiz, int *sp){
+  VLLEAF *leaf;
+  VLREC *recp;
+  int pid;
+  assert(villa && kbuf);
+  if(ksiz < 0) ksiz = strlen(kbuf);
+  if(villa->hleaf < VL_LEAFIDMIN || !(leaf = vlgethistleaf(villa, kbuf, ksiz))){
+    if((pid = vlsearchleaf(villa, kbuf, ksiz)) == -1) return NULL;
+    if(!(leaf = vlleafload(villa, pid))) return NULL;
+  }
+  if(!(recp = vlrecsearch(villa, leaf, kbuf, ksiz, NULL))){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(!villa->tran && !vlcacheadjust(villa)) return NULL;
+  if(sp) *sp = CB_DATUMSIZE(recp->first);
+  return CB_DATUMPTR(recp->first);
+}
+
+
+/* Refer to volatile cache of the key of the record where the cursor is. */
+const char *vlcurkeycache(VILLA *villa, int *sp){
+  VLLEAF *leaf;
+  VLREC *recp;
+  const char *kbuf;
+  int ksiz;
+  assert(villa);
+  if(villa->curleaf == -1){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(leaf = vlleafload(villa, villa->curleaf))){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+  kbuf = CB_DATUMPTR(recp->key);
+  ksiz = CB_DATUMSIZE(recp->key);
+  if(sp) *sp = ksiz;
+  return kbuf;
+}
+
+
+/* Refer to volatile cache of the value of the record where the cursor is. */
+const char *vlcurvalcache(VILLA *villa, int *sp){
+  VLLEAF *leaf;
+  VLREC *recp;
+  const char *vbuf;
+  int vsiz;
+  assert(villa);
+  if(villa->curleaf == -1){
+    dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
+    return FALSE;
+  }
+  if(!(leaf = vlleafload(villa, villa->curleaf))){
+    villa->curleaf = -1;
+    return FALSE;
+  }
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, villa->curknum);
+  if(villa->curvnum < 1){
+    vbuf = CB_DATUMPTR(recp->first);
+    vsiz = CB_DATUMSIZE(recp->first);
+  } else {
+    vbuf = CB_LISTVAL2(recp->rest, villa->curvnum - 1, vsiz);
+  }
+  if(sp) *sp = vsiz;
+  return vbuf;
+}
+
+
+/* Get a multiple cursor handle. */
+VLMULCUR *vlmulcuropen(VILLA *villa){
+  VLMULCUR *mulcur;
+  assert(villa);
+  if(villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return NULL;
+  }
+  CB_MALLOC(mulcur, sizeof(VLMULCUR));
+  mulcur->villa = villa;
+  mulcur->curleaf = -1;
+  mulcur->curknum = -1;
+  mulcur->curvnum = -1;
+  return mulcur;
+}
+
+
+/* Close a multiple cursor handle. */
+void vlmulcurclose(VLMULCUR *mulcur){
+  assert(mulcur);
+  free(mulcur);
+}
+
+
+/* Move a multiple cursor to the first record. */
+int vlmulcurfirst(VLMULCUR *mulcur){
+  VLMULCUR swap;
+  int rv;
+  assert(mulcur);
+  swap.curleaf = mulcur->villa->curleaf;
+  swap.curknum = mulcur->villa->curknum;
+  swap.curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = mulcur->curleaf;
+  mulcur->villa->curknum = mulcur->curknum;
+  mulcur->villa->curvnum = mulcur->curvnum;
+  rv = vlcurfirst(mulcur->villa);
+  mulcur->curleaf = mulcur->villa->curleaf;
+  mulcur->curknum = mulcur->villa->curknum;
+  mulcur->curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = swap.curleaf;
+  mulcur->villa->curknum = swap.curknum;
+  mulcur->villa->curvnum = swap.curvnum;
+  return rv;
+}
+
+
+/* Move a multiple cursor to the last record. */
+int vlmulcurlast(VLMULCUR *mulcur){
+  VLMULCUR swap;
+  int rv;
+  assert(mulcur);
+  swap.curleaf = mulcur->villa->curleaf;
+  swap.curknum = mulcur->villa->curknum;
+  swap.curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = mulcur->curleaf;
+  mulcur->villa->curknum = mulcur->curknum;
+  mulcur->villa->curvnum = mulcur->curvnum;
+  rv = vlcurlast(mulcur->villa);
+  mulcur->curleaf = mulcur->villa->curleaf;
+  mulcur->curknum = mulcur->villa->curknum;
+  mulcur->curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = swap.curleaf;
+  mulcur->villa->curknum = swap.curknum;
+  mulcur->villa->curvnum = swap.curvnum;
+  return rv;
+}
+
+
+/* Move a multiple cursor to the previous record. */
+int vlmulcurprev(VLMULCUR *mulcur){
+  VLMULCUR swap;
+  int rv;
+  assert(mulcur);
+  swap.curleaf = mulcur->villa->curleaf;
+  swap.curknum = mulcur->villa->curknum;
+  swap.curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = mulcur->curleaf;
+  mulcur->villa->curknum = mulcur->curknum;
+  mulcur->villa->curvnum = mulcur->curvnum;
+  rv = vlcurprev(mulcur->villa);
+  mulcur->curleaf = mulcur->villa->curleaf;
+  mulcur->curknum = mulcur->villa->curknum;
+  mulcur->curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = swap.curleaf;
+  mulcur->villa->curknum = swap.curknum;
+  mulcur->villa->curvnum = swap.curvnum;
+  return rv;
+}
+
+
+/* Move a multiple cursor to the next record. */
+int vlmulcurnext(VLMULCUR *mulcur){
+  VLMULCUR swap;
+  int rv;
+  assert(mulcur);
+  swap.curleaf = mulcur->villa->curleaf;
+  swap.curknum = mulcur->villa->curknum;
+  swap.curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = mulcur->curleaf;
+  mulcur->villa->curknum = mulcur->curknum;
+  mulcur->villa->curvnum = mulcur->curvnum;
+  rv = vlcurnext(mulcur->villa);
+  mulcur->curleaf = mulcur->villa->curleaf;
+  mulcur->curknum = mulcur->villa->curknum;
+  mulcur->curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = swap.curleaf;
+  mulcur->villa->curknum = swap.curknum;
+  mulcur->villa->curvnum = swap.curvnum;
+  return rv;
+}
+
+
+/* Move a multiple cursor to a position around a record. */
+int vlmulcurjump(VLMULCUR *mulcur, const char *kbuf, int ksiz, int jmode){
+  VLMULCUR swap;
+  int rv;
+  assert(mulcur);
+  swap.curleaf = mulcur->villa->curleaf;
+  swap.curknum = mulcur->villa->curknum;
+  swap.curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = mulcur->curleaf;
+  mulcur->villa->curknum = mulcur->curknum;
+  mulcur->villa->curvnum = mulcur->curvnum;
+  rv = vlcurjump(mulcur->villa, kbuf, ksiz, jmode);
+  mulcur->curleaf = mulcur->villa->curleaf;
+  mulcur->curknum = mulcur->villa->curknum;
+  mulcur->curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = swap.curleaf;
+  mulcur->villa->curknum = swap.curknum;
+  mulcur->villa->curvnum = swap.curvnum;
+  return rv;
+}
+
+
+/* Get the key of the record where a multiple cursor is. */
+char *vlmulcurkey(VLMULCUR *mulcur, int *sp){
+  VLMULCUR swap;
+  char *rv;
+  assert(mulcur);
+  swap.curleaf = mulcur->villa->curleaf;
+  swap.curknum = mulcur->villa->curknum;
+  swap.curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = mulcur->curleaf;
+  mulcur->villa->curknum = mulcur->curknum;
+  mulcur->villa->curvnum = mulcur->curvnum;
+  rv = vlcurkey(mulcur->villa, sp);
+  mulcur->curleaf = mulcur->villa->curleaf;
+  mulcur->curknum = mulcur->villa->curknum;
+  mulcur->curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = swap.curleaf;
+  mulcur->villa->curknum = swap.curknum;
+  mulcur->villa->curvnum = swap.curvnum;
+  return rv;
+}
+
+
+/* Get the value of the record where a multiple cursor is. */
+char *vlmulcurval(VLMULCUR *mulcur, int *sp){
+  VLMULCUR swap;
+  char *rv;
+  assert(mulcur);
+  swap.curleaf = mulcur->villa->curleaf;
+  swap.curknum = mulcur->villa->curknum;
+  swap.curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = mulcur->curleaf;
+  mulcur->villa->curknum = mulcur->curknum;
+  mulcur->villa->curvnum = mulcur->curvnum;
+  rv = vlcurval(mulcur->villa, sp);
+  mulcur->curleaf = mulcur->villa->curleaf;
+  mulcur->curknum = mulcur->villa->curknum;
+  mulcur->curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = swap.curleaf;
+  mulcur->villa->curknum = swap.curknum;
+  mulcur->villa->curvnum = swap.curvnum;
+  return rv;
+}
+
+
+/* Refer to volatile cache of the key of the record where a multiple cursor is. */
+const char *vlmulcurkeycache(VLMULCUR *mulcur, int *sp){
+  VLMULCUR swap;
+  const char *rv;
+  assert(mulcur);
+  swap.curleaf = mulcur->villa->curleaf;
+  swap.curknum = mulcur->villa->curknum;
+  swap.curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = mulcur->curleaf;
+  mulcur->villa->curknum = mulcur->curknum;
+  mulcur->villa->curvnum = mulcur->curvnum;
+  rv = vlcurkeycache(mulcur->villa, sp);
+  mulcur->curleaf = mulcur->villa->curleaf;
+  mulcur->curknum = mulcur->villa->curknum;
+  mulcur->curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = swap.curleaf;
+  mulcur->villa->curknum = swap.curknum;
+  mulcur->villa->curvnum = swap.curvnum;
+  return rv;
+}
+
+
+/* Refer to volatile cache of the value of the record where a multiple cursor is. */
+const char *vlmulcurvalcache(VLMULCUR *mulcur, int *sp){
+  VLMULCUR swap;
+  const char *rv;
+  assert(mulcur);
+  swap.curleaf = mulcur->villa->curleaf;
+  swap.curknum = mulcur->villa->curknum;
+  swap.curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = mulcur->curleaf;
+  mulcur->villa->curknum = mulcur->curknum;
+  mulcur->villa->curvnum = mulcur->curvnum;
+  rv = vlcurvalcache(mulcur->villa, sp);
+  mulcur->curleaf = mulcur->villa->curleaf;
+  mulcur->curknum = mulcur->villa->curknum;
+  mulcur->curvnum = mulcur->villa->curvnum;
+  mulcur->villa->curleaf = swap.curleaf;
+  mulcur->villa->curknum = swap.curknum;
+  mulcur->villa->curvnum = swap.curvnum;
+  return rv;
+}
+
+
+
+/*************************************************************************************************
+ * private objects
+ *************************************************************************************************/
+
+
+/* Compare keys of two records 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.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+static int vllexcompare(const char *aptr, int asiz, const char *bptr, int bsiz){
+  int i, min;
+  assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
+  min = asiz < bsiz ? asiz : bsiz;
+  for(i = 0; i < min; i++){
+    if(((unsigned char *)aptr)[i] != ((unsigned char *)bptr)[i])
+      return ((unsigned char *)aptr)[i] - ((unsigned char *)bptr)[i];
+  }
+  if(asiz == bsiz) return 0;
+  return asiz - bsiz;
+}
+
+
+/* Compare keys of two records as native integers.
+   `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.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+static int vlintcompare(const char *aptr, int asiz, const char *bptr, int bsiz){
+  int anum, bnum;
+  assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
+  if(asiz != bsiz) return asiz - bsiz;
+  anum = (asiz == sizeof(int) ? *(int *)aptr : INT_MIN);
+  bnum = (bsiz == sizeof(int) ? *(int *)bptr : INT_MIN);
+  return anum - bnum;
+}
+
+
+/* Compare keys of two records as numbers of big endian.
+   `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.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+static int vlnumcompare(const char *aptr, int asiz, const char *bptr, int bsiz){
+  int i;
+  assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
+  if(asiz != bsiz) return asiz - bsiz;
+  for(i = 0; i < asiz; i++){
+    if(aptr[i] != bptr[i]) return aptr[i] - bptr[i];
+  }
+  return 0;
+}
+
+
+/* Compare keys of two records as numeric strings of octal, decimal or hexadecimal.
+   `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.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+static int vldeccompare(const char *aptr, int asiz, const char *bptr, int bsiz){
+  assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
+  return (int)(strtod(aptr, NULL) - strtod(bptr, NULL));
+}
+
+
+/* Store a record composed of a pair of integers.
+   `depot' specifies an internal database handle.
+   `knum' specifies an integer of the key.
+   `vnum' specifies an integer of the value.
+   The return value is true if successful, else, it is false. */
+static int vldpputnum(DEPOT *depot, int knum, int vnum){
+  assert(depot);
+  return dpput(depot, (char *)&knum, sizeof(int), (char *)&vnum, sizeof(int), DP_DOVER);
+}
+
+
+/* Retrieve a record composed of a pair of integers.
+   `depot' specifies an internal database handle.
+   `knum' specifies an integer of the key.
+   `vip' specifies the pointer to a variable to assign the result to.
+   The return value is true if successful, else, it is false. */
+static int vldpgetnum(DEPOT *depot, int knum, int *vnp){
+  char *vbuf;
+  int vsiz;
+  assert(depot && vnp);
+  vbuf = dpget(depot, (char *)&knum, sizeof(int), 0, -1, &vsiz);
+  if(!vbuf || vsiz != sizeof(int)){
+    free(vbuf);
+    return FALSE;
+  }
+  *vnp = *(int *)vbuf;
+  free(vbuf);
+  return TRUE;
+}
+
+
+/* Create a new leaf.
+   `villa' specifies a database handle.
+   `prev' specifies the ID number of the previous leaf.
+   `next' specifies the ID number of the previous leaf.
+   The return value is a handle of the leaf. */
+static VLLEAF *vlleafnew(VILLA *villa, int prev, int next){
+  VLLEAF lent;
+  assert(villa);
+  lent.id = villa->lnum + VL_LEAFIDMIN;
+  lent.dirty = TRUE;
+  CB_LISTOPEN(lent.recs);
+  lent.prev = prev;
+  lent.next = next;
+  villa->lnum++;
+  cbmapput(villa->leafc, (char *)&(lent.id), sizeof(int), (char *)&lent, sizeof(VLLEAF), TRUE);
+  return (VLLEAF *)cbmapget(villa->leafc, (char *)&(lent.id), sizeof(int), NULL);
+}
+
+
+/* Remove a leaf from the cache.
+   `villa' specifies a database handle.
+   `id' specifies the ID number of the leaf.
+   The return value is true if successful, else, it is false. */
+static int vlleafcacheout(VILLA *villa, int id){
+  VLLEAF *leaf;
+  VLREC *recp;
+  CBLIST *recs;
+  int i, err, ln;
+  assert(villa && id >= VL_LEAFIDMIN);
+  if(!(leaf = (VLLEAF *)cbmapget(villa->leafc, (char *)&id, sizeof(int), NULL))) return FALSE;
+  err = FALSE;
+  if(leaf->dirty && !vlleafsave(villa, leaf)) err = TRUE;
+  recs = leaf->recs;
+  ln = CB_LISTNUM(recs);
+  for(i = 0; i < ln; i++){
+    recp = (VLREC *)CB_LISTVAL(recs, i);
+    CB_DATUMCLOSE(recp->key);
+    CB_DATUMCLOSE(recp->first);
+    if(recp->rest) CB_LISTCLOSE(recp->rest);
+  }
+  CB_LISTCLOSE(recs);
+  cbmapout(villa->leafc, (char *)&id, sizeof(int));
+  return err ? FALSE : TRUE;
+}
+
+
+/* Save a leaf into the database.
+   `villa' specifies a database handle.
+   `leaf' specifies a leaf handle.
+   The return value is true if successful, else, it is false. */
+static int vlleafsave(VILLA *villa, VLLEAF *leaf){
+  VLREC *recp;
+  CBLIST *recs;
+  CBDATUM *buf;
+  char vnumbuf[VL_VNUMBUFSIZ], *zbuf;
+  const char *vbuf;
+  int i, j, ksiz, vnum, vsiz, prev, next, vnumsiz, ln, zsiz;
+  assert(villa && leaf);
+  CB_DATUMOPEN(buf);
+  prev = leaf->prev;
+  if(prev == -1) prev = VL_NODEIDMIN - 1;
+  VL_SETVNUMBUF(vnumsiz, vnumbuf, prev);
+  CB_DATUMCAT(buf, vnumbuf, vnumsiz);
+  next = leaf->next;
+  if(next == -1) next = VL_NODEIDMIN - 1;
+  VL_SETVNUMBUF(vnumsiz, vnumbuf, next);
+  CB_DATUMCAT(buf, vnumbuf, vnumsiz);
+  recs = leaf->recs;
+  ln = CB_LISTNUM(recs);
+  for(i = 0; i < ln; i++){
+    recp = (VLREC *)CB_LISTVAL(recs, i);
+    ksiz = CB_DATUMSIZE(recp->key);
+    VL_SETVNUMBUF(vnumsiz, vnumbuf, ksiz);
+    CB_DATUMCAT(buf, vnumbuf, vnumsiz);
+    CB_DATUMCAT(buf, CB_DATUMPTR(recp->key), ksiz);
+    vnum = 1 + (recp->rest ? CB_LISTNUM(recp->rest) : 0);
+    VL_SETVNUMBUF(vnumsiz, vnumbuf, vnum);
+    CB_DATUMCAT(buf, vnumbuf, vnumsiz);
+    vsiz = CB_DATUMSIZE(recp->first);
+    VL_SETVNUMBUF(vnumsiz, vnumbuf, vsiz);
+    CB_DATUMCAT(buf, vnumbuf, vnumsiz);
+    CB_DATUMCAT(buf, CB_DATUMPTR(recp->first), vsiz);
+    if(recp->rest){
+      for(j = 0; j < CB_LISTNUM(recp->rest); j++){
+        vbuf = CB_LISTVAL2(recp->rest, j, vsiz);
+        VL_SETVNUMBUF(vnumsiz, vnumbuf, vsiz);
+        CB_DATUMCAT(buf, vnumbuf, vnumsiz);
+        CB_DATUMCAT(buf, vbuf, vsiz);
+      }
+    }
+  }
+  if(_qdbm_deflate && villa->cmode == VL_OZCOMP){
+    if(!(zbuf = _qdbm_deflate(CB_DATUMPTR(buf), CB_DATUMSIZE(buf), &zsiz, _QDBM_ZMRAW))){
+      CB_DATUMCLOSE(buf);
+      dpecodeset(DP_EMISC, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(!dpput(villa->depot, (char *)&(leaf->id), sizeof(int), zbuf, zsiz, DP_DOVER)){
+      CB_DATUMCLOSE(buf);
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      return FALSE;
+    }
+    free(zbuf);
+  } else if(_qdbm_lzoencode && villa->cmode == VL_OYCOMP){
+    if(!(zbuf = _qdbm_lzoencode(CB_DATUMPTR(buf), CB_DATUMSIZE(buf), &zsiz))){
+      CB_DATUMCLOSE(buf);
+      dpecodeset(DP_EMISC, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(!dpput(villa->depot, (char *)&(leaf->id), sizeof(int), zbuf, zsiz, DP_DOVER)){
+      CB_DATUMCLOSE(buf);
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      return FALSE;
+    }
+    free(zbuf);
+  } else if(_qdbm_bzencode && villa->cmode == VL_OXCOMP){
+    if(!(zbuf = _qdbm_bzencode(CB_DATUMPTR(buf), CB_DATUMSIZE(buf), &zsiz))){
+      CB_DATUMCLOSE(buf);
+      dpecodeset(DP_EMISC, __FILE__, __LINE__);
+      return FALSE;
+    }
+    if(!dpput(villa->depot, (char *)&(leaf->id), sizeof(int), zbuf, zsiz, DP_DOVER)){
+      CB_DATUMCLOSE(buf);
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      return FALSE;
+    }
+    free(zbuf);
+  } else {
+    if(!dpput(villa->depot, (char *)&(leaf->id), sizeof(int),
+              CB_DATUMPTR(buf), CB_DATUMSIZE(buf), DP_DOVER)){
+      CB_DATUMCLOSE(buf);
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      return FALSE;
+    }
+  }
+  CB_DATUMCLOSE(buf);
+  leaf->dirty = FALSE;
+  return TRUE;
+}
+
+
+/* Load a leaf from the database.
+   `villa' specifies a database handle.
+   `id' specifies the ID number of the leaf.
+   If successful, the return value is the pointer to the leaf, else, it is `NULL'. */
+static VLLEAF *vlleafload(VILLA *villa, int id){
+  char wbuf[VL_PAGEBUFSIZ], *buf, *rp, *kbuf, *vbuf, *zbuf;
+  int i, size, step, ksiz, vnum, vsiz, prev, next, zsiz;
+  VLLEAF *leaf, lent;
+  VLREC rec;
+  assert(villa && id >= VL_LEAFIDMIN);
+  if((leaf = (VLLEAF *)cbmapget(villa->leafc, (char *)&id, sizeof(int), NULL)) != NULL){
+    cbmapmove(villa->leafc, (char *)&id, sizeof(int), FALSE);
+    return leaf;
+  }
+  ksiz = -1;
+  prev = -1;
+  next = -1;
+  if((size = dpgetwb(villa->depot, (char *)&id, sizeof(int), 0, VL_PAGEBUFSIZ, wbuf)) > 0 &&
+     size < VL_PAGEBUFSIZ){
+    buf = NULL;
+  } else if(!(buf = dpget(villa->depot, (char *)&id, sizeof(int), 0, -1, &size))){
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  if(_qdbm_inflate && villa->cmode == VL_OZCOMP){
+    if(!(zbuf = _qdbm_inflate(buf ? buf : wbuf, size, &zsiz, _QDBM_ZMRAW))){
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      free(buf);
+      return NULL;
+    }
+    free(buf);
+    buf = zbuf;
+    size = zsiz;
+  } else if(_qdbm_lzodecode && villa->cmode == VL_OYCOMP){
+    if(!(zbuf = _qdbm_lzodecode(buf ? buf : wbuf, size, &zsiz))){
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      free(buf);
+      return NULL;
+    }
+    free(buf);
+    buf = zbuf;
+    size = zsiz;
+  } else if(_qdbm_bzdecode && villa->cmode == VL_OXCOMP){
+    if(!(zbuf = _qdbm_bzdecode(buf ? buf : wbuf, size, &zsiz))){
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      free(buf);
+      return NULL;
+    }
+    free(buf);
+    buf = zbuf;
+    size = zsiz;
+  }
+  rp = buf ? buf : wbuf;
+  if(size >= 1){
+    VL_READVNUMBUF(rp, size, prev, step);
+    rp += step;
+    size -= step;
+    if(prev >= VL_NODEIDMIN - 1) prev = -1;
+  }
+  if(size >= 1){
+    VL_READVNUMBUF(rp, size, next, step);
+    rp += step;
+    size -= step;
+    if(next >= VL_NODEIDMIN - 1) next = -1;
+  }
+  lent.id = id;
+  lent.dirty = FALSE;
+  CB_LISTOPEN(lent.recs);
+  lent.prev = prev;
+  lent.next = next;
+  while(size >= 1){
+    VL_READVNUMBUF(rp, size, ksiz, step);
+    rp += step;
+    size -= step;
+    if(size < ksiz) break;
+    kbuf = rp;
+    rp += ksiz;
+    size -= ksiz;
+    VL_READVNUMBUF(rp, size, vnum, step);
+    rp += step;
+    size -= step;
+    if(vnum < 1 || size < 1) break;
+    for(i = 0; i < vnum && size >= 1; i++){
+      VL_READVNUMBUF(rp, size, vsiz, step);
+      rp += step;
+      size -= step;
+      if(size < vsiz) break;
+      vbuf = rp;
+      rp += vsiz;
+      size -= vsiz;
+      if(i < 1){
+        CB_DATUMOPEN2(rec.key, kbuf, ksiz);
+        CB_DATUMOPEN2(rec.first, vbuf, vsiz);
+        rec.rest = NULL;
+      } else {
+        if(!rec.rest) CB_LISTOPEN(rec.rest);
+        CB_LISTPUSH(rec.rest, vbuf, vsiz);
+      }
+    }
+    if(i > 0) CB_LISTPUSH(lent.recs, (char *)&rec, sizeof(VLREC));
+  }
+  free(buf);
+  cbmapput(villa->leafc, (char *)&(lent.id), sizeof(int), (char *)&lent, sizeof(VLLEAF), TRUE);
+  return (VLLEAF *)cbmapget(villa->leafc, (char *)&(lent.id), sizeof(int), NULL);
+}
+
+
+/* Load the historical leaf from the database.
+   `villa' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a 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 VLLEAF *vlgethistleaf(VILLA *villa, const char *kbuf, int ksiz){
+  VLLEAF *leaf;
+  VLREC *recp;
+  int ln, rv;
+  assert(villa && kbuf && ksiz >= 0);
+  if(!(leaf = vlleafload(villa, villa->hleaf))) return NULL;
+  if((ln = CB_LISTNUM(leaf->recs)) < 2) return NULL;
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, 0);
+  rv = villa->cmp(kbuf, ksiz, CB_DATUMPTR(recp->key), CB_DATUMSIZE(recp->key));
+  if(rv == 0) return leaf;
+  if(rv < 0) return NULL;
+  recp = (VLREC *)CB_LISTVAL(leaf->recs, ln - 1);
+  rv = villa->cmp(kbuf, ksiz, CB_DATUMPTR(recp->key), CB_DATUMSIZE(recp->key));
+  if(rv <= 0 || leaf->next < VL_LEAFIDMIN) return leaf;
+  return NULL;
+}
+
+
+/* Add a record to a leaf.
+   `villa' specifies a database handle.
+   `leaf' specifies a leaf handle.
+   `dmode' specifies behavior when the key overlaps.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region of the value.
+   The return value is true if successful, else, it is false. */
+static int vlleafaddrec(VILLA *villa, VLLEAF *leaf, int dmode,
+                        const char *kbuf, int ksiz, const char *vbuf, int vsiz){
+  VLREC *recp, rec;
+  CBLIST *recs;
+  int i, rv, left, right, ln, tsiz;
+  char *tbuf;
+  assert(villa && leaf && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+  left = 0;
+  recs = leaf->recs;
+  ln = CB_LISTNUM(recs);
+  right = ln;
+  i = (left + right) / 2;
+  while(right >= left && i < ln){
+    recp = (VLREC *)CB_LISTVAL(recs, i);
+    rv = villa->cmp(kbuf, ksiz, CB_DATUMPTR(recp->key), CB_DATUMSIZE(recp->key));
+    if(rv == 0){
+      break;
+    } else if(rv <= 0){
+      right = i - 1;
+    } else {
+      left = i + 1;
+    }
+    i = (left + right) / 2;
+  }
+  while(i < ln){
+    recp = (VLREC *)CB_LISTVAL(recs, i);
+    rv = villa->cmp(kbuf, ksiz, CB_DATUMPTR(recp->key), CB_DATUMSIZE(recp->key));
+    if(rv == 0){
+      switch(dmode){
+      case VL_DKEEP:
+        return FALSE;
+      case VL_DCAT:
+        CB_DATUMCAT(recp->first, vbuf, vsiz);
+        break;
+      case VL_DDUP:
+        if(!recp->rest) CB_LISTOPEN(recp->rest);
+        CB_LISTPUSH(recp->rest, vbuf, vsiz);
+        villa->rnum++;
+        break;
+      case VL_DDUPR:
+        if(!recp->rest){
+          CB_DATUMTOMALLOC(recp->first, tbuf, tsiz);
+          CB_DATUMOPEN2(recp->first, vbuf, vsiz);
+          CB_LISTOPEN(recp->rest);
+          CB_LISTPUSHBUF(recp->rest, tbuf, tsiz);
+        } else {
+          cblistunshift(recp->rest, CB_DATUMPTR(recp->first), CB_DATUMSIZE(recp->first));
+          CB_DATUMSETSIZE(recp->first, 0);
+          CB_DATUMCAT(recp->first, vbuf, vsiz);
+        }
+        villa->rnum++;
+        break;
+      default:
+        CB_DATUMSETSIZE(recp->first, 0);
+        CB_DATUMCAT(recp->first, vbuf, vsiz);
+        break;
+      }
+      break;
+    } else if(rv < 0){
+      CB_DATUMOPEN2(rec.key, kbuf, ksiz);
+      CB_DATUMOPEN2(rec.first, vbuf, vsiz);
+      rec.rest = NULL;
+      CB_LISTINSERT(recs, i, (char *)&rec, sizeof(VLREC));
+      villa->rnum++;
+      break;
+    }
+    i++;
+  }
+  if(i >= ln){
+    CB_DATUMOPEN2(rec.key, kbuf, ksiz);
+    CB_DATUMOPEN2(rec.first, vbuf, vsiz);
+    rec.rest = NULL;
+    CB_LISTPUSH(recs, (char *)&rec, sizeof(VLREC));
+    villa->rnum++;
+  }
+  leaf->dirty = TRUE;
+  return TRUE;
+}
+
+
+/* Calculate the size of data of a leaf.
+   `leaf' specifies a leaf handle.
+   The return value is size of data of the leaf. */
+static int vlleafdatasize(VLLEAF *leaf){
+  VLREC *recp;
+  CBLIST *recs, *rest;
+  const char *vbuf;
+  int i, j, sum, rnum, restnum, vsiz;
+  assert(leaf);
+  sum = 0;
+  recs = leaf->recs;
+  rnum = CB_LISTNUM(recs);
+  for(i = 0; i < rnum; i++){
+    recp = (VLREC *)CB_LISTVAL(recs, i);
+    sum += CB_DATUMSIZE(recp->key);
+    sum += CB_DATUMSIZE(recp->first);
+    if(recp->rest){
+      rest = recp->rest;
+      restnum = CB_LISTNUM(rest);
+      for(j = 0; j < restnum; j++){
+        vbuf = CB_LISTVAL2(rest, j, vsiz);
+        sum += vsiz;
+      }
+    }
+  }
+  return sum;
+}
+
+
+/* Divide a leaf into two.
+   `villa' specifies a database handle.
+   `leaf' specifies a leaf handle.
+   The return value is the handle of a new leaf, or `NULL' on failure. */
+static VLLEAF *vlleafdivide(VILLA *villa, VLLEAF *leaf){
+  VLLEAF *newleaf, *nextleaf;
+  VLREC *recp;
+  CBLIST *recs, *newrecs;
+  int i, mid, ln;
+  assert(villa && leaf);
+  villa->hleaf = -1;
+  recs = leaf->recs;
+  mid = CB_LISTNUM(recs) / 2;
+  recp = (VLREC *)CB_LISTVAL(recs, mid);
+  newleaf = vlleafnew(villa, leaf->id, leaf->next);
+  if(newleaf->next != -1){
+    if(!(nextleaf = vlleafload(villa, newleaf->next))) return NULL;
+    nextleaf->prev = newleaf->id;
+    nextleaf->dirty = TRUE;
+  }
+  leaf->next = newleaf->id;
+  leaf->dirty = TRUE;
+  ln = CB_LISTNUM(recs);
+  newrecs = newleaf->recs;
+  for(i = mid; i < ln; i++){
+    recp = (VLREC *)CB_LISTVAL(recs, i);
+    CB_LISTPUSH(newrecs, (char *)recp, sizeof(VLREC));
+  }
+  ln = CB_LISTNUM(newrecs);
+  for(i = 0; i < ln; i++){
+    CB_LISTDROP(recs);
+  }
+  return newleaf;
+}
+
+
+/* Create a new node.
+   `villa' specifies a database handle.
+   `heir' specifies the ID of the child before the first index.
+   The return value is a handle of the node. */
+static VLNODE *vlnodenew(VILLA *villa, int heir){
+  VLNODE nent;
+  assert(villa && heir >= VL_LEAFIDMIN);
+  nent.id = villa->nnum + VL_NODEIDMIN;
+  nent.dirty = TRUE;
+  nent.heir = heir;
+  CB_LISTOPEN(nent.idxs);
+  villa->nnum++;
+  cbmapput(villa->nodec, (char *)&(nent.id), sizeof(int), (char *)&nent, sizeof(VLNODE), TRUE);
+  return (VLNODE *)cbmapget(villa->nodec, (char *)&(nent.id), sizeof(int), NULL);
+}
+
+
+/* Remove a node from the cache.
+   `villa' specifies a database handle.
+   `id' specifies the ID number of the node.
+   The return value is true if successful, else, it is false. */
+static int vlnodecacheout(VILLA *villa, int id){
+  VLNODE *node;
+  VLIDX *idxp;
+  int i, err, ln;
+  assert(villa && id >= VL_NODEIDMIN);
+  if(!(node = (VLNODE *)cbmapget(villa->nodec, (char *)&id, sizeof(int), NULL))) return FALSE;
+  err = FALSE;
+  if(node->dirty && !vlnodesave(villa, node)) err = TRUE;
+  ln = CB_LISTNUM(node->idxs);
+  for(i = 0; i < ln; i++){
+    idxp = (VLIDX *)CB_LISTVAL(node->idxs, i);
+    CB_DATUMCLOSE(idxp->key);
+  }
+  CB_LISTCLOSE(node->idxs);
+  cbmapout(villa->nodec, (char *)&id, sizeof(int));
+  return err ? FALSE : TRUE;
+}
+
+
+/* Save a node into the database.
+   `villa' specifies a database handle.
+   `node' specifies a node handle.
+   The return value is true if successful, else, it is false. */
+static int vlnodesave(VILLA *villa, VLNODE *node){
+  CBDATUM *buf;
+  char vnumbuf[VL_VNUMBUFSIZ];
+  VLIDX *idxp;
+  int i, heir, pid, ksiz, vnumsiz, ln;
+  assert(villa && node);
+  CB_DATUMOPEN(buf);
+  heir = node->heir;
+  VL_SETVNUMBUF(vnumsiz, vnumbuf, heir);
+  CB_DATUMCAT(buf, vnumbuf, vnumsiz);
+  ln = CB_LISTNUM(node->idxs);
+  for(i = 0; i < ln; i++){
+    idxp = (VLIDX *)CB_LISTVAL(node->idxs, i);
+    pid = idxp->pid;
+    VL_SETVNUMBUF(vnumsiz, vnumbuf, pid);
+    CB_DATUMCAT(buf, vnumbuf, vnumsiz);
+    ksiz = CB_DATUMSIZE(idxp->key);
+    VL_SETVNUMBUF(vnumsiz, vnumbuf, ksiz);
+    CB_DATUMCAT(buf, vnumbuf, vnumsiz);
+    CB_DATUMCAT(buf, CB_DATUMPTR(idxp->key), ksiz);
+  }
+  if(!dpput(villa->depot, (char *)&(node->id), sizeof(int),
+            CB_DATUMPTR(buf), CB_DATUMSIZE(buf), DP_DOVER)){
+    CB_DATUMCLOSE(buf);
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return FALSE;
+  }
+  CB_DATUMCLOSE(buf);
+  node->dirty = FALSE;
+  return TRUE;
+}
+
+
+/* Load a node from the database.
+   `villa' specifies a database handle.
+   `id' specifies the ID number of the node.
+   If successful, the return value is the pointer to the node, else, it is `NULL'. */
+static VLNODE *vlnodeload(VILLA *villa, int id){
+  char wbuf[VL_PAGEBUFSIZ], *buf, *rp, *kbuf;
+  int size, step, heir, pid, ksiz;
+  VLNODE *node, nent;
+  VLIDX idx;
+  assert(villa && id >= VL_NODEIDMIN);
+  if((node = (VLNODE *)cbmapget(villa->nodec, (char *)&id, sizeof(int), NULL)) != NULL){
+    cbmapmove(villa->nodec, (char *)&id, sizeof(int), FALSE);
+    return node;
+  }
+  heir = -1;
+  if((size = dpgetwb(villa->depot, (char *)&id, sizeof(int), 0, VL_PAGEBUFSIZ, wbuf)) > 0 &&
+     size < VL_PAGEBUFSIZ){
+    buf = NULL;
+  } else if(!(buf = dpget(villa->depot, (char *)&id, sizeof(int), 0, -1, &size))){
+    dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+    return NULL;
+  }
+  rp = buf ? buf : wbuf;
+  if(size >= 1){
+    VL_READVNUMBUF(rp, size, heir, step);
+    rp += step;
+    size -= step;
+  }
+  if(heir < 0){
+    free(buf);
+    return NULL;
+  }
+  nent.id = id;
+  nent.dirty = FALSE;
+  nent.heir = heir;
+  CB_LISTOPEN(nent.idxs);
+  while(size >= 1){
+    VL_READVNUMBUF(rp, size, pid, step);
+    rp += step;
+    size -= step;
+    if(size < 1) break;
+    VL_READVNUMBUF(rp, size, ksiz, step);
+    rp += step;
+    size -= step;
+    if(size < ksiz) break;
+    kbuf = rp;
+    rp += ksiz;
+    size -= ksiz;
+    idx.pid = pid;
+    CB_DATUMOPEN2(idx.key, kbuf, ksiz);
+    CB_LISTPUSH(nent.idxs, (char *)&idx, sizeof(VLIDX));
+  }
+  free(buf);
+  cbmapput(villa->nodec, (char *)&(nent.id), sizeof(int), (char *)&nent, sizeof(VLNODE), TRUE);
+  return (VLNODE *)cbmapget(villa->nodec, (char *)&(nent.id), sizeof(int), NULL);
+}
+
+
+/* Add an index to a node.
+   `villa' specifies a database handle.
+   `node' specifies a node handle.
+   `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 a key.
+   `ksiz' specifies the size of the region of the key. */
+static void vlnodeaddidx(VILLA *villa, VLNODE *node, int order,
+                         int pid, const char *kbuf, int ksiz){
+  VLIDX idx, *idxp;
+  int i, rv, left, right, ln;
+  assert(villa && node && pid >= VL_LEAFIDMIN && kbuf && ksiz >= 0);
+  idx.pid = pid;
+  CB_DATUMOPEN2(idx.key, kbuf, ksiz);
+  if(order){
+    CB_LISTPUSH(node->idxs, (char *)&idx, sizeof(VLIDX));
+  } else {
+    left = 0;
+    right = CB_LISTNUM(node->idxs);
+    i = (left + right) / 2;
+    ln = CB_LISTNUM(node->idxs);
+    while(right >= left && i < ln){
+      idxp = (VLIDX *)CB_LISTVAL(node->idxs, i);
+      rv = villa->cmp(kbuf, ksiz, CB_DATUMPTR(idxp->key), CB_DATUMSIZE(idxp->key));
+      if(rv == 0){
+        break;
+      } else if(rv <= 0){
+        right = i - 1;
+      } else {
+        left = i + 1;
+      }
+      i = (left + right) / 2;
+    }
+    ln = CB_LISTNUM(node->idxs);
+    while(i < ln){
+      idxp = (VLIDX *)CB_LISTVAL(node->idxs, i);
+      if(villa->cmp(kbuf, ksiz, CB_DATUMPTR(idxp->key), CB_DATUMSIZE(idxp->key)) < 0){
+        CB_LISTINSERT(node->idxs, i, (char *)&idx, sizeof(VLIDX));
+        break;
+      }
+      i++;
+    }
+    if(i >= CB_LISTNUM(node->idxs)) CB_LISTPUSH(node->idxs, (char *)&idx, sizeof(VLIDX));
+  }
+  node->dirty = TRUE;
+}
+
+
+/* Search the leaf corresponding to a key.
+   `villa' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.
+   The return value is the ID number of the leaf, or -1 on failure. */
+static int vlsearchleaf(VILLA *villa, const char *kbuf, int ksiz){
+  VLNODE *node;
+  VLIDX *idxp;
+  int i, pid, rv, left, right, ln;
+  assert(villa && kbuf && ksiz >= 0);
+  pid = villa->root;
+  idxp = NULL;
+  villa->hnum = 0;
+  villa->hleaf = -1;
+  while(pid >= VL_NODEIDMIN){
+    if(!(node = vlnodeload(villa, pid)) || (ln = CB_LISTNUM(node->idxs)) < 1){
+      dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
+      return -1;
+    }
+    villa->hist[villa->hnum++] = node->id;
+    left = 1;
+    right = ln;
+    i = (left + right) / 2;
+    while(right >= left && i < ln){
+      idxp = (VLIDX *)CB_LISTVAL(node->idxs, i);
+      rv = villa->cmp(kbuf, ksiz, CB_DATUMPTR(idxp->key), CB_DATUMSIZE(idxp->key));
+      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 = (VLIDX *)CB_LISTVAL(node->idxs, i);
+      if(villa->cmp(kbuf, ksiz, CB_DATUMPTR(idxp->key), CB_DATUMSIZE(idxp->key)) < 0){
+        if(i == 0){
+          pid = node->heir;
+          break;
+        }
+        idxp = (VLIDX *)CB_LISTVAL(node->idxs, i - 1);
+        pid = idxp->pid;
+        break;
+      }
+      i++;
+    }
+    if(i >= ln) pid = idxp->pid;
+  }
+  if(villa->lleaf == pid) villa->hleaf = pid;
+  villa->lleaf = pid;
+  return pid;
+}
+
+
+/* Adjust the caches for leaves and nodes.
+   `villa' specifies a database handle.
+   The return value is true if successful, else, it is false. */
+static int vlcacheadjust(VILLA *villa){
+  const char *tmp;
+  int i, pid, err;
+  err = FALSE;
+  if(cbmaprnum(villa->leafc) > villa->leafcnum){
+    cbmapiterinit(villa->leafc);
+    for(i = 0; i < VL_CACHEOUT; i++){
+      tmp = cbmapiternext(villa->leafc, NULL);
+      pid = *(int *)tmp;
+      if(!vlleafcacheout(villa, pid)) err = TRUE;
+    }
+  }
+  if(cbmaprnum(villa->nodec) > villa->nodecnum){
+    cbmapiterinit(villa->nodec);
+    for(i = 0; i < VL_CACHEOUT; i++){
+      tmp = cbmapiternext(villa->nodec, NULL);
+      pid = *(int *)tmp;
+      if(!vlnodecacheout(villa, pid)) err = TRUE;
+    }
+  }
+  return err ? FALSE : TRUE;
+}
+
+
+/* Search a record of a leaf.
+   `villa' specifies a database handle.
+   `leaf' specifies a leaf handle.
+   `kbuf' specifies the pointer to the region of a 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 VLREC *vlrecsearch(VILLA *villa, VLLEAF *leaf, const char *kbuf, int ksiz, int *ip){
+  VLREC *recp;
+  CBLIST *recs;
+  int i, rv, left, right, ln;
+  assert(villa && leaf && kbuf && ksiz >= 0);
+  recs = leaf->recs;
+  ln = CB_LISTNUM(recs);
+  left = 0;
+  right = ln;
+  i = (left + right) / 2;
+  while(right >= left && i < ln){
+    recp = (VLREC *)CB_LISTVAL(recs, i);
+    rv = villa->cmp(kbuf, ksiz, CB_DATUMPTR(recp->key), CB_DATUMSIZE(recp->key));
+    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;
+}
+
+
+/* Get flags of a database. */
+int vlgetflags(VILLA *villa){
+  assert(villa);
+  return dpgetflags(villa->depot);
+}
+
+
+/* Set flags of a database.
+   `villa' specifies a database handle connected as a writer.
+   `flags' specifies flags to set.  Lesser ten bits are reserved for internal use.
+   If successful, the return value is true, else, it is false. */
+int vlsetflags(VILLA *villa, int flags){
+  assert(villa);
+  if(!villa->wmode){
+    dpecodeset(DP_EMODE, __FILE__, __LINE__);
+    return FALSE;
+  }
+  return dpsetflags(villa->depot, flags);
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/villa.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/villa.h
===================================================================
--- box/trunk/qdbm/villa.h	                        (rev 0)
+++ box/trunk/qdbm/villa.h	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,758 @@
+/*************************************************************************************************
+ * The advanced API of QDBM
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _VILLA_H                         /* duplication check */
+#define _VILLA_H
+
+#if defined(__cplusplus)                 /* export for C++ */
+extern "C" {
+#endif
+
+
+#include <depot.h>
+#include <cabin.h>
+#include <stdlib.h>
+#include <time.h>
+
+
+#if defined(_MSC_VER) && !defined(QDBM_INTERNAL) && !defined(QDBM_STATIC)
+#define MYEXTERN extern __declspec(dllimport)
+#else
+#define MYEXTERN extern
+#endif
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+#define VL_LEVELMAX    64                /* max level of B+ tree */
+
+typedef struct {                         /* type of structure for a record */
+  CBDATUM *key;                          /* datum of the key */
+  CBDATUM *first;                        /* datum of the first value */
+  CBLIST *rest;                          /* list of the rest values */
+} VLREC;
+
+typedef struct {                         /* type of structure for index of a page */
+  int pid;                               /* ID number of the referring page */
+  CBDATUM *key;                          /* threshold key of the page */
+} VLIDX;
+
+typedef struct {                         /* type of structure for a leaf page */
+  int id;                                /* ID number of the leaf */
+  int dirty;                             /* whether to be written back */
+  CBLIST *recs;                          /* list of records */
+  int prev;                              /* ID number of the previous leaf */
+  int next;                              /* ID number of the next leaf */
+} VLLEAF;
+
+typedef struct {                         /* type of structure for a node page */
+  int id;                                /* ID number of the node */
+  int dirty;                             /* whether to be written back */
+  int heir;                              /* ID of the child before the first index */
+  CBLIST *idxs;                          /* list of indexes */
+} VLNODE;
+
+/* type of the pointer to a comparing 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.
+   The return value is positive if the former is big, negative if the latter is big, 0 if both
+   are equivalent. */
+typedef int (*VLCFUNC)(const char *aptr, int asiz, const char *bptr, int bsiz);
+MYEXTERN VLCFUNC VL_CMPLEX;              /* lexical comparing function */
+MYEXTERN VLCFUNC VL_CMPINT;              /* native integer comparing function */
+MYEXTERN VLCFUNC VL_CMPNUM;              /* big endian number comparing function */
+MYEXTERN VLCFUNC VL_CMPDEC;              /* decimal string comparing function */
+
+typedef struct {                         /* type of structure for a database handle */
+  DEPOT *depot;                          /* internal database handle */
+  VLCFUNC cmp;                           /* pointer to the comparing function */
+  int wmode;                             /* whether to be writable */
+  int cmode;                             /* compression mode for leaves */
+  int root;                              /* ID number of the root page */
+  int last;                              /* ID number of the last leaf */
+  int lnum;                              /* number of leaves */
+  int nnum;                              /* number of nodes */
+  int rnum;                              /* number of records */
+  CBMAP *leafc;                          /* cache for leaves */
+  CBMAP *nodec;                          /* cache for nodes */
+  int hist[VL_LEVELMAX];                 /* array history of visited nodes */
+  int hnum;                              /* number of elements of the history */
+  int hleaf;                             /* ID number of the leaf referred by the history */
+  int lleaf;                             /* ID number of the last visited leaf */
+  int curleaf;                           /* ID number of the leaf where the cursor is */
+  int curknum;                           /* index of the key where the cursor is */
+  int curvnum;                           /* index of the value where the cursor is */
+  int leafrecmax;                        /* max number of records in a leaf */
+  int nodeidxmax;                        /* max number of indexes in a node */
+  int leafcnum;                          /* max number of caching leaves */
+  int nodecnum;                          /* max number of caching nodes */
+  int avglsiz;                           /* average size of each leave */
+  int avgnsiz;                           /* average size of each node */
+  int tran;                              /* whether in the transaction */
+  int rbroot;                            /* root for rollback */
+  int rblast;                            /* last for rollback */
+  int rblnum;                            /* lnum for rollback */
+  int rbnnum;                            /* nnum for rollback */
+  int rbrnum;                            /* rnum for rollback */
+} VILLA;
+
+typedef struct {                         /* type of structure for a multiple cursor handle */
+  VILLA *villa;                          /* database handle */
+  int curleaf;                           /* ID number of the leaf where the cursor is */
+  int curknum;                           /* index of the key where the cursor is */
+  int curvnum;                           /* index of the value where the cursor is */
+} VLMULCUR;
+
+enum {                                   /* enumeration for open modes */
+  VL_OREADER = 1 << 0,                   /* open as a reader */
+  VL_OWRITER = 1 << 1,                   /* open as a writer */
+  VL_OCREAT = 1 << 2,                    /* a writer creating */
+  VL_OTRUNC = 1 << 3,                    /* a writer truncating */
+  VL_ONOLCK = 1 << 4,                    /* open without locking */
+  VL_OLCKNB = 1 << 5,                    /* lock without blocking */
+  VL_OZCOMP = 1 << 6,                    /* compress leaves with ZLIB */
+  VL_OYCOMP = 1 << 7,                    /* compress leaves with LZO */
+  VL_OXCOMP = 1 << 8                     /* compress leaves with BZIP2 */
+};
+
+enum {                                   /* enumeration for write modes */
+  VL_DOVER,                              /* overwrite the existing value */
+  VL_DKEEP,                              /* keep the existing value */
+  VL_DCAT,                               /* concatenate values */
+  VL_DDUP,                               /* allow duplication of keys */
+  VL_DDUPR                               /* allow duplication with reverse order */
+};
+
+enum {                                   /* enumeration for jump modes */
+  VL_JFORWARD,                           /* step forward */
+  VL_JBACKWARD                           /* step backward */
+};
+
+enum {                                   /* enumeration for insertion modes */
+  VL_CPCURRENT,                          /* overwrite the current record */
+  VL_CPBEFORE,                           /* insert before the current record */
+  VL_CPAFTER                             /* insert after the current record */
+};
+
+
+/* Get a database handle.
+   `name' specifies the name of a database file.
+   `omode' specifies the connection mode: `VL_OWRITER' as a writer, `VL_OREADER' as a reader.
+   If the mode is `VL_OWRITER', the following may be added by bitwise or: `VL_OCREAT', which
+   means it creates a new database if not exist, `VL_OTRUNC', which means it creates a new
+   database regardless if one exists, `VL_OZCOMP', which means leaves in the database are
+   compressed with ZLIB, `VL_OYCOMP', which means leaves in the database are compressed with LZO,
+   `VL_OXCOMP', which means leaves in the database are compressed with BZIP2.  Both of
+   `VL_OREADER' and `VL_OWRITER' can be added to by bitwise or: `VL_ONOLCK', which means it opens
+   a database file without file locking, or `VL_OLCKNB', which means locking is performed without
+   blocking.
+   `cmp' specifies a comparing function: `VL_CMPLEX' comparing keys in lexical order,
+   `VL_CMPINT' comparing keys as objects of `int' in native byte order, `VL_CMPNUM' comparing
+   keys as numbers of big endian, `VL_CMPDEC' comparing keys as decimal strings.  Any function
+   based on the declaration of the type `VLCFUNC' can be assigned to the comparing function.
+   The comparing function should be kept same in  the life of a database.
+   The return value is the database handle or `NULL' if it is not successful.
+   While connecting as a writer, an exclusive lock is invoked to the database file.
+   While connecting as a reader, a shared lock is invoked to the database file.  The thread
+   blocks until the lock is achieved.  `VL_OZCOMP', `VL_OYCOMP', and `VL_OXCOMP' are available
+   only if QDBM was built each with ZLIB, LZO, and BZIP2 enabled.  If `VL_ONOLCK' is used, the
+   application is responsible for exclusion control. */
+VILLA *vlopen(const char *name, int omode, VLCFUNC cmp);
+
+
+/* Close a database handle.
+   `villa' specifies a database handle.
+   If successful, the return value is true, else, it is false.
+   Because the region of a closed handle is released, it becomes impossible to use the handle.
+   Updating a database is assured to be written when the handle is closed.  If a writer opens
+   a database but does not close it appropriately, the database will be broken.  If the
+   transaction is activated and not committed, it is aborted. */
+int vlclose(VILLA *villa);
+
+
+/* Store a record.
+   `villa' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region of the value.  If it is negative, the size is
+   assigned with `strlen(vbuf)'.
+   `dmode' specifies behavior when the key overlaps, by the following values: `VL_DOVER',
+   which means the specified value overwrites the existing one, `VL_DKEEP', which means the
+   existing value is kept, `VL_DCAT', which means the specified value is concatenated at the
+   end of the existing value, `VL_DDUP', which means duplication of keys is allowed and the
+   specified value is added as the last one, `VL_DDUPR', which means duplication of keys is
+   allowed and the specified value is added as the first one.
+   If successful, the return value is true, else, it is false.
+   The cursor becomes unavailable due to updating database. */
+int vlput(VILLA *villa, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode);
+
+
+/* Delete a record.
+   `villa' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is true, else, it is false.  False is returned when no
+   record corresponds to the specified key.
+   When the key of duplicated records is specified, the first record of the same key is deleted.
+   The cursor becomes unavailable due to updating database. */
+int vlout(VILLA *villa, const char *kbuf, int ksiz);
+
+
+/* Retrieve a record.
+   `villa' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the specified key.
+   When the key of duplicated records is specified, the value of the first record of the same
+   key 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 if it is no longer in use. */
+char *vlget(VILLA *villa, const char *kbuf, int ksiz, int *sp);
+
+
+/* Get the size of the value of a record.
+   `villa' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is the size of the value of the corresponding record, else,
+   it is -1.  If multiple records correspond, the size of the first is returned. */
+int vlvsiz(VILLA *villa, const char *kbuf, int ksiz);
+
+
+/* Get the number of records corresponding a key.
+   `villa' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   The return value is the number of corresponding records.  If no record corresponds, 0 is
+   returned. */
+int vlvnum(VILLA *villa, const char *kbuf, int ksiz);
+
+
+/* Store plural records corresponding a key.
+   `villa' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `vals' specifies a list handle of values.  The list should not be empty.
+   If successful, the return value is true, else, it is false.
+   The cursor becomes unavailable due to updating database. */
+int vlputlist(VILLA *villa, const char *kbuf, int ksiz, const CBLIST *vals);
+
+
+/* Delete all records corresponding a key.
+   `villa' specifies a database handle connected as a writer.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is true, else, it is false.  False is returned when no
+   record corresponds to the specified key.
+   The cursor becomes unavailable due to updating database. */
+int vloutlist(VILLA *villa, const char *kbuf, int ksiz);
+
+
+/* Retrieve values of all records corresponding a key.
+   `villa' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   If successful, the return value is a list handle of the values of the corresponding records,
+   else, it is `NULL'.  `NULL' is returned when no record corresponds to the specified key.
+   Because the handle of the return value is opened with the function `cblistopen', it should
+   be closed with the function `cblistclose' if it is no longer in use. */
+CBLIST *vlgetlist(VILLA *villa, const char *kbuf, int ksiz);
+
+
+/* Retrieve concatenated values of all records corresponding a key.
+   `villa' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the concatenated values of
+   the corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds
+   to the specified key.  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 if it is no longer in use. */
+char *vlgetcat(VILLA *villa, const char *kbuf, int ksiz, int *sp);
+
+
+/* Move the cursor to the first record.
+   `villa' specifies a database handle.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record in the database. */
+int vlcurfirst(VILLA *villa);
+
+
+/* Move the cursor to the last record.
+   `villa' specifies a database handle.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record in the database. */
+int vlcurlast(VILLA *villa);
+
+
+/* Move the cursor to the previous record.
+   `villa' specifies a database handle.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no previous record. */
+int vlcurprev(VILLA *villa);
+
+
+/* Move the cursor to the next record.
+   `villa' specifies a database handle.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no next record. */
+int vlcurnext(VILLA *villa);
+
+
+/* Move the cursor to a position around a record.
+   `villa' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `jmode' specifies detail adjustment: `VL_JFORWARD', which means that the cursor is set to
+   the first record of the same key and that the cursor is set to the next substitute if
+   completely matching record does not exist, `VL_JBACKWARD', which means that the cursor is
+   set to the last record of the same key and that the cursor is set to the previous substitute
+   if completely matching record does not exist.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record corresponding the condition. */
+int vlcurjump(VILLA *villa, const char *kbuf, int ksiz, int jmode);
+
+
+/* Get the key of the record where the cursor is.
+   `villa' specifies a database handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the key of the corresponding
+   record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the cursor.
+   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 if it is no longer in use. */
+char *vlcurkey(VILLA *villa, int *sp);
+
+
+/* Get the value of the record where the cursor is.
+   `villa' specifies a database handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the cursor.
+   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 if it is no longer in use. */
+char *vlcurval(VILLA *villa, int *sp);
+
+
+/* Insert a record around the cursor.
+   `villa' specifies a database handle connected as a writer.
+   `vbuf' specifies the pointer to the region of a value.
+   `vsiz' specifies the size of the region of the value.  If it is negative, the size is
+   assigned with `strlen(vbuf)'.
+   `cpmode' specifies detail adjustment: `VL_CPCURRENT', which means that the value of the
+   current record is overwritten, `VL_CPBEFORE', which means that a new record is inserted before
+   the current record, `VL_CPAFTER', which means that a new record is inserted after the current
+   record.
+   If successful, the return value is true, else, it is false.  False is returned when no record
+   corresponds to the cursor.
+   After insertion, the cursor is moved to the inserted record. */
+int vlcurput(VILLA *villa, const char *vbuf, int vsiz, int cpmode);
+
+
+/* Delete the record where the cursor is.
+   `villa' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.  False is returned when no record
+   corresponds to the cursor.
+   After deletion, the cursor is moved to the next record if possible. */
+int vlcurout(VILLA *villa);
+
+
+/* Set the tuning parameters for performance.
+   `villa' specifies a database handle.
+   `lrecmax' specifies the max number of records in a leaf node of B+ tree.  If it is not more
+   than 0, the default value is specified.
+   `nidxmax' specifies the max number of indexes in a non-leaf node of B+ tree.  If it is not
+   more than 0, the default value is specified.
+   `lcnum' specifies the max number of caching leaf nodes.  If it is not more than 0, the
+   default value is specified.
+   `ncnum' specifies the max number of caching non-leaf nodes.  If it is not more than 0, the
+   default value is specified.
+   The default setting is equivalent to `vlsettuning(49, 192, 1024, 512)'.  Because tuning
+   parameters are not saved in a database, you should specify them every opening a database. */
+void vlsettuning(VILLA *villa, int lrecmax, int nidxmax, int lcnum, int ncnum);
+
+
+/* Set the size of the free block pool of a database handle.
+   `villa' specifies a database handle connected as a writer.
+   `size' specifies the size of the free block pool of a database.
+   If successful, the return value is true, else, it is false.
+   The default size of the free block pool is 256.  If the size is greater, the space efficiency
+   of overwriting values is improved with the time efficiency sacrificed. */
+int vlsetfbpsiz(VILLA *villa, int size);
+
+
+/* Synchronize updating contents with the file and the device.
+   `villa' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.
+   This function is useful when another process uses the connected database file.  This function
+   should not be used while the transaction is activated. */
+int vlsync(VILLA *villa);
+
+
+/* Optimize a database.
+   `villa' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.
+   In an alternating succession of deleting and storing with overwrite or concatenate,
+   dispensable regions accumulate.  This function is useful to do away with them.  This function
+   should not be used while the transaction is activated. */
+int vloptimize(VILLA *villa);
+
+
+/* Get the name of a database.
+   `villa' specifies a database handle.
+   If successful, the return value is the pointer to the region of the name of the database,
+   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 if it is no longer in use. */
+char *vlname(VILLA *villa);
+
+
+/* Get the size of a database file.
+   `villa' specifies a database handle.
+   If successful, the return value is the size of the database file, else, it is -1.
+   Because of the I/O buffer, the return value may be less than the hard size. */
+int vlfsiz(VILLA *villa);
+
+
+/* Get the number of the leaf nodes of B+ tree.
+   `villa' specifies a database handle.
+   If successful, the return value is the number of the leaf nodes, else, it is -1. */
+int vllnum(VILLA *villa);
+
+
+/* Get the number of the non-leaf nodes of B+ tree.
+   `villa' specifies a database handle.
+   If successful, the return value is the number of the non-leaf nodes, else, it is -1. */
+int vlnnum(VILLA *villa);
+
+
+/* Get the number of the records stored in a database.
+   `villa' specifies a database handle.
+   If successful, the return value is the number of the records stored in the database, else,
+   it is -1. */
+int vlrnum(VILLA *villa);
+
+
+/* Check whether a database handle is a writer or not.
+   `villa' specifies a database handle.
+   The return value is true if the handle is a writer, false if not. */
+int vlwritable(VILLA *villa);
+
+
+/* Check whether a database has a fatal error or not.
+   `villa' specifies a database handle.
+   The return value is true if the database has a fatal error, false if not. */
+int vlfatalerror(VILLA *villa);
+
+
+/* Get the inode number of a database file.
+   `villa' specifies a database handle.
+   The return value is the inode number of the database file. */
+int vlinode(VILLA *villa);
+
+
+/* Get the last modified time of a database.
+   `villa' specifies a database handle.
+   The return value is the last modified time of the database. */
+time_t vlmtime(VILLA *villa);
+
+
+/* Begin the transaction.
+   `villa' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.
+   Because this function does not perform mutual exclusion control in multi-thread, the
+   application is responsible for it.  Only one transaction can be activated with a database
+   handle at the same time. */
+int vltranbegin(VILLA *villa);
+
+
+/* Commit the transaction.
+   `villa' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.
+   Updating a database in the transaction is fixed when it is committed successfully. */
+int vltrancommit(VILLA *villa);
+
+
+/* Abort the transaction.
+   `villa' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false.
+   Updating a database in the transaction is discarded when it is aborted.  The state of the
+   database is rollbacked to before transaction. */
+int vltranabort(VILLA *villa);
+
+
+/* Remove a database file.
+   `name' specifies the name of a database file.
+   If successful, the return value is true, else, it is false. */
+int vlremove(const char *name);
+
+
+/* Repair a broken database file.
+   `name' specifies the name of a database file.
+   `cmp' specifies the comparing function of the database file.
+   If successful, the return value is true, else, it is false.
+   There is no guarantee that all records in a repaired database file correspond to the original
+   or expected state. */
+int vlrepair(const char *name, VLCFUNC cmp);
+
+
+/* Dump all records as endian independent data.
+   `villa' specifies a database handle.
+   `name' specifies the name of an output file.
+   If successful, the return value is true, else, it is false. */
+int vlexportdb(VILLA *villa, const char *name);
+
+
+/* Load all records from endian independent data.
+   `villa' specifies a database handle connected as a writer.  The database of the handle must
+   be empty.
+   `name' specifies the name of an input file.
+   If successful, the return value is true, else, it is false. */
+int vlimportdb(VILLA *villa, const char *name);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Number of division of the database for Vista. */
+#define vlcrdnum       (*vlcrdnumptr())
+
+
+/* Get the pointer of the variable of the number of division of the database for Vista.
+   The return value is the pointer of the variable. */
+int *vlcrdnumptr(void);
+
+
+/* Synchronize updating contents on memory.
+   `villa' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false. */
+int vlmemsync(VILLA *villa);
+
+
+/* Synchronize updating contents on memory, not physically.
+   `villa' specifies a database handle connected as a writer.
+   If successful, the return value is true, else, it is false. */
+int vlmemflush(VILLA *villa);
+
+
+/* Refer to volatile cache of a value of a record.
+   `villa' specifies a database handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the specified key.
+   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 *vlgetcache(VILLA *villa, const char *kbuf, int ksiz, int *sp);
+
+
+/* Refer to volatile cache of the key of the record where the cursor is.
+   `villa' specifies a database handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the key of the corresponding
+   record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the cursor.
+   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 *vlcurkeycache(VILLA *villa, int *sp);
+
+
+/* Refer to volatile cache of the value of the record where the cursor is.
+   `villa' specifies a database handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the cursor.
+   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 if it is no longer in use. */
+const char *vlcurvalcache(VILLA *villa, int *sp);
+
+
+/* Get a multiple cursor handle.
+   `villa' specifies a database handle connected as a reader.
+   The return value is a multiple cursor handle or `NULL' if it is not successful.
+   The returned object is should be closed before the database handle is closed.  Even if plural
+   cursors are fetched out of a database handle, they does not share the locations with each
+   other.  Note that this function can be used only if the database handle is connected as a
+   reader. */
+VLMULCUR *vlmulcuropen(VILLA *villa);
+
+
+/* Close a multiple cursor handle.
+   `mulcur' specifies a multiple cursor handle. */
+void vlmulcurclose(VLMULCUR *mulcur);
+
+
+/* Move a multiple cursor to the first record.
+   `mulcur' specifies a multiple cursor handle.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record in the database. */
+int vlmulcurfirst(VLMULCUR *mulcur);
+
+
+/* Move a multiple cursor to the last record.
+   `mulcur' specifies a multiple cursor handle.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record in the database. */
+int vlmulcurlast(VLMULCUR *mulcur);
+
+
+/* Move a multiple cursor to the previous record.
+   `mulcur' specifies a multiple cursor handle.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no previous record. */
+int vlmulcurprev(VLMULCUR *mulcur);
+
+
+/* Move a multiple cursor to the next record.
+   `mulcur' specifies a multiple cursor handle.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no next record. */
+int vlmulcurnext(VLMULCUR *mulcur);
+
+
+/* Move a multiple cursor to a position around a record.
+   `mulcur' specifies a multiple cursor handle.
+   `kbuf' specifies the pointer to the region of a key.
+   `ksiz' specifies the size of the region of the key.  If it is negative, the size is assigned
+   with `strlen(kbuf)'.
+   `jmode' specifies detail adjustment: `VL_JFORWARD', which means that the cursor is set to
+   the first record of the same key and that the cursor is set to the next substitute if
+   completely matching record does not exist, `VL_JBACKWARD', which means that the cursor is
+   set to the last record of the same key and that the cursor is set to the previous substitute
+   if completely matching record does not exist.
+   If successful, the return value is true, else, it is false.  False is returned if there is
+   no record corresponding the condition. */
+int vlmulcurjump(VLMULCUR *mulcur, const char *kbuf, int ksiz, int jmode);
+
+
+/* Get the key of the record where a multiple cursor is.
+   `mulcur' specifies a multiple cursor handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the key of the corresponding
+   record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the cursor.
+   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 if it is no longer in use. */
+char *vlmulcurkey(VLMULCUR *mulcur, int *sp);
+
+
+/* Get the value of the record where a multiple cursor is.
+   `mulcur' specifies a multiple cursor handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the cursor.
+   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 if it is no longer in use. */
+char *vlmulcurval(VLMULCUR *mulcur, int *sp);
+
+
+/* Refer to volatile cache of the key of the record where a multiple cursor is.
+   `mulcur' specifies a multiple cursor handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the key of the corresponding
+   record, else, it is `NULL'.  `NULL' is returned when no record corresponds to the cursor.
+   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 *vlmulcurkeycache(VLMULCUR *mulcur, int *sp);
+
+
+/* Refer to volatile cache of the value of the record where a multiple cursor is.
+   `mulcur' specifies a multiple cursor handle.
+   `sp' specifies the pointer to a variable to which the size of the region of the return
+   value is assigned.  If it is `NULL', it is not used.
+   If successful, the return value is the pointer to the region of the value of the
+   corresponding record, else, it is `NULL'.  `NULL' is returned when no record corresponds to
+   the cursor.
+   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 if it is no longer in use. */
+const char *vlmulcurvalcache(VLMULCUR *mulcur, int *sp);
+
+
+/* Get flags of a database.
+   `villa' specifies a database handle.
+   The return value is the flags of a database. */
+int vlgetflags(VILLA *villa);
+
+
+/* Set flags of a database.
+   `villa' specifies a database handle connected as a writer.
+   `flags' specifies flags to set.  Least ten bits are reserved for internal use.
+   If successful, the return value is true, else, it is false. */
+int vlsetflags(VILLA *villa, int flags);
+
+
+
+#undef MYEXTERN
+
+#if defined(__cplusplus)                 /* export for C++ */
+}
+#endif
+
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/villa.h
___________________________________________________________________
Added: svn:mime-type
   + text/x-chdr
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/vista.c
===================================================================
--- box/trunk/qdbm/vista.c	                        (rev 0)
+++ box/trunk/qdbm/vista.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,171 @@
+/*************************************************************************************************
+ * Implementation of Vista
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#define QDBM_INTERNAL  1
+#define _VISTA_C       1
+
+#include "vista.h"
+#include "myconf.h"
+
+
+
+/*************************************************************************************************
+ * macros to convert Depot to Curia
+ *************************************************************************************************/
+
+
+#define DEPOT          CURIA
+
+#define \
+  dpopen(name, omode, bnum) \
+  cropen(name, omode, ((bnum / vlcrdnum) * 2), vlcrdnum)
+
+#define \
+  dpclose(db) \
+  crclose(db)
+
+#define \
+  dpput(db, kbuf, ksiz, vbuf, vsiz, dmode) \
+  crput(db, kbuf, ksiz, vbuf, vsiz, dmode)
+
+#define \
+  dpout(db, kbuf, ksiz) \
+  crout(db, kbuf, ksiz)
+
+#define \
+  dpget(db, kbuf, ksiz, start, max, sp) \
+  crget(db, kbuf, ksiz, start, max, sp)
+
+#define \
+  dpgetwb(db, kbuf, ksiz, start, max, vbuf) \
+  crgetwb(db, kbuf, ksiz, start, max, vbuf)
+
+#define \
+  dpvsiz(db, kbuf, ksiz) \
+  crvsiz(db, kbuf, ksiz)
+
+#define \
+  dpiterinit(db) \
+  criterinit(db)
+
+#define \
+  dpiternext(db, sp) \
+  criternext(db, sp)
+
+#define \
+  dpsetalign(db, align) \
+  crsetalign(db, align)
+
+#define \
+  dpsetfbpsiz(db, size) \
+  crsetfbpsiz(db, size)
+
+#define \
+  dpsync(db) \
+  crsync(db)
+
+#define \
+  dpoptimize(db, bnum) \
+  croptimize(db, bnum)
+
+#define \
+  dpname(db) \
+  crname(db)
+
+#define \
+  dpfsiz(db) \
+  crfsiz(db)
+
+#define \
+  dpbnum(db) \
+  crbnum(db)
+
+#define \
+  dpbusenum(db) \
+  crbusenum(db)
+
+#define \
+  dprnum(db) \
+  crrnum(db)
+
+#define \
+  dpwritable(db) \
+  crwritable(db)
+
+#define \
+  dpfatalerror(db) \
+  crfatalerror(db)
+
+#define \
+  dpinode(db) \
+  crinode(db)
+
+#define \
+  dpmtime(db) \
+  crmtime(db)
+
+#define \
+  dpfdesc(db) \
+  crfdesc(db)
+
+#define \
+  dpremove(db) \
+  crremove(db)
+
+#define \
+  dprepair(db) \
+  crrepair(db)
+
+#define \
+  dpexportdb(db, name) \
+  crexportdb(db, name)
+
+#define \
+  dpimportdb(db, name) \
+  crimportdb(db, name)
+
+#define \
+  dpsnaffle(db, name) \
+  crsnaffle(db, name)
+
+#define \
+  dpmemsync(db) \
+  crmemsync(db)
+
+#define \
+  dpmemflush(db) \
+  crmemflush(db)
+
+#define \
+  dpgetflags(db) \
+  crgetflags(db)
+
+#define \
+  dpsetflags(db, flags) \
+  crsetflags(db, flags)
+
+
+
+/*************************************************************************************************
+ * including real implementation
+ *************************************************************************************************/
+
+
+#include "villa.c"
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/vista.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/vista.h
===================================================================
--- box/trunk/qdbm/vista.h	                        (rev 0)
+++ box/trunk/qdbm/vista.h	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,138 @@
+/*************************************************************************************************
+ * The extended advanced API of QDBM
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _VISTA_H                         /* duplication check */
+#define _VISTA_H
+
+#if defined(__cplusplus)                 /* export for C++ */
+extern "C" {
+#endif
+
+
+
+/*************************************************************************************************
+ * macros to borrow symbols from Villa
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <curia.h>
+#include <cabin.h>
+#include <stdlib.h>
+
+#define VLREC          VSTREC
+#define VLIDX          VSTIDX
+#define VLLEAF         VSTLEAF
+#define VLNODE         VSTNODE
+#define VLCFUNC        VSTCFUNC
+
+#define VL_CMPLEX      VST_CMPLEX
+#define VL_CMPINT      VST_CMPINT
+#define VL_CMPNUM      VST_CMPNUM
+#define VL_CMPDEC      VST_CMPDEC
+
+#define VILLA          VISTA
+
+#define VL_OREADER     VST_OREADER
+#define VL_OWRITER     VST_OWRITER
+#define VL_OCREAT      VST_OCREAT
+#define VL_OTRUNC      VST_OTRUNC
+#define VL_ONOLCK      VST_ONOLCK
+#define VL_OLCKNB      VST_OLCKNB
+#define VL_OZCOMP      VST_OZCOMP
+
+#define VL_DOVER       VST_DOVER
+#define VL_DKEEP       VST_DKEEP
+#define VL_DCAT        VST_DCAT
+#define VL_DDUP        VST_DDUP
+
+#define VL_JFORWARD    VST_JFORWARD
+#define VL_JBACKWARD   VST_JBACKWARD
+
+#define vlopen         vstopen
+#define vlclose        vstclose
+#define vlput          vstput
+#define vlout          vstout
+#define vlget          vstget
+#define vlvsiz         vstvsiz
+#define vlvnum         vstvnum
+#define vlputlist      vstputlist
+#define vloutlist      vstoutlist
+#define vlgetlist      vstgetlist
+#define vlgetcat       vstgetcat
+#define vlcurfirst     vstcurfirst
+#define vlcurlast      vstcurlast
+#define vlcurprev      vstcurprev
+#define vlcurnext      vstcurnext
+#define vlcurjump      vstcurjump
+#define vlcurkey       vstcurkey
+#define vlcurval       vstcurval
+#define vlcurput       vstcurput
+#define vlcurout       vstcurout
+#define vlsettuning    vstsettuning
+#define vlsync         vstsync
+#define vloptimize     vstoptimize
+#define vlname         vstname
+#define vlfsiz         vstfsiz
+#define vllnum         vstlnum
+#define vlnnum         vstnnum
+#define vlrnum         vstrnum
+#define vlwritable     vstwritable
+#define vlfatalerror   vstfatalerror
+#define vlinode        vstinode
+#define vlmtime        vstmtime
+#define vltranbegin    vsttranbegin
+#define vltrancommit   vsttrancommit
+#define vltranabort    vsttranabort
+#define vlremove       vstremove
+#define vlrepair       vstrepair
+#define vlexportdb     vstexportdb
+#define vlimportdb     vstimportdb
+#define vlcrdnumptr    vstcrdnumptr
+#define vlmemsync      vstmemsync
+#define vlmemflush     vstmemflush
+#define vlgetcache     vstgetcache
+#define vlcurkeycache  vstcurkeycache
+#define vlcurvalcache  vstcurvalcache
+#define vlmulcuropen   vstmulcuropen
+#define vlmulcurclose  vstmulcurclose
+#define vlmulcurfirst  vstmulcurfirst
+#define vlmulcurlast   vstmulcurlast
+#define vlmulcurprev   vstmulcurprev
+#define vlmulcurnext   vstmulcurnext
+#define vlmulcurjump   vstmulcurjump
+#define vlmulcurkey    vstmulcurkey
+#define vlmulcurval    vstmulcurval
+#define vlmulcurkeycache  vstmulcurkeycache
+#define vlmulcurvalcache  vstmulcurvalcache
+#define vlsetfbpsiz    vstsetfbpsiz
+#define vlgetflags     vstgetflags
+#define vlsetflags     vstsetflags
+
+#if !defined(_VISTA_C)
+#include <villa.h>
+#endif
+
+
+
+#if defined(__cplusplus)                 /* export for C++ */
+}
+#endif
+
+#endif                                   /* duplication check */
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/vista.h
___________________________________________________________________
Added: svn:mime-type
   + text/x-chdr
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/vlmgr.c
===================================================================
--- box/trunk/qdbm/vlmgr.c	                        (rev 0)
+++ box/trunk/qdbm/vlmgr.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,968 @@
+/*************************************************************************************************
+ * Utility for debugging Villa and its applications
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+char *hextoobj(const char *str, int *sp);
+char *dectoiobj(const char *str, int *sp);
+int runcreate(int argc, char **argv);
+int runput(int argc, char **argv);
+int runout(int argc, char **argv);
+int runget(int argc, char **argv);
+int runlist(int argc, char **argv);
+int runoptimize(int argc, char **argv);
+int runinform(int argc, char **argv);
+int runremove(int argc, char **argv);
+int runrepair(int argc, char **argv);
+int runexportdb(int argc, char **argv);
+int runimportdb(int argc, char **argv);
+void pdperror(const char *name);
+void printobj(const char *obj, int size);
+void printobjhex(const char *obj, int size);
+int docreate(const char *name, int cmode);
+int doput(const char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+          int dmode, VLCFUNC cmp);
+int doout(const char *name, const char *kbuf, int ksiz, VLCFUNC cmp, int lb);
+int doget(const char *name, int opts, const char *kbuf, int ksiz, VLCFUNC cmp,
+          int lb, int ox, int nb);
+int dolist(const char *name, int opts, const char *tbuf, int tsiz, const char *bbuf, int bsiz,
+           VLCFUNC cmp, int ki, int kb, int vb, int ox, int gt, int lt, int max, int desc);
+int dooptimize(const char *name);
+int doinform(const char *name, int opts);
+int doremove(const char *name);
+int dorepair(const char *name, VLCFUNC cmp);
+int doexportdb(const char *name, const char *file, VLCFUNC cmp);
+int doimportdb(const char *name, const char *file, VLCFUNC cmp);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  char *env;
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if((env = getenv("QDBMDBGFD")) != NULL) dpdbgfd = atoi(env);
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "create")){
+    rv = runcreate(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], "inform")){
+    rv = runinform(argc, argv);
+  } else if(!strcmp(argv[1], "remove")){
+    rv = runremove(argc, argv);
+  } else if(!strcmp(argv[1], "repair")){
+    rv = runrepair(argc, argv);
+  } else if(!strcmp(argv[1], "exportdb")){
+    rv = runexportdb(argc, argv);
+  } else if(!strcmp(argv[1], "importdb")){
+    rv = runimportdb(argc, argv);
+  } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){
+    printf("Powered by QDBM version %s on %s%s\n",
+           dpversion, dpsysname, dpisreentrant ? " (reentrant)" : "");
+    printf("Copyright (c) 2000-2007 Mikio Hirabayashi\n");
+    rv = 0;
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: administration utility for Villa\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s create [-cz|-cy|-cx] name\n", progname);
+  fprintf(stderr, "  %s put [-kx|-ki] [-vx|-vi|-vf] [-keep|-cat|-dup] name key val\n", progname);
+  fprintf(stderr, "  %s out [-l] [-kx|-ki] name key\n", progname);
+  fprintf(stderr, "  %s get [-nl] [-l] [-kx|-ki] [-ox] [-n] name key\n", progname);
+  fprintf(stderr, "  %s list [-nl] [-k|-v] [-kx|-ki] [-ox] [-top key] [-bot key] [-gt] [-lt]"
+          " [-max num] [-desc] name\n", progname);
+  fprintf(stderr, "  %s optimize name\n", progname);
+  fprintf(stderr, "  %s inform [-nl] name\n", progname);
+  fprintf(stderr, "  %s remove name\n", progname);
+  fprintf(stderr, "  %s repair [-ki] name\n", progname);
+  fprintf(stderr, "  %s exportdb [-ki] name file\n", progname);
+  fprintf(stderr, "  %s importdb [-ki] name file\n", progname);
+  fprintf(stderr, "  %s version\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* create a binary object from a hexadecimal string */
+char *hextoobj(const char *str, int *sp){
+  char *buf, mbuf[3];
+  int len, i, j;
+  len = strlen(str);
+  if(!(buf = malloc(len + 1))) return NULL;
+  j = 0;
+  for(i = 0; i < len; i += 2){
+    while(strchr(" \n\r\t\f\v", str[i])){
+      i++;
+    }
+    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;
+}
+
+
+/* create a integer object from a decimal string */
+char *dectoiobj(const char *str, int *sp){
+  char *buf;
+  int num;
+  num = atoi(str);
+  if(!(buf = malloc(sizeof(int)))) return NULL;
+  *(int *)buf = num;
+  *sp = sizeof(int);
+  return buf;
+}
+
+
+/* parse arguments of create command */
+int runcreate(int argc, char **argv){
+  char *name;
+  int i, cmode, rv;
+  name = NULL;
+  cmode = 0;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-cz")){
+        cmode |= VL_OZCOMP;
+      } else if(!strcmp(argv[i], "-cy")){
+        cmode |= VL_OYCOMP;
+      } else if(!strcmp(argv[i], "-cx")){
+        cmode |= VL_OXCOMP;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = docreate(name, cmode);
+  return rv;
+}
+
+
+/* parse arguments of put command */
+int runput(int argc, char **argv){
+  char *name, *key, *val, *kbuf, *vbuf;
+  int i, kx, ki, vx, vi, vf, ksiz, vsiz, rv;
+  int dmode;
+  name = NULL;
+  kx = FALSE;
+  ki = FALSE;
+  vx = FALSE;
+  vi = FALSE;
+  vf = FALSE;
+  key = NULL;
+  val = NULL;
+  dmode = VL_DOVER;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else if(!strcmp(argv[i], "-vx")){
+        vx = TRUE;
+      } else if(!strcmp(argv[i], "-vi")){
+        vi = TRUE;
+      } else if(!strcmp(argv[i], "-vf")){
+        vf = TRUE;
+      } else if(!strcmp(argv[i], "-keep")){
+        dmode = VL_DKEEP;
+      } else if(!strcmp(argv[i], "-cat")){
+        dmode = VL_DCAT;
+      } else if(!strcmp(argv[i], "-dup")){
+        dmode = VL_DDUP;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else if(!val){
+      val = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key || !val) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(vx){
+    vbuf = hextoobj(val, &vsiz);
+  } else if(vi){
+    vbuf = dectoiobj(val, &vsiz);
+  } else if(vf){
+    vbuf = cbreadfile(val, &vsiz);
+  } else {
+    vbuf = cbmemdup(val, -1);
+    vsiz = -1;
+  }
+  if(kbuf && vbuf){
+    rv = doput(name, kbuf, ksiz, vbuf, vsiz, dmode, ki ? VL_CMPINT : VL_CMPLEX);
+  } else {
+    if(vf){
+      fprintf(stderr, "%s: %s: cannot read\n", progname, val);
+    } else {
+      fprintf(stderr, "%s: out of memory\n", progname);
+    }
+    rv = 1;
+  }
+  free(kbuf);
+  free(vbuf);
+  return rv;
+}
+
+
+/* parse arguments of out command */
+int runout(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, kx, ki, lb, ksiz, rv;
+  name = NULL;
+  kx = FALSE;
+  ki = FALSE;
+  lb = FALSE;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-l")){
+        lb = TRUE;
+      } else if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(kbuf){
+    rv = doout(name, kbuf, ksiz, ki ? VL_CMPINT : VL_CMPLEX, lb);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of get command */
+int runget(int argc, char **argv){
+  char *name, *key, *kbuf;
+  int i, opts, lb, kx, ki, ox, nb, ksiz, rv;
+  name = NULL;
+  opts = 0;
+  lb = FALSE;
+  kx = FALSE;
+  ki = FALSE;
+  ox = FALSE;
+  nb = FALSE;
+  key = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        opts |= VL_ONOLCK;
+      } else if(!strcmp(argv[i], "-l")){
+        lb = TRUE;
+      } else if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else if(!strcmp(argv[i], "-n")){
+        nb = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!key){
+      key = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !key) usage();
+  if(kx){
+    kbuf = hextoobj(key, &ksiz);
+  } else if(ki){
+    kbuf = dectoiobj(key, &ksiz);
+  } else {
+    kbuf = cbmemdup(key, -1);
+    ksiz = -1;
+  }
+  if(kbuf){
+    rv = doget(name, opts, kbuf, ksiz, ki ? VL_CMPINT : VL_CMPLEX, lb, ox, nb);
+  } else {
+    fprintf(stderr, "%s: out of memory\n", progname);
+    rv = 1;
+  }
+  free(kbuf);
+  return rv;
+}
+
+
+/* parse arguments of list command */
+int runlist(int argc, char **argv){
+  char *name, *top, *bot, *tbuf, *bbuf, *nstr;
+  int i, opts, kb, vb, kx, ki, ox, gt, lt, max, desc, tsiz, bsiz, rv;
+  name = NULL;
+  opts = 0;
+  kb = FALSE;
+  vb = FALSE;
+  kx = FALSE;
+  ki = FALSE;
+  ox = FALSE;
+  gt = FALSE;
+  lt = FALSE;
+  max = -1;
+  desc = FALSE;
+  top = NULL;
+  bot = NULL;
+  nstr = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        opts |= VL_ONOLCK;
+      } else if(!strcmp(argv[i], "-k")){
+        kb = TRUE;
+      } else if(!strcmp(argv[i], "-v")){
+        vb = TRUE;
+      } else if(!strcmp(argv[i], "-kx")){
+        kx = TRUE;
+      } else if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else if(!strcmp(argv[i], "-ox")){
+        ox = TRUE;
+      } else if(!strcmp(argv[i], "-top")){
+        if(++i >= argc) usage();
+        top = argv[i];
+      } else if(!strcmp(argv[i], "-bot")){
+        if(++i >= argc) usage();
+        bot = argv[i];
+      } else if(!strcmp(argv[i], "-gt")){
+        gt = TRUE;
+      } else if(!strcmp(argv[i], "-lt")){
+        lt = TRUE;
+      } else if(!strcmp(argv[i], "-max")){
+        if(++i >= argc) usage();
+        max = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-desc")){
+        desc = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  tbuf = NULL;
+  bbuf = NULL;
+  if(kx){
+    if(top) tbuf = hextoobj(top, &tsiz);
+    if(bot) bbuf = hextoobj(bot, &bsiz);
+  } else if(ki){
+    if(top) tbuf = dectoiobj(top, &tsiz);
+    if(bot) bbuf = dectoiobj(bot, &bsiz);
+  } else {
+    if(top){
+      tbuf = cbmemdup(top, -1);
+      tsiz = strlen(tbuf);
+    }
+    if(bot){
+      bbuf = cbmemdup(bot, -1);
+      bsiz = strlen(bbuf);
+    }
+  }
+  rv = dolist(name, opts, tbuf, tsiz, bbuf, bsiz, ki ? VL_CMPINT : VL_CMPLEX, ki,
+              kb, vb, ox, gt, lt, max, desc);
+  free(tbuf);
+  free(bbuf);
+  return rv;
+}
+
+
+/* parse arguments of optimize command */
+int runoptimize(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dooptimize(name);
+  return rv;
+}
+
+
+/* parse arguments of inform command */
+int runinform(int argc, char **argv){
+  char *name;
+  int i, opts, rv;
+  name = NULL;
+  opts = 0;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-nl")){
+        opts |= VL_ONOLCK;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doinform(name, opts);
+  return rv;
+}
+
+
+/* parse arguments of remove command */
+int runremove(int argc, char **argv){
+  char *name;
+  int i, rv;
+  name = NULL;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      usage();
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doremove(name);
+  return rv;
+}
+
+
+/* parse arguments of repair command */
+int runrepair(int argc, char **argv){
+  char *name;
+  int i, ki, rv;
+  name = NULL;
+  ki = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = dorepair(name, ki ? VL_CMPINT : VL_CMPLEX);
+  return rv;
+}
+
+
+/* parse arguments of exportdb command */
+int runexportdb(int argc, char **argv){
+  char *name, *file;
+  int i, ki, rv;
+  name = NULL;
+  file = NULL;
+  ki = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !file) usage();
+  rv = doexportdb(name, file, ki ? VL_CMPINT : VL_CMPLEX);
+  return rv;
+}
+
+
+/* parse arguments of importdb command */
+int runimportdb(int argc, char **argv){
+  char *name, *file;
+  int i, ki, rv;
+  name = NULL;
+  file = NULL;
+  ki = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-ki")){
+        ki = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!file){
+      file = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !file) usage();
+  rv = doimportdb(name, file, ki ? VL_CMPINT : VL_CMPLEX);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* print an object */
+void printobj(const char *obj, int size){
+  int i;
+  for(i = 0; i < size; i++){
+    putchar(obj[i]);
+  }
+}
+
+
+/* print an object as a hexadecimal string */
+void printobjhex(const char *obj, int size){
+  int i;
+  for(i = 0; i < size; i++){
+    printf("%s%02X", i > 0 ? " " : "", ((const unsigned char *)obj)[i]);
+  }
+}
+
+
+/* perform create command */
+int docreate(const char *name, int cmode){
+  VILLA *villa;
+  int omode;
+  omode = VL_OWRITER | VL_OCREAT | VL_OTRUNC | cmode;
+  if(!(villa = vlopen(name, omode, VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform put command */
+int doput(const char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+          int dmode, VLCFUNC cmp){
+  VILLA *villa;
+  if(!(villa = vlopen(name, VL_OWRITER, cmp))){
+    pdperror(name);
+    return 1;
+  }
+  if(!vlput(villa, kbuf, ksiz, vbuf, vsiz, dmode)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform out command */
+int doout(const char *name, const char *kbuf, int ksiz, VLCFUNC cmp, int lb){
+  VILLA *villa;
+  if(!(villa = vlopen(name, VL_OWRITER, cmp))){
+    pdperror(name);
+    return 1;
+  }
+  if(lb){
+    if(!vloutlist(villa, kbuf, ksiz)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  } else {
+    if(!vlout(villa, kbuf, ksiz)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform get command */
+int doget(const char *name, int opts, const char *kbuf, int ksiz, VLCFUNC cmp,
+          int lb, int ox, int nb){
+  VILLA *villa;
+  CBLIST *vals;
+  char *vbuf;
+  int vsiz;
+  if(!(villa = vlopen(name, VL_OREADER | opts, cmp))){
+    pdperror(name);
+    return 1;
+  }
+  if(lb){
+    if(!(vals = vlgetlist(villa, kbuf, ksiz))){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+    while((vbuf = cblistshift(vals, &vsiz)) != NULL){
+      if(ox){
+        printobjhex(vbuf, vsiz);
+      } else {
+        printobj(vbuf, vsiz);
+      }
+      free(vbuf);
+      putchar('\n');
+    }
+    cblistclose(vals);
+  } else {
+    if(!(vbuf = vlget(villa, kbuf, ksiz, &vsiz))){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+    if(ox){
+      printobjhex(vbuf, vsiz);
+    } else {
+      printobj(vbuf, vsiz);
+    }
+    free(vbuf);
+    if(!nb) putchar('\n');
+  }
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform list command */
+int dolist(const char *name, int opts, const char *tbuf, int tsiz, const char *bbuf, int bsiz,
+           VLCFUNC cmp, int ki, int kb, int vb, int ox, int gt, int lt, int max, int desc){
+  VILLA *villa;
+  char *kbuf, *vbuf;
+  int ksiz, vsiz, show, rv;
+  if(!(villa = vlopen(name, VL_OREADER | opts, cmp))){
+    pdperror(name);
+    return 1;
+  }
+  if(max < 0) max = vlrnum(villa);
+  if(desc){
+    if(bbuf){
+      vlcurjump(villa, bbuf, bsiz, VL_JBACKWARD);
+    } else {
+      vlcurlast(villa);
+    }
+    show = 0;
+    while(show < max && (kbuf = vlcurkey(villa, &ksiz)) != NULL){
+      if(bbuf && lt){
+        if(cmp(kbuf, ksiz, bbuf, bsiz) == 0){
+          free(kbuf);
+          vlcurnext(villa);
+          continue;
+        }
+        lt = FALSE;
+      }
+      if(tbuf){
+        rv = cmp(kbuf, ksiz, tbuf, tsiz);
+        if(rv < 0 || (gt && rv == 0)){
+          free(kbuf);
+          break;
+        }
+      }
+      if(!(vbuf = vlcurval(villa, &vsiz))){
+        free(kbuf);
+        break;
+      }
+      if(ox){
+        if(!vb) printobjhex(kbuf, ksiz);
+        if(!kb && !vb) putchar('\t');
+        if(!kb) printobjhex(vbuf, vsiz);
+      } else {
+        if(!vb) printobj(kbuf, ksiz);
+        if(!kb && !vb) putchar('\t');
+        if(!kb) printobj(vbuf, vsiz);
+      }
+      putchar('\n');
+      free(kbuf);
+      free(vbuf);
+      show++;
+      vlcurprev(villa);
+    }
+  } else {
+    if(tbuf){
+      vlcurjump(villa, tbuf, tsiz, VL_JFORWARD);
+    } else {
+      vlcurfirst(villa);
+    }
+    show = 0;
+    while(show < max && (kbuf = vlcurkey(villa, &ksiz)) != NULL){
+      if(tbuf && gt){
+        if(cmp(kbuf, ksiz, tbuf, tsiz) == 0){
+          free(kbuf);
+          vlcurnext(villa);
+          continue;
+        }
+        gt = FALSE;
+      }
+      if(bbuf){
+        rv = cmp(kbuf, ksiz, bbuf, bsiz);
+        if(rv > 0 || (lt && rv == 0)){
+          free(kbuf);
+          break;
+        }
+      }
+      if(!(vbuf = vlcurval(villa, &vsiz))){
+        free(kbuf);
+        break;
+      }
+      if(ox){
+        if(!vb) printobjhex(kbuf, ksiz);
+        if(!kb && !vb) putchar('\t');
+        if(!kb) printobjhex(vbuf, vsiz);
+      } else {
+        if(!vb) printobj(kbuf, ksiz);
+        if(!kb && !vb) putchar('\t');
+        if(!kb) printobj(vbuf, vsiz);
+      }
+      putchar('\n');
+      free(kbuf);
+      free(vbuf);
+      show++;
+      vlcurnext(villa);
+    }
+  }
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform optimize command */
+int dooptimize(const char *name){
+  VILLA *villa;
+  if(!(villa = vlopen(name, VL_OWRITER, VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  if(!vloptimize(villa)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform inform command */
+int doinform(const char *name, int opts){
+  VILLA *villa;
+  char *tmp;
+  if(!(villa = vlopen(name, VL_OREADER | opts, VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  tmp = vlname(villa);
+  printf("name: %s\n", tmp ? tmp : "(null)");
+  free(tmp);
+  printf("file size: %d\n", vlfsiz(villa));
+  printf("leaf nodes: %d\n", vllnum(villa));
+  printf("non-leaf nodes: %d\n", vlnnum(villa));
+  printf("records: %d\n", vlrnum(villa));
+  printf("inode number: %d\n", vlinode(villa));
+  printf("modified time: %.0f\n", (double)vlmtime(villa));
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform remove command */
+int doremove(const char *name){
+  if(!vlremove(name)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform repair command */
+int dorepair(const char *name, VLCFUNC cmp){
+  if(!vlrepair(name, cmp)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform exportdb command */
+int doexportdb(const char *name, const char *file, VLCFUNC cmp){
+  VILLA *villa;
+  if(!(villa = vlopen(name, VL_OREADER, cmp))){
+    pdperror(name);
+    return 1;
+  }
+  if(!vlexportdb(villa, file)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+/* perform importdb command */
+int doimportdb(const char *name, const char *file, VLCFUNC cmp){
+  VILLA *villa;
+  if(!(villa = vlopen(name, VL_OWRITER | VL_OCREAT | VL_OTRUNC, cmp))){
+    pdperror(name);
+    return 1;
+  }
+  if(!vlimportdb(villa, file)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/vlmgr.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/vltest.c
===================================================================
--- box/trunk/qdbm/vltest.c	                        (rev 0)
+++ box/trunk/qdbm/vltest.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,1507 @@
+/*************************************************************************************************
+ * Test cases of Villa
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <time.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+#define RECBUFSIZ      32                /* buffer for records */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+int runwrite(int argc, char **argv);
+int runread(int argc, char **argv);
+int runrdup(int argc, char **argv);
+int runcombo(int argc, char **argv);
+int runwicked(int argc, char **argv);
+int printfflush(const char *format, ...);
+void pdperror(const char *name);
+int myrand(void);
+int dowrite(const char *name, int rnum, int ii, int cmode,
+            int lrecmax, int nidxmax, int lcnum, int ncnum, int fbp);
+int doread(const char *name, int ii, int vc);
+int dordup(const char *name, int rnum, int pnum, int ii, int cmode, int cc,
+           int lrecmax, int nidxmax, int lcnum, int ncnum, int fbp);
+int docombo(const char *name, int cmode);
+int dowicked(const char *name, int rnum, int cb, int cmode);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  char *env;
+  int rv;
+  cbstdiobin();
+  if((env = getenv("QDBMDBGFD")) != NULL) dpdbgfd = atoi(env);
+  progname = argv[0];
+  if(argc < 2) usage();
+  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], "rdup")){
+    rv = runrdup(argc, argv);
+  } else if(!strcmp(argv[1], "combo")){
+    rv = runcombo(argc, argv);
+  } else if(!strcmp(argv[1], "wicked")){
+    rv = runwicked(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: test cases for Villa\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s write [-int] [-cz|-cy|-cx] [-tune lrecmax nidxmax lcnum ncnum]"
+          " [-fbp num] name rnum\n", progname);
+  fprintf(stderr, "  %s read [-int] [-vc] name\n", progname);
+  fprintf(stderr, "  %s rdup [-int] [-cz|-cy|-cx] [-cc] [-tune lrecmax nidxmax lcnum ncnum]"
+          " [-fbp num] name rnum pnum\n", progname);
+  fprintf(stderr, "  %s combo [-cz|-cy|-cx] name\n", progname);
+  fprintf(stderr, "  %s wicked [-c] [-cz|-cy|-cx] name rnum\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of write command */
+int runwrite(int argc, char **argv){
+  char *name, *rstr;
+  int i, rnum, ii, cmode, lrecmax, nidxmax, lcnum, ncnum, fbp, rv;
+  name = NULL;
+  rstr = NULL;
+  rnum = 0;
+  ii = FALSE;
+  cmode = 0;
+  lrecmax = -1;
+  nidxmax = -1;
+  lcnum = -1;
+  ncnum = -1;
+  fbp = -1;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-int")){
+        ii = TRUE;
+      } else if(!strcmp(argv[i], "-cz")){
+        cmode |= VL_OZCOMP;
+      } else if(!strcmp(argv[i], "-cy")){
+        cmode |= VL_OYCOMP;
+      } else if(!strcmp(argv[i], "-cx")){
+        cmode |= VL_OXCOMP;
+      } else if(!strcmp(argv[i], "-tune")){
+        if(++i >= argc) usage();
+        lrecmax = atoi(argv[i]);
+        if(++i >= argc) usage();
+        nidxmax = atoi(argv[i]);
+        if(++i >= argc) usage();
+        lcnum = atoi(argv[i]);
+        if(++i >= argc) usage();
+        ncnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-fbp")){
+        if(++i >= argc) usage();
+        fbp = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = dowrite(name, rnum, ii, cmode, lrecmax, nidxmax, lcnum, ncnum, fbp);
+  return rv;
+}
+
+
+/* parse arguments of read command */
+int runread(int argc, char **argv){
+  char *name;
+  int i, ii, vc, rv;
+  name = NULL;
+  ii = FALSE;
+  vc = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-int")){
+        ii = TRUE;
+      } else if(!strcmp(argv[i], "-vc")){
+        vc = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doread(name, ii, vc);
+  return rv;
+}
+
+
+/* parse arguments of rdup command */
+int runrdup(int argc, char **argv){
+  char *name, *rstr, *pstr;
+  int i, rnum, pnum, ii, cmode, cc, lrecmax, nidxmax, lcnum, ncnum, fbp, rv;
+  name = NULL;
+  rstr = NULL;
+  pstr = NULL;
+  rnum = 0;
+  pnum = 0;
+  ii = FALSE;
+  cmode = 0;
+  cc = FALSE;
+  lrecmax = -1;
+  nidxmax = -1;
+  lcnum = -1;
+  ncnum = -1;
+  fbp = -1;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-int")){
+        ii = TRUE;
+      } else if(!strcmp(argv[i], "-cz")){
+        cmode |= VL_OZCOMP;
+      } else if(!strcmp(argv[i], "-cy")){
+        cmode |= VL_OYCOMP;
+      } else if(!strcmp(argv[i], "-cx")){
+        cmode |= VL_OXCOMP;
+      } else if(!strcmp(argv[i], "-cc")){
+        cc = TRUE;
+      } else if(!strcmp(argv[i], "-tune")){
+        if(++i >= argc) usage();
+        lrecmax = atoi(argv[i]);
+        if(++i >= argc) usage();
+        nidxmax = atoi(argv[i]);
+        if(++i >= argc) usage();
+        lcnum = atoi(argv[i]);
+        if(++i >= argc) usage();
+        ncnum = atoi(argv[i]);
+      } else if(!strcmp(argv[i], "-fbp")){
+        if(++i >= argc) usage();
+        fbp = atoi(argv[i]);
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else if(!pstr){
+      pstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr || !pstr) usage();
+  rnum = atoi(rstr);
+  pnum = atoi(pstr);
+  if(rnum < 1 || pnum < 1) usage();
+  rv = dordup(name, rnum, pnum, ii, cmode, cc, lrecmax, nidxmax, lcnum, ncnum, fbp);
+  return rv;
+}
+
+
+/* parse arguments of combo command */
+int runcombo(int argc, char **argv){
+  char *name;
+  int i, cmode, rv;
+  name = NULL;
+  cmode = 0;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-cz")){
+        cmode |= VL_OZCOMP;
+      } else if(!strcmp(argv[i], "-cy")){
+        cmode |= VL_OYCOMP;
+      } else if(!strcmp(argv[i], "-cx")){
+        cmode |= VL_OXCOMP;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = docombo(name, cmode);
+  return rv;
+}
+
+
+/* parse arguments of wicked command */
+int runwicked(int argc, char **argv){
+  char *name, *rstr;
+  int i, cb, cmode, rnum, rv;
+  name = NULL;
+  rstr = NULL;
+  cb = FALSE;
+  cmode = 0;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-c")){
+        cb = TRUE;
+      } else if(!strcmp(argv[i], "-cz")){
+        cmode |= VL_OZCOMP;
+      } else if(!strcmp(argv[i], "-cy")){
+        cmode |= VL_OYCOMP;
+      } else if(!strcmp(argv[i], "-cx")){
+        cmode |= VL_OXCOMP;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else if(!rstr){
+      rstr = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name || !rstr) usage();
+  rnum = atoi(rstr);
+  if(rnum < 1) usage();
+  rv = dowicked(name, rnum, cb, cmode);
+  return rv;
+}
+
+
+/* print formatted string and flush the buffer */
+int printfflush(const char *format, ...){
+  va_list ap;
+  int rv;
+  va_start(ap, format);
+  rv = vprintf(format, ap);
+  if(fflush(stdout) == EOF) rv = -1;
+  va_end(ap);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* pseudo random number generator */
+int myrand(void){
+  static int cnt = 0;
+  if(cnt == 0) srand(time(NULL));
+  return (rand() * rand() + (rand() >> (sizeof(int) * 4)) + (cnt++)) & INT_MAX;
+}
+
+
+/* perform write command */
+int dowrite(const char *name, int rnum, int ii, int cmode,
+            int lrecmax, int nidxmax, int lcnum, int ncnum, int fbp){
+  VILLA *villa;
+  int i, omode, err, len;
+  char buf[RECBUFSIZ];
+  printfflush("<Writing Test>\n  name=%s  rnum=%d  int=%d  cmode=%d  "
+              "lrecmax=%d  nidxmax=%d  lcnum=%d  ncnum=%d  fbp=%d\n\n",
+              name, rnum, ii, cmode, lrecmax, nidxmax, lcnum, ncnum, fbp);
+  /* open a database */
+  omode = VL_OWRITER | VL_OCREAT | VL_OTRUNC | cmode;
+  if(!(villa = vlopen(name, omode, ii ? VL_CMPINT : VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  err = FALSE;
+  /* set tuning parameters */
+  if(lrecmax > 0) vlsettuning(villa, lrecmax, nidxmax, lcnum, ncnum);
+  if(fbp >= 0) vlsetfbpsiz(villa, fbp);
+  /* loop for each record */
+  for(i = 1; i <= rnum; i++){
+    /* store a record */
+    if(ii){
+      if(!vlput(villa, (char *)&i, sizeof(int), (char *)&i, sizeof(int), VL_DOVER)){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+    } else {
+      len = sprintf(buf, "%08d", i);
+      if(!vlput(villa, buf, len, buf, len, VL_DOVER)){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+    }
+    /* print progression */
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return 0;
+}
+
+
+/* perform read command */
+int doread(const char *name, int ii, int vc){
+  VILLA *villa;
+  int i, rnum, err, len;
+  const char *cval;
+  char buf[RECBUFSIZ], *val;
+  printfflush("<Reading Test>\n  name=%s  int=%d\n\n", name, ii);
+  /* open a database */
+  if(!(villa = vlopen(name, VL_OREADER, ii ? VL_CMPINT : VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  /* get the number of records */
+  rnum = vlrnum(villa);
+  err = FALSE;
+  /* loop for each record */
+  for(i = 1; i <= rnum; i++){
+    /* retrieve a record */
+    if(ii){
+      if(vc){
+        if(!(cval = vlgetcache(villa, (char *)&i, sizeof(int), NULL))){
+          pdperror(name);
+          err = TRUE;
+          break;
+        }
+      } else {
+        if(!(val = vlget(villa, (char *)&i, sizeof(int), NULL))){
+          pdperror(name);
+          err = TRUE;
+          break;
+        }
+        free(val);
+      }
+    } else {
+      len = sprintf(buf, "%08d", i);
+      if(vc){
+        if(!(cval = vlgetcache(villa, buf, len, NULL))){
+          pdperror(name);
+          err = TRUE;
+          break;
+        }
+      } else {
+        if(!(val = vlget(villa, buf, len, NULL))){
+          pdperror(name);
+          err = TRUE;
+          break;
+        }
+        free(val);
+      }
+    }
+    /* print progression */
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d)\n", i);
+      }
+    }
+  }
+  /* close the database */
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return 0;
+}
+
+
+/* perform rdup command */
+int dordup(const char *name, int rnum, int pnum, int ii, int cmode, int cc,
+           int lrecmax, int nidxmax, int lcnum, int ncnum, int fbp){
+  VILLA *villa;
+  int i, omode, err, dmode, vi, len;
+  char buf[RECBUFSIZ];
+  printfflush("<Random Writing Test>\n  name=%s  rnum=%d  int=%d  cmode=%d  "
+              "lrecmax=%d  nidxmax=%d  lcnum=%d  ncnum=%d  fbp=%d\n\n",
+              name, rnum, ii, cmode, lrecmax, nidxmax, lcnum, ncnum, fbp);
+  omode = VL_OWRITER | VL_OCREAT | VL_OTRUNC | cmode;
+  if(!(villa = vlopen(name, omode, ii ? VL_CMPINT : VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  err = FALSE;
+  if(lrecmax > 0) vlsettuning(villa, lrecmax, nidxmax, lcnum, ncnum);
+  if(fbp >= 0) vlsetfbpsiz(villa, fbp);
+  for(i = 1; i <= rnum; i++){
+    dmode = i % 3 == 0 ? VL_DDUPR : VL_DDUP;
+    if(cc && myrand() % 2 == 0) dmode = VL_DCAT;
+    vi = myrand() % pnum + 1;
+    if(ii){
+      if(!vlput(villa, (char *)&vi, sizeof(int), (char *)&vi, sizeof(int), dmode)){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+    } else {
+      len = sprintf(buf, "%08d", vi);
+      if(!vlput(villa, buf, len, buf, len, dmode)){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+    }
+    if(rnum > 250 && i % (rnum / 250) == 0){
+      putchar('.');
+      fflush(stdout);
+      if(i == rnum || i % (rnum / 10) == 0){
+        printfflush(" (%08d: fsiz=%d lnum=%d nnum=%d)\n",
+                    i, vlfsiz(villa), vllnum(villa), vlnnum(villa));
+      }
+    }
+  }
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return 0;
+}
+
+
+/* perform combo command */
+int docombo(const char *name, int cmode){
+  VILLA *villa;
+  VLMULCUR **mulcurs;
+  char buf[RECBUFSIZ], *vbuf, *kbuf;
+  int i, j, omode, len, vsiz, ksiz, fsiz, lnum, nnum, rnum;
+  CBLIST *alist, *dlist;
+  const char *ap, *dp;
+  printfflush("<Combination Test>\n  name=%s  cmode=%d\n\n", name, cmode);
+  printfflush("Creating a database with VL_CMPLEX ... ");
+  omode = VL_OWRITER | VL_OCREAT | VL_OTRUNC | cmode;
+  if(!(villa = vlopen(name, omode, VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Setting tuning parameters with 3, 4, 16, 16 ... ");
+  vlsettuning(villa, 3, 4, 16, 16);
+  printfflush("ok\n");
+  printfflush("Adding 100 records with VL_DOVER ... ");
+  for(i = 1; i <= 100; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!vlput(villa, buf, len, buf, len, VL_DOVER)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking records ... ");
+  for(i = 1; i <= 100; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(vbuf = vlget(villa, buf, len, &vsiz))){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != 8 || vlvsiz(villa, buf, len) != 8){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      vlclose(villa);
+      return 1;
+    }
+    if(vlvnum(villa, buf, len) != 1){
+      fprintf(stderr, "%s: %s: invalid vnum\n", progname, name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Deleting x1 - x5 records ... ");
+  for(i = 1; i <= 100; i++){
+    if(i % 10 < 1 || i % 10 > 5) continue;
+    len = sprintf(buf, "%08d", i);
+    if(!vlout(villa, buf, len)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Adding 100 records with VL_DOVER ... ");
+  for(i = 1; i <= 100; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!vlput(villa, buf, len, buf, len, VL_DOVER)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Deleting x1 - x5 records ... ");
+  for(i = 1; i <= 100; i++){
+    if(i % 10 < 1 || i % 10 > 5) continue;
+    len = sprintf(buf, "%08d", i);
+    if(!vlout(villa, buf, len)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Checking number of records ... ");
+  if(vlrnum(villa) != 50){
+    fprintf(stderr, "%s: %s: invalid rnum\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 100 records with VL_DDUP ... ");
+  for(i = 1; i <= 100; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!vlput(villa, buf, len, buf, len, VL_DDUP)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Deleting x6 - x0 records ... ");
+  for(i = 1; i <= 100; i++){
+    if(i % 10 >= 1 && i % 10 <= 5) continue;
+    len = sprintf(buf, "%08d", i);
+    if(!vlout(villa, buf, len)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Optimizing the database ... ");
+  if(!vloptimize(villa)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Checking number of records ... ");
+  if(vlrnum(villa) != 100){
+    fprintf(stderr, "%s: %s: invalid rnum\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Checking records ... ");
+  for(i = 1; i <= 100; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!(vbuf = vlget(villa, buf, len, &vsiz))){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+    free(vbuf);
+    if(vsiz != 8){
+      fprintf(stderr, "%s: %s: invalid vsiz\n", progname, name);
+      vlclose(villa);
+      return 1;
+    }
+    if(vlvnum(villa, buf, len) != 1){
+      fprintf(stderr, "%s: %s: invalid vnum\n", progname, name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Deleting x6 - x0 records ... ");
+  for(i = 1; i <= 100; i++){
+    if(i % 10 >= 1 && i % 10 <= 5) continue;
+    len = sprintf(buf, "%08d", i);
+    if(!vlout(villa, buf, len)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Scanning with the cursor in ascending order ... ");
+  if(!vlcurfirst(villa)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  i = 0;
+  do {
+    kbuf = NULL;
+    vbuf = NULL;
+    if(!(kbuf = vlcurkey(villa, &ksiz)) || !(vbuf = vlcurval(villa, &vsiz))){
+      pdperror(name);
+      free(kbuf);
+      free(vbuf);
+      vlclose(villa);
+      return 1;
+    }
+    free(kbuf);
+    free(vbuf);
+    i++;
+  } while(vlcurnext(villa));
+  if(i != 50){
+    fprintf(stderr, "%s: %s: invalid cursor\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  if(dpecode != DP_ENOITEM){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Scanning with the cursor in decending order ... ");
+  if(!vlcurlast(villa)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  i = 0;
+  do {
+    kbuf = NULL;
+    vbuf = NULL;
+    if(!(kbuf = vlcurkey(villa, &ksiz)) || !(vbuf = vlcurval(villa, &vsiz))){
+      pdperror(name);
+      free(kbuf);
+      free(vbuf);
+      vlclose(villa);
+      return 1;
+    }
+    free(kbuf);
+    free(vbuf);
+    i++;
+  } while(vlcurprev(villa));
+  if(i != 50){
+    fprintf(stderr, "%s: %s: invalid cursor\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  if(dpecode != DP_ENOITEM){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 50 random records with VL_DDUPR ... ");
+  for(i = 0; i < 50; i++){
+    len = sprintf(buf, "%08d", myrand() % 100 + 1);
+    if(!vlput(villa, buf, len, buf, len, VL_DDUPR)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Deleting 80 random records ... ");
+  i = 0;
+  while(i < 80){
+    len = sprintf(buf, "%08d", myrand() % 100 + 1);
+    if(!vlout(villa, buf, len)){
+      if(dpecode == DP_ENOITEM) continue;
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+    i++;
+  }
+  printfflush("ok\n");
+  alist = cblistopen();
+  dlist = cblistopen();
+  printfflush("Scanning with the cursor in ascending order ... ");
+  if(!vlcurfirst(villa)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  i = 0;
+  do {
+    kbuf = NULL;
+    vbuf = NULL;
+    if(!(kbuf = vlcurkey(villa, &ksiz)) || !(vbuf = vlcurval(villa, &vsiz))){
+      pdperror(name);
+      cblistclose(alist);
+      cblistclose(dlist);
+      free(kbuf);
+      free(vbuf);
+      vlclose(villa);
+      return 1;
+    }
+    cblistpush(alist, kbuf, ksiz);
+    free(kbuf);
+    free(vbuf);
+    i++;
+  } while(vlcurnext(villa));
+  if(i != 20){
+    fprintf(stderr, "%s: %s: invalid cursor\n", progname, name);
+    cblistclose(alist);
+    cblistclose(dlist);
+    vlclose(villa);
+    return 1;
+  }
+  if(dpecode != DP_ENOITEM){
+    pdperror(name);
+    cblistclose(alist);
+    cblistclose(dlist);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Scanning with the cursor in decending order ... ");
+  if(!vlcurlast(villa)){
+    pdperror(name);
+    cblistclose(alist);
+    cblistclose(dlist);
+    vlclose(villa);
+    return 1;
+  }
+  i = 0;
+  do {
+    kbuf = NULL;
+    vbuf = NULL;
+    if(!(kbuf = vlcurkey(villa, &ksiz)) || !(vbuf = vlcurval(villa, &vsiz))){
+      pdperror(name);
+      free(kbuf);
+      free(vbuf);
+      cblistclose(alist);
+      cblistclose(dlist);
+      vlclose(villa);
+      return 1;
+    }
+    cblistunshift(dlist, kbuf, ksiz);
+    free(kbuf);
+    free(vbuf);
+    i++;
+  } while(vlcurprev(villa));
+  if(i != 20){
+    fprintf(stderr, "%s: %s: invalid cursor\n", progname, name);
+    cblistclose(alist);
+    cblistclose(dlist);
+    vlclose(villa);
+    return 1;
+  }
+  if(dpecode != DP_ENOITEM){
+    pdperror(name);
+    cblistclose(alist);
+    cblistclose(dlist);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Matching result of ascending scan and desending scan  ... ");
+  for(i = 0; i < cblistnum(alist); i++){
+    ap = cblistval(alist, i, NULL);
+    dp = cblistval(dlist, i, NULL);
+    if(strcmp(ap, dp)){
+      fprintf(stderr, "%s: %s: not match\n", progname, name);
+      cblistclose(alist);
+      cblistclose(dlist);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  cblistsort(alist);
+  for(i = 0; i < cblistnum(alist); i++){
+    ap = cblistval(alist, i, NULL);
+    dp = cblistval(dlist, i, NULL);
+    if(strcmp(ap, dp)){
+      fprintf(stderr, "%s: %s: not match\n", progname, name);
+      cblistclose(alist);
+      cblistclose(dlist);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  cblistclose(alist);
+  cblistclose(dlist);
+  printfflush("Resetting tuning parameters with 41, 80, 32, 32 ... ");
+  vlsettuning(villa, 41, 80, 32, 32);
+  printfflush("ok\n");
+  printfflush("Adding 1000 random records with VL_DDUP ... ");
+  for(i = 0; i < 1000; i++){
+    len = sprintf(buf, "%08d", myrand() % 1000 + 1);
+    if(!vlput(villa, buf, len, buf, len, VL_DDUP)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Resetting tuning parameters with 8, 5, 16, 16 ... ");
+  vlsettuning(villa, 8, 5, 16, 16);
+  printfflush("ok\n");
+  printfflush("Adding 1000 random records with VL_DDUP ... ");
+  for(i = 0; i < 1000; i++){
+    len = sprintf(buf, "%08d", myrand() % 1000 + 1);
+    if(!vlput(villa, buf, len, buf, len, VL_DDUP)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Beginning the transaction ... ");
+  if(!vltranbegin(villa)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 100 random records with VL_DDUP ... ");
+  for(i = 0; i < 100; i++){
+    len = sprintf(buf, "%08d", myrand() % 1000 + 1);
+    if(!vlput(villa, buf, len, buf, len, VL_DDUP)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Scanning and checking ... ");
+  i = 0;
+  for(vlcurlast(villa); (kbuf = vlcurkey(villa, &ksiz)) != NULL; vlcurprev(villa)){
+    if(vlvnum(villa, kbuf, ksiz) < 1 || !(vbuf = vlcurval(villa, NULL))){
+      pdperror(name);
+      free(kbuf);
+      vlclose(villa);
+      return 1;
+    }
+    free(vbuf);
+    free(kbuf);
+    i++;
+  }
+  if(i != vlrnum(villa)){
+    fprintf(stderr, "%s: %s: invalid\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Committing the transaction ... ");
+  if(!vltrancommit(villa)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Scanning and checking ... ");
+  i = 0;
+  for(vlcurlast(villa); (kbuf = vlcurkey(villa, &ksiz)) != NULL; vlcurprev(villa)){
+    if(vlvnum(villa, kbuf, ksiz) < 1 || !(vbuf = vlcurval(villa, NULL))){
+      pdperror(name);
+      free(kbuf);
+      vlclose(villa);
+      return 1;
+    }
+    free(vbuf);
+    free(kbuf);
+    i++;
+  }
+  if(i != vlrnum(villa)){
+    fprintf(stderr, "%s: %s: invalid\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  lnum = vllnum(villa);
+  nnum = vlnnum(villa);
+  rnum = vlrnum(villa);
+  fsiz = vlfsiz(villa);
+  printfflush("Beginning the transaction ... ");
+  if(!vltranbegin(villa)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 100 random records with VL_DDUP ... ");
+  for(i = 0; i < 100; i++){
+    len = sprintf(buf, "%08d", myrand() % 1000 + 1);
+    if(!vlput(villa, buf, len, buf, len, VL_DDUP)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Aborting the transaction ... ");
+  if(!vltranabort(villa)){
+    pdperror(name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Checking rollback ... ");
+  if(vlfsiz(villa) != fsiz || vllnum(villa) != lnum ||
+     vlnnum(villa) != nnum || vlrnum(villa) != rnum){
+    fprintf(stderr, "%s: %s: invalid\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Scanning and checking ... ");
+  i = 0;
+  for(vlcurlast(villa); (kbuf = vlcurkey(villa, &ksiz)) != NULL; vlcurprev(villa)){
+    if(vlvnum(villa, kbuf, ksiz) < 1 || !(vbuf = vlcurval(villa, NULL))){
+      pdperror(name);
+      free(kbuf);
+      vlclose(villa);
+      return 1;
+    }
+    free(vbuf);
+    free(kbuf);
+    i++;
+  }
+  if(i != vlrnum(villa)){
+    fprintf(stderr, "%s: %s: invalid\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Closing the database ... ");
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Creating a database with VL_CMPLEX ... ");
+  omode = VL_OWRITER | VL_OCREAT | VL_OTRUNC | cmode;
+  if(!(villa = vlopen(name, omode, VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Setting tuning parameters with 5, 6, 16, 16 ... ");
+  vlsettuning(villa, 5, 6, 16, 16);
+  printfflush("ok\n");
+  printfflush("Adding 3 * 3 records with VL_DDUP ... ");
+  for(i = 0; i < 3; i++){
+    for(j = 0; j < 3; j++){
+      len = sprintf(buf, "%08d", j);
+      if(!vlput(villa, buf, len, buf, -1, VL_DDUP)){
+        pdperror(name);
+        vlclose(villa);
+        return 1;
+      }
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Inserting records with the cursor ... ");
+  if(!vlcurjump(villa, "00000001", -1, VL_JFORWARD) ||
+     !vlcurput(villa, "first", -1, VL_CPAFTER) || !vlcurput(villa, "second", -1, VL_CPAFTER) ||
+     !vlcurnext(villa) ||
+     !vlcurput(villa, "third", -1, VL_CPAFTER) ||
+     strcmp(vlcurvalcache(villa, NULL), "third") ||
+     !vlcurput(villa, "fourth", -1, VL_CPCURRENT) ||
+     strcmp(vlcurvalcache(villa, NULL), "fourth") ||
+     !vlcurjump(villa, "00000001", -1, VL_JFORWARD) ||
+     strcmp(vlcurvalcache(villa, NULL), "00000001") ||
+     !vlcurput(villa, "one", -1, VL_CPBEFORE) || !vlcurput(villa, "two", -1, VL_CPBEFORE) ||
+     !vlcurput(villa, "three", -1, VL_CPBEFORE) || !vlcurput(villa, "five", -1, VL_CPBEFORE) ||
+     !vlcurnext(villa) ||
+     !vlcurput(villa, "four", -1, VL_CPBEFORE) ||
+     strcmp(vlcurvalcache(villa, NULL), "four") ||
+     !vlcurjump(villa, "00000001*", -1, VL_JBACKWARD) ||
+     strcmp(vlcurvalcache(villa, NULL), "00000001") ||
+     !vlcurput(villa, "omega", -1, VL_CPAFTER) ||
+     strcmp(vlcurkeycache(villa, NULL), "00000001") ||
+     strcmp(vlcurvalcache(villa, NULL), "omega") ||
+     !vlcurjump(villa, "00000000*", -1, VL_JFORWARD) ||
+     !vlcurput(villa, "alpha", -1, VL_CPBEFORE) ||
+     strcmp(vlcurvalcache(villa, NULL), "alpha") ||
+     !vlcurprev(villa) ||
+     strcmp(vlcurkeycache(villa, NULL), "00000000") ||
+     strcmp(vlcurvalcache(villa, NULL), "00000000") ||
+     !vlcurput(villa, "before", -1, VL_CPAFTER) ||
+     strcmp(vlcurvalcache(villa, NULL), "before") ||
+     !vlcurjump(villa, "00000001*", -1, VL_JFORWARD) ||
+     !vlcurput(villa, "after", -1, VL_CPBEFORE) ||
+     strcmp(vlcurvalcache(villa, NULL), "after") ||
+     !vlcurfirst(villa) ||
+     strcmp(vlcurvalcache(villa, NULL), "00000000") ||
+     !vlcurput(villa, "top", -1, VL_CPBEFORE) ||
+     strcmp(vlcurvalcache(villa, NULL), "top") ||
+     !vlcurlast(villa) ||
+     !vlcurput(villa, "bottom", -1, VL_CPAFTER) ||
+     strcmp(vlcurvalcache(villa, NULL), "bottom")){
+    fprintf(stderr, "%s: %s: invalid\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Deleting records with the cursor ... ");
+  if(!vlcurjump(villa, "00000000*", -1, VL_JBACKWARD) ||
+     strcmp(vlcurvalcache(villa, NULL), "before") ||
+     !vlcurout(villa) ||
+     strcmp(vlcurvalcache(villa, NULL), "alpha") ||
+     !vlcurout(villa) ||
+     strcmp(vlcurvalcache(villa, NULL), "five") ||
+     !vlcurfirst(villa) || !vlcurnext(villa) ||
+     !vlcurout(villa) || !vlcurout(villa) || !vlcurout(villa) ||
+     strcmp(vlcurvalcache(villa, NULL), "five") ||
+     !vlcurprev(villa) ||
+     strcmp(vlcurvalcache(villa, NULL), "top") ||
+     !vlcurout(villa) ||
+     strcmp(vlcurvalcache(villa, NULL), "five") ||
+     !vlcurjump(villa, "00000002", -1, VL_JBACKWARD) ||
+     strcmp(vlcurvalcache(villa, NULL), "bottom") ||
+     !vlcurout(villa) ||
+     !vlcurjump(villa, "00000001", -1, VL_JBACKWARD) ||
+     !vlcurout(villa) ||
+     !vlcurout(villa) || !vlcurout(villa) || !vlcurout(villa) ||
+     strcmp(vlcurkeycache(villa, NULL), "00000002") ||
+     strcmp(vlcurvalcache(villa, NULL), "00000002") ||
+     !vlcurout(villa) || vlcurout(villa) ||
+     !vlcurfirst(villa) ||
+     strcmp(vlcurvalcache(villa, NULL), "five")){
+    fprintf(stderr, "%s: %s: invalid\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  vlcurfirst(villa);
+  while(vlcurout(villa)){
+    free(vlcurval(villa, NULL));
+  }
+  if(vlrnum(villa) != 0){
+    printf("%d\n", vlrnum(villa));
+    fprintf(stderr, "%s: %s: invalid\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  for(i = 0; i < 1000; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!vlput(villa, buf, len, buf, -1, VL_DKEEP)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  for(i = 200; i < 800; i++){
+    len = sprintf(buf, "%08d", i);
+    if(!vlout(villa, buf, len)){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  vlcurfirst(villa);
+  while(vlcurout(villa)){
+    free(vlcurval(villa, NULL));
+  }
+  if(vlrnum(villa) != 0){
+    printf("%d\n", vlrnum(villa));
+    fprintf(stderr, "%s: %s: invalid\n", progname, name);
+    vlclose(villa);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Adding 3 * 100 records with VL_DDUP ... ");
+  for(i = 1; i <= 100; i++){
+    len = sprintf(buf, "%08d", i);
+    for(j = 0; j < 3; j++){
+      if(!vlput(villa, buf, len, buf, len, VL_DDUP)){
+        pdperror(name);
+        vlclose(villa);
+        return 1;
+      }
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Closing the database ... ");
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Opening the database as a reader ... ");
+  if(!(villa = vlopen(name, VL_OREADER, VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("Opening multiple cursors ... ");
+  mulcurs = cbmalloc(sizeof(VLMULCUR *) * 8);
+  for(i = 0; i < 8; i++){
+    if(!(mulcurs[i] = vlmulcuropen(villa))){
+      pdperror(name);
+      vlclose(villa);
+      return 1;
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Scanning multiple cursors ... ");
+  for(i = 0; i < 8; i++){
+    if(i % 2 == 0){
+      vlmulcurfirst(mulcurs[i]);
+    } else {
+      vlmulcurlast(mulcurs[i]);
+    }
+  }
+  for(i = 0; i < 300; i++){
+    for(j = 0; j < 8; j++){
+      if(j % 2 == 0){
+        if(!(vbuf = vlmulcurkey(mulcurs[j], &vsiz))){
+          pdperror(name);
+          vlclose(villa);
+          return 1;
+        }
+        free(vbuf);
+        vlmulcurnext(mulcurs[j]);
+      } else {
+        if(!(vbuf = vlmulcurval(mulcurs[j], &vsiz))){
+          pdperror(name);
+          vlclose(villa);
+          return 1;
+        }
+        free(vbuf);
+        vlmulcurprev(mulcurs[j]);
+      }
+    }
+  }
+  printfflush("ok\n");
+  printfflush("Closing multiple cursors ... ");
+  for(i = 0; i < 8; i++){
+    vlmulcurclose(mulcurs[i]);
+  }
+  free(mulcurs);
+  printfflush("ok\n");
+  printfflush("Closing the database ... ");
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  printfflush("ok\n");
+  printfflush("all ok\n\n");
+  return 0;
+}
+
+
+/* perform wicked command */
+int dowicked(const char *name, int rnum, int cb, int cmode){
+  VILLA *villa;
+  CBMAP *map;
+  int i, j, omode, len, err, ksiz, vsiz, tran, mksiz, mvsiz, rsiz;
+  const char *mkbuf, *mvbuf;
+  char buf[32], *kbuf, *vbuf;
+  CBLIST *list;
+  printfflush("<Wicked Writing Test>\n  name=%s  rnum=%d\n\n", name, rnum);
+  omode = VL_OWRITER | VL_OCREAT | VL_OTRUNC | cmode;
+  if(!(villa = vlopen(name, omode, VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  err = FALSE;
+  tran = FALSE;
+  vlsettuning(villa, 5, 10, 64, 64);
+  map = NULL;
+  if(cb) map = cbmapopen();
+  for(i = 1; i <= rnum; i++){
+    len = sprintf(buf, "%08d", myrand() % rnum + 1);
+    switch(cb ? (myrand() % 5) : myrand() % 16){
+    case 0:
+      putchar('O');
+      if(!vlput(villa, buf, len, buf, len, VL_DOVER)) err = TRUE;
+      if(map) cbmapput(map, buf, len, buf, len, TRUE);
+      break;
+    case 1:
+      putchar('K');
+      if(!vlput(villa, buf, len, buf, len, VL_DKEEP) && dpecode != DP_EKEEP) err = TRUE;
+      if(map) cbmapput(map, buf, len, buf, len, FALSE);
+      break;
+    case 2:
+      putchar('C');
+      if(!vlput(villa, buf, len, buf, len, VL_DCAT)) err = TRUE;
+      if(map) cbmapputcat(map, buf, len, buf, len);
+      break;
+    case 3:
+      putchar('D');
+      if(!vlout(villa, buf, len) && dpecode != DP_ENOITEM) err = TRUE;
+      if(map) cbmapout(map, buf, len);
+      break;
+    case 4:
+      putchar('G');
+      if((vbuf = vlget(villa, buf, len, NULL)) != NULL){
+        free(vbuf);
+      } else if(dpecode != DP_ENOITEM){
+        err = TRUE;
+      }
+      break;
+    case 5:
+      putchar('V');
+      if(vlvsiz(villa, buf, len) < 0 && dpecode != DP_ENOITEM) err = TRUE;
+      if(!vlvnum(villa, buf, len) && dpecode != DP_ENOITEM) err = TRUE;
+      break;
+    case 6:
+      putchar('X');
+      list = cblistopen();
+      cblistpush(list, buf, len);
+      cblistpush(list, buf, len);
+      if(!vlputlist(villa, buf, len, list)) err = TRUE;
+      cblistclose(list);
+      break;
+    case 7:
+      putchar('Y');
+      if(!vloutlist(villa, buf, len) && dpecode != DP_ENOITEM) err = TRUE;
+      break;
+    case 8:
+      putchar('Z');
+      if((list = vlgetlist(villa, buf, len)) != NULL){
+        cblistclose(list);
+      } else if(dpecode != DP_ENOITEM){
+        err = TRUE;
+      }
+      if((vbuf = vlgetcat(villa, buf, len, NULL)) != NULL){
+        free(vbuf);
+      } else if(dpecode != DP_ENOITEM){
+        err = TRUE;
+      }
+      break;
+    case 9:
+      putchar('Q');
+      if(vlcurjump(villa, buf, len, VL_JFORWARD)){
+        for(j = 0; j < 3 && (kbuf = vlcurkey(villa, &ksiz)); j++){
+          if(VL_CMPLEX(buf, len, kbuf, ksiz) > 0) err = TRUE;
+          if(strcmp(vlcurkeycache(villa, NULL), kbuf)) err = TRUE;
+          if((vbuf = vlcurval(villa, &vsiz)) != NULL){
+            if(strcmp(vlcurvalcache(villa, NULL), vbuf)) err = TRUE;
+            free(vbuf);
+          } else {
+            err = TRUE;
+          }
+          free(kbuf);
+          if(!vlcurnext(villa) && dpecode != DP_ENOITEM) err = TRUE;
+        }
+      } else {
+        if(dpecode != DP_ENOITEM) err = TRUE;
+      }
+      break;
+    case 10:
+      putchar('W');
+      if(vlcurjump(villa, buf, len, VL_JBACKWARD)){
+        for(j = 0; j < 3 && (kbuf = vlcurkey(villa, &ksiz)); j++){
+          if(VL_CMPLEX(buf, len, kbuf, ksiz) < 0) err = TRUE;
+          if(strcmp(vlcurkeycache(villa, NULL), kbuf)) err = TRUE;
+          if((vbuf = vlcurval(villa, &vsiz)) != NULL){
+            if(strcmp(vlcurvalcache(villa, NULL), vbuf)) err = TRUE;
+            free(vbuf);
+          } else {
+            err = TRUE;
+          }
+          free(kbuf);
+          if(!vlcurprev(villa) && dpecode != DP_ENOITEM) err = TRUE;
+        }
+      } else {
+        if(dpecode != DP_ENOITEM) err = TRUE;
+      }
+      break;
+    case 11:
+      putchar('L');
+      if(myrand() % 3 == 0 &&
+         !vlcurjump(villa, buf, len, i % 3 == 0 ? VL_JFORWARD : VL_JBACKWARD) &&
+         dpecode != DP_ENOITEM) err = TRUE;
+      for(j = myrand() % 5; j >= 0; j--){
+        switch(myrand() % 6){
+        case 0:
+          if(!vlcurput(villa, buf, len, VL_CPAFTER) && dpecode != DP_ENOITEM) err = TRUE;
+          break;
+        case 1:
+          if(!vlcurput(villa, buf, len, VL_CPBEFORE) && dpecode != DP_ENOITEM) err = TRUE;
+          break;
+        case 2:
+          if(!vlcurput(villa, buf, len, VL_CPCURRENT) && dpecode != DP_ENOITEM) err = TRUE;
+          break;
+        default:
+          if(!vlcurout(villa)){
+            if(dpecode != DP_ENOITEM) err = TRUE;
+            break;
+          }
+          break;
+        }
+      }
+      break;
+    case 12:
+      if(tran ? myrand() % 32 != 0 : myrand() % 1024 != 0){
+        putchar('N');
+        break;
+      }
+      putchar('T');
+      if(tran){
+        if(myrand() % 5 == 0){
+          if(!vltranabort(villa)) err = TRUE;
+        } else {
+          if(!vltrancommit(villa)) err = TRUE;
+        }
+        tran = FALSE;
+      } else {
+        if(!vltranbegin(villa)) err = TRUE;
+        tran = TRUE;
+      }
+      break;
+    default:
+      putchar('P');
+      if(!vlput(villa, buf, len, buf, len, myrand() % 3 == 0 ? VL_DDUPR : VL_DDUP)) err = TRUE;
+      break;
+    }
+    if(i % 50 == 0) printfflush(" (%08d)\n", i);
+    if(err){
+      pdperror(name);
+      break;
+    }
+  }
+  if(tran){
+    if(!vltranabort(villa)) err = TRUE;
+  }
+  if(!vloptimize(villa)){
+    pdperror(name);
+    err = TRUE;
+  }
+  if((rnum = vlrnum(villa)) == -1){
+    pdperror(name);
+    err = TRUE;
+  }
+  if(!vlcurfirst(villa)){
+    pdperror(name);
+    err = TRUE;
+  }
+  i = 0;
+  do {
+    kbuf = NULL;
+    vbuf = NULL;
+    if(!(kbuf = vlcurkey(villa, &ksiz)) || !(vbuf = vlcurval(villa, &vsiz)) ||
+       ksiz != 8 || vsiz % 8 != 0 || vlvnum(villa, kbuf, ksiz) < 1){
+      pdperror(name);
+      free(kbuf);
+      free(vbuf);
+      err = TRUE;
+      break;
+    }
+    free(kbuf);
+    free(vbuf);
+    i++;
+  } while(vlcurnext(villa));
+  if(i != rnum){
+    fprintf(stderr, "%s: %s: invalid cursor\n", progname, name);
+    err = TRUE;
+  }
+  if(dpecode != DP_ENOITEM){
+    pdperror(name);
+    err = TRUE;
+  }
+  if(!vlcurlast(villa)){
+    pdperror(name);
+    err = TRUE;
+  }
+  i = 0;
+  do {
+    kbuf = NULL;
+    vbuf = NULL;
+    if(!(kbuf = vlcurkey(villa, &ksiz)) || !(vbuf = vlcurval(villa, &vsiz)) ||
+       ksiz != 8 || vsiz % 8 != 0 || vlvnum(villa, kbuf, ksiz) < 1){
+      pdperror(name);
+      free(kbuf);
+      free(vbuf);
+      err = TRUE;
+      break;
+    }
+    free(kbuf);
+    free(vbuf);
+    i++;
+  } while(vlcurprev(villa));
+  if(i != rnum){
+    fprintf(stderr, "%s: %s: invalid cursor\n", progname, name);
+    err = TRUE;
+  }
+  if(dpecode != DP_ENOITEM){
+    pdperror(name);
+    err = TRUE;
+  }
+  if(map){
+    printfflush("Matching records ... ");
+    cbmapiterinit(map);
+    while((mkbuf = cbmapiternext(map, &mksiz)) != NULL){
+      mvbuf = cbmapget(map, mkbuf, mksiz, &mvsiz);
+      if(!(vbuf = vlget(villa, mkbuf, mksiz, &rsiz))){
+        pdperror(name);
+        err = TRUE;
+        break;
+      }
+      if(rsiz != mvsiz || memcmp(vbuf, mvbuf, rsiz)){
+        fprintf(stderr, "%s: %s: unmatched record\n", progname, name);
+        free(vbuf);
+        err = TRUE;
+        break;
+      }
+      free(vbuf);
+    }
+    cbmapclose(map);
+    if(!err) printfflush("ok\n");
+  }
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  if(!err) printfflush("ok\n\n");
+  return err ? 1 : 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/vltest.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: box/trunk/qdbm/vltsv.c
===================================================================
--- box/trunk/qdbm/vltsv.c	                        (rev 0)
+++ box/trunk/qdbm/vltsv.c	2010-01-09 16:15:40 UTC (rev 2590)
@@ -0,0 +1,261 @@
+/*************************************************************************************************
+ * Mutual converter between a database of Villa and a TSV text
+ *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
+ * This file is part of QDBM, Quick Database Manager.
+ * QDBM 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.  QDBM 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 QDBM; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <depot.h>
+#include <cabin.h>
+#include <villa.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef TRUE
+#define TRUE           1                 /* boolean true */
+#undef FALSE
+#define FALSE          0                 /* boolean false */
+
+
+/* for RISC OS */
+#if defined(__riscos__) || defined(__riscos)
+#include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+
+/* global variables */
+const char *progname;                    /* program name */
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+void usage(void);
+int runimport(int argc, char **argv);
+int runexport(int argc, char **argv);
+void pdperror(const char *name);
+char *getl(void);
+int doimport(const char *name, int bin);
+int doexport(const char *name, int bin);
+
+
+/* main routine */
+int main(int argc, char **argv){
+  int rv;
+  cbstdiobin();
+  progname = argv[0];
+  if(argc < 2) usage();
+  rv = 0;
+  if(!strcmp(argv[1], "import")){
+    rv = runimport(argc, argv);
+  } else if(!strcmp(argv[1], "export")){
+    rv = runexport(argc, argv);
+  } else {
+    usage();
+  }
+  return rv;
+}
+
+
+/* print the usage and exit */
+void usage(void){
+  fprintf(stderr, "%s: mutual converter between TSV and Villa database\n", progname);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "usage:\n");
+  fprintf(stderr, "  %s import [-bin] name\n", progname);
+  fprintf(stderr, "  %s export [-bin] name\n", progname);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+
+/* parse arguments of import command */
+int runimport(int argc, char **argv){
+  char *name;
+  int i, bin, rv;
+  name = NULL;
+  bin = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-bin")){
+        bin = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doimport(name, bin);
+  return rv;
+}
+
+
+/* parse arguments of export command */
+int runexport(int argc, char **argv){
+  char *name;
+  int i, bin, rv;
+  name = NULL;
+  bin = FALSE;
+  for(i = 2; i < argc; i++){
+    if(!name && argv[i][0] == '-'){
+      if(!strcmp(argv[i], "-bin")){
+        bin = TRUE;
+      } else {
+        usage();
+      }
+    } else if(!name){
+      name = argv[i];
+    } else {
+      usage();
+    }
+  }
+  if(!name) usage();
+  rv = doexport(name, bin);
+  return rv;
+}
+
+
+/* print an error message */
+void pdperror(const char *name){
+  fprintf(stderr, "%s: %s: %s\n", progname, name, dperrmsg(dpecode));
+}
+
+
+/* read a line */
+char *getl(void){
+  char *buf;
+  int c, len, blen;
+  buf = NULL;
+  len = 0;
+  blen = 256;
+  while((c = getchar()) != EOF){
+    if(blen <= len) blen *= 2;
+    buf = cbrealloc(buf, blen + 1);
+    if(c == '\n') c = '\0';
+    buf[len++] = c;
+    if(c == '\0') break;
+  }
+  if(!buf) return NULL;
+  buf[len] = '\0';
+  return buf;
+}
+
+
+/* perform import command */
+int doimport(const char *name, int bin){
+  VILLA *villa;
+  char *buf, *kbuf, *vbuf, *ktmp, *vtmp;
+  int i, err, ktsiz, vtsiz;
+  /* open a database */
+  if(!(villa = vlopen(name, VL_OWRITER | VL_OCREAT, VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  /* loop for each line */
+  err = FALSE;
+  for(i = 1; (buf = getl()) != NULL; i++){
+    kbuf = buf;
+    if((vbuf = strchr(buf, '\t')) != NULL){
+      *vbuf = '\0';
+      vbuf++;
+      /* store a record */
+      if(bin){
+        ktmp = cbbasedecode(kbuf, &ktsiz);
+        vtmp = cbbasedecode(vbuf, &vtsiz);
+        if(!vlput(villa, ktmp, ktsiz, vtmp, vtsiz, VL_DDUP)){
+          pdperror(name);
+          err = TRUE;
+        }
+        free(vtmp);
+        free(ktmp);
+      } else {
+        if(!vlput(villa, kbuf, -1, vbuf, -1, VL_DDUP)){
+          pdperror(name);
+          err = TRUE;
+          break;
+        }
+      }
+    } else {
+      fprintf(stderr, "%s: %s: invalid format in line %d\n", progname, name, i);
+    }
+    free(buf);
+    if(err) break;
+  }
+  /* close the database */
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return err ? 1 : 0;
+}
+
+
+/* perform export command */
+int doexport(const char *name, int bin){
+  VILLA *villa;
+  char *kbuf, *vbuf, *tmp;
+  int err, ksiz, vsiz;
+  /* open a database */
+  if(!(villa = vlopen(name, VL_OREADER, VL_CMPLEX))){
+    pdperror(name);
+    return 1;
+  }
+  /* initialize the cursor */
+  vlcurfirst(villa);
+  /* loop for each key */
+  err = FALSE;
+  while((kbuf = vlcurkey(villa, &ksiz)) != NULL){
+    /* retrieve a value with a key */
+    if(!(vbuf = vlcurval(villa, &vsiz))){
+      pdperror(name);
+      free(kbuf);
+      err = TRUE;
+      break;
+    }
+    /* output data */
+    if(bin){
+      tmp = cbbaseencode(kbuf, ksiz);
+      printf("%s\t", tmp);
+      free(tmp);
+      tmp = cbbaseencode(vbuf, vsiz);
+      printf("%s\n", tmp);
+      free(tmp);
+    } else {
+      printf("%s\t%s\n", kbuf, vbuf);
+    }
+    /* free resources */
+    free(vbuf);
+    free(kbuf);
+    /* step the cursor */
+    vlcurnext(villa);
+  }
+  /* check whether all records were retrieved */
+  if(dpecode != DP_ENOITEM){
+    pdperror(name);
+    err = TRUE;
+  }
+  /* close the database */
+  if(!vlclose(villa)){
+    pdperror(name);
+    return 1;
+  }
+  return 0;
+}
+
+
+
+/* END OF FILE */


Property changes on: box/trunk/qdbm/vltsv.c
___________________________________________________________________
Added: svn:eol-style
   + native




More information about the Boxbackup-commit mailing list