Index: /releases/0.12.0/INSTALL
===================================================================
--- /releases/0.12.0/INSTALL	(revision 175)
+++ /releases/0.12.0/INSTALL	(revision 175)
@@ -0,0 +1,54 @@
+RegLookup Installation
+======================
+
+Prerequisites
+-------------
+
+This package doesn't require much for installation.  Just what typically
+comes with any free operating system.  Be sure you have:
+
+ - GNU Make
+ - GCC
+
+Note that iconv support is required, as specified in IEEE Std 1003.1 
+(POSIX.1-2001).  Some platforms still do not contain support for this 
+natively, in which case you may need to install libiconv from:
+  http://www.gnu.org/software/libiconv/
+
+
+Survival Commands
+-----------------
+
+make
+# and as root
+make install
+
+If GNU Make isn't your default, you may need to run `gmake' instead.
+
+
+Advanced Installation
+---------------------
+To install in a custom directory, simply change one or more of the
+following make variables to suit your needs:
+
+ PREFIX      Top level install directory.  Used as base for rest unless
+             they too are overridden. (This defaults to /usr/local)
+
+ BIN_PREFIX  Location for executable programs to be installed.
+
+ DOC_PREFIX  Location for documentation.
+
+ MAN_PREFIX  A path in the MANPATH.
+
+
+Use the following syntax (with GNU Make) to override a variable:
+
+# as root
+make VAR=value install
+
+
+For more information, see the Makefiles.
+
+Primitive build features have been added to support cross-compiling to
+Windows binaries using MinGW.  For more information on this, see: 
+  doc/mingw-build.txt
Index: /releases/0.12.0/LICENSE
===================================================================
--- /releases/0.12.0/LICENSE	(revision 175)
+++ /releases/0.12.0/LICENSE	(revision 175)
@@ -0,0 +1,676 @@
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, 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
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+ 
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+  
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state 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 program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
Index: /releases/0.12.0/Makefile
===================================================================
--- /releases/0.12.0/Makefile	(revision 175)
+++ /releases/0.12.0/Makefile	(revision 175)
@@ -0,0 +1,76 @@
+# $Id$
+
+# Installation prefixes.  Change to install elsewhere.
+
+PREFIX=/usr/local
+BIN_PREFIX=$(PREFIX)/bin
+DOC_PREFIX=$(PREFIX)/share/doc/reglookup
+MAN_PREFIX=$(PREFIX)/man
+
+################################################################################
+
+CC=gcc
+OPTS=-std=gnu99 -pedantic -Wall -ggdb
+#OPTS=-std=gnu99 -pedantic -Wall
+INC:=-I$(PWD)/include -I/usr/local/include 
+LIB=-L/usr/local/lib -lm
+BIN_EXT=
+EXTRA_OBJ=
+
+UNAME := $(shell uname)
+ifneq ($(UNAME),Linux) 	
+  LIB:=$(LIB) -liconv
+endif
+
+
+################################################################################
+# MinGW cross-compiling build settings
+ifdef BUILD_MINGW
+
+## These may need to be changed
+CC=i586-mingw32msvc-cc
+LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib
+
+## These probably do not need to be changed
+BIN_EXT=.exe
+INC:=$(INC) -I$(LIBICONV_PATH)/include
+EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a
+
+endif
+################################################################################
+
+
+BUILD=$(CURDIR)/build
+BUILD_BIN=$(BUILD)/bin
+BUILD_DOC=$(BUILD)/doc
+
+BUILD_TREE=$(BUILD_BIN) $(BUILD_DOC)
+SUB_DIRS=lib src doc bin
+
+FILES=$(REGLOOKUP)
+.PHONY: $(SUB_DIRS) clean
+export
+
+
+all: $(BUILD_TREE) $(SUB_DIRS)
+
+#XXX: This should be more generalized.
+install: all
+	mkdir -p $(BIN_PREFIX)
+	mkdir -p $(DOC_PREFIX)
+	mkdir -p $(MAN_PREFIX)/man1
+	$(MAKE) -C bin install
+	$(MAKE) -C src install
+	$(MAKE) -C doc install
+
+
+$(SUB_DIRS):
+	$(MAKE) -C $@
+
+$(BUILD_TREE):
+	mkdir -p $@
+
+clean:
+	$(MAKE) -C src clean
+	$(MAKE) -C lib clean
+	rm -rf $(BUILD)/*
Index: /releases/0.12.0/bin/Makefile
===================================================================
--- /releases/0.12.0/bin/Makefile	(revision 175)
+++ /releases/0.12.0/bin/Makefile	(revision 175)
@@ -0,0 +1,17 @@
+# $Id$
+
+################################################################################
+
+TIMELINE=$(BUILD_BIN)/reglookup-timeline
+FILES=$(TIMELINE)
+
+all: $(FILES)
+
+$(TIMELINE):
+	cp reglookup-timeline $@
+
+install:
+	install -m 0755 $(FILES) $(BIN_PREFIX)
+
+clean:
+	rm -f *~
Index: /releases/0.12.0/bin/reglookup-timeline
===================================================================
--- /releases/0.12.0/bin/reglookup-timeline	(revision 175)
+++ /releases/0.12.0/bin/reglookup-timeline	(revision 175)
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# This script is a wrapper for reglookup, and reads one or more registry
+# files to produce an MTIME sorted output.  This is helpful when building
+# timelines for investigations.
+#
+# Copyright (C) 2005-2007,2010 Timothy D. Morgan
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+# 
+# This program 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 General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+#
+# $Id$
+
+
+usage()
+{
+  echo "Usage: $0 [-H] [-V] <REGISTRY_FILE> [<REGISTRY_FILE> ...]" 1>&2
+  echo "   -H  Omit header line" 1>&2
+  echo "   -V  Include values with parent timestamps" 1>&2
+}
+
+if [ $# -eq 0 ]; then
+  usage
+  echo "ERROR: requires at least one parameter" 1>&2
+  exit 1
+fi
+
+PRINT_HEADER=true
+if [ "$1" = "-H" ]; then
+  PRINT_HEADER=false
+  shift
+fi
+
+OPTS='-t KEY'
+if [ "$1" = "-V" ]; then
+  OPTS='-i'
+  shift
+fi
+
+if [ "$PRINT_HEADER" = "true" ]; then
+  echo "MTIME,FILE,PATH"
+fi
+
+for F in $@; do
+  reglookup $OPTS -H "$F" | awk -F',' '{ printf "%s,'"$F"',%s\n",$4,$1; }'
+done | sort
Index: /releases/0.12.0/doc/Makefile
===================================================================
--- /releases/0.12.0/doc/Makefile	(revision 175)
+++ /releases/0.12.0/doc/Makefile	(revision 175)
@@ -0,0 +1,35 @@
+# $Id$
+
+BUILD_FILES=$(BUILD_DOC)/man/man1/reglookup.1.gz\
+	$(BUILD_DOC)/man/man1/reglookup-timeline.1.gz\
+	$(BUILD_DOC)/man/man1/reglookup-recover.1.gz
+
+default: $(BUILD_FILES)
+
+$(BUILD_DOC)/man/man1:
+	mkdir -p $(BUILD_DOC)/man/man1
+
+$(BUILD_DOC)/man/man1/reglookup.1.gz: man/man1/reglookup.1.gz $(BUILD_DOC)/man/man1
+	cp man/man1/reglookup.1.gz $@
+
+$(BUILD_DOC)/man/man1/reglookup-timeline.1.gz: man/man1/reglookup-timeline.1.gz $(BUILD_DOC)/man/man1
+	cp man/man1/reglookup-timeline.1.gz $@
+
+$(BUILD_DOC)/man/man1/reglookup-recover.1.gz: man/man1/reglookup-recover.1.gz $(BUILD_DOC)/man/man1
+	cp man/man1/reglookup-recover.1.gz $@
+
+install:
+	cp -r $(BUILD_DOC)/* $(DOC_PREFIX)
+	ln -sf $(DOC_PREFIX)/man/man1/*  $(MAN_PREFIX)/man1
+
+
+#XXX: Used during release only
+release:
+	#XXX: The sed filter is a temporary hack to work around a bug in docbook2x-man
+	#     See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=516165
+	docbook2x-man --to-stdout reglookup.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup.1
+	docbook2x-man --to-stdout reglookup-timeline.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-timeline.1
+	docbook2x-man --to-stdout reglookup-recover.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-recover.1
+	cd man/man1 && gzip -9 -f reglookup.1
+	cd man/man1 && gzip -9 -f reglookup-timeline.1
+	cd man/man1 && gzip -9 -f reglookup-recover.1
Index: /releases/0.12.0/doc/devel/README
===================================================================
--- /releases/0.12.0/doc/devel/README	(revision 175)
+++ /releases/0.12.0/doc/devel/README	(revision 175)
@@ -0,0 +1,4 @@
+Developer's documentation previously stored here has been removed from the
+main software distribution trunk.  It is still available in the Subversion
+repository, however, and may be obtained here:
+  http://code.google.com/p/reglookup/source/browse/
Index: /releases/0.12.0/doc/mingw-build.txt
===================================================================
--- /releases/0.12.0/doc/mingw-build.txt	(revision 175)
+++ /releases/0.12.0/doc/mingw-build.txt	(revision 175)
@@ -0,0 +1,80 @@
+Cross-compiling RegLookup to Windows with MinGW
+===============================================
+
+MinGW can be used to compile Windows binaries from UNIX environments.
+The following instructions outline the steps required to build
+reglookup.exe and reglookup-recover.exe.  This process is experimental
+and Windows binaries have not been well tested.  You have been warned.
+
+Prerequisites
+-------------
+
+- Before you start, ensure you have MinGW installed.  Under Debian,
+  install the `mingw32' package.
+
+- Download pre-compiled libiconv packages from here:
+   http://gnuwin32.sourceforge.net/packages/libiconv.htm
+
+  You will need to download the "Binaries" and "Developer files"
+  packages, whose files are named libiconv-VERSION-bin.zip and
+  libiconv-VERSION-lib.zip respectively.
+
+- Unpack both zip files into a designated top-level directory.
+  Suggested commands:
+  $ mkdir /usr/local/src/libiconv-VERSION-bin /usr/local/src/libiconv-VERSION-lib
+  $ cd /usr/local/src/libiconv-VERSION-bin
+  $ unzip .../path/to/libiconv-VERSION-bin.zip
+  $ cd /usr/local/src/libiconv-VERSION-lib
+  $ unzip .../path/to/libiconv-VERSION-lib.zip
+  
+
+Building
+--------
+
+Review the top level RegLookup Makefile to ensure the settings match
+your environment.  Find the conditional block which looks like:
+
+################################################################################
+# MinGW cross-compiling build settings
+ifdef BUILD_MINGW
+
+## These may need to be changed
+CC=i586-mingw32msvc-cc
+LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib
+
+## These probably do not need to be changed
+BIN_EXT=.exe
+INC:=$(INC) -I$(LIBICONV_PATH)/include
+EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a
+
+endif
+################################################################################
+
+
+If either the CC or LIBICONV_PATH settings are incorrect for your
+system, either update the Makefile, or override these options at build
+time when you run make.  For instance, the above settings in the
+Makefile are correct, you can execute the build by running:
+
+$ make BUILD_MINGW=1
+
+Alternatively, you may override the variables above with:
+
+$ make BUILD_MINGW=1 CC=my-mingw-binary LIBICONV_PATH=.../path/to/libiconv-VERSION-lib
+
+Once the build is complete, you'll find the .exe files under the
+build/bin directory.
+
+
+Installation
+------------
+Naturally, there is no install Makefile target for the MinGW build
+process, since we aren't installing on the local system.  To install
+these binaries on a Windows machine, simply copy over the reglookup.exe
+and reglookup-recover.exe files from the build/bin directory to the
+desired host.  In addition, you will need to install the libiconv2.dll
+file on that host (either in the same directory as the reglookup
+executables, or somewhere in the DLL search path).  This file is
+available in the libiconv-VERSION-bin.zip file you downloaded earlier,
+under the `bin' subdirectory.
+
Index: /releases/0.12.0/doc/reglookup-recover.1.docbook
===================================================================
--- /releases/0.12.0/doc/reglookup-recover.1.docbook	(revision 175)
+++ /releases/0.12.0/doc/reglookup-recover.1.docbook	(revision 175)
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id='reglookup-recover.1'>
+  <!--  $Id$ -->
+  <refmeta>
+    <refentrytitle>reglookup</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo class="sectdesc">File Conversion Utilities</refmiscinfo>
+  </refmeta>
+  <refnamediv id='name'>
+    <refname>reglookup-recover</refname>
+    <refpurpose>Windows NT+ registry deleted data recovery tool</refpurpose>
+  </refnamediv>
+
+  <refsect1 id='synopsis'>
+    <title>SYNOPSIS</title>
+    <para>
+      <command>
+	reglookup-recover [options] <replaceable>registry-file</replaceable>
+      </command> 
+    </para>
+  </refsect1>
+
+  <refsect1 id='description'>
+    <title>DESCRIPTION</title>
+    <para>
+        reglookup-recover attempts to scour a Windows registry hive for 
+        deleted data structures and outputs those found in a CSV-like format.
+    </para>
+  </refsect1>
+
+  <refsect1 id='options'>
+    <title>OPTIONS</title>
+    <para>
+      <command>reglookup-recover</command> accepts the following parameters:
+    </para>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-v</option>
+        </term>
+        <listitem>
+	  <para>
+	    Verbose output.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-h</option>
+        </term>
+        <listitem>
+	  <para>
+	    Enables the printing of a column header row. (default)
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-H</option>
+        </term>
+        <listitem>
+	  <para>
+	    Disables the printing of a column header row.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-l</option>
+        </term>
+        <listitem>
+	  <para>
+            Display cells which could not be interpreted as valid
+            registry structures at the end of the output.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-L</option>
+        </term>
+        <listitem>
+	  <para>
+            Do not display cells which could not be interpreted as valid
+            registry structures.  This is the default behavior.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-r</option>
+        </term>
+        <listitem>
+	  <para>
+            Display raw cell contents for cells which were interpreted as intact
+            data structures.  This additional output will appear on the same
+            line as the interpreted data.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-R</option>
+        </term>
+        <listitem>
+	  <para>
+            Do not display raw cell contents for cells which were interpreted
+            as intact data structures.  This is the default behavior.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option><replaceable>registry-file</replaceable></option>
+        </term>
+        <listitem>
+	  <para>
+	    Required argument.  Specifies the location of the
+	    registry file to read.  The system registry files should be
+	    found under:
+	    <command>%SystemRoot%/system32/config</command>.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id='output'>
+    <title>OUTPUT</title>
+    <para>
+      <!-- XXX: this should be a bit more formal -->
+      <command>reglookup-recover</command> generates a comma-separated values (CSV) 
+      like output and writes it to stdout. For more information on the syntax of 
+      the general format, see <command>reglookup(1)</command>.
+    </para>
+    <para>
+      This tool is new and the output format, particularly the included columns, 
+      may change in future revisions.  When this format stablizes, additional
+      documentation will be included here.
+    </para>
+  </refsect1>
+
+  <refsect1 id='examples'>
+    <title>EXAMPLES</title>
+    <para>
+      To dump the recoverable contents of a system registry hive:
+    </para>
+    <para>
+      <screen>
+	reglookup-recover /mnt/win/c/WINDOWS/system32/config/system
+      </screen>
+    </para>
+    <para>
+      Extract all available unallocated data, including unparsable unallocated
+      space and the raw data associated with parsed cells in a user-specific
+      registry:
+    </para>
+    <para>
+      <screen>
+	reglookup-recover -r -l '/mnt/win/c/Documents and Settings/user/NTUSER.DAT'
+      </screen>
+    </para>
+  </refsect1>
+
+  <refsect1 id='bugs'>
+    <title>BUGS</title>
+    <para>
+      This program has been smoke-tested against most current Windows target 
+      platforms, but a comprehensive test suite has not yet been developed.
+      (Please report results to the development mailing list if you encounter
+       any bugs.  Sample registry files and/or patches are greatly appreciated.)
+    </para>
+    <para>
+      This program is new as of RegLookup release 0.9.0 and should be considered 
+      unstable.
+    </para>
+    <para>
+      For more information on registry format details and the recovery 
+      algorithm, see:
+        http://sentinelchicken.com/research/registry_format/
+        http://sentinelchicken.com/research/registry_recovery/
+    </para>
+  </refsect1>
+
+  <refsect1 id='credits'>
+    <title>CREDITS</title>
+    <para>
+      This program was written by Timothy D. Morgan.
+    </para>
+  </refsect1>
+
+  <refsect1 id='license'>
+    <title>LICENSE</title>
+    <para>
+      Please see the file "LICENSE" included with this software
+      distribution.
+    </para>
+    <para>      
+      This program 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 General Public License version 3 for more details.
+    </para>
+  </refsect1>
+
+  <refsect1 id='seealso'>
+    <title>SEE ALSO</title>
+    <para>
+      reglookup-timeline(1) reglookup-recover(1)
+    </para>
+  </refsect1>
+</refentry>
Index: /releases/0.12.0/doc/reglookup-timeline.1.docbook
===================================================================
--- /releases/0.12.0/doc/reglookup-timeline.1.docbook	(revision 175)
+++ /releases/0.12.0/doc/reglookup-timeline.1.docbook	(revision 175)
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id='reglookup-timeline.1'>
+  <!--  $Id$ -->
+  <refmeta>
+    <refentrytitle>reglookup-timeline</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo class="sectdesc">File Conversion Utilities</refmiscinfo>
+  </refmeta>
+  <refnamediv id='name'>
+    <refname>reglookup-timeline</refname>
+    <refpurpose>Windows NT+ registry MTIME timeline generator</refpurpose>
+  </refnamediv>
+
+  <refsect1 id='synopsis'>
+    <title>SYNOPSIS</title>
+    <para>
+      <command>
+	reglookup-timeline [<replaceable>-H</replaceable>] 
+                            <replaceable>registry-file</replaceable>
+                           [<replaceable>registry-file</replaceable> ...]
+      </command>
+    </para>
+  </refsect1>
+
+  <refsect1 id='description'>
+    <title>DESCRIPTION</title>
+    <para>
+      This script is a wrapper for <command>reglookup(1)</command>, and reads
+      one or more registry
+      files to produce an MTIME-sorted output.  This is helpful when building
+      timelines for forensic investigations.
+    </para>
+  </refsect1>
+
+  <refsect1 id='parameters'>
+    <title>PARAMETERS</title>
+    <para>
+      <command>reglookup-timeline</command> accepts one or more registry file
+      names.  All of the provided registries will be parsed using 
+      <command>reglookup(1)</command>.  The <command>-H</command> option may be
+      used to omit the header line.
+    </para>
+  </refsect1>
+
+  <refsect1 id='output'>
+    <title>OUTPUT</title>
+    <para>
+      <command>reglookup-timeline</command> generates a comma-separated 
+      values (CSV) compatible format to stdout.  While the output of 
+      <command>reglookup-timeline</command> and <command>reglookup(1)</command>
+      differ in the columns returned, the base format is the same.  
+    </para>
+    <para>
+      Currently, <command>reglookup-timeline</command> returns three columns:
+      MTIME, FILE, and PATH.  Only rows representing registry keys are returned,
+      since MTIMEs are not stored for values.  The FILE column indicates which
+      registry file (provided as an argument) the key came from.  Finally, the
+      PATH field contains the full registry path to the key.  Records are 
+      returned sorted in ascending order based on the MTIME column.
+    </para>
+  </refsect1>
+
+  <refsect1 id='bugs'>
+    <title>BUGS</title>
+    <para>
+      This script is new, and as such it's interface may change significantly 
+      over the next few revisions.  In particular, additional command line 
+      options will likely be added, and the output of the script may be altered
+      in minor ways.
+    </para>
+    <para>
+      It is very difficult to find documentation on what precise operations 
+      cause the MTIMEs to be updated.  Basic experimentation indicates that 
+      a key's stamp is updated anytime an immediate sub-value or sub-key is 
+      created, renamed, deleted, or it's value is modified.  If this MTIME 
+      data is critical to an investigation, any conclusions should be 
+      validated through experimentation in a controlled lab environment.
+    </para>
+    <para>
+      This software should be considered unstable at this time.
+    </para>
+  </refsect1>
+
+  <refsect1 id='credits'>
+    <title>CREDITS</title>
+    <para>
+      This script was written by Timothy D. Morgan based on suggestions
+      from Uwe Danz.
+    </para>
+    <para>
+      Please see source code for a full list of copyrights.
+    </para>
+  </refsect1>
+
+  <refsect1 id='license'>
+    <title>LICENSE</title>
+    <para>
+      Please see the file "LICENSE" included with this software
+      distribution.
+    </para>
+    <para>
+      This program 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 General Public License version 3 for more details.
+    </para>
+  </refsect1>
+
+  <refsect1 id='seealso'>
+    <title>SEE ALSO</title>
+    <para>
+      reglookup(1) reglookup-recover(1)
+    </para>
+  </refsect1>
+</refentry>
Index: /releases/0.12.0/doc/reglookup.1.docbook
===================================================================
--- /releases/0.12.0/doc/reglookup.1.docbook	(revision 175)
+++ /releases/0.12.0/doc/reglookup.1.docbook	(revision 175)
@@ -0,0 +1,372 @@
+???<?xml version="1.0" encoding="UTF-8"?>
+<refentry id='reglookup.1'>
+  <!--  $Id$ -->
+  <refmeta>
+    <refentrytitle>reglookup</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo class="sectdesc">File Conversion Utilities</refmiscinfo>
+  </refmeta>
+  <refnamediv id='name'>
+    <refname>reglookup</refname>
+    <refpurpose>Windows NT+ registry reader/lookup tool</refpurpose>
+  </refnamediv>
+
+  <refsect1 id='synopsis'>
+    <title>SYNOPSIS</title>
+    <para>
+      <command>
+	reglookup [options] <replaceable>registry-file</replaceable>
+      </command> 
+    </para>
+  </refsect1>
+
+  <refsect1 id='description'>
+    <title>DESCRIPTION</title>
+    <para>
+        reglookup is designed to read windows registry elements and
+        print them out to stdout in a CSV-like format.  It has filtering
+        options to narrow the focus of the output.  This tool is
+        designed to work with on Windows NT-based registries.
+    </para>
+  </refsect1>
+
+  <refsect1 id='options'>
+    <title>OPTIONS</title>
+    <para>
+      <command>reglookup</command> accepts the following parameters:
+    </para>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-p <replaceable>prefix-filter</replaceable></option>
+        </term>
+        <listitem>
+          <para>
+	    Specify a path prefix filter.  Only keys/values under
+	    this registry path will be output.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-t <replaceable>type-filter</replaceable></option>
+        </term>
+        <listitem>
+          <para>
+	    Specify a type filter.  Only elements which match this
+	    registry data type will be printed.  Acceptable values
+	    are:
+            <command>
+	      NONE, SZ, EXPAND_SZ, BINARY, DWORD, DWORD_BE,
+	      LINK, MULTI_SZ, RSRC_LIST, RSRC_DESC, RSRC_REQ_LIST, QWORD
+	    </command>
+	    and
+	    <command>
+	      KEY
+	    </command>
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-h</option>
+        </term>
+        <listitem>
+	  <para>
+	    Enables the printing of a column header row. (default)
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+    
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-i</option>
+        </term>
+        <listitem>
+	  <para>
+	    Printed values inherit the timestamp of their parent key, which is
+            printed along with them.  Note that this timestamp is not
+            necessarily meaningful for any given value values because timestamps
+            are saved on keys only and you cannot tell which value has been
+            modified since a change to any value of a given key would update the
+            time stamp.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-H</option>
+        </term>
+        <listitem>
+	  <para>
+	    Disables the printing of a column header row.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-s</option>
+        </term>
+        <listitem>
+	  <para>
+	    Adds five additional columns to output containing 
+	    information from key security descriptors and rarely used
+            fields.  The columns are: owner, group, sacl, dacl, class.
+	    (This feature's output has not been extensively tested.)
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-S</option>
+        </term>
+        <listitem>
+	  <para>
+	    Disables the printing of security descriptor
+	    information. (default)
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-v</option>
+        </term>
+        <listitem>
+	  <para>
+	    Verbose output.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option><replaceable>registry-file</replaceable></option>
+        </term>
+        <listitem>
+	  <para>
+	    Required argument.  Specifies the location of the
+	    registry file to read.  The system registry files should be
+	    found under:
+	    <command>%SystemRoot%/system32/config</command>.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id='output'>
+    <title>OUTPUT</title>
+    <para>
+      <!-- XXX: this should be a bit more formal -->
+      <command>reglookup</command> generates comma-separated values (CSV) 
+      and writes them to stdout.  The format is designed to simplify parsing 
+      algorithms of other tools by quoting CSV special characters using a 
+      common hexadecimal format.  Specifically, special characters or non-ascii 
+      bytes are converted to "\xQQ" where QQ is the hexadecimal value for 
+      the byte.
+    </para>
+    <para>
+      The number of columns or fields in each line is fixed for a given run 
+      of the program, but may vary based on the command line options provided.
+      See the header line for information on which fields are available and 
+      what they contain.
+    </para>
+    <para>
+      Some fields in some lines may contain sub-fields which require additional
+      delimiters.  If these sub-delimiters occur in these sub-fields, they are 
+      also encoded in the same way as commas or other special characters are.  
+      Currently, the second, third, and fourth level delimiters are "|", ":", 
+      and " ", respectively.  These are particularly important to take note of 
+      when security attributes are printed.  Please note that these delimiters
+      may occur in fields that are not sub-delimited, and should not be 
+      interpreted as special.
+    </para>
+    <para>
+      Security attributes of registry keys have a complex structure which is 
+      outlined here.  Each key will generally have an associated ACL (Access 
+      Control List), which is made up of ACEs (Access Control Entries).  Each 
+      ACE is delimited by the secondary delimiter mentioned above, "|".  The 
+      fields within an ACE are delimited by the third-level delimiter, ":", 
+      and consist of a SID, the ACE type (ALLOW, DENY, etc), a list of access 
+      rights, and a list of flags.  The last two fields are delimited by the 
+      fourth-level delimiter " ".  These final lists are simply human-readable 
+      interpretations of bits.  The access rights abbreviations are listed 
+      below along with their Microsoft-assigned names:
+      <screen>
+      QRY_VAL		KEY_QUERY_VALUE
+      SET_VAL		KEY_SET_VALUE
+      CREATE_KEY	KEY_CREATE_SUB_KEY
+      ENUM_KEYS		KEY_ENUMERATE_SUB_KEYS
+      NOTIFY		KEY_NOTIFY
+      CREATE_LNK	KEY_CREATE_LINK
+      WOW64_64		KEY_WOW64_64KEY
+      WOW64_32		KEY_WOW64_32KEY
+      DELETE		DELETE
+      R_CONT		READ_CONTROL
+      W_DAC		WRITE_DAC
+      W_OWNER		WRITE_OWNER
+      SYNC		SYNCHRONIZE
+      SYS_SEC		ACCESS_SYSTEM_SECURITY
+      MAX_ALLWD		MAXIMUM_ALLOWED
+      GEN_A		GENERIC_ALL
+      GEN_X		GENERIC_EXECUTE
+      GEN_W		GENERIC_WRITE
+      GEN_R		GENERIC_READ
+      </screen>
+
+      And the meaning of each flag is:
+      <screen>
+      OI	Object Inherit
+      CI	Container Inherit
+      NP	Non-Propagate
+      IO	Inherit Only
+      IA	Inherited ACE
+      </screen>
+
+      Please see the following references for more information:
+      <screen>
+        http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
+        http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
+        http://msdn2.microsoft.com/en-us/library/aa772242.aspx
+        http://support.microsoft.com/kb/220167
+      </screen>
+    </para>
+    <para>
+      Note that some of the bits listed above have either not been allocated by 
+      Microsoft, or simply aren't documented.  If any bits are set in the above 
+      two fields that aren't recognized, a hexidecimal representation of all of 
+      these mystery bits will be included in the output.  For instance, if the 
+      lowest bit and third lowest bit were not recognized while being set, 
+      the number "0x5" would be included as an element in the list.
+    </para>
+    <para>
+      While the ACL/ACE output format is mostly stable at this point, minor 
+      changes may be introduced in future versions.
+    </para>
+  </refsect1>
+
+  <refsect1 id='examples'>
+    <title>EXAMPLES</title>
+    <para>
+      To read and print the contents of an entire system registry
+      file: 
+    </para>
+    <para>
+      <screen>
+	reglookup /mnt/win/c/WINNT/system32/config/system
+      </screen>
+    </para>
+    <para>
+      To limit the output to just those entries under the Services
+      key: 
+    </para>
+    <para>
+      <screen>
+	reglookup -p /ControlSet002/Services /mnt/win/c/WINNT/system32/config/system
+      </screen>
+    </para>
+    <para>
+      To limit the output to all registry values of type BINARY:
+    </para>
+    <para>
+      <screen>
+	reglookup -t BINARY /mnt/win/c/WINNT/system32/config/system
+      </screen>
+    </para>
+    <para>
+      And to limit the output to BINARY values under the Services key:
+    </para>
+    <para>
+      <screen>
+	reglookup -t BINARY -p /ControlSet002/Services /mnt/win/c/WINNT/system32/config/system
+      </screen>
+    </para>
+  </refsect1>
+
+  <refsect1 id='bugs'>
+    <title>BUGS</title>
+    <para>
+      This program has been smoke-tested against most current Windows target 
+      platforms, but a comprehensive test suite has not yet been developed.
+      (Please report results to the development mailing list if you encounter
+       any bugs.  Sample registry files and/or patches are greatly appreciated.)
+    </para>
+    <para>
+      The SID conversions haven't been carefully checked for accuracy.
+    </para>
+    <para>
+      The MTIME conversions appear correctly produce the stored UTC timestamp.
+      However, due to the periodicity of registry writes, and the complexity
+      of the conversion, a small amount of error (on the order of seconds) may
+      be possible.  The documentation available online from Microsoft on 
+      this field is very poor.
+    </para>
+    <para>
+      Backslashes are currently considered special characters, to make 
+      parsing easier for automated tools.  However, this causes paths 
+      to be difficult to read by mere mortals.
+    </para>
+    <para>
+      For more information on registry format details, see:
+        http://sentinelchicken.com/research/registry_format/
+    </para>
+  </refsect1>
+
+  <refsect1 id='credits'>
+    <title>CREDITS</title>
+    <para>
+      This program was initially based on editreg.c by 
+      Richard Sharpe.  It has since been rewritten to use a modified
+      version the regfio library written by Gerald Carter.  Heavy
+      modifications to the library and the original command line
+      interface have been done by Timothy D. Morgan.
+    </para>
+    <para>
+      Please see source code for a full list of copyrights.
+    </para>
+  </refsect1>
+
+  <refsect1 id='license'>
+    <title>LICENSE</title>
+    <para>
+      Please see the file "LICENSE" included with this software
+      distribution.
+    </para>
+    <para>      
+      This program 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 General Public License version 3 for more details.
+    </para>
+  </refsect1>
+
+  <refsect1 id='seealso'>
+    <title>SEE ALSO</title>
+    <para>
+      reglookup-timeline(1) reglookup-recover(1)
+    </para>
+  </refsect1>
+</refentry>
Index: /releases/0.12.0/include/byteorder.h
===================================================================
--- /releases/0.12.0/include/byteorder.h	(revision 175)
+++ /releases/0.12.0/include/byteorder.h	(revision 175)
@@ -0,0 +1,183 @@
+/*
+ * Branched from Samba project Subversion repository, version #2:
+ *  http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/byteorder.h
+ *
+ * Unix SMB/CIFS implementation.
+ * SMB Byte handling
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 1992-1998 Andrew Tridgell 
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#ifndef _BYTEORDER_H
+#define _BYTEORDER_H
+
+/**
+ * @file
+ *
+ * This file implements macros for machine independent short and 
+ * int manipulation
+
+@verbatim
+
+Here is a description of this file that I emailed to the samba list once:
+
+> I am confused about the way that byteorder.h works in Samba. I have
+> looked at it, and I would have thought that you might make a distinction
+> between LE and BE machines, but you only seem to distinguish between 386
+> and all other architectures.
+> 
+> Can you give me a clue?
+
+sure.
+
+The distinction between 386 and other architectures is only there as
+an optimisation. You can take it out completely and it will make no
+difference. The routines (macros) in byteorder.h are totally byteorder
+independent. The 386 optimsation just takes advantage of the fact that
+the x86 processors don't care about alignment, so we don't have to
+align ints on int boundaries etc. If there are other processors out
+there that aren't alignment sensitive then you could also define
+CAREFUL_ALIGNMENT=0 on those processors as well.
+
+Ok, now to the macros themselves. I'll take a simple example, say we
+want to extract a 2 byte integer from a SMB packet and put it into a
+type called uint16_t that is in the local machines byte order, and you
+want to do it with only the assumption that uint16_t is _at_least_ 16
+bits long (this last condition is very important for architectures
+that don't have any int types that are 2 bytes long)
+
+You do this:
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+
+then to extract a uint16_t value at offset 25 in a buffer you do this:
+
+char *buffer = foo_bar();
+uint16_t xx = SVAL(buffer,25);
+
+We are using the byteoder independence of the ANSI C bitshifts to do
+the work. A good optimising compiler should turn this into efficient
+code, especially if it happens to have the right byteorder :-)
+
+I know these macros can be made a bit tidier by removing some of the
+casts, but you need to look at byteorder.h as a whole to see the
+reasoning behind them. byteorder.h defines the following macros:
+
+SVAL(buf,pos) - extract a 2 byte SMB value
+IVAL(buf,pos) - extract a 4 byte SMB value
+SVALS(buf,pos) signed version of SVAL()
+IVALS(buf,pos) signed version of IVAL()
+
+SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
+SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
+SSVALS(buf,pos,val) - signed version of SSVAL()
+SIVALS(buf,pos,val) - signed version of SIVAL()
+
+RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
+RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
+RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
+RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
+RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
+RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
+RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
+
+it also defines lots of intermediate macros, just ignore those :-)
+
+@endverbatim
+
+*/
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right" 
+   byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
+#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */
+#define PVAL(buf,pos) (CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
+
+
+#if CAREFUL_ALIGNMENT
+
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
+#define SVALS(buf,pos) ((int16_t)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16_t)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
+
+#else /* CAREFUL_ALIGNMENT */
+
+/* this handles things for architectures like the 386 that can handle
+   alignment errors */
+/*
+   WARNING: This section is dependent on the length of int16_t and int32_t
+   being correct 
+*/
+
+/* get single value from an SMB buffer */
+#define SVAL(buf,pos) (*(const uint16_t *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32_t *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVALS(buf,pos) (*(const int16_t *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32_t *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+
+/* store single value in an SMB buffer */
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16_t)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32_t)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16_t)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32_t)(val))
+
+#endif /* CAREFUL_ALIGNMENT */
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
+#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
+
+/* Alignment macros. */
+#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
+#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
+
+#endif /* _BYTEORDER_H */
Index: /releases/0.12.0/include/lru_cache.h
===================================================================
--- /releases/0.12.0/include/lru_cache.h	(revision 175)
+++ /releases/0.12.0/include/lru_cache.h	(revision 175)
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/**
+ * @file
+ *
+ * A data structure which approximates a least recently used (LRU) cache.
+ * Implemented as a basic randomized hash table.
+ */
+
+
+#ifndef LRU_CACHE_H
+#define LRU_CACHE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "talloc.h"
+
+struct lru_cache_element;
+typedef struct lru_cache_element lru_cache_element; 
+
+struct lru_cache_element
+{
+  void* index;
+  uint32_t index_len;
+  void* data;
+  lru_cache_element* next;
+  lru_cache_element* older;
+  lru_cache_element* newer;
+};
+
+
+/** XXX: document this. */
+typedef struct _lru_cache
+{
+  uint32_t secret;
+  uint32_t num_keys;
+  uint32_t num_buckets;
+  uint32_t max_keys;
+  lru_cache_element* oldest;
+  lru_cache_element* newest;
+  lru_cache_element** table;
+  bool talloc_data;
+} lru_cache;
+
+
+/**
+ * XXX: finish documenting.
+ */
+lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret);
+
+
+/**
+ * XXX: finish documenting.
+ */
+lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, 
+				uint32_t secret, bool talloc_data);
+
+
+/**
+ * XXX: finish documenting.
+ */
+void lru_cache_destroy(lru_cache* ht);
+
+
+/**
+ * XXX: finish documenting.
+ */
+bool lru_cache_update(lru_cache* ht, const void* index, 
+		      uint32_t index_len, void* data);
+
+/**
+ * XXX: finish documenting.
+ *
+ * @return A pointer to data previously stored at index.
+ *         If no data was found at index, NULL is returned.
+ */
+void* lru_cache_find(lru_cache* ht, const void* index, 
+		     uint32_t index_len);
+
+/**
+ * XXX: finish documenting.
+ *
+ * Removes entry from table at index.
+ *
+ * @return A pointer to data that was there previously or NULL if no entry is
+ *         at index.
+ */
+bool lru_cache_remove(lru_cache* ht, const void* index, 
+		      uint32_t index_len);
+
+#endif
Index: /releases/0.12.0/include/range_list.h
===================================================================
--- /releases/0.12.0/include/range_list.h	(revision 175)
+++ /releases/0.12.0/include/range_list.h	(revision 175)
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2008-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/**
+ * @file 
+ *
+ * A data structure which stores a list of address ranges.
+ * 
+ * range_lists support basic in-place modifications and maintain the address
+ * space in sorted order.  Looking up a range_list_element is implemented 
+ * through binary search.
+ */
+
+#ifndef _RANGE_LIST_H
+#define _RANGE_LIST_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include "talloc.h"
+
+typedef struct _range_list_element
+{
+  uint32_t offset;
+  uint32_t length;
+  void* data;
+} range_list_element;
+
+
+/** XXX: document this. */
+typedef struct _range_list
+{
+  range_list_element** elements;
+  uint32_t elem_alloced;
+  uint32_t size;
+} range_list;
+
+
+/** Allocates a new range_list.
+ *
+ * @return A newly allocated range_list, or NULL if an error occurred.
+ */
+range_list* range_list_new();
+
+
+/** Frees the memory associated with a range_list, including the elements, but
+ *  not any data parameters referenced by those elements.  
+ *
+ * If rl is NULL, does nothing.
+ *
+ * @param rl the range_list to be free()d.
+ */
+void range_list_free(range_list* rl);
+
+
+/** Query the current number of elements on a range_list
+ *
+ * @param rl the range_list to query
+ *
+ * @return The number of elements currently in the list.
+ */
+uint32_t range_list_size(const range_list* rl);
+
+
+/** Adds an element to the range_list.  
+ *
+ * The new element must not overlap with others.
+ * NOTE: this is a slow operation.
+ *
+ * @param rl     the range list to update
+ * @param offset the starting point for the range
+ * @param length the length of the range
+ * @param data   misc data associated with this range element
+ *
+ * @return  true on success, false on failure.
+ *
+ * Failures can occur due to memory limitations, max_size limitations,
+ * or if the submitted range overlaps with an existing element.  Other
+ * errors may also be possible.
+ */
+bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data);
+
+
+/** Removes an element from the list.  
+ *
+ * The element data structure will be freed, but the data property will not be.
+ *
+ * @param rl    the range_list to modify
+ * @param index the element index to remove
+ *
+ * @return true if the element was successfully removed, false otherwise.
+ */
+bool range_list_remove(range_list* rl, uint32_t index);
+
+
+/** Retrieves the element for a given index.
+ *
+ * @param rl    the range_list being queried.
+ * @param index the element index desired.
+ * 
+ * @return The element for a given index, or NULL if the element is not 
+ *         available.
+ */
+const range_list_element* range_list_get(const range_list* rl, uint32_t index);
+
+
+/** Attempts to find the unique element whose range encompasses offset.
+ *
+ * @param rl     the range_list being queried.
+ * @param offset the location for which an element is desired.
+ *
+ * @return A matching element index or a negative value if none could be found.
+ */
+int32_t range_list_find(const range_list* rl, uint32_t offset);
+
+
+/** Same as range_list_find(), but returns the data associated with an element.
+ *
+ * @param rl     the range_list being queried.
+ * @param offset the address to search for in the ranges
+ *
+ * @return The data element of the matching element index or NULL if none could
+ *         be found.
+ *
+ *  NOTE: May also return NULL if an element matched but the data
+ *        element was never set.
+ */
+void* range_list_find_data(const range_list* rl, uint32_t offset);
+
+
+/**  Splits an existing element into two elements in place.
+ *
+ * The resulting list will contain an additional element whose offset 
+ * is the one provided and whose length extends to the end of the old element
+ * (the one identified by the index).  The original element's offset will 
+ * remain the same while it's length is shortened such that it is contiguous
+ * with the newly created element.  The newly created element will have an index 
+ * of one more than the current element.
+ *
+ * Both the original element and the newly created element will reference the 
+ * original element's data.
+ *
+ * @param rl     the range_list to modify
+ * @param index  the index of the element to be split
+ * @param offset the at which the element will be split
+ *
+ * @return true if the element was successfully split, false otherwise.
+ */
+bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset);
+
+
+/** Determines whether or not a specified range exists contiguously within the
+ *  range_list.
+ *
+ * @param rl     the range_list to search
+ * @param start  the offset at the beginning of the range
+ * @param length the length of the range
+ *
+ * @return true if the specified range exists and is complete, false otherwise.
+ */
+bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length);
+
+#endif
Index: /releases/0.12.0/include/regfi.h
===================================================================
--- /releases/0.12.0/include/regfi.h	(revision 175)
+++ /releases/0.12.0/include/regfi.h	(revision 175)
@@ -0,0 +1,1450 @@
+/*
+ * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2005 Gerald (Jerry) Carter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/**
+ * @file
+ * Windows NT (and later) read-only registry library
+ *
+ * This library is intended for use in digital forensics investigations, but 
+ * is likely useful in other applications.
+ *
+ * Branched from Samba project Subversion repository, version #6903:
+ *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h?rev=6903&view=auto
+ *
+ * Since then, it has been heavily rewritten, simplified, and improved.
+ */
+
+/**
+ * @mainpage Home
+ *
+ * The regfi library is a read-only NT registry library which serves as the main
+ * engine behind the reglookup tool.  It is designed with digital forensic 
+ * analysis in mind, but it should also be useful in other tools which need to 
+ * efficiently traverse and query registry data structures.
+ *
+ * The library is broken down into four main parts, the 
+ * @ref regfiBase "Base Layer", which any code dependent on the library will 
+ * likely need to rely on, as well as three main functional layers: 
+ * @li @ref regfiIteratorLayer
+ * @li @ref regfiGlueLayer
+ * @li @ref regfiParseLayer
+ *
+ * Most users will find that a combination of the Base Layer and the Iterator Layer 
+ * will be sufficient for accessing registry hive files.  Those who are wiling
+ * to dive deep into registry data structures, for instance to recover deleted
+ * data structures or to research Windows registry behavior in detail, will 
+ * find the Parse Layer to be quite useful.
+ */
+
+
+#ifndef _REGFI_H
+#define _REGFI_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <iconv.h>
+
+#include "byteorder.h"
+#include "talloc.h"
+#include "winsec.h"
+#include "void_stack.h"
+#include "range_list.h"
+#include "lru_cache.h"
+
+/******************************************************************************/
+
+/* regfi library error message types */
+#define REGFI_MSG_INFO  0x0001
+#define REGFI_MSG_WARN  0x0004
+#define REGFI_MSG_ERROR 0x0010
+
+typedef uint8_t REGFI_ENCODING;
+/* regfi library supported character encodings */
+#define REGFI_ENCODING_ASCII   0
+#define REGFI_ENCODING_UTF8    1
+#define REGFI_ENCODING_DEFAULT REGFI_ENCODING_ASCII
+/* UTF16LE is not supported for output */
+#define REGFI_ENCODING_UTF16LE 2
+
+#define REGFI_NUM_ENCODINGS    3
+
+/* Windows is lame */
+#ifdef O_BINARY
+#define REGFI_OPEN_FLAGS O_RDONLY|O_BINARY
+#else
+#define REGFI_OPEN_FLAGS O_RDONLY
+#endif
+
+/* Registry data types */
+#define REG_NONE                       0
+#define REG_SZ		               1
+#define REG_EXPAND_SZ                  2
+#define REG_BINARY 	               3
+#define REG_DWORD	               4
+#define REG_DWORD_LE	               4  /* DWORD, little endian */
+#define REG_DWORD_BE	               5  /* DWORD, big endian */
+#define REG_LINK                       6
+#define REG_MULTI_SZ  	               7
+#define REG_RESOURCE_LIST              8
+#define REG_FULL_RESOURCE_DESCRIPTOR   9
+#define REG_RESOURCE_REQUIREMENTS_LIST 10
+#define REG_QWORD                      11 /* 64-bit little endian */
+/* XXX: Has MS defined a REG_QWORD_BE? */
+/* Not a real type in the registry */
+#define REG_KEY                    0x7FFFFFFF
+
+#define REGFI_OFFSET_NONE          0xffffffff
+
+
+/* This maximum depth is described here:
+ * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+ */
+#define REGFI_MAX_DEPTH		   512
+
+/* This limit defines the maximum number of levels deep that ri subkey list
+ * trees can go.
+ */
+/* XXX: This is totally arbitrary right now.
+ *      The actual limit may need to be discovered by experimentation.
+ */
+#define REGFI_MAX_SUBKEY_DEPTH     255
+
+
+/* Header sizes and magic number lengths for various records */
+#define REGFI_HBIN_ALLOC           0x1000 /* Minimum allocation unit for HBINs */
+#define REGFI_REGF_SIZE            0x1000 /* "regf" header block size */
+#define REGFI_REGF_MAGIC_SIZE      4
+#define REGFI_REGF_NAME_SIZE       64
+#define REGFI_REGF_RESERVED1_SIZE  340
+#define REGFI_REGF_RESERVED2_SIZE  3528
+#define REGFI_HBIN_MAGIC_SIZE      4
+#define REGFI_CELL_MAGIC_SIZE      2
+#define REGFI_HBIN_HEADER_SIZE     0x20
+#define REGFI_NK_MIN_LENGTH        0x4C
+#define REGFI_VK_MIN_LENGTH        0x14
+#define REGFI_SK_MIN_LENGTH        0x14
+#define REGFI_SUBKEY_LIST_MIN_LEN  0x4
+#define REGFI_BIG_DATA_MIN_LENGTH  0xC
+
+
+/* Constants used for validation */
+/* XXX: Can we add clock resolution validation as well as range?  It has
+ *      been reported that Windows timestamps are never more than a
+ *      certain granularity (250ms?), which could be used to help
+ *      eliminate false positives.  Would need to verify this and
+ *      perhaps conservatively implement a check.
+ */
+ /* Minimum time is Jan 1, 1990 00:00:00 */
+#define REGFI_MTIME_MIN_HIGH       0x01B41E6D
+#define REGFI_MTIME_MIN_LOW        0x26F98000
+ /* Maximum time is Jan 1, 2290 00:00:00
+  * (We hope no one is using Windows by then...) 
+  */
+#define REGFI_MTIME_MAX_HIGH       0x03047543
+#define REGFI_MTIME_MAX_LOW        0xC80A4000
+
+
+/* Flags for the vk records */
+#define REGFI_VK_FLAG_ASCIINAME    0x0001
+#define REGFI_VK_DATA_IN_OFFSET    0x80000000
+#define REGFI_VK_MAX_DATA_LENGTH   1024*1024  /* XXX: This is arbitrary */
+
+
+/* Known key flags */
+/*******************/
+/* These next two show up on normal-seeming keys in Vista and W2K3 registries */
+#define REGFI_NK_FLAG_UNKNOWN1     0x4000
+#define REGFI_NK_FLAG_UNKNOWN2     0x1000
+
+/* This next one shows up in some Vista "software" registries */
+/* XXX: This shows up in the following two SOFTWARE keys in Vista:
+ *   /Wow6432Node/Microsoft
+ *   /Wow6432Node/Microsoft/Cryptography
+ *  
+ * It comes along with UNKNOWN2 and ASCIINAME for a total flags value of 0x10A0
+ */
+#define REGFI_NK_FLAG_UNKNOWN3     0x0080
+
+/* Predefined handle.  Rumor has it that the valuelist count for this key is 
+ * where the handle is stored.
+ * http://msdn.microsoft.com/en-us/library/ms724836(VS.85).aspx
+ */
+#define REGFI_NK_FLAG_PREDEF_KEY   0x0040
+
+/* The name will be in ASCII if this next bit is set, otherwise UTF-16LE */
+#define REGFI_NK_FLAG_ASCIINAME    0x0020
+
+/* Symlink key.  
+ * See: http://www.codeproject.com/KB/system/regsymlink.aspx 
+ */
+#define REGFI_NK_FLAG_LINK         0x0010
+
+/* This key cannot be deleted */
+#define REGFI_NK_FLAG_NO_RM        0x0008
+
+/* Root of a hive */
+#define REGFI_NK_FLAG_ROOT         0x0004
+
+/* Mount point of another hive.  NULL/(default) value indicates which hive 
+ * and where in the hive it points to. 
+ */
+#define REGFI_NK_FLAG_HIVE_LINK    0x0002 
+
+/* These keys shouldn't be stored on disk, according to:
+ * http://geekswithblogs.net/sdorman/archive/2007/12/24/volatile-registry-keys.aspx
+ */
+#define REGFI_NK_FLAG_VOLATILE     0x0001
+
+/* Useful for identifying unknown flag types */
+#define REGFI_NK_KNOWN_FLAGS       (REGFI_NK_FLAG_PREDEF_KEY\
+				    | REGFI_NK_FLAG_ASCIINAME\
+				    | REGFI_NK_FLAG_LINK\
+				    | REGFI_NK_FLAG_NO_RM\
+				    | REGFI_NK_FLAG_ROOT\
+				    | REGFI_NK_FLAG_HIVE_LINK\
+				    | REGFI_NK_FLAG_VOLATILE\
+				    | REGFI_NK_FLAG_UNKNOWN1\
+				    | REGFI_NK_FLAG_UNKNOWN2\
+                                    | REGFI_NK_FLAG_UNKNOWN3)
+
+
+#define CHAR_BIT 8
+#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
+		    : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
+#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+typedef struct _regfi_nttime
+{
+  uint32_t low;
+  uint32_t high;
+} REGFI_NTTIME;
+
+
+/** HBIN block information
+ * @ingroup regfiMiddleLayer
+ */
+typedef struct _regfi_hbin 
+{
+  /** Offset of this HBIN in the registry file */
+  uint32_t file_off;
+
+  /** Number of active records pointing to this block (not used currently) */
+  uint32_t ref_count;
+
+  /** Offset from first hbin block */
+  uint32_t first_hbin_off;
+
+  /** Block size of this block Should be a multiple of 4096 (0x1000) */
+  uint32_t block_size;
+
+  /** Relative offset to next block.  
+   * 
+   * @note This value may be unreliable!
+   */
+  uint32_t next_block;
+
+  /** Magic number for the HBIN (should be "hbin"). */
+  uint8_t magic[REGFI_HBIN_MAGIC_SIZE];
+} REGFI_HBIN;
+
+
+/* Subkey List -- list of key offsets and hashed names for consistency */
+typedef struct 
+{
+  /* Virtual offset of NK record or additional subkey list, 
+   * depending on this list's type. 
+   */
+  uint32_t offset;
+
+  uint32_t hash;
+} REGFI_SUBKEY_LIST_ELEM;
+
+
+/** Subkey-list structure
+ * @ingroup regfiMiddleLayer
+ */
+typedef struct _regfi_subkey_list
+{
+  /* Real offset of this record's cell in the file */
+  uint32_t offset;
+
+  uint32_t cell_size;
+  
+  /* Number of immediate children */
+  uint32_t num_children;  
+
+  /* Total number of keys referenced by this list and it's children */
+  uint32_t num_keys;      
+
+  REGFI_SUBKEY_LIST_ELEM* elements;
+  uint8_t magic[REGFI_CELL_MAGIC_SIZE];
+
+  /* Set if the magic indicates this subkey list points to child subkey lists */
+  bool recursive_type;  
+} REGFI_SUBKEY_LIST;
+
+
+typedef uint32_t REGFI_VALUE_LIST_ELEM;
+/** Value-list structure
+ * @ingroup regfiMiddleLayer
+ */
+typedef struct _regfi_value_list
+{
+  /* Actual number of values referenced by this list.  
+   * May differ from parent key's num_values if there were parsing errors. 
+   */
+  uint32_t num_values;
+
+  REGFI_VALUE_LIST_ELEM* elements;
+} REGFI_VALUE_LIST;
+
+
+/** Class name structure (used in storing SysKeys)
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_classname
+{
+  /** As converted to requested REGFI_ENCODING */
+  char* interpreted;
+
+  /** Represents raw buffer read from classname cell.
+   *
+   * Length of this item is specified in the size field.
+   */
+  uint8_t* raw;
+
+  /** Length of the raw data.
+   *
+   * May be shorter than that indicated by parent key.
+   */
+  uint16_t size;
+} REGFI_CLASSNAME;
+
+
+/** Data record structure
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_data
+{
+  /** Data type of this data, as indicated by the referencing VK record. */
+  uint32_t type;
+
+  /** Length of the raw data. */
+  uint32_t size;
+
+  /** This is always present, representing the raw data cell contents. */
+  uint8_t* raw;
+
+  /** Represents the length of the interpreted value. Meaning is type-specific. */
+  uint32_t interpreted_size;
+
+  /** These items represent interpreted versions of the REGFI_DATA::raw field.
+   *
+   * Only use the appropriate member according to the REGFI_DATA::type field.
+   * In the event of an unknown type, use only the REGFI_DATA::raw field.
+   */
+  union _regfi_data_interpreted
+  {
+    /** REG_NONE 
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* none; 
+
+    /** REG_SZ 
+     *
+     * Stored as a NUL terminated string.  Converted to the specified
+     * REGFI_ENCODING.
+     */
+    uint8_t* string;
+
+    /** REG_EXPAND_SZ 
+     *
+     * Stored as a NUL terminated string.  Converted to the specified
+     * REGFI_ENCODING.
+     */
+    uint8_t* expand_string;
+
+    /** REG_BINARY
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* binary;
+
+    /** REG_DWORD */
+    uint32_t dword;
+
+    /** REG_DWORD_BE */
+    uint32_t dword_be;
+
+    /** REG_LINK
+     *
+     * Stored as a NUL terminated string.  Converted to the specified
+     * REGFI_ENCODING.
+     */
+    uint8_t* link;
+
+    /** REG_MULTI_SZ 
+     *
+     * Stored as a list of uint8_t* pointers, terminated with a NULL pointer.
+     * Each string element in the list is NUL terminated, and the character set
+     * is determined by the specified REGFI_ENCODING.
+     */
+    uint8_t** multiple_string;
+
+    /** REG_QWORD */
+    uint64_t qword;
+
+    /* The following are treated as binary currently, but this may change in
+     * the future as the formats become better understood.
+     */
+
+    /** REG_RESOURCE_LIST
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* resource_list;
+
+    /** REG_FULL_RESOURCE_DESCRIPTOR
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* full_resource_descriptor;
+
+    /** REG_RESOURCE_REQUIREMENTS_LIST
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* resource_requirements_list;
+  } interpreted;
+} REGFI_DATA;
+
+
+/** Value structure
+ * @ingroup regfiBase
+ */
+typedef struct
+{
+  /** Real offset of this record's cell in the file */
+  uint32_t offset;	
+
+  /** ((start_offset - end_offset) & 0xfffffff8) */
+  uint32_t cell_size;
+
+  /* XXX: deprecated */
+  REGFI_DATA* data;
+
+  /** The name of this value converted to desired REGFI_ENCODING.  
+   *
+   * This conversion typically occurs automatically through REGFI_ITERATOR
+   * settings.  String is NUL terminated.
+   */
+  char*    valuename;
+
+  /** The raw value name
+   *
+   * Length of the buffer is stored in name_length.
+   */
+  uint8_t* valuename_raw;
+
+  /** Length of valuename_raw */
+  uint16_t name_length;
+
+  /** Offset from beginning of this hbin block */
+  uint32_t hbin_off;
+  
+  /** Size of the value's data as reported in the VK record.
+   *
+   * May be different than that obtained while parsing the data cell itself.
+   */
+  uint32_t data_size;
+
+  /** Virtual offset of data cell */
+  uint32_t data_off;
+
+  /** Value's data type */
+  uint32_t type;
+
+  /** VK record's magic number (should be "vk") */
+  uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
+
+  /** VK record flags */
+  uint16_t flags;
+
+  /* XXX: A 2-byte field of unknown purpose stored in the VK record */
+  uint16_t unknown1;
+
+  /** Whether or not the data record is stored in the VK record's data_off field. 
+   *
+   * This information is derived from the high bit of the raw data size field.
+   */
+  bool     data_in_offset;
+} REGFI_VK_REC;
+
+
+/* Key Security */
+struct _regfi_sk_rec;
+
+/** Security structure
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_sk_rec 
+{
+  /** Real file offset of this record */
+  uint32_t offset;
+
+  /** ((start_offset - end_offset) & 0xfffffff8) */
+  uint32_t cell_size;
+
+  /** The stored Windows security descriptor for this SK record */
+  WINSEC_DESC* sec_desc;
+
+  /** Offset of this record from beginning of this hbin block */
+  uint32_t hbin_off;
+  
+  /** Offset of the previous SK record in the linked list of SK records */
+  uint32_t prev_sk_off;
+
+  /** Offset of the next SK record in the linked list of SK records */
+  uint32_t next_sk_off;
+
+  /** Number of keys referencing this SK record */
+  uint32_t ref_count;
+
+  /** Size of security descriptor (sec_desc) */
+  uint32_t desc_size;
+
+  /* XXX: A 2-byte field of unknown purpose */
+  uint16_t unknown_tag;
+
+  /** The magic number for this record (should be "sk") */
+  uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
+} REGFI_SK_REC;
+
+
+/** Key structure
+ * @ingroup regfiBase
+ */
+typedef struct
+{
+  /** Real offset of this record's cell in the file */
+  uint32_t offset;
+
+  /** Actual or estimated length of the cell.  
+   * Always in multiples of 8. 
+   */
+  uint32_t cell_size;
+
+  /** Preloaded value-list for this key.
+   * This element is loaded automatically when using the iterator interface and
+   * possibly some lower layer interfaces.
+   */
+  REGFI_VALUE_LIST* values;
+
+
+  /** Preloaded subkey-list for this key.
+   * This element is loaded automatically when using the iterator interface and
+   * possibly some lower layer interfaces.
+   */
+  REGFI_SUBKEY_LIST* subkeys;
+  
+  /** Key flags */
+  uint16_t flags;
+
+  /** Magic number of key (should be "nk") */
+  uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
+
+  /** Key's last modification time */
+  REGFI_NTTIME mtime;
+
+  /** Length of keyname_raw */
+  uint16_t name_length;
+
+  /** Length of referenced classname */
+  uint16_t classname_length;
+
+  /** The name of this key converted to desired REGFI_ENCODING.  
+   *
+   * This conversion typically occurs automatically through REGFI_ITERATOR
+   * settings.  String is NUL terminated.
+   */
+  char* keyname;
+
+  /** The raw key name
+   *
+   * Length of the buffer is stored in name_length.
+   */
+  uint8_t* keyname_raw;
+
+  /** Virutal offset of parent key */
+  uint32_t parent_off;
+
+  /** Virutal offset of classname key */
+  uint32_t classname_off;
+  
+  /* XXX: max subkey name * 2 */
+  uint32_t max_bytes_subkeyname;
+
+  /* XXX: max subkey classname length (as if) */
+  uint32_t max_bytes_subkeyclassname;
+
+  /* XXX: max valuename * 2 */
+  uint32_t max_bytes_valuename;
+
+  /* XXX: max value data size */
+  uint32_t max_bytes_value;
+  
+  /* XXX: Fields of unknown purpose */
+  uint32_t unknown1;
+  uint32_t unknown2;
+  uint32_t unknown3;
+  uint32_t unk_index;		    /* nigel says run time index ? */
+  
+  /** Number of subkeys */
+  uint32_t num_subkeys;
+
+  /** Virtual offset of subkey-list */
+  uint32_t subkeys_off;
+
+  /** Number of values for this key */
+  uint32_t num_values;
+
+  /** Virtual offset of value-list */
+  uint32_t values_off;
+
+  /** Virtual offset of SK record */
+  uint32_t sk_off;
+} REGFI_NK_REC;
+
+
+
+/** Registry hive file data structure
+ *
+ * This essential structure stores run-time information about a single open
+ * registry hive as well as file header (REGF block) data.  This structure 
+ * also stores a list of warnings and error messages generated while parsing
+ * the registry hive.  These can be tuned using @ref regfi_set_message_mask.  
+ * Messages may be retrieved using @ref regfi_get_messages.
+ *
+ * @note If the message mask is set to record any messages, dependent code 
+ *       must use @ref regfi_get_messages periodically to clear the message
+ *       queue. Otherwise, this structure will grow in size over time as 
+ *       messages queue up.
+ *
+ * @ingroup regfiBase
+ */ 
+typedef struct 
+{
+  /* Run-time information */
+  /************************/
+  /* file descriptor */
+  int fd;
+
+  /* For sanity checking (not part of the registry header) */
+  uint32_t file_length;
+
+  /* Metadata about hbins */
+  range_list* hbins;
+
+  /* SK record cached since they're repeatedly reused */
+  lru_cache* sk_cache;
+
+  /* Error/warning/info messages returned by lower layer functions */
+  char* last_message;
+
+  /* Mask for error message types that will be stored. */
+  uint16_t msg_mask;
+
+
+  /* Data parsed from file header */
+  /********************************/
+  uint8_t  magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */
+
+ /* These sequence numbers should match if
+  * the hive was properly synced to disk.
+  */
+  uint32_t sequence1;            
+  uint32_t sequence2;
+
+  REGFI_NTTIME mtime;
+  uint32_t major_version;  /* Set to 1 in all known hives */
+  uint32_t minor_version;  /* Set to 3 or 5 in all known hives */
+  uint32_t type;           /* XXX: Unverified.  Set to 0 in all known hives */
+  uint32_t format;         /* XXX: Unverified.  Set to 1 in all known hives */
+
+  uint32_t root_cell;  /* Offset to root cell in the first (or any?) hbin block */
+  uint32_t last_block; /* Offset to last hbin block in file */
+
+  uint32_t cluster;    /* XXX: Unverified. Set to 1 in all known hives */
+
+  /* Matches hive's base file name. Stored in UTF-16LE */
+  uint8_t file_name[REGFI_REGF_NAME_SIZE];
+
+  WINSEC_UUID* rm_id;       /* XXX: Unverified. */
+  WINSEC_UUID* log_id;      /* XXX: Unverified. */
+  WINSEC_UUID* tm_id;       /* XXX: Unverified. */
+  uint32_t flags;             /* XXX: Unverified. */
+  uint32_t guid_signature;    /* XXX: Unverified. */
+
+  uint32_t checksum;          /* Stored checksum from file */
+  uint32_t computed_checksum; /* Our own calculation of the checksum. 
+			     * (XOR of bytes 0x0000 - 0x01FB) */
+
+  WINSEC_UUID* thaw_tm_id;  /* XXX: Unverified. */
+  WINSEC_UUID* thaw_rm_id;  /* XXX: Unverified. */
+  WINSEC_UUID* thaw_log_id; /* XXX: Unverified. */
+  uint32_t boot_type;         /* XXX: Unverified. */
+  uint32_t boot_recover;      /* XXX: Unverified. */
+
+  /* This seems to include random junk.  Possibly unsanitized memory left over
+   * from when header block was written.  For instance, chunks of nk records 
+   * can be found, though often it's all 0s. */
+  uint8_t reserved1[REGFI_REGF_RESERVED1_SIZE];
+
+  /* This is likely reserved and unusued currently.  (Should be all 0s.)
+   * Included here for easier access in looking for hidden data 
+   * or doing research. */
+  uint8_t reserved2[REGFI_REGF_RESERVED2_SIZE];
+
+} REGFI_FILE;
+
+
+/** Registry hive iterator
+ * @ingroup regfiIteratorLayer
+ */
+typedef struct _regfi_iterator
+{
+  /** The registry hive this iterator is associated with */
+  REGFI_FILE* f;
+
+  /** All current parent keys and associated iterator positions */
+  void_stack* key_positions;
+
+  /** The current key */
+  REGFI_NK_REC* cur_key;
+
+  /** The encoding that all strings are converted to as set during iterator
+   *  creation.
+   */
+  REGFI_ENCODING string_encoding;
+
+  /** Index of the current subkey */
+  uint32_t cur_subkey;
+
+  /** Index of the current value */
+  uint32_t cur_value;
+} REGFI_ITERATOR;
+
+
+typedef struct _regfi_iter_position
+{
+  REGFI_NK_REC* nk;
+  uint32_t cur_subkey;
+  /* We could store a cur_value here as well, but didn't see 
+   * the use in it right now.
+   */
+} REGFI_ITER_POSITION;
+
+
+/** General purpose buffer with stored length
+ * @ingroup regfiBottomLayer
+ */
+typedef struct _regfi_buffer
+{
+  uint8_t* buf;
+  uint32_t len;
+} REGFI_BUFFER;
+
+
+
+/******************************************************************************/
+/** 
+ * @defgroup regfiBase Base Layer: Essential Functions and Data Structures
+ *
+ * These functions are either necessary for normal use of the regfi API or just
+ * don't fit particularly well in any of the other layers.
+ */
+/******************************************************************************/
+
+/** Attempts to open a registry hive and allocate related data structures.
+ * 
+ * @param filename A string containing the relative or absolute path of the
+ *               registry hive to be opened.
+ *
+ * @return A reference to a newly allocated REGFI_FILE structure, 
+ *         if successful;  NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+REGFI_FILE*           regfi_open(const char* filename);
+
+
+/** Parses file headers of an already open registry hive file and 
+ *  allocates related structures for further parsing.
+ *
+ * @param fd A file descriptor of an already open file.  Must be seekable.
+ *
+ * @return A reference to a newly allocated REGFI_FILE structure, if successful;
+ *         NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+REGFI_FILE*           regfi_alloc(int fd);
+
+
+/** Closes and frees an open registry hive.
+ *
+ * @param file The registry structure to close.
+ *
+ * @return 0 on success, -1 on failure with errno set.  
+ *         errno codes are similar to those of close(2).
+ *
+ * @ingroup regfiBase
+ */
+int                   regfi_close(REGFI_FILE* file);
+
+
+/** Frees a hive's data structures without closing the underlying file.
+ *
+ * @param file The registry structure to free.
+ *
+ * @ingroup regfiBase
+ */
+void                  regfi_free(REGFI_FILE* file);
+
+
+/** Get errors, warnings, and/or verbose information relating to processing of
+ *  the given registry file.
+ *
+ * @param file the structure for the registry file
+ *
+ * @return A newly allocated char* which must be free()d by the caller.
+ *
+ * @ingroup regfiBase
+ */
+char*                 regfi_get_messages(REGFI_FILE* file);
+
+
+/** Set the verbosity level of errors and warnings generated by the library
+ *  (as accessible via regfi_get_messages).
+ *
+ * This may be called at any time and will take effect immediately.
+ *
+ * @param file   the structure for the registry file
+ *
+ * @param mask   an integer representing the types of messages desired.
+ *               Acceptable values are created through bitwise ORs of 
+ *               REGFI_MSG_* values.  For instance, if only errors and
+ *               informational messages were desired (but not warnings),
+ *               then one would specify: REGFI_MSG_ERROR|REGFI_MSG_INFO
+ *               New REGFI_FILE structures are created with:
+ *                REGFI_MSG_ERROR|REGFI_MSG_WARN
+ *               Note that error and warning messages will continue to
+ *               accumulate in memory if they are not fetched using
+ *               regfi_get_messages and then freed by the caller.
+ *               To disable error messages entirely, supply 0, which
+ *               will prevent message accumulation.  
+ *
+ * @ingroup regfiBase
+ */
+void                  regfi_set_message_mask(REGFI_FILE* file, uint16_t mask);
+
+
+/* Dispose of previously parsed records */
+
+/** Frees a key structure previously returned by one of the API functions
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiBase
+ */
+void                  regfi_free_key(REGFI_NK_REC* nk);
+
+
+/** Frees a value structure previously returned by one of the API functions
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiBase
+ */
+void                  regfi_free_value(REGFI_VK_REC* vk);
+
+
+
+/******************************************************************************/
+/** 
+ * @defgroup regfiIteratorLayer Iterator Layer: Primary regfi Library Interface
+ *
+ * This top layer of API functions provides an iterator interface which makes
+ * traversing registry data structures easy in both single-threaded and 
+ * multi-threaded scenarios.
+ */
+/******************************************************************************/
+
+/** Creates a new iterator for the provided registry file.
+ *
+ * @param file The opened registry file the iterator should be created for.
+ *
+ * @param output_encoding Character encoding that strings should be returned in.
+ *                        Only supply the REGFI_ENCODING_* constants, as others 
+ *                        will be rejected.
+ *                        The following values are currently accepted:
+ *                        REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
+ *                        REGFI_ENCODING_ASCII
+ *                        REGFI_ENCODING_UTF8
+ *
+ * @return A newly allocated REGFI_ITERATOR. 
+ *         Must be free()d with regfi_iterator_free.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_ITERATOR*       regfi_iterator_new(REGFI_FILE* file,
+					 REGFI_ENCODING output_encoding);
+
+
+/** Frees a registry file iterator previously created by regfi_iterator_new.
+ *
+ * This does not affect the underlying registry file's allocation status.
+ *
+ * @param i the iterator to be freed
+ *
+ * @ingroup regfiIteratorLayer
+ */
+void                  regfi_iterator_free(REGFI_ITERATOR* i);
+
+
+/** Traverse deeper into the registry tree at the current subkey.
+ *
+ * @param i the iterator
+ *
+ * @return  true on success, false on failure.  
+ *          Note that subkey and value indexes are preserved.  That is, if a
+ *          regfi_iterator_up call occurs later (reversing the effect of this
+ *          call) then the subkey and value referenced prior to the
+ *          regfi_iterator_down call will still be referenced.  This  makes
+ *          depth-first iteration particularly easy.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool                  regfi_iterator_down(REGFI_ITERATOR* i);
+
+
+/** Traverse up to the current key's parent key.
+ *
+ * @param i the iterator
+ *
+ * @return  true on success, false on failure.  Any subkey or value state
+ *          associated with the current key is lost.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool                  regfi_iterator_up(REGFI_ITERATOR* i);
+
+
+/** Traverse up to the root key of the hive.
+ *
+ * @param i the iterator
+ *
+ * @return true on success, false on failure.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool                  regfi_iterator_to_root(REGFI_ITERATOR* i);
+
+
+/** Traverse down multiple levels in the registry hive.
+ *
+ * XXX: This currently only accepts ASCII key names.  Need to look into
+ *      accepting other encodings.
+ *
+ * @param i    the iterator
+ * @param path a list of key names representing the path.  This list must
+ *             contain NUL terminated strings.  The list itself is
+ *             terminated with a NULL pointer.  All path elements must be
+ *             keys; value names are not accepted (even as the last
+ *             element).
+ *
+ * @return true on success, false on failure.  If any element of path is not
+ *                 found, false will be returned and the iterator will remain
+ *                 in its original position.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path);
+
+
+/** Returns the currently referenced key.
+ *
+ * @param i the iterator
+ *
+ * @return A read-only key structure for the current key, or NULL on failure.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+const REGFI_NK_REC*   regfi_iterator_cur_key(REGFI_ITERATOR* i);
+
+
+/** Returns the SK (security) record referenced by the current key.
+ *
+ * @param i the iterator
+ *
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+const REGFI_SK_REC*   regfi_iterator_cur_sk(REGFI_ITERATOR* i);
+
+
+/** Sets the internal subkey index to the first subkey referenced by the current
+ *  key and returns that key.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the newly referenced first 
+ *         subkey, or NULL on failure.  Failure may be due to a lack of any
+ *         subkeys or other errors.  Newly allocated keys must be freed with
+ *         regfi_free_key.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_NK_REC*         regfi_iterator_first_subkey(REGFI_ITERATOR* i);
+
+
+/** Returns the currently indexed subkey.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the currently referenced subkey,
+ *         or NULL on failure.  Newly allocated keys must be freed with 
+ *         regfi_free_key.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_NK_REC*         regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
+
+
+/** Increments the internal subkey index to the next key in the subkey-list and
+ *  returns the subkey for that index.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the next subkey or NULL on
+ *         failure.  Newly allocated keys must be freed with regfi_free_key.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_NK_REC*         regfi_iterator_next_subkey(REGFI_ITERATOR* i);
+
+
+/** Searches for a subkey with a given name under the current key.
+ *
+ * @param i           the iterator
+ * @param subkey_name subkey name to search for
+ *
+ * @return True if such a subkey was found, false otherwise.  If a subkey is
+ *         found, the current subkey index is set to that subkey.  Otherwise,
+ *         the subkey index remains at the same location as before the call.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool                  regfi_iterator_find_subkey(REGFI_ITERATOR* i, 
+						 const char* subkey_name);
+
+/** Sets the internal value index to the first value referenced by the current
+ *  key and returns that value.
+ *
+ * @param i the iterator
+ *
+ * @return  A newly allocated value structure for the newly referenced first
+ *          value, or NULL on failure.  Failure may be due to a lack of any
+ *          values or other errors.  Newly allocated keys must be freed with
+ *          regfi_free_value.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_VK_REC*         regfi_iterator_first_value(REGFI_ITERATOR* i);
+
+
+/** Returns the currently indexed value.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated value structure for the currently referenced value,
+ *         or NULL on failure.  Newly allocated values must be freed with 
+ *         regfi_free_value.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_VK_REC*         regfi_iterator_cur_value(REGFI_ITERATOR* i);
+
+
+/** Increments the internal value index to the next value in the value-list and
+ *  returns the value for that index.
+ *
+ * @param i the iterator
+ *
+ * @return  A newly allocated key structure for the next value or NULL on 
+ *          failure.  Newly allocated keys must be freed with regfi_free_value.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_VK_REC*         regfi_iterator_next_value(REGFI_ITERATOR* i);
+
+
+/** Searches for a value with a given name under the current key.
+ *
+ * @param i          the iterator
+ * @param value_name value name to search for
+ *
+ * @return True if such a value was found, false otherwise.  If a value is 
+ *         found, the current value index is set to that value.  Otherwise,
+ *         the value index remains at the same location as before the call.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool                  regfi_iterator_find_value(REGFI_ITERATOR* i, 
+						const char* value_name);
+
+/** Retrieves classname for a given key.
+ *
+ * @param i   the iterator
+ * @param key the key whose classname is desired
+ *
+ * @return Returns a newly allocated classname structure, or NULL on failure.
+ *         Classname structures must be freed with regfi_free_classname.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_CLASSNAME*      regfi_iterator_fetch_classname(REGFI_ITERATOR* i, 
+						     const REGFI_NK_REC* key);
+
+
+/** Retrieves data for a given value.
+ *
+ * @param i     the iterator
+ * @param value the value whose data is desired
+ *
+ * @return Returns a newly allocated data structure, or NULL on failure.
+ *         Data structures must be freed with regfi_free_data.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_DATA*           regfi_iterator_fetch_data(REGFI_ITERATOR* i, 
+						const REGFI_VK_REC* value);
+
+
+
+/******************************************************************************/
+/** 
+ * @defgroup regfiGlueLayer Glue Layer: Logical Data Structure Loading 
+ */
+/******************************************************************************/
+
+/** Loads a key at a given file offset along with associated data structures.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_NK_REC*         regfi_load_key(REGFI_FILE* file, uint32_t offset, 
+				     REGFI_ENCODING output_encoding, 
+				     bool strict);
+
+
+/** Loads a value at a given file offset alng with associated data structures.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_VK_REC*         regfi_load_value(REGFI_FILE* file, uint32_t offset, 
+				       REGFI_ENCODING output_encoding, 
+				       bool strict);
+
+
+/** Loads a logical subkey list in its entirety which may span multiple records.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_SUBKEY_LIST*    regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
+					    uint32_t num_keys, uint32_t max_size,
+					    bool strict);
+
+
+/** Loads a valuelist.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_VALUE_LIST*     regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, 
+					   uint32_t num_values, uint32_t max_size,
+					   bool strict);
+
+
+/** Loads a data record which may be contained in the virtual offset, in a
+ *  single cell, or in multiple cells through big data records.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_BUFFER          regfi_load_data(REGFI_FILE* file, uint32_t voffset,
+				      uint32_t length, bool data_in_offset,
+				      bool strict);
+
+
+/** Loads the data associated with a big data record at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_BUFFER          regfi_load_big_data(REGFI_FILE* file, uint32_t offset, 
+					  uint32_t data_length,uint32_t cell_length,
+					  range_list* used_ranges,
+					  bool strict);
+
+
+/** Given raw data, attempts to interpret the data based on a specified registry
+ *  data type.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+bool                  regfi_interpret_data(REGFI_FILE* file, 
+					   REGFI_ENCODING string_encoding,
+					   uint32_t type, REGFI_DATA* data);
+
+
+/** Frees the memory associated with a REGFI_CLASSNAME data structure.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+void                  regfi_free_classname(REGFI_CLASSNAME* classname);
+
+
+/** Frees the memory associated with a REGFI_DATA data structure.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+void                  regfi_free_data(REGFI_DATA* data);
+
+
+/* These are cached so return values don't need to be freed. */
+
+/** Loads an "sk" security record at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+const REGFI_SK_REC*   regfi_load_sk(REGFI_FILE* file, uint32_t offset,
+				    bool strict);
+
+
+/** Retrieves the HBIN data structure stored at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+const REGFI_HBIN*     regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset);
+
+
+
+/******************************************************************************/
+/**
+ * @defgroup regfiParseLayer Parsing Layer: Direct Data Structure Access 
+ */
+/******************************************************************************/
+
+REGFI_FILE*           regfi_parse_regf(int fd, bool strict);
+REGFI_HBIN*           regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, 
+				       bool strict);
+
+
+/** Parses an NK record at the specified offset
+ *
+ * @param file     the registry file structure
+ * @param offset   the offset of the cell (not the record) to be parsed.
+ * @param max_size the maximum size the NK cell could be. (for validation)
+ * @param strict   if true, rejects any malformed records.  Otherwise,
+ *                 tries to minimally validate integrity.
+ *
+ * @return A newly allocated NK record structure, or NULL on failure.
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_NK_REC*         regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
+				     uint32_t max_size, bool strict);
+
+
+/** Parses a single cell containing a subkey-list record.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_SUBKEY_LIST*    regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
+					     uint32_t max_size, bool strict);
+
+
+/** Parses a VK (value) record at the specified offset
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_VK_REC*         regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
+				     uint32_t max_size, bool strict);
+
+
+/** Parses an SK (security) record at the specified offset
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_SK_REC*         regfi_parse_sk(REGFI_FILE* file, uint32_t offset, 
+				     uint32_t max_size, bool strict);
+
+
+/** Retrieves information on all cells in the registry hive which are 
+ *  currently in the unallocated status.  
+ *
+ * The unallocated status is determined based soley on the cell length sign.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+range_list*           regfi_parse_unalloc_cells(REGFI_FILE* file);
+
+
+/** Helper function to parse a cell
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+bool                  regfi_parse_cell(int fd, uint32_t offset, 
+				       uint8_t* hdr, uint32_t hdr_len,
+				       uint32_t* cell_length, bool* unalloc);
+
+
+/** Parses a classname cell
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+uint8_t*                regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
+					    uint16_t* name_length, 
+					    uint32_t max_size, bool strict);
+
+
+/** Parses a single-cell data record
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_BUFFER          regfi_parse_data(REGFI_FILE* file, uint32_t offset,
+				       uint32_t length, bool strict);
+
+
+/** Parses a "little data" record which is stored entirely within the 
+ *  provided virtual offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_BUFFER          regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset, 
+					      uint32_t length, bool strict);
+
+
+/******************************************************************************/
+/*    Private Functions                                                       */
+/******************************************************************************/
+REGFI_NK_REC*         regfi_rootkey(REGFI_FILE* file, 
+				    REGFI_ENCODING output_encoding);
+void                  regfi_subkeylist_free(REGFI_SUBKEY_LIST* list);
+uint32_t              regfi_read(int fd, uint8_t* buf, uint32_t* length);
+
+const char*           regfi_type_val2str(unsigned int val);
+int                   regfi_type_str2val(const char* str);
+
+char*                 regfi_get_sacl(WINSEC_DESC* sec_desc);
+char*                 regfi_get_dacl(WINSEC_DESC* sec_desc);
+char*                 regfi_get_owner(WINSEC_DESC* sec_desc);
+char*                 regfi_get_group(WINSEC_DESC* sec_desc);
+
+REGFI_SUBKEY_LIST*    regfi_merge_subkeylists(uint16_t num_lists, 
+					      REGFI_SUBKEY_LIST** lists,
+					      bool strict);
+REGFI_SUBKEY_LIST*    regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset,
+						uint32_t max_size, bool strict,
+						uint8_t depth_left);
+void                  regfi_add_message(REGFI_FILE* file, uint16_t msg_type, 
+					const char* fmt, ...);
+REGFI_NK_REC*         regfi_copy_nk(const REGFI_NK_REC* nk);
+REGFI_VK_REC*         regfi_copy_vk(const REGFI_VK_REC* vk);
+int32_t               regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset);
+int32_t               regfi_conv_charset(const char* input_charset, 
+					 const char* output_charset,
+					 uint8_t* input, char* output, 
+					 uint32_t input_len, uint32_t output_max);
+REGFI_DATA*           regfi_buffer_to_data(REGFI_BUFFER raw_data);
+
+/* XXX: move to base API and document */
+void                  regfi_unix2nt_time(REGFI_NTTIME* nt, time_t t);
+time_t                regfi_nt2unix_time(const REGFI_NTTIME* nt);
+
+
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk, 
+			     REGFI_ENCODING output_encoding, bool strict);
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk, 
+			       REGFI_ENCODING output_encoding, bool strict);
+
+
+#endif	/* _REGFI_H */
Index: /releases/0.12.0/include/talloc.h
===================================================================
--- /releases/0.12.0/include/talloc.h	(revision 175)
+++ /releases/0.12.0/include/talloc.h	(revision 175)
@@ -0,0 +1,169 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba temporary memory allocation functions
+
+   Copyright (C) Andrew Tridgell 2004-2005
+   Copyright (C) Stefan Metzmacher 2006
+   Copyright (C) Timothy D. Morgan 2009 
+   
+     ** NOTE! The following LGPL license applies to the talloc library.
+     ** This does NOT imply that all of reglookup is released under the LGPL
+   
+   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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+/* $Id$ */
+
+#ifndef _TALLOC_H_
+#define _TALLOC_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdbool.h>
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+/*
+  this uses a little trick to allow __LINE__ to be stringified
+*/
+#ifndef __location__
+#define __TALLOC_STRING_LINE1__(s)    #s
+#define __TALLOC_STRING_LINE2__(s)   __TALLOC_STRING_LINE1__(s)
+#define __TALLOC_STRING_LINE3__  __TALLOC_STRING_LINE2__(__LINE__)
+#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
+#endif
+
+#ifndef TALLOC_DEPRECATED
+#define TALLOC_DEPRECATED 0
+#endif
+
+#ifndef PRINTF_ATTRIBUTE
+#if (__GNUC__ >= 3)
+/** Use gcc attribute to check printf fns.  a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. Note that some gcc 2.x versions don't handle this
+ * properly **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
+
+#define talloc_set_destructor(ptr, function) \
+	_talloc_set_destructor((ptr), (int (*)(void *))(function))
+#define _TALLOC_TYPEOF(ptr) void *
+#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr))
+
+#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr))
+#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr))
+
+/* useful macros for creating type checked pointers */
+#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
+#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
+#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr)))
+
+#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
+
+#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
+
+#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
+#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
+#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
+#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count)
+
+#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
+#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
+
+#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
+
+#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
+#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
+
+#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
+
+/* The following definitions come from talloc.c  */
+void *_talloc(const void *context, size_t size);
+void *talloc_pool(const void *context, size_t size);
+void _talloc_set_destructor(const void *ptr, int (*destructor)(void *));
+int talloc_increase_ref_count(const void *ptr);
+size_t talloc_reference_count(const void *ptr);
+void *_talloc_reference(const void *context, const void *ptr);
+int talloc_unlink(const void *context, void *ptr);
+const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+void talloc_set_name_const(const void *ptr, const char *name);
+void *talloc_named(const void *context, size_t size, 
+		   const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+void *talloc_named_const(const void *context, size_t size, const char *name);
+const char *talloc_get_name(const void *ptr);
+void *talloc_check_name(const void *ptr, const char *name);
+void *talloc_parent(const void *ptr);
+const char *talloc_parent_name(const void *ptr);
+void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+int talloc_free(void *ptr);
+void talloc_free_children(void *ptr);
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
+void *_talloc_steal(const void *new_ctx, const void *ptr);
+void *_talloc_move(const void *new_ctx, const void *pptr);
+size_t talloc_total_size(const void *ptr);
+size_t talloc_total_blocks(const void *ptr);
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+			    void (*callback)(const void *ptr,
+			  		     int depth, int max_depth,
+					     int is_ref,
+					     void *private_data),
+			    void *private_data);
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f);
+void talloc_report_full(const void *ptr, FILE *f);
+void talloc_report(const void *ptr, FILE *f);
+void talloc_enable_null_tracking(void);
+void talloc_disable_null_tracking(void);
+void talloc_enable_leak_report(void);
+void talloc_enable_leak_report_full(void);
+void *_talloc_zero(const void *ctx, size_t size, const char *name);
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
+void *talloc_autofree_context(void);
+size_t talloc_get_size(const void *ctx);
+void *talloc_find_parent_byname(const void *ctx, const char *name);
+void talloc_show_parents(const void *context, FILE *file);
+int talloc_is_parent(const void *context, const void *ptr);
+
+char *talloc_strdup(const void *t, const char *p);
+char *talloc_strdup_append(char *s, const char *a);
+char *talloc_strdup_append_buffer(char *s, const char *a);
+
+char *talloc_strndup(const void *t, const char *p, size_t n);
+char *talloc_strndup_append(char *s, const char *a, size_t n);
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n);
+
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+#endif
Index: /releases/0.12.0/include/void_stack.h
===================================================================
--- /releases/0.12.0/include/void_stack.h	(revision 175)
+++ /releases/0.12.0/include/void_stack.h	(revision 175)
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2005,2007,2009-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/** 
+ *@file
+ * 
+ * This is a very simple implementation of a stack which stores chunks of
+ * memory of any type.
+ */
+
+
+#ifndef _VOID_STACK_H
+#define _VOID_STACK_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include "talloc.h"
+
+/** XXX: document this. */
+typedef struct _void_stack
+{
+  void** elements;
+  unsigned short max_size;
+  unsigned short top;
+} void_stack;
+
+
+/** XXX: document this. */
+typedef struct _void_stack_iterator
+{
+  const void_stack* stack;
+  unsigned short cur;
+} void_stack_iterator;
+
+
+/** Allocates a new void_stack.
+ *
+ * @param max_size the maxiumum number of elements 
+ *                 which may be pushed onto the stack.
+ *
+ * @return a pointer to the newly allocated void_stack, 
+ *         or NULL if an error occurred.
+ */
+void_stack* void_stack_new(unsigned short max_size);
+
+
+/** Makes a shallow copy of void_stack.
+ *
+ * @param v the stack to make a copy of.
+ *
+ * @return a pointer to the duplicate void_stack, or NULL if an error occurred.
+ */
+void_stack* void_stack_copy(const void_stack* v);
+
+
+/** Makes a shallow copy of void_stack in reverse order.
+ *
+ * @param v the stack to make a copy of.
+ *
+ * @return a pointer to the duplicate void_stack 
+ *         (which will be in reverse order), or NULL if an error occurred.
+ */
+void_stack* void_stack_copy_reverse(const void_stack* v);
+
+
+/** Frees the memory associated with a void_stack, but not the elements held
+ *  on the stack.
+ *
+ * @param stack the stack to be free()d.
+ */
+void void_stack_free(void_stack* stack);
+
+
+/** Frees the memory associated with a void_stack and the elements referenced 
+ *  by the stack.  
+ *
+ * Do not use this function if the elements of the stack 
+ * are also free()d elsewhere, or contain pointers to other memory which 
+ * cannot be otherwise free()d.
+ *
+ * @param stack the stack to be free()d.
+ */
+void void_stack_free_deep(void_stack* stack);
+
+
+/** Query the current number of elements on a void_stack()
+ *
+ * @param stack the void_stack to query
+ *
+ * @return the number of elements currently on the stack.
+ */
+unsigned short void_stack_size(const void_stack* stack);
+
+
+/** Removes the top element on a void_stack and returns a reference to it.
+ *
+ * @param stack the void_stack to pop
+ *
+ * @return a pointer to the popped stack element, or NULL if no elements exist
+ *         on the stack.
+ */
+void* void_stack_pop(void_stack* stack);
+
+
+/** Puts a new element on the top of a void_stack.
+ *
+ * @param stack the void_stack being modified.
+ * @param e the element to be added
+ *
+ * @return true if the element was successfully added, false otherwise.
+ */
+bool void_stack_push(void_stack* stack, void* e);
+
+
+/** Returns a pointer to the current element on the top of the stack.
+ *
+ * @param stack the void_stack being queried.
+ *
+ * @return a pointer to the current element on the top of the stack, or NULL if
+ *         no elements exist in the stack.
+ */
+const void* void_stack_cur(const void_stack* stack);
+
+
+/** Creates a new iterator for the specified void_stack.
+ *
+ * @param stack the void_stack to be referenced by the new iterator
+ *
+ * @return a new void_stack_iterator, or NULL if an error occurred.
+ */
+void_stack_iterator* void_stack_iterator_new(const void_stack* stack);
+
+
+/** Frees a void_stack_iterator.
+ *
+ * Does not affect the void_stack referenced by the iterator.
+ *
+ * @param iter the void_stack_iterator to be free()d.
+ */
+void void_stack_iterator_free(void_stack_iterator* iter);
+
+
+/** Returns a pointer to the the next element in the stack.
+ *
+ * Iterates over elements starting in order from the oldest element (bottom of the stack).
+ *
+ * @param iter the void_stack_iterator used to lookup the next element.
+ *
+ * @return a pointer to the next element.
+ */
+const void* void_stack_iterator_next(void_stack_iterator* iter);
+
+
+/* XXX: for completeness, might want to add a void_stack_iterator_first()
+ *      function, to return iterator to first element
+ */
+#endif
Index: /releases/0.12.0/include/winsec.h
===================================================================
--- /releases/0.12.0/include/winsec.h	(revision 175)
+++ /releases/0.12.0/include/winsec.h	(revision 175)
@@ -0,0 +1,312 @@
+/* 
+ * Copyright (C) 2005,2009-2010 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team 
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/**
+ * @file
+ *
+ * A small library for interpreting Windows Security Descriptors.
+ * This library was originally based on Samba source from:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * The library has been heavily rewritten and improved based on information
+ * provided by Microsoft at: 
+ *    http://msdn.microsoft.com/en-us/library/cc230366%28PROT.10%29.aspx
+ */
+
+#ifndef _WINSEC_H
+#define _WINSEC_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "talloc.h"
+#include "byteorder.h"
+
+
+/* This is the maximum number of subauths in a SID, as defined here:
+ *   http://msdn.microsoft.com/en-us/library/cc230371(PROT.10).aspx
+ */
+#define WINSEC_MAX_SUBAUTHS 15
+
+#define WINSEC_DESC_HEADER_SIZE     (5 * sizeof(uint32_t))
+#define WINSEC_ACL_HEADER_SIZE      (2 * sizeof(uint32_t))
+#define WINSEC_ACE_MIN_SIZE         16
+
+/* XXX: Fill in definitions of other flags */
+/* This self relative flag means offsets contained in the descriptor are relative
+ * to the descriptor's offset.  This had better be true in the registry.
+ */
+#define WINSEC_DESC_SELF_RELATIVE   0x8000
+#define WINSEC_DESC_SACL_PRESENT    0x0010
+#define WINSEC_DESC_DACL_PRESENT    0x0004
+
+#define WINSEC_ACE_OBJECT_PRESENT              0x00000001 
+#define WINSEC_ACE_OBJECT_INHERITED_PRESENT    0x00000002
+#define WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT  0x5
+#define WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT   0x6
+#define WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT    0x7
+#define WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT    0x8
+
+
+/** XXX: document this. */
+typedef struct _winsec_uuid
+{
+  /** XXX: document this. */
+  uint32_t time_low;
+
+  /** XXX: document this. */
+  uint16_t time_mid;
+
+  /** XXX: document this. */
+  uint16_t time_hi_and_version;
+
+  /** XXX: document this. */
+  uint8_t  clock_seq[2];
+
+  /** XXX: document this. */
+  uint8_t  node[6];
+} WINSEC_UUID;
+
+
+/** XXX: document this. */
+typedef struct _winsec_sid
+{
+  /** SID revision number */
+  uint8_t  sid_rev_num;
+
+  /** Number of sub-authorities */
+  uint8_t  num_auths;
+
+  /** Identifier Authority */
+  uint8_t  id_auth[6];
+
+  /** Pointer to sub-authorities.
+   * 
+   * @note The values in these uint32_t's are in *native* byteorder, not
+   * neccessarily little-endian...... JRA.
+   */
+  uint32_t sub_auths[WINSEC_MAX_SUBAUTHS];   /* XXX: Make this dynamically allocated? */
+} WINSEC_DOM_SID;
+
+
+/** XXX: document this. */
+typedef struct _winsec_ace
+{
+  /** xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+  uint8_t type;
+
+  /** xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+  uint8_t flags;
+
+  /** XXX: finish documenting */
+  uint16_t size;
+
+  /** XXX: finish documenting */
+  uint32_t access_mask;
+  
+  /* This stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+
+  /** xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+  uint32_t  obj_flags;
+
+  /** Object GUID */
+  WINSEC_UUID* obj_guid;
+
+  /** Inherited object GUID */
+  WINSEC_UUID* inh_guid;
+
+  /* eof object stuff */
+  
+  /** XXX: finish documenting */
+  WINSEC_DOM_SID* trustee;
+
+} WINSEC_ACE;
+
+
+/** XXX: document this. */
+typedef struct _winsec_acl
+{
+  /** 0x0003 */
+  uint16_t revision;
+
+  /** Size, in bytes, of the entire ACL structure */
+  uint16_t size;
+
+  /** Number of Access Control Entries */
+  uint32_t num_aces;
+  
+  /** XXX: document this. */
+  WINSEC_ACE** aces;
+
+} WINSEC_ACL;
+
+
+/** XXX: document this. */
+typedef struct _winsec_desc
+{
+  /** 0x01 */
+  uint8_t revision;
+
+  /** XXX: better explain this
+   *
+   * "If the Control field has the RM flag set, then this field contains the
+   *  resource manager (RM) control value. ... Otherwise, this field is reserved
+   *  and MUST be set to zero." -- Microsoft.
+   *  See:
+   *   http://msdn.microsoft.com/en-us/library/cc230371%28PROT.10%29.aspx
+   */
+  uint8_t sbz1;
+
+  /** WINSEC_DESC_* flags */
+  uint16_t control;
+  
+  /** Offset to owner sid */
+  uint32_t off_owner_sid;
+
+  /** Offset to group sid */
+  uint32_t off_grp_sid;
+
+  /** Offset to system list of permissions */
+  uint32_t off_sacl;
+
+  /** Offset to list of permissions */
+  uint32_t off_dacl;
+
+  /** XXX: document this */
+  WINSEC_DOM_SID* owner_sid; 
+
+  /** XXX: document this */
+  WINSEC_DOM_SID* grp_sid;
+
+  /** System ACL */
+  WINSEC_ACL* sacl;
+
+  /** User ACL */
+  WINSEC_ACL* dacl;
+
+} WINSEC_DESC;
+
+
+/**
+ *
+ * XXX: finish documenting
+ */
+WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
+
+
+/**
+ *
+ * XXX: finish documenting
+ */
+void winsec_free_descriptor(WINSEC_DESC* desc);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
+			       const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+WINSEC_ACL* winsec_parse_acl(void* talloc_ctx, 
+			     const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+WINSEC_ACE* winsec_parse_ace(void* talloc_ctx, 
+			     const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx, 
+				     const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx, 
+			       const uint8_t* buf, uint32_t buf_len);
+
+
+/**
+ *
+ * XXX: finish documenting
+ */
+size_t winsec_sid_size(const WINSEC_DOM_SID* sid);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+bool winsec_ace_object(uint8_t type);
+
+#endif /* _WINSEC_H */
Index: /releases/0.12.0/lib/Makefile
===================================================================
--- /releases/0.12.0/lib/Makefile	(revision 175)
+++ /releases/0.12.0/lib/Makefile	(revision 175)
@@ -0,0 +1,28 @@
+# $Id: Makefile 30 2005-07-16 14:31:27Z tim $
+
+################################################################################
+
+FILES=regfi.o winsec.o void_stack.o range_list.o lru_cache.o talloc.o
+
+all: $(FILES)
+
+regfi.o: regfi.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ regfi.c
+
+winsec.o: winsec.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ winsec.c
+
+void_stack.o: void_stack.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ void_stack.c
+
+range_list.o: range_list.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ range_list.c
+
+lru_cache.o: lru_cache.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ lru_cache.c
+
+talloc.o: talloc.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ talloc.c
+
+clean:
+	rm -f $(FILES)
Index: /releases/0.12.0/lib/lru_cache.c
===================================================================
--- /releases/0.12.0/lib/lru_cache.c	(revision 175)
+++ /releases/0.12.0/lib/lru_cache.c	(revision 175)
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2008-2009 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/**
+ * @file
+ */
+
+#include "lru_cache.h"
+
+
+#define LRU_CACHE_DEBUG 0
+
+/* XXX: really should replace this with a real universal hash or other
+ *      fast HMAC.
+ */ 
+static uint32_t lru_cache_compute_hash(uint32_t num_buckets,
+				       uint32_t secret,
+				       const void* buf,
+				       uint32_t buf_len)
+{
+  uint32_t i;
+  uint32_t ret_val = 0x243f6a88;
+  unsigned char* s = (unsigned char*)&secret;
+  const unsigned char* b = (unsigned char*)buf;
+
+  for(i=0; i<buf_len; i++)
+    ret_val = (ret_val+(i^s[i%4])*b[i]) % num_buckets;
+  
+  return ret_val;
+}
+
+/* Returns approximately floor(log_2(n)) (log base 2 of n, floored) 
+ * If n == 0, returns 0
+ */
+static uint32_t lru_cache_floor_log2(uint32_t n)
+{
+  uint32_t ret_val;
+  
+  for(ret_val=31; ret_val > 1; ret_val--)
+    if((n & (1 << ret_val)) != 0)
+      return ret_val;
+
+  return 0;
+}
+
+#if 0
+static void lru_cache_print(lru_cache* ht)
+{
+  uint32_t i;
+  lru_cache_element* cur;
+
+  printf("from newest to oldest:\n");
+  for(cur=ht->newest; cur != NULL; cur=cur->older)
+  {
+    /*    write(STDOUT_FILENO, cur->index, cur->index_len);*/
+    printf("%p", (void*)cur);
+    printf("\n");
+    if(cur->older == ht->newest)
+    {
+      printf("??? Loop in LRU list!!");
+      break;
+    }
+  }
+  printf("\n");
+
+  printf("table:\n");
+  for(i=0; i<ht->num_buckets; i++)
+  {
+    printf("%.8X: ", i);
+    for(cur=ht->table[i]; cur != NULL; cur=cur->next)
+    {
+      /*      write(STDOUT_FILENO, cur->index, cur->index_len);*/
+      printf("%p", (void*)cur);
+      printf("|");
+
+      if(cur->next == ht->table[i])
+      {
+	printf("??? Loop in table chain!!");
+	break;
+      }
+    }
+    printf("\n");
+  }
+}
+#endif
+
+
+lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret)
+{
+  return lru_cache_create_ctx(NULL, max_keys, secret, false);
+}
+
+
+lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, 
+				uint32_t secret, bool talloc_data)
+{
+  lru_cache* ret_val;
+
+  ret_val = talloc(talloc_ctx, lru_cache);
+  if(ret_val == NULL)
+    return NULL;
+
+  if(max_keys == 0)
+    ret_val->num_buckets = 1024;
+  else
+  {
+    ret_val->num_buckets = max_keys/lru_cache_floor_log2(max_keys);
+    if(ret_val->num_buckets < 1)
+      ret_val->num_buckets = 1;
+  }
+
+  ret_val->table = talloc_array(ret_val, 
+				lru_cache_element*, ret_val->num_buckets);
+  if(ret_val->table == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  
+  ret_val->oldest = NULL;
+  ret_val->newest = NULL;
+  ret_val->max_keys = max_keys;
+  ret_val->secret = secret;
+  ret_val->talloc_data = talloc_data;
+  ret_val->num_keys = 0;
+  memset(ret_val->table, 0, ret_val->num_buckets*sizeof(lru_cache_element*));
+
+  return ret_val;
+}
+
+
+void lru_cache_destroy(lru_cache* ht)
+{
+  ht->secret = 0;
+  talloc_free(ht);
+}
+
+
+
+bool lru_cache_update(lru_cache* ht, const void* index, 
+		      uint32_t index_len, void* data)
+{
+  uint32_t hash, lru_hash;
+  lru_cache_element* cur;
+  lru_cache_element* last = NULL;
+  lru_cache_element* e = NULL;
+  void* tmp_index;
+
+  hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len);
+  for(cur = ht->table[hash]; cur != NULL && e == NULL; cur=cur->next)
+  {
+    if((index_len == cur->index_len) 
+       && memcmp(cur->index, index, index_len) == 0)
+    { e = cur; }
+  }
+  
+  if(e != NULL)
+  { /* We found the index, so we're going to overwrite the data.
+     * We also need to reposition the element to the newest position,
+     * so remove it from the list for now.
+     */
+    if(ht->talloc_data)
+      talloc_free(e->data);
+
+    if(e->newer == NULL)
+      ht->newest = e->older;
+    else
+      e->newer->older = e->older;
+
+    if(e->older == NULL)
+      ht->oldest = e->newer;
+    else
+      e->older->newer = e->newer;
+  }
+  else
+  { /* We didn't find an identical index. */
+    
+    if((ht->max_keys != 0) && (ht->num_keys >= ht->max_keys))
+    { /* Eliminate the least recently used item, but reuse the element
+       * structure to minimize reallocation. 
+       */
+      e = ht->oldest;
+      if(ht->newest == ht->oldest)
+      {
+	ht->newest = NULL;
+	ht->oldest = NULL;
+      }
+      else
+      {
+	ht->oldest = e->newer;
+	e->newer->older = NULL;
+      }
+      e->newer = NULL;
+      e->older = NULL;
+
+      last = NULL;
+      lru_hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, 
+					e->index, e->index_len);
+      for(cur = ht->table[lru_hash]; cur != e && cur != NULL; 
+	  last=cur, cur=cur->next)
+      {	continue; }
+
+      if(last == NULL)
+	ht->table[lru_hash] = e->next;
+      else
+	last->next = e->next;
+      e->next = NULL;
+
+      if(ht->talloc_data)
+	talloc_free(e->data);
+
+      tmp_index = talloc_realloc_size(e, e->index, index_len);
+      if(tmp_index == NULL)
+      {
+	talloc_free(e);
+	return false;
+      }
+      else
+	e->index = tmp_index;
+    }
+    else
+    { /* Brand new element because we have room to spare. */
+
+      e = talloc(ht->table, lru_cache_element);
+      if(e == NULL)
+	return false;
+      
+      e->index = talloc_size(e, index_len);
+      if(e->index == NULL)
+      {
+	talloc_free(e);
+	return false;
+      }
+      
+      /* New entry, increment counters. */
+      ht->num_keys++;
+    }
+    memcpy(e->index, index, index_len);
+    e->index_len = index_len;
+
+    /* Insert at beginning of chain, in a vaguely LRU style */
+    e->next = ht->table[hash];
+    ht->table[hash] = e;
+  }
+  e->data = data;
+  if(ht->talloc_data)
+    talloc_steal(e, e->data);
+
+  /* Finally, let's insert the element to the newest position in the LRU list.*/
+  if(ht->newest != NULL)
+    ht->newest->newer = e;
+  e->newer = NULL;
+  e->older = ht->newest;
+  ht->newest = e;
+  if(ht->oldest == NULL)
+    ht->oldest = e;
+
+  return true;
+}
+
+
+void* lru_cache_find(lru_cache* ht, const void* index,
+		     uint32_t index_len)
+{
+  uint32_t hash;
+  lru_cache_element* cur;
+
+  hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len);
+  for(cur = ht->table[hash]; (cur != NULL); cur = cur->next)
+  {
+    if((index_len == cur->index_len)
+       && memcmp(cur->index, index, index_len) == 0)
+    { break; }
+  }
+  
+  if(cur != NULL && cur->newer != NULL)
+  { /* Need to move this element up to the newest slot. */
+
+    cur->newer->older = cur->older;
+
+    if(cur->older == NULL)
+      ht->oldest = cur->newer;
+    else
+      cur->older->newer = cur->newer;
+
+    cur->newer = NULL;
+    cur->older = ht->newest;
+    ht->newest->newer = cur;
+    ht->newest = cur;
+  }
+
+  if(cur != NULL)
+    return cur->data;
+  else
+    return NULL;
+}
+
+
+
+bool lru_cache_remove(lru_cache* ht, const void* index, 
+		      uint32_t index_len)
+{
+  uint32_t hash;
+  lru_cache_element* cur;
+  lru_cache_element* last = NULL;
+
+  hash = lru_cache_compute_hash(ht->num_buckets, ht->secret,
+				index, index_len);
+  for(cur=ht->table[hash]; (cur != NULL);
+      last=cur, cur=cur->next)
+  {
+    if((index_len == cur->index_len) 
+       && memcmp(cur->index, index, index_len) == 0)
+    { break; }
+  }
+
+  if(cur == NULL)
+    return false;
+
+  /* Detach from list */
+  if(cur->newer == NULL)
+    ht->newest = cur->older;
+  else
+    cur->newer->older = cur->older;
+  
+  if(cur->older == NULL)
+    ht->oldest = cur->newer;
+  else
+    cur->older->newer = cur->newer;
+
+  /* Detach from hash table */
+  if(last == NULL)
+    ht->table[hash] = cur->next;
+  else
+    last->next = cur->next;
+
+  talloc_free(cur);
+  
+  /* Removing entry, decrement counters. */
+  ht->num_keys--;
+  
+  return true;
+}
Index: /releases/0.12.0/lib/range_list.c
===================================================================
--- /releases/0.12.0/lib/range_list.c	(revision 175)
+++ /releases/0.12.0/lib/range_list.c	(revision 175)
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2008-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/**
+ * @file
+ */
+
+#include "range_list.h"
+
+
+/*******************/
+/* Private symbols */
+/*******************/
+#define RANGE_LIST_ALLOC_SIZE 256
+
+#if 0 /* For debugging */
+#include <stdio.h>
+static void range_list_print(const range_list* rl)
+{
+  uint32_t i;
+  for(i=0; i<rl->size; i++)
+    fprintf(stderr, " %d=%p,%d,%d,%p", i, (void*)rl->elements[i],
+	    rl->elements[i]->offset, rl->elements[i]->length, 
+	    rl->elements[i]->data);
+  fprintf(stderr, "\n");
+}
+#endif
+
+/*
+ * Inserts elem into rl at the specified index and updates rl->size. 
+ * Memory reallocation of rl->elements is handled when necessary, and
+ * rl->elem_alloced is updated in this case..  Returns false if memory
+ * could not be allocated.  
+ */
+static bool range_list_insert(range_list* rl, range_list_element* elem, uint32_t index)
+{
+  uint32_t i;
+  range_list_element** tmp;
+
+  if(rl->size == rl->elem_alloced)
+  {
+    tmp = talloc_realloc(rl, rl->elements, range_list_element*, 
+			 (rl->elem_alloced+RANGE_LIST_ALLOC_SIZE));
+    if(tmp == NULL)
+      return false;
+    rl->elements = tmp;
+    rl->elem_alloced += RANGE_LIST_ALLOC_SIZE;
+  }
+
+  /* Do the shuffle to the right. */
+  for(i=rl->size; i > index; i--)
+    rl->elements[i] = rl->elements[i-1];
+  rl->elements[index] = elem;
+
+  rl->size++;
+  return true;
+}
+
+/*
+ * Finds the element with the closest offset to that provided, such that
+ * the element's offset <= the provided offset.  If no such element
+ * exists, this returns -1 which indicates that the provided offset
+ * appears before all elements.
+ */
+static int32_t range_list_find_previous(const range_list* rl, uint32_t offset)
+{
+  uint32_t h_idx, l_idx, cur_idx;
+  uint32_t h_val, l_val;
+  range_list_element* cur_elem;
+
+  if((rl->size == 0) || (offset < rl->elements[0]->offset))
+    return -1;
+
+  if(offset >= rl->elements[rl->size-1]->offset)
+    return rl->size-1;
+
+  h_idx = rl->size-1;
+  l_idx = 0;
+  while(h_idx != l_idx)
+  {
+    h_val = rl->elements[h_idx]->offset + rl->elements[h_idx]->length;
+    l_val = rl->elements[l_idx]->offset;
+    /* Make an educated guess as to the "middle" index based on the
+     * ratios of the offset and high/low values.
+     */
+    cur_idx = (uint32_t)ceil((((double)offset-l_val)/(h_val-l_val))*(h_idx-l_idx));
+    if(cur_idx > h_idx)
+      cur_idx = h_idx;
+    if(cur_idx < l_idx)
+      cur_idx = l_idx;
+    cur_elem = rl->elements[cur_idx];
+
+    if((offset >= cur_elem->offset) && (offset < rl->elements[cur_idx+1]->offset))
+      return cur_idx;
+    
+    if(offset < cur_elem->offset)
+      h_idx = cur_idx-1;
+    else
+      l_idx = cur_idx+1;
+  }
+
+  return h_idx;
+}
+
+
+/******************/
+/* Public symbols */
+/******************/
+range_list* range_list_new()
+{
+  range_list* rl;
+
+  rl = talloc(NULL, range_list);
+  if(rl == NULL)
+    return NULL;
+
+  rl->elements = talloc_array(rl, range_list_element*, RANGE_LIST_ALLOC_SIZE);
+  if(rl->elements == NULL)
+  {
+    talloc_free(rl);
+    return NULL;
+  }
+
+  rl->elem_alloced = RANGE_LIST_ALLOC_SIZE;
+  rl->size = 0;
+
+  return rl;
+}
+
+
+void range_list_free(range_list* rl)
+{
+  if(rl != NULL)
+    talloc_free(rl);
+}
+
+
+uint32_t range_list_size(const range_list* rl)
+{
+  return rl->size;
+}
+
+
+
+bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data)
+{
+  uint32_t insert_index;
+  range_list_element* elem;
+  range_list_element* prev_elem;
+  /*fprintf(stderr, "DEBUG: rl->size=%d\n", rl->size);*/
+  /* Sorry, limited to 2**31-1 elements. */
+  if(rl->size >= 0x7FFFFFFF)
+    return false;
+
+  /* 0-length ranges aren't allowed. */
+  if(length == 0)
+    return false;
+  
+  /* Check for integer overflows */
+  if((uint32_t)(offset+length) < offset || (uint32_t)(offset+length) < length)
+    return false;
+
+  /* Find insertion point and validate there are no overlaps */
+  insert_index = range_list_find_previous(rl, offset)+1;
+  
+  /* Does the previous element overlap with this one? */
+  if(insert_index > 0)
+  {
+    prev_elem = rl->elements[insert_index-1];
+    if(offset < prev_elem->length + prev_elem->offset)
+      return false;
+  }
+
+  /* Does this new element overlap with the next one? */
+  if((insert_index+1 < rl->size) 
+     && (offset+length > rl->elements[insert_index+1]->offset))
+    return false;
+
+  elem = talloc(rl->elements, range_list_element);
+  if(elem == NULL)
+    return false;
+  elem->offset = offset;
+  elem->length = length;
+  elem->data = data;
+  
+  if(!range_list_insert(rl, elem, insert_index))
+  {
+    talloc_free(elem);
+    return false;
+  }
+
+  return true;
+}
+
+
+bool range_list_remove(range_list* rl, uint32_t index)
+{
+  uint32_t i;
+  range_list_element** tmp;
+
+  if(index >= rl->size)
+    return false;
+
+  talloc_free(rl->elements[index]);
+
+  /* Do the shuffle to the left. */
+  for(i=index; i < (rl->size-1); i++)
+    rl->elements[i] = rl->elements[i+1];
+  rl->elements[rl->size-1] = NULL;
+  rl->size--;
+
+  /* Try to keep memory usage down */
+  if(rl->size + 2 * RANGE_LIST_ALLOC_SIZE  < rl->elem_alloced)
+  {
+    tmp = talloc_realloc(rl, rl->elements, range_list_element*, 
+			 (rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE));
+    if(tmp != NULL)
+    {
+      rl->elements = tmp;
+      rl->elem_alloced -= 2*RANGE_LIST_ALLOC_SIZE;
+    }
+  }
+
+  return true;
+}
+
+
+const range_list_element* range_list_get(const range_list* rl, uint32_t index)
+{
+  if(index >= rl->size)
+    return NULL;
+
+  return rl->elements[index];
+}
+
+
+int32_t range_list_find(const range_list* rl, uint32_t offset)
+{
+  uint32_t prev_idx;
+  range_list_element* elem;
+
+  if(rl->size == 0)
+    return -1;
+
+  if((offset < rl->elements[0]->offset)
+     || (offset > rl->elements[rl->size-1]->offset 
+	 + rl->elements[rl->size-1]->length))
+    return -2;
+
+  prev_idx = range_list_find_previous(rl, offset);
+  elem = rl->elements[prev_idx];
+  if(offset < elem->offset+elem->length)
+    return prev_idx;
+
+  return -3;
+}
+
+
+void* range_list_find_data(const range_list* rl, uint32_t offset)
+{
+  int32_t index = range_list_find(rl, offset);
+  if(index < 0)
+    return NULL;
+
+  return rl->elements[index]->data;
+}
+
+
+bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset)
+{
+  range_list_element* cur_elem;
+  range_list_element* new_elem;
+
+  if(index >= rl->size)
+    return false;
+
+  cur_elem = rl->elements[index];
+  if((offset <= cur_elem->offset) 
+     || (offset >= cur_elem->offset+cur_elem->length))
+    return false;
+
+  new_elem = talloc(rl->elements, range_list_element);
+  if(new_elem == NULL)
+    return false;
+  
+  new_elem->offset = offset;
+  new_elem->length = cur_elem->offset + cur_elem->length - offset;
+  new_elem->data = cur_elem->data;
+  
+  if(!range_list_insert(rl, new_elem, index+1))
+  {
+    talloc_free(new_elem);
+    return false;
+  }
+
+  cur_elem->length = new_elem->offset - cur_elem->offset;
+
+  return true;
+}
+
+
+bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length)
+{
+  int32_t idx1, idx2;
+
+  idx1 = range_list_find(rl, start);
+  if(idx1 < 0)
+    return false;
+
+  idx2 = range_list_find(rl, start+length);
+  if(idx2 < 0)
+    return false;
+
+  if(idx1 == idx2)
+    return true;
+
+  while(idx1 != idx2)
+  {
+    if(rl->elements[idx1]->offset + rl->elements[idx1]->length 
+       != rl->elements[idx1+1]->offset)
+      return false;
+    idx1++;
+  }
+
+  return true;
+}
Index: /releases/0.12.0/lib/regfi.c
===================================================================
--- /releases/0.12.0/lib/regfi.c	(revision 175)
+++ /releases/0.12.0/lib/regfi.c	(revision 175)
@@ -0,0 +1,3261 @@
+/*
+ * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2005 Gerald (Jerry) Carter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/** 
+ * @file
+ *
+ * Windows NT (and later) read-only registry library
+ *
+ * See @ref regfi.h for more information.
+ *
+ * Branched from Samba project Subversion repository, version #7470:
+ *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
+ *
+ * Since then, it has been heavily rewritten, simplified, and improved.
+ */
+
+#include "regfi.h"
+
+
+/* Registry types mapping */
+const unsigned int regfi_num_reg_types = 12;
+static const char* regfi_type_names[] =
+  {"NONE", "SZ", "EXPAND_SZ", "BINARY", "DWORD", "DWORD_BE", "LINK",
+   "MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
+
+const char* regfi_encoding_names[] =
+  {"US-ASCII//TRANSLIT", "UTF-8//TRANSLIT", "UTF-16LE//TRANSLIT"};
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_add_message(REGFI_FILE* file, uint16_t msg_type, const char* fmt, ...)
+{
+  /* XXX: This function is not particularly efficient,
+   *      but then it is mostly used during errors. 
+   */
+  uint32_t buf_size, buf_used;
+  char* new_msg;
+  va_list args;
+
+  if((file->msg_mask & msg_type) != 0)
+  {
+    if(file->last_message == NULL)
+      buf_used = 0;
+    else
+      buf_used = strlen(file->last_message);
+    
+    buf_size = buf_used+strlen(fmt)+160;
+    new_msg = realloc(file->last_message, buf_size);
+    if(new_msg == NULL)
+      /* XXX: should we report this? */
+      return;
+
+    switch (msg_type)
+    {
+    case REGFI_MSG_INFO:
+      strcpy(new_msg+buf_used, "INFO: ");
+      buf_used += 6;
+      break;
+    case REGFI_MSG_WARN:
+      strcpy(new_msg+buf_used, "WARN: ");
+      buf_used += 6;
+      break;
+    case REGFI_MSG_ERROR:
+      strcpy(new_msg+buf_used, "ERROR: ");
+      buf_used += 7;
+      break;
+    }
+
+    va_start(args, fmt);
+    vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args);
+    va_end(args);
+    strncat(new_msg, "\n", buf_size-1);
+    
+    file->last_message = new_msg;
+  }
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+char* regfi_get_messages(REGFI_FILE* file)
+{
+  char* ret_val = file->last_message;
+  file->last_message = NULL;
+
+  return ret_val;
+}
+
+
+void regfi_set_message_mask(REGFI_FILE* file, uint16_t mask)
+{
+  file->msg_mask = mask;
+}
+
+
+/******************************************************************************
+ * Returns NULL for an invalid e
+ *****************************************************************************/
+static const char* regfi_encoding_int2str(REGFI_ENCODING e)
+{
+  if(e < REGFI_NUM_ENCODINGS)
+    return regfi_encoding_names[e];
+
+  return NULL;
+}
+
+
+/******************************************************************************
+ * Returns NULL for an invalid val
+ *****************************************************************************/
+const char* regfi_type_val2str(unsigned int val)
+{
+  if(val == REG_KEY)
+    return "KEY";
+  
+  if(val >= regfi_num_reg_types)
+    return NULL;
+  
+  return regfi_type_names[val];
+}
+
+
+/******************************************************************************
+ * Returns -1 on error
+ *****************************************************************************/
+int regfi_type_str2val(const char* str)
+{
+  int i;
+
+  if(strcmp("KEY", str) == 0)
+    return REG_KEY;
+
+  for(i=0; i < regfi_num_reg_types; i++)
+    if (strcmp(regfi_type_names[i], str) == 0) 
+      return i;
+
+  if(strcmp("DWORD_LE", str) == 0)
+    return REG_DWORD_LE;
+
+  return -1;
+}
+
+
+/* Security descriptor formatting functions  */
+
+const char* regfi_ace_type2str(uint8_t type)
+{
+  static const char* map[7] 
+    = {"ALLOW", "DENY", "AUDIT", "ALARM", 
+       "ALLOW CPD", "OBJ ALLOW", "OBJ DENY"};
+  if(type < 7)
+    return map[type];
+  else
+    /* XXX: would be nice to return the unknown integer value.  
+     *      However, as it is a const string, it can't be free()ed later on, 
+     *      so that would need to change. 
+     */
+    return "UNKNOWN";
+}
+
+
+/* XXX: need a better reference on the meaning of each flag. */
+/* For more info, see:
+ *   http://msdn2.microsoft.com/en-us/library/aa772242.aspx
+ */
+char* regfi_ace_flags2str(uint8_t flags)
+{
+  static const char* flag_map[32] = 
+    { "OI", /* Object Inherit */
+      "CI", /* Container Inherit */
+      "NP", /* Non-Propagate */
+      "IO", /* Inherit Only */
+      "IA", /* Inherited ACE */
+      NULL,
+      NULL,
+      NULL,
+    };
+
+  char* ret_val = malloc(35*sizeof(char));
+  char* fo = ret_val;
+  uint32_t i;
+  uint8_t f;
+
+  if(ret_val == NULL)
+    return NULL;
+
+  fo[0] = '\0';
+  if (!flags)
+    return ret_val;
+
+  for(i=0; i < 8; i++)
+  {
+    f = (1<<i);
+    if((flags & f) && (flag_map[i] != NULL))
+    {
+      strcpy(fo, flag_map[i]);
+      fo += strlen(flag_map[i]);
+      *(fo++) = ' ';
+      flags ^= f;
+    }
+  }
+  
+  /* Any remaining unknown flags are added at the end in hex. */
+  if(flags != 0)
+    sprintf(fo, "0x%.2X ", flags);
+
+  /* Chop off the last space if we've written anything to ret_val */
+  if(fo != ret_val)
+    fo[-1] = '\0';
+
+  return ret_val;
+}
+
+
+char* regfi_ace_perms2str(uint32_t perms)
+{
+  uint32_t i, p;
+  /* This is more than is needed by a fair margin. */
+  char* ret_val = malloc(350*sizeof(char));
+  char* r = ret_val;
+
+  /* Each represents one of 32 permissions bits.  NULL is for undefined/reserved bits.
+   * For more information, see:
+   *   http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
+   *   http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
+   */
+  static const char* perm_map[32] = 
+    {/* object-specific permissions (registry keys, in this case) */
+      "QRY_VAL",       /* KEY_QUERY_VALUE */
+      "SET_VAL",       /* KEY_SET_VALUE */
+      "CREATE_KEY",    /* KEY_CREATE_SUB_KEY */
+      "ENUM_KEYS",     /* KEY_ENUMERATE_SUB_KEYS */
+      "NOTIFY",        /* KEY_NOTIFY */
+      "CREATE_LNK",    /* KEY_CREATE_LINK - Reserved for system use. */
+      NULL,
+      NULL,
+      "WOW64_64",      /* KEY_WOW64_64KEY */
+      "WOW64_32",      /* KEY_WOW64_32KEY */
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      /* standard access rights */
+      "DELETE",        /* DELETE */
+      "R_CONT",        /* READ_CONTROL */
+      "W_DAC",         /* WRITE_DAC */
+      "W_OWNER",       /* WRITE_OWNER */
+      "SYNC",          /* SYNCHRONIZE - Shouldn't be set in registries */
+      NULL,
+      NULL,
+      NULL,
+      /* other generic */
+      "SYS_SEC",       /* ACCESS_SYSTEM_SECURITY */
+      "MAX_ALLWD",     /* MAXIMUM_ALLOWED */
+      NULL,
+      NULL,
+      "GEN_A",         /* GENERIC_ALL */
+      "GEN_X",         /* GENERIC_EXECUTE */
+      "GEN_W",         /* GENERIC_WRITE */
+      "GEN_R",         /* GENERIC_READ */
+    };
+
+
+  if(ret_val == NULL)
+    return NULL;
+
+  r[0] = '\0';
+  for(i=0; i < 32; i++)
+  {
+    p = (1<<i);
+    if((perms & p) && (perm_map[i] != NULL))
+    {
+      strcpy(r, perm_map[i]);
+      r += strlen(perm_map[i]);
+      *(r++) = ' ';
+      perms ^= p;
+    }
+  }
+  
+  /* Any remaining unknown permission bits are added at the end in hex. */
+  if(perms != 0)
+    sprintf(r, "0x%.8X ", perms);
+
+  /* Chop off the last space if we've written anything to ret_val */
+  if(r != ret_val)
+    r[-1] = '\0';
+
+  return ret_val;
+}
+
+
+char* regfi_sid2str(WINSEC_DOM_SID* sid)
+{
+  uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
+  uint32_t left = size;
+  uint8_t comps = sid->num_auths;
+  char* ret_val = malloc(size);
+  
+  if(ret_val == NULL)
+    return NULL;
+
+  if(comps > WINSEC_MAX_SUBAUTHS)
+    comps = WINSEC_MAX_SUBAUTHS;
+
+  left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
+
+  for (i = 0; i < comps; i++) 
+    left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
+
+  return ret_val;
+}
+
+
+char* regfi_get_acl(WINSEC_ACL* acl)
+{
+  uint32_t i, extra, size = 0;
+  const char* type_str;
+  char* flags_str;
+  char* perms_str;
+  char* sid_str;
+  char* ace_delim = "";
+  char* ret_val = NULL;
+  char* tmp_val = NULL;
+  bool failed = false;
+  char field_delim = ':';
+
+  for (i = 0; i < acl->num_aces && !failed; i++)
+  {
+    sid_str = regfi_sid2str(acl->aces[i]->trustee);
+    type_str = regfi_ace_type2str(acl->aces[i]->type);
+    perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask);
+    flags_str = regfi_ace_flags2str(acl->aces[i]->flags);
+    
+    if(flags_str != NULL && perms_str != NULL 
+       && type_str != NULL && sid_str != NULL)
+    {
+      /* XXX: this is slow */
+      extra = strlen(sid_str) + strlen(type_str) 
+	+ strlen(perms_str) + strlen(flags_str) + 5;
+      tmp_val = realloc(ret_val, size+extra);
+
+      if(tmp_val == NULL)
+      {
+	free(ret_val);
+	ret_val = NULL;
+	failed = true;
+      }
+      else
+      {
+	ret_val = tmp_val;
+	size += sprintf(ret_val+size, "%s%s%c%s%c%s%c%s",
+			ace_delim,sid_str,
+			field_delim,type_str,
+			field_delim,perms_str,
+			field_delim,flags_str);
+	ace_delim = "|";
+      }
+    }
+    else
+      failed = true;
+
+    if(sid_str != NULL)
+      free(sid_str);
+    if(sid_str != NULL)
+      free(perms_str);
+    if(sid_str != NULL)
+      free(flags_str);
+  }
+
+  return ret_val;
+}
+
+
+char* regfi_get_sacl(WINSEC_DESC *sec_desc)
+{
+  if (sec_desc->sacl)
+    return regfi_get_acl(sec_desc->sacl);
+  else
+    return NULL;
+}
+
+
+char* regfi_get_dacl(WINSEC_DESC *sec_desc)
+{
+  if (sec_desc->dacl)
+    return regfi_get_acl(sec_desc->dacl);
+  else
+    return NULL;
+}
+
+
+char* regfi_get_owner(WINSEC_DESC *sec_desc)
+{
+  return regfi_sid2str(sec_desc->owner_sid);
+}
+
+
+char* regfi_get_group(WINSEC_DESC *sec_desc)
+{
+  return regfi_sid2str(sec_desc->grp_sid);
+}
+
+
+/*****************************************************************************
+ * This function is just like read(2), except that it continues to
+ * re-try reading from the file descriptor if EINTR or EAGAIN is received.  
+ * regfi_read will attempt to read length bytes from fd and write them to buf.
+ *
+ * On success, 0 is returned.  Upon failure, an errno code is returned.
+ *
+ * The number of bytes successfully read is returned through the length 
+ * parameter by reference.  If both the return value and length parameter are 
+ * returned as 0, then EOF was encountered immediately
+ *****************************************************************************/
+uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length)
+{
+  uint32_t rsize = 0;
+  uint32_t rret = 0;
+
+  do
+  {
+    rret = read(fd, buf + rsize, *length - rsize);
+    if(rret > 0)
+      rsize += rret;
+  }while(*length - rsize > 0 
+         && (rret > 0 || (rret == -1 && (errno == EAGAIN || errno == EINTR))));
+  
+  *length = rsize;
+  if (rret == -1 && errno != EINTR && errno != EAGAIN)
+    return errno;
+
+  return 0;
+}
+
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+bool regfi_parse_cell(int fd, uint32_t offset, uint8_t* hdr, uint32_t hdr_len,
+		      uint32_t* cell_length, bool* unalloc)
+{
+  uint32_t length;
+  int32_t raw_length;
+  uint8_t tmp[4];
+
+  if(lseek(fd, offset, SEEK_SET) == -1)
+    return false;
+
+  length = 4;
+  if((regfi_read(fd, tmp, &length) != 0) || length != 4)
+    return false;
+  raw_length = IVALS(tmp, 0);
+
+  if(raw_length < 0)
+  {
+    (*cell_length) = raw_length*(-1);
+    (*unalloc) = false;
+  }
+  else
+  {
+    (*cell_length) = raw_length;
+    (*unalloc) = true;
+  }
+
+  if(*cell_length - 4 < hdr_len)
+    return false;
+
+  if(hdr_len > 0)
+  {
+    length = hdr_len;
+    if((regfi_read(fd, hdr, &length) != 0) || length != hdr_len)
+      return false;
+  }
+
+  return true;
+}
+
+
+/******************************************************************************
+ * Given an offset and an hbin, is the offset within that hbin?
+ * The offset is a virtual file offset.
+ ******************************************************************************/
+static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32_t voffset)
+{
+  if(!hbin)
+    return false;
+
+  if((voffset > hbin->first_hbin_off) 
+     && (voffset < (hbin->first_hbin_off + hbin->block_size)))
+    return true;
+		
+  return false;
+}
+
+
+
+/******************************************************************************
+ * Provide a physical offset and receive the correpsonding HBIN
+ * block for it.  NULL if one doesn't exist.
+ ******************************************************************************/
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset)
+{
+  return (const REGFI_HBIN*)range_list_find_data(file->hbins, offset);
+}
+
+
+/******************************************************************************
+ * Calculate the largest possible cell size given a physical offset.
+ * Largest size is based on the HBIN the offset is currently a member of.
+ * Returns negative values on error.
+ * (Since cells can only be ~2^31 in size, this works out.)
+ ******************************************************************************/
+int32_t regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset)
+{
+  const REGFI_HBIN* hbin = regfi_lookup_hbin(file, offset);
+  if(hbin == NULL)
+    return -1;
+
+  return (hbin->block_size + hbin->file_off) - offset;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset, 
+					 uint32_t num_keys, uint32_t max_size, 
+					 bool strict)
+{
+  REGFI_SUBKEY_LIST* ret_val;
+
+  ret_val = regfi_load_subkeylist_aux(file, offset, max_size, strict, 
+				      REGFI_MAX_SUBKEY_DEPTH);
+  if(ret_val == NULL)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Failed to load subkey list at"
+		      " offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  if(num_keys != ret_val->num_keys)
+  {
+    /*  Not sure which should be authoritative, the number from the 
+     *  NK record, or the number in the subkey list.  Just emit a warning for
+     *  now if they don't match.
+     */
+    regfi_add_message(file, REGFI_MSG_WARN, "Number of subkeys listed in parent"
+		      " (%d) did not match number found in subkey list/tree (%d)"
+		      " while parsing subkey list/tree at offset 0x%.8X.", 
+		      num_keys, ret_val->num_keys, offset);
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset, 
+					     uint32_t max_size, bool strict,
+					     uint8_t depth_left)
+{
+  REGFI_SUBKEY_LIST* ret_val;
+  REGFI_SUBKEY_LIST** sublists;
+  uint32_t i, num_sublists, off;
+  int32_t sublist_maxsize;
+
+  if(depth_left == 0)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Maximum depth reached"
+		      " while parsing subkey list/tree at offset 0x%.8X.", 
+		      offset);
+    return NULL;
+  }
+
+  ret_val = regfi_parse_subkeylist(file, offset, max_size, strict);
+  if(ret_val == NULL)
+    return NULL;
+
+  if(ret_val->recursive_type)
+  {
+    num_sublists = ret_val->num_children;
+    sublists = (REGFI_SUBKEY_LIST**)malloc(num_sublists 
+					   * sizeof(REGFI_SUBKEY_LIST*));
+    for(i=0; i < num_sublists; i++)
+    {
+      off = ret_val->elements[i].offset + REGFI_REGF_SIZE;
+
+      sublist_maxsize = regfi_calc_maxsize(file, off);
+      if(sublist_maxsize < 0)
+	sublists[i] = NULL;
+      else
+	sublists[i] = regfi_load_subkeylist_aux(file, off, sublist_maxsize, 
+						strict, depth_left-1);
+    }
+    talloc_free(ret_val);
+
+    return regfi_merge_subkeylists(num_sublists, sublists, strict);
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset, 
+					  uint32_t max_size, bool strict)
+{
+  REGFI_SUBKEY_LIST* ret_val;
+  uint32_t i, cell_length, length, elem_size, read_len;
+  uint8_t* elements = NULL;
+  uint8_t buf[REGFI_SUBKEY_LIST_MIN_LEN];
+  bool unalloc;
+  bool recursive_type;
+
+  if(!regfi_parse_cell(file->fd, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN, 
+		       &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while "
+		      "parsing subkey-list at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  if(cell_length > max_size)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Cell size longer than max_size"
+		      " while parsing subkey-list at offset 0x%.8X.", offset);
+    if(strict)
+      return NULL;
+    cell_length = max_size & 0xFFFFFFF8;
+  }
+
+  recursive_type = false;
+  if(buf[0] == 'r' && buf[1] == 'i')
+  {
+    recursive_type = true;
+    elem_size = sizeof(uint32_t);
+  }
+  else if(buf[0] == 'l' && buf[1] == 'i')
+    elem_size = sizeof(uint32_t);
+  else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
+    elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
+  else
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Unknown magic number"
+		      " (0x%.2X, 0x%.2X) encountered while parsing"
+		      " subkey-list at offset 0x%.8X.", buf[0], buf[1], offset);
+    return NULL;
+  }
+
+  ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->offset = offset;
+  ret_val->cell_size = cell_length;
+  ret_val->magic[0] = buf[0];
+  ret_val->magic[1] = buf[1];
+  ret_val->recursive_type = recursive_type;
+  ret_val->num_children = SVAL(buf, 0x2);
+
+  if(!recursive_type)
+    ret_val->num_keys = ret_val->num_children;
+
+  length = elem_size*ret_val->num_children;
+  if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t) < length)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Number of elements too large for"
+		      " cell while parsing subkey-list at offset 0x%.8X.", 
+		      offset);
+    if(strict)
+      goto fail;
+    length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t);
+  }
+
+  ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, 
+				   ret_val->num_children);
+  if(ret_val->elements == NULL)
+    goto fail;
+
+  elements = (uint8_t*)malloc(length);
+  if(elements == NULL)
+    goto fail;
+
+  read_len = length;
+  if(regfi_read(file->fd, elements, &read_len) != 0 || read_len != length)
+    goto fail;
+
+  if(elem_size == sizeof(uint32_t))
+  {
+    for (i=0; i < ret_val->num_children; i++)
+    {
+      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
+      ret_val->elements[i].hash = 0;
+    }
+  }
+  else
+  {
+    for (i=0; i < ret_val->num_children; i++)
+    {
+      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
+      ret_val->elements[i].hash = IVAL(elements, i*elem_size+4);
+    }
+  }
+  free(elements);
+
+  return ret_val;
+
+ fail:
+  if(elements != NULL)
+    free(elements);
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists, 
+					   REGFI_SUBKEY_LIST** lists,
+					   bool strict)
+{
+  uint32_t i,j,k;
+  REGFI_SUBKEY_LIST* ret_val;
+
+  if(lists == NULL)
+    return NULL;
+  ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
+
+  if(ret_val == NULL)
+    return NULL;
+  
+  /* Obtain total number of elements */
+  ret_val->num_keys = 0;
+  for(i=0; i < num_lists; i++)
+  {
+    if(lists[i] != NULL)
+      ret_val->num_keys += lists[i]->num_children;
+  }
+  ret_val->num_children = ret_val->num_keys;
+
+  if(ret_val->num_keys > 0)
+  {
+    ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM,
+				     ret_val->num_keys);
+    k=0;
+
+    if(ret_val->elements != NULL)
+    {
+      for(i=0; i < num_lists; i++)
+      {
+	if(lists[i] != NULL)
+	{
+	  for(j=0; j < lists[i]->num_keys; j++)
+	  {
+	    ret_val->elements[k].hash = lists[i]->elements[j].hash;
+	    ret_val->elements[k++].offset = lists[i]->elements[j].offset;
+	  }
+	}
+      }
+    }
+  }
+  
+  for(i=0; i < num_lists; i++)
+    regfi_subkeylist_free(lists[i]);
+  free(lists);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, 
+			     bool strict)
+{
+  REGFI_SK_REC* ret_val;
+  uint8_t* sec_desc_buf = NULL;
+  uint32_t cell_length, length;
+  uint8_t sk_header[REGFI_SK_MIN_LENGTH];
+  bool unalloc = false;
+
+  if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
+		       &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse SK record cell"
+		      " at offset 0x%.8X.", offset);
+    return NULL;
+  }
+   
+  if(sk_header[0] != 's' || sk_header[1] != 'k')
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
+		      " SK record at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  ret_val = talloc(NULL, REGFI_SK_REC);
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->offset = offset;
+  /* XXX: Is there a way to be more conservative (shorter) with 
+   *      cell length when cell is unallocated?
+   */
+  ret_val->cell_size = cell_length;
+
+  if(ret_val->cell_size > max_size)
+    ret_val->cell_size = max_size & 0xFFFFFFF8;
+  if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
+     || (strict && (ret_val->cell_size & 0x00000007) != 0))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while"
+		      " parsing SK record at offset 0x%.8X.", offset);
+    goto fail;
+  }
+
+  ret_val->magic[0] = sk_header[0];
+  ret_val->magic[1] = sk_header[1];
+
+  ret_val->unknown_tag = SVAL(sk_header, 0x2);
+  ret_val->prev_sk_off = IVAL(sk_header, 0x4);
+  ret_val->next_sk_off = IVAL(sk_header, 0x8);
+  ret_val->ref_count = IVAL(sk_header, 0xC);
+  ret_val->desc_size = IVAL(sk_header, 0x10);
+
+  if((ret_val->prev_sk_off & 0x00000007) != 0
+     || (ret_val->next_sk_off & 0x00000007) != 0)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "SK record's next/previous offsets"
+		      " are not a multiple of 8 while parsing SK record at"
+		      " offset 0x%.8X.", offset);
+    goto fail;
+  }
+
+  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Security descriptor too large for"
+		      " cell while parsing SK record at offset 0x%.8X.", 
+		      offset);
+    goto fail;
+  }
+
+  sec_desc_buf = (uint8_t*)malloc(ret_val->desc_size);
+  if(sec_desc_buf == NULL)
+    goto fail;
+
+  length = ret_val->desc_size;
+  if(regfi_read(file->fd, sec_desc_buf, &length) != 0 
+     || length != ret_val->desc_size)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read security"
+		      " descriptor while parsing SK record at offset 0x%.8X.",
+		      offset);
+    goto fail;
+  }
+
+  if(!(ret_val->sec_desc = winsec_parse_desc(ret_val, sec_desc_buf, 
+						   ret_val->desc_size)))
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to parse security"
+		      " descriptor while parsing SK record at offset 0x%.8X.",
+		      offset);
+    goto fail;
+  }
+
+  free(sec_desc_buf);
+  return ret_val;
+
+ fail:
+  if(sec_desc_buf != NULL)
+    free(sec_desc_buf);
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset, 
+					uint32_t num_values, bool strict)
+{
+  REGFI_VALUE_LIST* ret_val;
+  uint32_t i, cell_length, length, read_len;
+  bool unalloc;
+
+  if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read cell header"
+		      " while parsing value list at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  if((cell_length & 0x00000007) != 0)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Cell length not a multiple of 8"
+		      " while parsing value list at offset 0x%.8X.", offset);
+    if(strict)
+      return NULL;
+    cell_length = cell_length & 0xFFFFFFF8;
+  }
+
+  if((num_values * sizeof(uint32_t)) > cell_length-sizeof(uint32_t))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Too many values found"
+		      " while parsing value list at offset 0x%.8X.", offset);
+    if(strict)
+      return NULL;
+    num_values = cell_length/sizeof(uint32_t) - sizeof(uint32_t);
+  }
+
+  read_len = num_values*sizeof(uint32_t);
+  ret_val = talloc(NULL, REGFI_VALUE_LIST);
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->elements = (REGFI_VALUE_LIST_ELEM*)talloc_size(ret_val, read_len);
+  if(ret_val->elements == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  ret_val->num_values = num_values;
+
+  length = read_len;
+  if((regfi_read(file->fd, (uint8_t*)ret_val->elements, &length) != 0) 
+     || length != read_len)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read value pointers"
+		      " while parsing value list at offset 0x%.8X.", offset);
+    talloc_free(ret_val);
+    return NULL;
+  }
+  
+  for(i=0; i < num_values; i++)
+  {
+    /* Fix endianness */
+    ret_val->elements[i] = IVAL(&ret_val->elements[i], 0);
+
+    /* Validate the first num_values values to ensure they make sense */
+    if(strict)
+    {
+      /* XXX: Need to revisit this file length check when we start dealing 
+       *      with partial files. */
+      if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length)
+	 || ((ret_val->elements[i] & 0x00000007) != 0))
+      {
+	regfi_add_message(file, REGFI_MSG_WARN, "Invalid value pointer"
+			  " (0x%.8X) found while parsing value list at offset"
+			  " 0x%.8X.", ret_val->elements[i], offset);
+	talloc_free(ret_val);
+	return NULL;
+      }
+    }
+  }
+
+  return ret_val;
+}
+
+
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk, 
+			       REGFI_ENCODING output_encoding, bool strict)
+{
+  /* XXX: Registry value names are supposedly limited to 16383 characters 
+   *      according to:
+   *      http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+   *      Might want to emit a warning if this is exceeded.  
+   *      It is expected that "characters" could be variable width.
+   *      Also, it may be useful to use this information to limit false positives
+   *      when recovering deleted VK records.
+   */
+  int32_t tmp_size;
+  REGFI_ENCODING from_encoding = (vk->flags & REGFI_VK_FLAG_ASCIINAME)
+    ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
+
+  if(from_encoding == output_encoding)
+  {
+    vk->valuename_raw = talloc_realloc(vk, vk->valuename_raw,
+					    uint8_t, vk->name_length+1);
+    vk->valuename_raw[vk->name_length] = '\0';
+    vk->valuename = (char*)vk->valuename_raw;
+  }
+  else
+  {
+    vk->valuename = talloc_array(vk, char, vk->name_length+1);
+    if(vk->valuename == NULL)
+    {
+      regfi_free_value(vk);
+      return;
+    }
+
+    tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+				  regfi_encoding_int2str(output_encoding),
+				  vk->valuename_raw, vk->valuename,
+				  vk->name_length, vk->name_length+1);
+    if(tmp_size < 0)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting"
+			" valuename to encoding %s.  Error message: %s",
+			regfi_encoding_int2str(output_encoding), 
+			strerror(-tmp_size));
+      talloc_free(vk->valuename);
+      vk->valuename = NULL;
+    }
+  }
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset, 
+			       REGFI_ENCODING output_encoding, bool strict)
+{
+  REGFI_VK_REC* ret_val = NULL;
+  int32_t max_size;
+
+  max_size = regfi_calc_maxsize(file, offset);
+  if(max_size < 0)
+    return NULL;
+  
+  ret_val = regfi_parse_vk(file, offset, max_size, strict);
+  if(ret_val == NULL)
+    return NULL;
+
+  regfi_interpret_valuename(file, ret_val, output_encoding, strict);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * If !strict, the list may contain NULLs, VK records may point to NULL.
+ ******************************************************************************/
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, 
+				       uint32_t num_values, uint32_t max_size,
+				       bool strict)
+{
+  uint32_t usable_num_values;
+
+  if((num_values+1) * sizeof(uint32_t) > max_size)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Number of values indicated by"
+		      " parent key (%d) would cause cell to straddle HBIN"
+		      " boundary while loading value list at offset"
+		      " 0x%.8X.", num_values, offset);
+    if(strict)
+      return NULL;
+    usable_num_values = max_size/sizeof(uint32_t) - sizeof(uint32_t);
+  }
+  else
+    usable_num_values = num_values;
+
+  return regfi_parse_valuelist(file, offset, usable_num_values, strict);
+}
+
+
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk, 
+			     REGFI_ENCODING output_encoding, bool strict)
+{
+  /* XXX: Registry key names are supposedly limited to 255 characters according to:
+   *      http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+   *      Might want to emit a warning if this is exceeded.  
+   *      It is expected that "characters" could be variable width.
+   *      Also, it may be useful to use this information to limit false positives
+   *      when recovering deleted NK records.
+   */
+  int32_t tmp_size;
+  REGFI_ENCODING from_encoding = (nk->flags & REGFI_NK_FLAG_ASCIINAME) 
+    ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
+  
+  if(from_encoding == output_encoding)
+  {
+    nk->keyname_raw = talloc_realloc(nk, nk->keyname_raw, uint8_t, nk->name_length+1);
+    nk->keyname_raw[nk->name_length] = '\0';
+    nk->keyname = (char*)nk->keyname_raw;
+  }
+  else
+  {
+    nk->keyname = talloc_array(nk, char, nk->name_length+1);
+    if(nk->keyname == NULL)
+    {
+      regfi_free_key(nk);
+      return;
+    }
+
+    tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+				  regfi_encoding_int2str(output_encoding),
+				  nk->keyname_raw, nk->keyname,
+				  nk->name_length, nk->name_length+1);
+    if(tmp_size < 0)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting"
+			" keyname to encoding %s.  Error message: %s",
+			regfi_encoding_int2str(output_encoding), 
+			strerror(-tmp_size));
+      talloc_free(nk->keyname);
+      nk->keyname = NULL;
+    }
+  }
+}
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset, 
+			     REGFI_ENCODING output_encoding, bool strict)
+{
+  REGFI_NK_REC* nk;
+  uint32_t off;
+  int32_t max_size;
+
+  max_size = regfi_calc_maxsize(file, offset);
+  if (max_size < 0) 
+    return NULL;
+
+  /* get the initial nk record */
+  if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at"
+		      " offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  regfi_interpret_keyname(file, nk, output_encoding, strict);
+
+  /* get value list */
+  if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) 
+  {
+    off = nk->values_off + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, off);
+    if(max_size < 0)
+    {
+      if(strict)
+      {
+	regfi_free_key(nk);
+	return NULL;
+      }
+      else
+	nk->values = NULL;
+
+    }
+    else
+    {
+      nk->values = regfi_load_valuelist(file, off, nk->num_values, 
+					max_size, true);
+      if(nk->values == NULL)
+      {
+	regfi_add_message(file, REGFI_MSG_WARN, "Could not load value list"
+			  " for NK record at offset 0x%.8X.", offset);
+	if(strict)
+	{
+	  regfi_free_key(nk);
+	  return NULL;
+	}
+      }
+      talloc_steal(nk, nk->values);
+    }
+  }
+
+  /* now get subkey list */
+  if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE)) 
+  {
+    off = nk->subkeys_off + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, off);
+    if(max_size < 0) 
+    {
+      if(strict)
+      {
+	regfi_free_key(nk);
+	return NULL;
+      }
+      else
+	nk->subkeys = NULL;
+    }
+    else
+    {
+      nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys,
+					  max_size, true);
+
+      if(nk->subkeys == NULL)
+      {
+	regfi_add_message(file, REGFI_MSG_WARN, "Could not load subkey list"
+			  " while parsing NK record at offset 0x%.8X.", offset);
+	nk->num_subkeys = 0;
+      }
+      talloc_steal(nk, nk->subkeys);
+    }
+  }
+
+  return nk;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict)
+{
+  REGFI_SK_REC* ret_val = NULL;
+  int32_t max_size;
+  void* failure_ptr = NULL;
+  
+  /* First look if we have already parsed it */
+  ret_val = (REGFI_SK_REC*)lru_cache_find(file->sk_cache, &offset, 4);
+
+  /* Bail out if we have previously cached a parse failure at this offset. */
+  if(ret_val == (void*)REGFI_OFFSET_NONE)
+    return NULL;
+
+  if(ret_val == NULL)
+  {
+    max_size = regfi_calc_maxsize(file, offset);
+    if(max_size < 0)
+      return NULL;
+
+    ret_val = regfi_parse_sk(file, offset, max_size, strict);
+    if(ret_val == NULL)
+    { /* Cache the parse failure and bail out. */
+      failure_ptr = talloc(NULL, uint32_t);
+      if(failure_ptr == NULL)
+	return NULL;
+      *(uint32_t*)failure_ptr = REGFI_OFFSET_NONE;
+      lru_cache_update(file->sk_cache, &offset, 4, failure_ptr);
+      return NULL;
+    }
+
+    lru_cache_update(file->sk_cache, &offset, 4, ret_val);
+  }
+
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_NK_REC* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin, 
+				 REGFI_ENCODING output_encoding)
+{
+  REGFI_NK_REC* nk = NULL;
+  uint32_t cell_length;
+  uint32_t cur_offset = hbin->file_off+REGFI_HBIN_HEADER_SIZE;
+  uint32_t hbin_end = hbin->file_off+hbin->block_size;
+  bool unalloc;
+
+  while(cur_offset < hbin_end)
+  {
+    if(!regfi_parse_cell(file->fd, cur_offset, NULL, 0, &cell_length, &unalloc))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell at offset"
+			" 0x%.8X while searching for root key.", cur_offset);
+      return NULL;
+    }
+    
+    if(!unalloc)
+    {
+      nk = regfi_load_key(file, cur_offset, output_encoding, true);
+      if(nk != NULL)
+      {
+	if(nk->flags & REGFI_NK_FLAG_ROOT)
+	  return nk;
+      }
+    }
+
+    cur_offset += cell_length;
+  }
+
+  return NULL;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_FILE* regfi_open(const char* filename)
+{
+  REGFI_FILE* ret_val;
+  int fd;
+
+  /* open an existing file */
+  if ((fd = open(filename, REGFI_OPEN_FLAGS)) == -1)
+  {
+    /* fprintf(stderr, "regfi_open: failure to open %s (%s)\n", filename, strerror(errno));*/
+    return NULL;
+  }
+
+  ret_val = regfi_alloc(fd);
+
+  if(ret_val == NULL)
+    close(fd);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_FILE* regfi_alloc(int fd)
+{
+  struct stat sbuf;
+  REGFI_FILE* rb;
+  REGFI_HBIN* hbin = NULL;
+  uint32_t hbin_off, file_length, cache_secret;
+  bool rla;
+
+  /* Determine file length.  Must be at least big enough 
+   * for the header and one hbin. 
+   */
+  if (fstat(fd, &sbuf) == -1)
+    return NULL;
+  file_length = sbuf.st_size;
+  if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
+    return NULL;
+
+  /* Read file header */
+  if ((rb = regfi_parse_regf(fd, true)) == NULL) 
+  {
+    /* fprintf(stderr, "regfi_alloc: Failed to read initial REGF block\n"); */
+    return NULL;
+  }
+  rb->file_length = file_length;  
+
+  rb->hbins = range_list_new();
+  if(rb->hbins == NULL)
+  {
+    /* fprintf(stderr, "regfi_alloc: Failed to create HBIN list.\n"); */
+    talloc_free(rb);
+    return NULL;
+  }
+  talloc_steal(rb, rb->hbins);
+
+  rla = true;
+  hbin_off = REGFI_REGF_SIZE;
+  hbin = regfi_parse_hbin(rb, hbin_off, true);
+  while(hbin && rla)
+  {
+    rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
+    if(rla)
+      talloc_steal(rb->hbins, hbin);
+    hbin_off = hbin->file_off + hbin->block_size;
+    hbin = regfi_parse_hbin(rb, hbin_off, true);
+  }
+
+  /* This secret isn't very secret, but we don't need a good one.  This 
+   * secret is just designed to prevent someone from trying to blow our
+   * caching and make things slow.
+   */
+  cache_secret = 0x15DEAD05^time(NULL)^(getpid()<<16);
+
+  /* Cache an unlimited number of SK records.  Typically there are very few. */
+  rb->sk_cache = lru_cache_create_ctx(rb, 0, cache_secret, true);
+
+  /* Default message mask */
+  rb->msg_mask = REGFI_MSG_ERROR|REGFI_MSG_WARN;
+
+  /* success */
+  return rb;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+int regfi_close(REGFI_FILE* file)
+{
+  int fd;
+
+  /* nothing to do if there is no open file */
+  if ((file == NULL) || (file->fd == -1))
+    return 0;
+
+  fd = file->fd;
+  file->fd = -1;
+
+  regfi_free(file);
+
+  return close(fd);
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_free(REGFI_FILE *file)
+{
+  if(file->last_message != NULL)
+    free(file->last_message);
+
+  talloc_free(file);
+}
+
+
+/******************************************************************************
+ * First checks the offset given by the file header, then checks the
+ * rest of the file if that fails.
+ ******************************************************************************/
+REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file, REGFI_ENCODING output_encoding)
+{
+  REGFI_NK_REC* nk = NULL;
+  REGFI_HBIN* hbin;
+  uint32_t root_offset, i, num_hbins;
+  
+  if(!file)
+    return NULL;
+
+  root_offset = file->root_cell+REGFI_REGF_SIZE;
+  nk = regfi_load_key(file, root_offset, output_encoding, true);
+  if(nk != NULL)
+  {
+    if(nk->flags & REGFI_NK_FLAG_ROOT)
+      return nk;
+  }
+
+  regfi_add_message(file, REGFI_MSG_WARN, "File header indicated root key at"
+		    " location 0x%.8X, but no root key found."
+		    " Searching rest of file...", root_offset);
+  
+  /* If the file header gives bad info, scan through the file one HBIN
+   * block at a time looking for an NK record with a root key type.
+   */
+  num_hbins = range_list_size(file->hbins);
+  for(i=0; i < num_hbins && nk == NULL; i++)
+  {
+    hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
+    nk = regfi_find_root_nk(file, hbin, output_encoding);
+  }
+
+  return nk;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_key(REGFI_NK_REC* nk)
+{
+  regfi_subkeylist_free(nk->subkeys);
+  talloc_free(nk);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_value(REGFI_VK_REC* vk)
+{
+  talloc_free(vk);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list)
+{
+  if(list != NULL)
+  {
+    talloc_free(list);
+  }
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file, 
+				   REGFI_ENCODING output_encoding)
+{
+  REGFI_NK_REC* root;
+  REGFI_ITERATOR* ret_val;
+
+  if(output_encoding != REGFI_ENCODING_UTF8
+     && output_encoding != REGFI_ENCODING_ASCII)
+  { 
+    regfi_add_message(file, REGFI_MSG_ERROR, "Invalid output_encoding supplied"
+		      " in creation of regfi iterator.");
+    return NULL;
+  }
+
+  ret_val = talloc(NULL, REGFI_ITERATOR);
+  if(ret_val == NULL)
+    return NULL;
+
+  root = regfi_rootkey(file, output_encoding);
+  if(root == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  ret_val->key_positions = void_stack_new(REGFI_MAX_DEPTH);
+  if(ret_val->key_positions == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  talloc_steal(ret_val, ret_val->key_positions);
+
+  ret_val->f = file;
+  ret_val->cur_key = root;
+  ret_val->cur_subkey = 0;
+  ret_val->cur_value = 0;
+  ret_val->string_encoding = output_encoding;
+    
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_iterator_free(REGFI_ITERATOR* i)
+{
+  talloc_free(i);
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+/* XXX: some way of indicating reason for failure should be added. */
+bool regfi_iterator_down(REGFI_ITERATOR* i)
+{
+  REGFI_NK_REC* subkey;
+  REGFI_ITER_POSITION* pos;
+
+  pos = talloc(i->key_positions, REGFI_ITER_POSITION);
+  if(pos == NULL)
+    return false;
+
+  subkey = (REGFI_NK_REC*)regfi_iterator_cur_subkey(i);
+  if(subkey == NULL)
+  {
+    talloc_free(pos);
+    return false;
+  }
+
+  pos->nk = i->cur_key;
+  pos->cur_subkey = i->cur_subkey;
+  if(!void_stack_push(i->key_positions, pos))
+  {
+    talloc_free(pos);
+    regfi_free_key(subkey);
+    return false;
+  }
+  talloc_steal(i, subkey);
+
+  i->cur_key = subkey;
+  i->cur_subkey = 0;
+  i->cur_value = 0;
+
+  return true;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_up(REGFI_ITERATOR* i)
+{
+  REGFI_ITER_POSITION* pos;
+
+  pos = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions);
+  if(pos == NULL)
+    return false;
+
+  regfi_free_key(i->cur_key);
+  i->cur_key = pos->nk;
+  i->cur_subkey = pos->cur_subkey;
+  i->cur_value = 0;
+  talloc_free(pos);
+
+  return true;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_to_root(REGFI_ITERATOR* i)
+{
+  while(regfi_iterator_up(i))
+    continue;
+
+  return true;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
+{
+  REGFI_NK_REC* subkey;
+  bool found = false;
+  uint32_t old_subkey = i->cur_subkey;
+
+  if(subkey_name == NULL)
+    return false;
+
+  /* XXX: this alloc/free of each sub key might be a bit excessive */
+  subkey = (REGFI_NK_REC*)regfi_iterator_first_subkey(i);
+  while((subkey != NULL) && (found == false))
+  {
+    if(subkey->keyname != NULL 
+       && strcasecmp(subkey->keyname, subkey_name) == 0)
+      found = true;
+    else
+    {
+      regfi_free_key(subkey);
+      subkey = (REGFI_NK_REC*)regfi_iterator_next_subkey(i);
+    }
+  }
+
+  if(found == false)
+  {
+    i->cur_subkey = old_subkey;
+    return false;
+  }
+
+  regfi_free_key(subkey);
+  return true;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
+{
+  uint32_t x;
+  if(path == NULL)
+    return false;
+
+  for(x=0; 
+      ((path[x] != NULL) && regfi_iterator_find_subkey(i, path[x])
+       && regfi_iterator_down(i));
+      x++)
+  { continue; }
+
+  if(path[x] == NULL)
+    return true;
+  
+  /* XXX: is this the right number of times? */
+  for(; x > 0; x--)
+    regfi_iterator_up(i);
+  
+  return false;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
+{
+  return i->cur_key;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
+{
+  if(i->cur_key == NULL || i->cur_key->sk_off == REGFI_OFFSET_NONE)
+    return NULL;
+
+  return regfi_load_sk(i->f, i->cur_key->sk_off + REGFI_REGF_SIZE, true);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
+{
+  i->cur_subkey = 0;
+  return regfi_iterator_cur_subkey(i);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
+{
+  uint32_t nk_offset;
+
+  /* see if there is anything left to report */
+  if (!(i->cur_key) || (i->cur_key->subkeys_off==REGFI_OFFSET_NONE)
+      || (i->cur_subkey >= i->cur_key->num_subkeys))
+    return NULL;
+
+  nk_offset = i->cur_key->subkeys->elements[i->cur_subkey].offset;
+
+  return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, i->string_encoding, 
+			true);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+/* XXX: some way of indicating reason for failure should be added. */
+REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
+{
+  REGFI_NK_REC* subkey;
+
+  i->cur_subkey++;
+  subkey = regfi_iterator_cur_subkey(i);
+
+  if(subkey == NULL)
+    i->cur_subkey--;
+
+  return subkey;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
+{
+  REGFI_VK_REC* cur;
+  bool found = false;
+  uint32_t old_value = i->cur_value;
+
+  /* XXX: cur->valuename can be NULL in the registry.  
+   *      Should we allow for a way to search for that? 
+   */
+  if(value_name == NULL)
+    return false;
+
+  cur = regfi_iterator_first_value(i);
+  while((cur != NULL) && (found == false))
+  {
+    if((cur->valuename != NULL)
+       && (strcasecmp(cur->valuename, value_name) == 0))
+      found = true;
+    else
+    {
+      regfi_free_value(cur);
+      cur = regfi_iterator_next_value(i);
+    }
+  }
+  
+  if(found == false)
+  {
+    i->cur_value = old_value;
+    return false;
+  }
+
+  regfi_free_value(cur);
+  return true;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
+{
+  i->cur_value = 0;
+  return regfi_iterator_cur_value(i);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
+{
+  REGFI_VK_REC* ret_val = NULL;
+  uint32_t voffset;
+
+  if(i->cur_key->values != NULL && i->cur_key->values->elements != NULL)
+  {
+    if(i->cur_value < i->cur_key->values->num_values)
+    {
+      voffset = i->cur_key->values->elements[i->cur_value];
+      ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE, 
+				 i->string_encoding, true);
+    }
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
+{
+  REGFI_VK_REC* ret_val;
+
+  i->cur_value++;
+  ret_val = regfi_iterator_cur_value(i);
+  if(ret_val == NULL)
+    i->cur_value--;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i, 
+						const REGFI_NK_REC* key)
+{
+  REGFI_CLASSNAME* ret_val;
+  uint8_t* raw;
+  char* interpreted;
+  uint32_t offset;
+  int32_t conv_size, max_size;
+  uint16_t parse_length;
+
+  if(key->classname_off == REGFI_OFFSET_NONE || key->classname_length == 0)
+    return NULL;
+
+  offset = key->classname_off + REGFI_REGF_SIZE;
+  max_size = regfi_calc_maxsize(i->f, offset);
+  if(max_size <= 0)
+    return NULL;
+
+  parse_length = key->classname_length;
+  raw = regfi_parse_classname(i->f, offset, &parse_length, max_size, true);
+  
+  if(raw == NULL)
+  {
+    regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse class"
+		      " name at offset 0x%.8X for key record at offset 0x%.8X.",
+		      offset, key->offset);
+    return NULL;
+  }
+
+  ret_val = talloc(NULL, REGFI_CLASSNAME);
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->raw = raw;
+  ret_val->size = parse_length;
+  talloc_steal(ret_val, raw);
+
+  interpreted = talloc_array(NULL, char, parse_length);
+
+  conv_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+				 regfi_encoding_int2str(i->string_encoding),
+				 raw, interpreted,
+				 parse_length, parse_length);
+  if(conv_size < 0)
+  {
+    regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred while"
+		      " converting classname to charset %s.  Error message: %s",
+		      i->string_encoding, strerror(-conv_size));
+    talloc_free(interpreted);
+    ret_val->interpreted = NULL;
+  }
+  else
+  {
+    interpreted = talloc_realloc(NULL, interpreted, char, conv_size);
+    ret_val->interpreted = interpreted;
+    talloc_steal(ret_val, interpreted);
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i, 
+				      const REGFI_VK_REC* value)
+{
+  REGFI_DATA* ret_val = NULL;
+  REGFI_BUFFER raw_data;
+
+  if(value->data_size != 0)
+  {
+    raw_data = regfi_load_data(i->f, value->data_off, value->data_size,
+			      value->data_in_offset, true);
+    if(raw_data.buf == NULL)
+    {
+      regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse data record"
+			" while parsing VK record at offset 0x%.8X.",
+			value->offset);
+    }
+    else
+    {
+      ret_val = regfi_buffer_to_data(raw_data);
+
+      if(ret_val == NULL)
+      {
+	regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred in converting"
+			  " data buffer to data structure while interpreting "
+			  "data for VK record at offset 0x%.8X.",
+			  value->offset);
+	talloc_free(raw_data.buf);
+	return NULL;
+      }
+
+      if(!regfi_interpret_data(i->f, i->string_encoding, value->type, ret_val))
+      {
+	regfi_add_message(i->f, REGFI_MSG_INFO, "Error occurred while"
+			  " interpreting data for VK record at offset 0x%.8X.",
+			  value->offset);
+      }
+    }
+  }
+  
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_classname(REGFI_CLASSNAME* classname)
+{
+  talloc_free(classname);
+}
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_data(REGFI_DATA* data)
+{
+  talloc_free(data);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
+{
+  REGFI_DATA* ret_val;
+
+  if(raw_data.buf == NULL)
+    return NULL;
+
+  ret_val = talloc(NULL, REGFI_DATA);
+  if(ret_val == NULL)
+    return NULL;
+  
+  talloc_steal(ret_val, raw_data.buf);
+  ret_val->raw = raw_data.buf;
+  ret_val->size = raw_data.len;
+  ret_val->interpreted_size = 0;
+  ret_val->interpreted.qword = 0;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
+			  uint32_t type, REGFI_DATA* data)
+{
+  uint8_t** tmp_array;
+  uint8_t* tmp_str;
+  int32_t tmp_size;
+  uint32_t i, j, array_size;
+
+  if(data == NULL)
+    return false;
+
+  switch (type)
+  {
+  case REG_SZ:
+  case REG_EXPAND_SZ:
+  /* REG_LINK is a symbolic link, stored as a unicode string. */
+  case REG_LINK:
+    tmp_str = talloc_array(NULL, uint8_t, data->size);
+    if(tmp_str == NULL)
+    {
+      data->interpreted.string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+      
+    tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+				  regfi_encoding_int2str(string_encoding),
+				  data->raw, (char*)tmp_str, 
+				  data->size, data->size);
+    if(tmp_size < 0)
+    {
+      regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while"
+			" converting data of type %d to %s.  Error message: %s",
+			type, string_encoding, strerror(-tmp_size));
+      talloc_free(tmp_str);
+      data->interpreted.string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+
+    tmp_str = talloc_realloc(NULL, tmp_str, uint8_t, tmp_size);
+    data->interpreted.string = tmp_str;
+    data->interpreted_size = tmp_size;
+    talloc_steal(data, tmp_str);
+    break;
+
+  case REG_DWORD:
+    if(data->size < 4)
+    {
+      data->interpreted.dword = 0;
+      data->interpreted_size = 0;
+      return false;
+    }
+    data->interpreted.dword = IVAL(data->raw, 0);
+    data->interpreted_size = 4;
+    break;
+
+  case REG_DWORD_BE:
+    if(data->size < 4)
+    {
+      data->interpreted.dword_be = 0;
+      data->interpreted_size = 0;
+      return false;
+    }
+    data->interpreted.dword_be = RIVAL(data->raw, 0);
+    data->interpreted_size = 4;
+    break;
+
+  case REG_QWORD:
+    if(data->size < 8)
+    {
+      data->interpreted.qword = 0;
+      data->interpreted_size = 0;
+      return false;
+    }
+    data->interpreted.qword = 
+      (uint64_t)IVAL(data->raw, 0) + (((uint64_t)IVAL(data->raw, 4))<<32);
+    data->interpreted_size = 8;
+    break;
+    
+  case REG_MULTI_SZ:
+    tmp_str = talloc_array(NULL, uint8_t, data->size);
+    if(tmp_str == NULL)
+    {
+      data->interpreted.multiple_string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+
+    /* Attempt to convert entire string from UTF-16LE to output encoding,
+     * then parse and quote fields individually.
+     */
+    tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+				  regfi_encoding_int2str(string_encoding),
+				  data->raw, (char*)tmp_str,
+				  data->size, data->size);
+    if(tmp_size < 0)
+    {
+      regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while"
+			" converting data of type %d to %s.  Error message: %s",
+			type, string_encoding, strerror(-tmp_size));
+      talloc_free(tmp_str);
+      data->interpreted.multiple_string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+
+    array_size = tmp_size+1;
+    tmp_array = talloc_array(NULL, uint8_t*, array_size);
+    if(tmp_array == NULL)
+    {
+      talloc_free(tmp_str);
+      data->interpreted.string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+    
+    tmp_array[0] = tmp_str;
+    for(i=0,j=1; i < tmp_size && j < array_size-1; i++)
+    {
+      if(tmp_str[i] == '\0' && (i+1 < tmp_size))
+	tmp_array[j++] = tmp_str+i+1;
+    }
+    tmp_array[j] = NULL;
+    tmp_array = talloc_realloc(NULL, tmp_array, uint8_t*, j+1);
+    data->interpreted.multiple_string = tmp_array;
+    /* XXX: how meaningful is this?  should we store number of strings instead? */
+    data->interpreted_size = tmp_size;
+    talloc_steal(tmp_array, tmp_str);
+    talloc_steal(data, tmp_array);
+    break;
+
+  /* XXX: Dont know how to interpret these yet, just treat as binary */
+  case REG_NONE:
+    data->interpreted.none = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_RESOURCE_LIST:
+    data->interpreted.resource_list = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_FULL_RESOURCE_DESCRIPTOR:
+    data->interpreted.full_resource_descriptor = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_RESOURCE_REQUIREMENTS_LIST:
+    data->interpreted.resource_requirements_list = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_BINARY:
+    data->interpreted.binary = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  default:
+    data->interpreted.qword = 0;
+    data->interpreted_size = 0;
+    return false;
+  }
+
+  data->type = type;
+  return true;
+}
+
+
+/******************************************************************************
+ * Convert from UTF-16LE to specified character set. 
+ * On error, returns a negative errno code.
+ *****************************************************************************/
+int32_t regfi_conv_charset(const char* input_charset, const char* output_charset,
+			 uint8_t* input, char* output, 
+			 uint32_t input_len, uint32_t output_max)
+{
+  iconv_t conv_desc;
+  char* inbuf = (char*)input;
+  char* outbuf = output;
+  size_t in_len = (size_t)input_len;
+  size_t out_len = (size_t)(output_max-1);
+  int ret;
+
+  /* XXX: Consider creating a couple of conversion descriptors earlier,
+   *      storing them on an iterator so they don't have to be recreated
+   *      each time.
+   */
+
+  /* Set up conversion descriptor. */
+  conv_desc = iconv_open(output_charset, input_charset);
+
+  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
+  if(ret == -1)
+  {
+    iconv_close(conv_desc);
+    return -errno;
+  }
+  *outbuf = '\0';
+
+  iconv_close(conv_desc);  
+  return output_max-out_len-1;
+}
+
+
+
+/*******************************************************************
+ * Computes the checksum of the registry file header.
+ * buffer must be at least the size of a regf header (4096 bytes).
+ *******************************************************************/
+static uint32_t regfi_compute_header_checksum(uint8_t* buffer)
+{
+  uint32_t checksum, x;
+  int i;
+
+  /* XOR of all bytes 0x0000 - 0x01FB */
+
+  checksum = x = 0;
+  
+  for ( i=0; i<0x01FB; i+=4 ) {
+    x = IVAL(buffer, i );
+    checksum ^= x;
+  }
+  
+  return checksum;
+}
+
+
+/*******************************************************************
+ * XXX: Add way to return more detailed error information.
+ *******************************************************************/
+REGFI_FILE* regfi_parse_regf(int fd, bool strict)
+{
+  uint8_t file_header[REGFI_REGF_SIZE];
+  uint32_t length;
+  REGFI_FILE* ret_val;
+
+  ret_val = talloc(NULL, REGFI_FILE);
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->fd = fd;
+  ret_val->sk_cache = NULL;
+  ret_val->last_message = NULL;
+  ret_val->hbins = NULL;
+  
+  length = REGFI_REGF_SIZE;
+  if((regfi_read(fd, file_header, &length)) != 0 || length != REGFI_REGF_SIZE)
+    goto fail;
+  
+  ret_val->checksum = IVAL(file_header, 0x1FC);
+  ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
+  if (strict && (ret_val->checksum != ret_val->computed_checksum))
+    goto fail;
+
+  memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE);
+  if(memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0)
+  {
+    if(strict)
+      goto fail;
+    regfi_add_message(ret_val, REGFI_MSG_WARN, "Magic number mismatch "
+		      "(%.2X %.2X %.2X %.2X) while parsing hive header",
+		      ret_val->magic[0], ret_val->magic[1], 
+		      ret_val->magic[2], ret_val->magic[3]);
+  }
+  ret_val->sequence1 = IVAL(file_header, 0x4);
+  ret_val->sequence2 = IVAL(file_header, 0x8);
+  ret_val->mtime.low = IVAL(file_header, 0xC);
+  ret_val->mtime.high = IVAL(file_header, 0x10);
+  ret_val->major_version = IVAL(file_header, 0x14);
+  ret_val->minor_version = IVAL(file_header, 0x18);
+  ret_val->type = IVAL(file_header, 0x1C);
+  ret_val->format = IVAL(file_header, 0x20);
+  ret_val->root_cell = IVAL(file_header, 0x24);
+  ret_val->last_block = IVAL(file_header, 0x28);
+
+  ret_val->cluster = IVAL(file_header, 0x2C);
+
+  memcpy(ret_val->file_name, file_header+0x30,  REGFI_REGF_NAME_SIZE);
+
+  /* XXX: Should we add a warning if these uuid parsers fail?  Can they? */
+  ret_val->rm_id = winsec_parse_uuid(ret_val, file_header+0x70, 16);
+  ret_val->log_id = winsec_parse_uuid(ret_val, file_header+0x80, 16);
+  ret_val->flags = IVAL(file_header, 0x90);
+  ret_val->tm_id = winsec_parse_uuid(ret_val, file_header+0x94, 16);
+  ret_val->guid_signature = IVAL(file_header, 0xa4);
+
+  memcpy(ret_val->reserved1, file_header+0xa8, REGFI_REGF_RESERVED1_SIZE);
+  memcpy(ret_val->reserved2, file_header+0x200, REGFI_REGF_RESERVED2_SIZE);
+
+  ret_val->thaw_tm_id = winsec_parse_uuid(ret_val, file_header+0xFC8, 16);
+  ret_val->thaw_rm_id = winsec_parse_uuid(ret_val, file_header+0xFD8, 16);
+  ret_val->thaw_log_id = winsec_parse_uuid(ret_val, file_header+0xFE8, 16);
+  ret_val->boot_type = IVAL(file_header, 0xFF8);
+  ret_val->boot_recover = IVAL(file_header, 0xFFC);
+
+  return ret_val;
+
+ fail:
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+
+/******************************************************************************
+ * Given real file offset, read and parse the hbin at that location
+ * along with it's associated cells.
+ ******************************************************************************/
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict)
+{
+  REGFI_HBIN *hbin;
+  uint8_t hbin_header[REGFI_HBIN_HEADER_SIZE];
+  uint32_t length;
+  
+  if(offset >= file->file_length)
+    return NULL;
+
+  if(lseek(file->fd, offset, SEEK_SET) == -1)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
+		      " while parsing hbin at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  length = REGFI_HBIN_HEADER_SIZE;
+  if((regfi_read(file->fd, hbin_header, &length) != 0) 
+     || length != REGFI_HBIN_HEADER_SIZE)
+    return NULL;
+
+  if(lseek(file->fd, offset, SEEK_SET) == -1)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
+		      " while parsing hbin at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  hbin = talloc(NULL, REGFI_HBIN);
+  if(hbin == NULL)
+    return NULL;
+  hbin->file_off = offset;
+
+  memcpy(hbin->magic, hbin_header, 4);
+  if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
+  {
+    regfi_add_message(file, REGFI_MSG_INFO, "Magic number mismatch "
+		      "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
+		      " 0x%.8X.", hbin->magic[0], hbin->magic[1], 
+		      hbin->magic[2], hbin->magic[3], offset);
+    talloc_free(hbin);
+    return NULL;
+  }
+
+  hbin->first_hbin_off = IVAL(hbin_header, 0x4);
+  hbin->block_size = IVAL(hbin_header, 0x8);
+  /* this should be the same thing as hbin->block_size but just in case */
+  hbin->next_block = IVAL(hbin_header, 0x1C);
+
+
+  /* Ensure the block size is a multiple of 0x1000 and doesn't run off 
+   * the end of the file. 
+   */
+  /* XXX: This may need to be relaxed for dealing with 
+   *      partial or corrupt files. 
+   */
+  if((offset + hbin->block_size > file->file_length)
+     || (hbin->block_size & 0xFFFFF000) != hbin->block_size)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "The hbin offset is not aligned"
+		      " or runs off the end of the file"
+		      " while parsing hbin at offset 0x%.8X.", offset);
+    talloc_free(hbin);
+    return NULL;
+  }
+
+  return hbin;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset, 
+			     uint32_t max_size, bool strict)
+{
+  uint8_t nk_header[REGFI_NK_MIN_LENGTH];
+  REGFI_NK_REC* ret_val;
+  uint32_t length,cell_length;
+  bool unalloc = false;
+
+  if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
+		       &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
+		      " while parsing NK record at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  /* A bit of validation before bothering to allocate memory */
+  if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
+		      " NK record at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  ret_val = talloc(NULL, REGFI_NK_REC);
+  if(ret_val == NULL)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to allocate memory while"
+		      " parsing NK record at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  ret_val->values = NULL;
+  ret_val->subkeys = NULL;
+  ret_val->offset = offset;
+  ret_val->cell_size = cell_length;
+
+  if(ret_val->cell_size > max_size)
+    ret_val->cell_size = max_size & 0xFFFFFFF8;
+  if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
+     || (strict && (ret_val->cell_size & 0x00000007) != 0))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "A length check failed while"
+		      " parsing NK record at offset 0x%.8X.", offset);
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  ret_val->magic[0] = nk_header[0x0];
+  ret_val->magic[1] = nk_header[0x1];
+  ret_val->flags = SVAL(nk_header, 0x2);
+  
+  if((ret_val->flags & ~REGFI_NK_KNOWN_FLAGS) != 0)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Unknown key flags (0x%.4X) while"
+		      " parsing NK record at offset 0x%.8X.", 
+		      (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset);
+  }
+
+  ret_val->mtime.low = IVAL(nk_header, 0x4);
+  ret_val->mtime.high = IVAL(nk_header, 0x8);
+  /* If the key is unallocated and the MTIME is earlier than Jan 1, 1990
+   * or later than Jan 1, 2290, we consider this a bad key.  This helps
+   * weed out some false positives during deleted data recovery.
+   */
+  if(unalloc
+     && ((ret_val->mtime.high < REGFI_MTIME_MIN_HIGH 
+	  && ret_val->mtime.low < REGFI_MTIME_MIN_LOW)
+	 || (ret_val->mtime.high > REGFI_MTIME_MAX_HIGH 
+	     && ret_val->mtime.low > REGFI_MTIME_MAX_LOW)))
+    return NULL;
+
+  ret_val->unknown1 = IVAL(nk_header, 0xC);
+  ret_val->parent_off = IVAL(nk_header, 0x10);
+  ret_val->num_subkeys = IVAL(nk_header, 0x14);
+  ret_val->unknown2 = IVAL(nk_header, 0x18);
+  ret_val->subkeys_off = IVAL(nk_header, 0x1C);
+  ret_val->unknown3 = IVAL(nk_header, 0x20);
+  ret_val->num_values = IVAL(nk_header, 0x24);
+  ret_val->values_off = IVAL(nk_header, 0x28);
+  ret_val->sk_off = IVAL(nk_header, 0x2C);
+  ret_val->classname_off = IVAL(nk_header, 0x30);
+
+  ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34);
+  ret_val->max_bytes_subkeyclassname = IVAL(nk_header, 0x38);
+  ret_val->max_bytes_valuename = IVAL(nk_header, 0x3C);
+  ret_val->max_bytes_value = IVAL(nk_header, 0x40);
+  ret_val->unk_index = IVAL(nk_header, 0x44);
+
+  ret_val->name_length = SVAL(nk_header, 0x48);
+  ret_val->classname_length = SVAL(nk_header, 0x4A);
+  ret_val->keyname = NULL;
+
+  if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
+  {
+    if(strict)
+    {
+      regfi_add_message(file, REGFI_MSG_ERROR, "Contents too large for cell"
+			" while parsing NK record at offset 0x%.8X.", offset);
+      talloc_free(ret_val);
+      return NULL;
+    }
+    else
+      ret_val->name_length = ret_val->cell_size - REGFI_NK_MIN_LENGTH;
+  }
+  else if (unalloc)
+  { /* Truncate cell_size if it's much larger than the apparent total record length. */
+    /* Round up to the next multiple of 8 */
+    length = (ret_val->name_length + REGFI_NK_MIN_LENGTH) & 0xFFFFFFF8;
+    if(length < ret_val->name_length + REGFI_NK_MIN_LENGTH)
+      length+=8;
+
+    /* If cell_size is still greater, truncate. */
+    if(length < ret_val->cell_size)
+      ret_val->cell_size = length;
+  }
+
+  ret_val->keyname_raw = talloc_array(ret_val, uint8_t, ret_val->name_length);
+  if(ret_val->keyname_raw == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  /* Don't need to seek, should be at the right offset */
+  length = ret_val->name_length;
+  if((regfi_read(file->fd, (uint8_t*)ret_val->keyname_raw, &length) != 0)
+     || length != ret_val->name_length)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read key name"
+		      " while parsing NK record at offset 0x%.8X.", offset);
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  return ret_val;
+}
+
+
+uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset, 
+			     uint16_t* name_length, uint32_t max_size, bool strict)
+{
+  uint8_t* ret_val = NULL;
+  uint32_t length;
+  uint32_t cell_length;
+  bool unalloc = false;
+
+  if(*name_length > 0 && offset != REGFI_OFFSET_NONE 
+     && (offset & 0x00000007) == 0)
+  {
+    if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
+			" while parsing class name at offset 0x%.8X.", offset);
+	return NULL;
+    }
+
+    if((cell_length & 0x0000007) != 0)
+    {
+      regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8"
+			" while parsing class name at offset 0x%.8X.", offset);
+      return NULL;
+    }
+
+    if(cell_length > max_size)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Cell stretches past hbin "
+			"boundary while parsing class name at offset 0x%.8X.",
+			offset);
+      if(strict)
+	return NULL;
+      cell_length = max_size;
+    }
+
+    if((cell_length - 4) < *name_length)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Class name is larger than"
+			" cell_length while parsing class name at offset"
+			" 0x%.8X.", offset);
+      if(strict)
+	return NULL;
+      *name_length = cell_length - 4;
+    }
+    
+    ret_val = talloc_array(NULL, uint8_t, *name_length);
+    if(ret_val != NULL)
+    {
+      length = *name_length;
+      if((regfi_read(file->fd, ret_val, &length) != 0)
+	 || length != *name_length)
+      {
+	regfi_add_message(file, REGFI_MSG_ERROR, "Could not read class name"
+			  " while parsing class name at offset 0x%.8X.", offset);
+	talloc_free(ret_val);
+	return NULL;
+      }
+    }
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
+			     uint32_t max_size, bool strict)
+{
+  REGFI_VK_REC* ret_val;
+  uint8_t vk_header[REGFI_VK_MIN_LENGTH];
+  uint32_t raw_data_size, length, cell_length;
+  bool unalloc = false;
+
+  if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
+		       &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
+		      " while parsing VK record at offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  ret_val = talloc(NULL, REGFI_VK_REC);
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->offset = offset;
+  ret_val->cell_size = cell_length;
+  ret_val->valuename = NULL;
+  ret_val->valuename_raw = NULL;
+  
+  if(ret_val->cell_size > max_size)
+    ret_val->cell_size = max_size & 0xFFFFFFF8;
+  if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
+     || (ret_val->cell_size & 0x00000007) != 0)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered"
+		      " while parsing VK record at offset 0x%.8X.", offset);
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  ret_val->magic[0] = vk_header[0x0];
+  ret_val->magic[1] = vk_header[0x1];
+  if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k'))
+  {
+    /* XXX: This does not account for deleted keys under Win2K which
+     *      often have this (and the name length) overwritten with
+     *      0xFFFF. 
+     */
+    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch"
+		      " while parsing VK record at offset 0x%.8X.", offset);
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  ret_val->name_length = SVAL(vk_header, 0x2);
+  raw_data_size = IVAL(vk_header, 0x4);
+  ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET;
+  /* The data is typically stored in the offset if the size <= 4, 
+   * in which case this flag is set. 
+   */
+  ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET);
+  ret_val->data_off = IVAL(vk_header, 0x8);
+  ret_val->type = IVAL(vk_header, 0xC);
+  ret_val->flags = SVAL(vk_header, 0x10);
+  ret_val->unknown1 = SVAL(vk_header, 0x12);
+
+  if(ret_val->name_length > 0)
+  {
+    if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Name too long for remaining cell"
+			" space while parsing VK record at offset 0x%.8X.",
+			offset);
+      if(strict)
+      {
+	talloc_free(ret_val);
+	return NULL;
+      }
+      else
+	ret_val->name_length = ret_val->cell_size - REGFI_VK_MIN_LENGTH - 4;
+    }
+
+    /* Round up to the next multiple of 8 */
+    cell_length = (ret_val->name_length + REGFI_VK_MIN_LENGTH + 4) & 0xFFFFFFF8;
+    if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4)
+      cell_length+=8;
+
+    ret_val->valuename_raw = talloc_array(ret_val, uint8_t, ret_val->name_length);
+    if(ret_val->valuename_raw == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+
+    length = ret_val->name_length;
+    if((regfi_read(file->fd, (uint8_t*)ret_val->valuename_raw, &length) != 0)
+       || length != ret_val->name_length)
+    {
+      regfi_add_message(file, REGFI_MSG_ERROR, "Could not read value name"
+			" while parsing VK record at offset 0x%.8X.", offset);
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+  else
+    cell_length = REGFI_VK_MIN_LENGTH + 4;
+
+  if(unalloc)
+  {
+    /* If cell_size is still greater, truncate. */
+    if(cell_length < ret_val->cell_size)
+      ret_val->cell_size = cell_length;
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
+			     uint32_t length, bool data_in_offset,
+			     bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint32_t cell_length, offset;
+  int32_t max_size;
+  bool unalloc;
+  
+  /* Microsoft's documentation indicates that "available memory" is 
+   * the limit on value sizes for the more recent registry format version.
+   * This is not only annoying, but it's probably also incorrect, since clearly
+   * value data sizes are limited to 2^31 (high bit used as a flag) and even 
+   * with big data records, the apparent max size is:
+   *   16344 * 2^16 = 1071104040 (~1GB).
+   *
+   * We choose to limit it to 1M which was the limit in older versions and 
+   * should rarely be exceeded unless the file is corrupt or malicious. 
+   * For more info, see:
+   *   http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+   */
+  /* XXX: add way to skip this check at user discression. */
+  if(length > REGFI_VK_MAX_DATA_LENGTH)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Value data size %d larger than "
+		      "%d, truncating...", length, REGFI_VK_MAX_DATA_LENGTH);
+    length = REGFI_VK_MAX_DATA_LENGTH;
+  }
+
+  if(data_in_offset)
+    return regfi_parse_little_data(file, voffset, length, strict);
+  else
+  {
+    offset = voffset + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, offset);
+    if(max_size < 0)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
+			" at offset 0x%.8X.", offset);
+      goto fail;
+    }
+    
+    if(!regfi_parse_cell(file->fd, offset, NULL, 0,
+			 &cell_length, &unalloc))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+			" parsing data record at offset 0x%.8X.", offset);
+      goto fail;
+    }
+
+    if((cell_length & 0x00000007) != 0)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8"
+			" while parsing data record at offset 0x%.8X.",
+			offset);
+      goto fail;
+    }
+
+    if(cell_length > max_size)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Cell extends past HBIN boundary"
+			" while parsing data record at offset 0x%.8X.",
+			offset);
+      goto fail;
+    }
+
+    if(cell_length - 4 < length)
+    {
+      /* XXX: All big data records thus far have been 16 bytes long.  
+       *      Should we check for this precise size instead of just 
+       *      relying upon the above check?
+       */
+      if (file->major_version >= 1 && file->minor_version >= 5)
+      {
+	/* Attempt to parse a big data record */
+	return regfi_load_big_data(file, offset, length, cell_length, 
+				   NULL, strict);
+      }
+      else
+      {
+	regfi_add_message(file, REGFI_MSG_WARN, "Data length (0x%.8X) larger than"
+			  " remaining cell length (0x%.8X)"
+			  " while parsing data record at offset 0x%.8X.", 
+			  length, cell_length - 4, offset);
+	if(strict)
+	  goto fail;
+	else
+	  length = cell_length - 4;
+      }
+    }
+
+    ret_val = regfi_parse_data(file, offset, length, strict);
+  }
+
+  return ret_val;
+
+ fail:
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses the common case data records stored in a single cell.
+ ******************************************************************************/
+REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
+			      uint32_t length, bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint32_t read_length;
+
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  
+  if(lseek(file->fd, offset+4, SEEK_SET) == -1)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not seek while "
+		      "reading data at offset 0x%.8X.", offset);
+    return ret_val;
+  }
+
+  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+    return ret_val;
+  ret_val.len = length;
+  
+  read_length = length;
+  if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
+     || read_length != length)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
+		      " parsing data record at offset 0x%.8X.", offset);
+    talloc_free(ret_val.buf);
+    ret_val.buf = NULL;
+    ret_val.buf = 0;
+  }
+
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset,
+				     uint32_t length, bool strict)
+{
+  uint8_t i;
+  REGFI_BUFFER ret_val;
+
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+
+  if(length > 4)
+  {
+    regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
+		      " while parsing data record. (voffset=0x%.8X, length=%d)",
+		      voffset, length);
+    return ret_val;
+  }
+
+  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+    return ret_val;
+  ret_val.len = length;
+  
+  for(i = 0; i < length; i++)
+    ret_val.buf[i] = (uint8_t)((voffset >> i*8) & 0xFF);
+
+  return ret_val;
+}
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_parse_big_data_header(REGFI_FILE* file, uint32_t offset, 
+					 uint32_t max_size, bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint32_t cell_length;
+  bool unalloc;
+
+  /* XXX: do something with unalloc? */
+  ret_val.buf = (uint8_t*)talloc_array(NULL, uint8_t, REGFI_BIG_DATA_MIN_LENGTH);
+  if(ret_val.buf == NULL)
+    goto fail;
+
+  if(REGFI_BIG_DATA_MIN_LENGTH > max_size)
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Big data header exceeded max_size "
+		      "while parsing big data header at offset 0x%.8X.",offset);
+    goto fail;
+  }
+
+  if(!regfi_parse_cell(file->fd, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
+		       &cell_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+		      " parsing big data header at offset 0x%.8X.", offset);
+    goto fail;
+  }
+
+  if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b'))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number"
+		      " (0x%.2X, 0x%.2X) encountered while parsing"
+		      " big data header at offset 0x%.8X.", 
+		      ret_val.buf[0], ret_val.buf[1], offset);
+    goto fail;
+  }
+
+  ret_val.len = REGFI_BIG_DATA_MIN_LENGTH;
+  return ret_val;
+
+ fail:
+  if(ret_val.buf != NULL)
+  {
+    talloc_free(ret_val.buf);
+    ret_val.buf = NULL;
+  }
+  ret_val.len = 0;
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
+				      uint16_t num_chunks, bool strict)
+{
+  uint32_t* ret_val;
+  uint32_t indirect_length;
+  int32_t max_size;
+  uint16_t i;
+  bool unalloc;
+
+  /* XXX: do something with unalloc? */
+
+  max_size = regfi_calc_maxsize(file, offset);
+  if((max_size < 0) || (num_chunks*sizeof(uint32_t) + 4 > max_size))
+    return NULL;
+
+  ret_val = (uint32_t*)talloc_array(NULL, uint32_t, num_chunks);
+  if(ret_val == NULL)
+    goto fail;
+
+  if(!regfi_parse_cell(file->fd, offset, (uint8_t*)ret_val,
+		       num_chunks*sizeof(uint32_t),
+		       &indirect_length, &unalloc))
+  {
+    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+		      " parsing big data indirect record at offset 0x%.8X.", 
+		      offset);
+    goto fail;
+  }
+
+  /* Convert pointers to proper endianess, verify they are aligned. */
+  for(i=0; i<num_chunks; i++)
+  {
+    ret_val[i] = IVAL(ret_val, i*sizeof(uint32_t));
+    if((ret_val[i] & 0x00000007) != 0)
+      goto fail;
+  }
+  
+  return ret_val;
+
+ fail:
+  if(ret_val != NULL)
+    talloc_free(ret_val);
+  return NULL;
+}
+
+
+/******************************************************************************
+ * Arguments:
+ *  file       --
+ *  offsets    -- list of virtual offsets.
+ *  num_chunks -- 
+ *  strict     --
+ *
+ * Returns:
+ *  A range_list with physical offsets and complete lengths 
+ *  (including cell headers) of associated cells.  
+ *  No data in range_list elements.
+ ******************************************************************************/
+range_list* regfi_parse_big_data_cells(REGFI_FILE* file, uint32_t* offsets,
+				       uint16_t num_chunks, bool strict)
+{
+  uint32_t cell_length, chunk_offset;
+  range_list* ret_val;
+  uint16_t i;
+  bool unalloc;
+  
+  /* XXX: do something with unalloc? */
+  ret_val = range_list_new();
+  if(ret_val == NULL)
+    goto fail;
+  
+  for(i=0; i<num_chunks; i++)
+  {
+    chunk_offset = offsets[i]+REGFI_REGF_SIZE;
+    if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0,
+			 &cell_length, &unalloc))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
+			" parsing big data chunk at offset 0x%.8X.", 
+			chunk_offset);
+      goto fail;
+    }
+
+    if(!range_list_add(ret_val, chunk_offset, cell_length, NULL))
+      goto fail;
+  }
+
+  return ret_val;
+
+ fail:
+  if(ret_val != NULL)
+    range_list_free(ret_val);
+  return NULL;
+}
+
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, 
+				 uint32_t offset, uint32_t data_length, 
+				 uint32_t cell_length, range_list* used_ranges,
+				 bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint16_t num_chunks, i;
+  uint32_t read_length, data_left, tmp_len, indirect_offset;
+  uint32_t* indirect_ptrs = NULL;
+  REGFI_BUFFER bd_header;
+  range_list* bd_cells = NULL;
+  const range_list_element* cell_info;
+
+  ret_val.buf = NULL;
+
+  /* XXX: Add better error/warning messages */
+
+  bd_header = regfi_parse_big_data_header(file, offset, cell_length, strict);
+  if(bd_header.buf == NULL)
+    goto fail;
+
+  /* Keep track of used space for use by reglookup-recover */
+  if(used_ranges != NULL)
+    if(!range_list_add(used_ranges, offset, cell_length, NULL))
+      goto fail;
+
+  num_chunks = SVAL(bd_header.buf, 0x2);
+  indirect_offset = IVAL(bd_header.buf, 0x4) + REGFI_REGF_SIZE;
+  talloc_free(bd_header.buf);
+
+  indirect_ptrs = regfi_parse_big_data_indirect(file, indirect_offset,
+						num_chunks, strict);
+  if(indirect_ptrs == NULL)
+    goto fail;
+
+  if(used_ranges != NULL)
+    if(!range_list_add(used_ranges, indirect_offset, num_chunks*4+4, NULL))
+      goto fail;
+  
+  if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
+    goto fail;
+  data_left = data_length;
+
+  bd_cells = regfi_parse_big_data_cells(file, indirect_ptrs, num_chunks, strict);
+  if(bd_cells == NULL)
+    goto fail;
+
+  talloc_free(indirect_ptrs);
+  indirect_ptrs = NULL;
+  
+  for(i=0; (i<num_chunks) && (data_left>0); i++)
+  {
+    cell_info = range_list_get(bd_cells, i);
+    if(cell_info == NULL)
+      goto fail;
+
+    /* XXX: This should be "cell_info->length-4" to account for the 4 byte cell
+     *      length.  However, it has been observed that some (all?) chunks
+     *      have an additional 4 bytes of 0 at the end of their cells that 
+     *      isn't part of the data, so we're trimming that off too. 
+     *      Perhaps it's just an 8 byte alignment requirement...
+     */
+    if(cell_info->length - 8 >= data_left)
+    {
+      if(i+1 != num_chunks)
+      {
+	regfi_add_message(file, REGFI_MSG_WARN, "Left over chunks detected "
+			  "while constructing big data at offset 0x%.8X "
+			  "(chunk offset 0x%.8X).", offset, cell_info->offset);
+      }
+      read_length = data_left;
+    }
+    else
+      read_length = cell_info->length - 8;
+
+
+    if(read_length > regfi_calc_maxsize(file, cell_info->offset))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "A chunk exceeded the maxsize "
+			"while constructing big data at offset 0x%.8X "
+			"(chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail;
+    }
+
+    if(lseek(file->fd, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1)
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not seek to chunk while "
+			"constructing big data at offset 0x%.8X "
+			"(chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail;
+    }
+
+    tmp_len = read_length;
+    if(regfi_read(file->fd, ret_val.buf+(data_length-data_left), 
+		  &read_length) != 0 || (read_length != tmp_len))
+    {
+      regfi_add_message(file, REGFI_MSG_WARN, "Could not read data chunk while"
+			" constructing big data at offset 0x%.8X"
+			" (chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail;
+    }
+
+    if(used_ranges != NULL)
+      if(!range_list_add(used_ranges, cell_info->offset,cell_info->length,NULL))
+	goto fail;
+
+    data_left -= read_length;
+  }
+  range_list_free(bd_cells);
+
+  ret_val.len = data_length-data_left;
+  return ret_val;
+
+ fail:
+  if(ret_val.buf != NULL)
+    talloc_free(ret_val.buf);
+  if(indirect_ptrs != NULL)
+    talloc_free(indirect_ptrs);
+  if(bd_cells != NULL)
+    range_list_free(bd_cells);
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  return ret_val;
+}
+
+
+range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
+{
+  range_list* ret_val;
+  REGFI_HBIN* hbin;
+  const range_list_element* hbins_elem;
+  uint32_t i, num_hbins, curr_off, cell_len;
+  bool is_unalloc;
+
+  ret_val = range_list_new();
+  if(ret_val == NULL)
+    return NULL;
+
+  num_hbins = range_list_size(file->hbins);
+  for(i=0; i<num_hbins; i++)
+  {
+    hbins_elem = range_list_get(file->hbins, i);
+    if(hbins_elem == NULL)
+      break;
+    hbin = (REGFI_HBIN*)hbins_elem->data;
+
+    curr_off = REGFI_HBIN_HEADER_SIZE;
+    while(curr_off < hbin->block_size)
+    {
+      if(!regfi_parse_cell(file->fd, hbin->file_off+curr_off, NULL, 0,
+			   &cell_len, &is_unalloc))
+	break;
+      
+      if((cell_len == 0) || ((cell_len & 0x00000007) != 0))
+      {
+	regfi_add_message(file, REGFI_MSG_ERROR, "Bad cell length encountered"
+			  " while parsing unallocated cells at offset 0x%.8X.",
+			  hbin->file_off+curr_off);
+	break;
+      }
+
+      /* for some reason the record_size of the last record in
+	 an hbin block can extend past the end of the block
+	 even though the record fits within the remaining 
+	 space....aaarrrgggghhhhhh */  
+      if(curr_off + cell_len >= hbin->block_size)
+	cell_len = hbin->block_size - curr_off;
+      
+      if(is_unalloc)
+	range_list_add(ret_val, hbin->file_off+curr_off, 
+		       cell_len, NULL);
+      
+      curr_off = curr_off+cell_len;
+    }
+  }
+
+  return ret_val;
+}
+
+
+/* From lib/time.c */
+
+/****************************************************************************
+ Put a 8 byte filetime from a time_t
+ This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t)
+{
+  double d;
+  
+  if (t==0) 
+  {
+    nt->low = 0;
+    nt->high = 0;
+    return;
+  }
+  
+  if (t == TIME_T_MAX) 
+  {
+    nt->low = 0xffffffff;
+    nt->high = 0x7fffffff;
+    return;
+  }		
+  
+  if (t == -1) 
+  {
+    nt->low = 0xffffffff;
+    nt->high = 0xffffffff;
+    return;
+  }		
+  
+  /* this converts GMT to kludge-GMT */
+  /* XXX: This was removed due to difficult dependency requirements.  
+   *      So far, times appear to be correct without this adjustment, but 
+   *      that may be proven wrong with adequate testing. 
+   */
+  /* t -= TimeDiff(t) - get_serverzone(); */
+  
+  d = (double)(t);
+  d += TIME_FIXUP_CONSTANT;
+  d *= 1.0e7;
+  
+  nt->high = (uint32_t)(d * (1.0/(4.0*(double)(1<<30))));
+  nt->low  = (uint32_t)(d - ((double)nt->high)*4.0*(double)(1<<30));
+}
+
+
+/****************************************************************************
+ Interpret an 8 byte "filetime" structure to a time_t
+ It's originally in "100ns units since jan 1st 1601"
+
+ An 8 byte value of 0xffffffffffffffff will be returned as (time_t)0.
+
+ It appears to be kludge-GMT (at least for file listings). This means
+ its the GMT you get by taking a localtime and adding the
+ serverzone. This is NOT the same as GMT in some cases. This routine
+ converts this to real GMT.
+****************************************************************************/
+time_t regfi_nt2unix_time(const REGFI_NTTIME* nt)
+{
+  double d;
+  time_t ret;
+  /* The next two lines are a fix needed for the 
+     broken SCO compiler. JRA. */
+  time_t l_time_min = TIME_T_MIN;
+  time_t l_time_max = TIME_T_MAX;
+  
+  if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
+    return(0);
+  
+  d = ((double)nt->high)*4.0*(double)(1<<30);
+  d += (nt->low&0xFFF00000);
+  d *= 1.0e-7;
+  
+  /* now adjust by 369 years to make the secs since 1970 */
+  d -= TIME_FIXUP_CONSTANT;
+  
+  if (d <= l_time_min)
+    return (l_time_min);
+  
+  if (d >= l_time_max)
+    return (l_time_max);
+  
+  ret = (time_t)(d+0.5);
+  
+  /* this takes us from kludge-GMT to real GMT */
+  /* XXX: This was removed due to difficult dependency requirements.  
+   *      So far, times appear to be correct without this adjustment, but 
+   *      that may be proven wrong with adequate testing. 
+   */
+  /*
+    ret -= get_serverzone();
+    ret += LocTimeDiff(ret);
+  */
+
+  return(ret);
+}
+
+/* End of stuff from lib/time.c */
Index: /releases/0.12.0/lib/talloc.c
===================================================================
--- /releases/0.12.0/lib/talloc.c	(revision 175)
+++ /releases/0.12.0/lib/talloc.c	(revision 175)
@@ -0,0 +1,1696 @@
+/* 
+   Samba Unix SMB/CIFS implementation.
+
+   Samba trivial allocation library - new interface
+     inspired by http://swapped.cc/halloc/
+
+   NOTE: Please read talloc_guide.txt for full documentation
+
+   Copyright (C) Andrew Tridgell 2004
+   Copyright (C) Stefan Metzmacher 2006
+   Copyright (C) Timothy D. Morgan 2009 
+ 
+     ** NOTE! The following LGPL license applies to the talloc library.
+     ** This does NOT imply that all of reglookup is released under the LGPL
+   
+   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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+/* $Id$ */
+
+#include "talloc.h"
+
+/* use this to force every realloc to change the pointer, to stress test
+   code that might not cope */
+#define ALWAYS_REALLOC 0
+
+
+#define MAX_TALLOC_SIZE 0x10000000
+#define TALLOC_MAGIC 0xe814ec70
+#define TALLOC_FLAG_FREE 0x01
+#define TALLOC_FLAG_LOOP 0x02
+#define TALLOC_FLAG_POOL 0x04		/* This is a talloc pool */
+#define TALLOC_FLAG_POOLMEM 0x08	/* This is allocated in a pool */
+#define TALLOC_MAGIC_REFERENCE ((const char *)1)
+
+/* by default we abort when given a bad pointer (such as when talloc_free() is called 
+   on a pointer that came from malloc() */
+#ifndef TALLOC_ABORT
+#define TALLOC_ABORT(reason) abort()
+#endif
+
+#ifndef discard_const_p
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
+#else
+# define discard_const_p(type, ptr) ((type *)(ptr))
+#endif
+#endif
+
+/* these macros gain us a few percent of speed on gcc */
+#if (__GNUC__ >= 3)
+/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1
+   as its first argument */
+#define likely(x)   __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#else
+#define likely(x) x
+#define unlikely(x) x
+#endif
+
+/* this null_context is only used if talloc_enable_leak_report() or
+   talloc_enable_leak_report_full() is called, otherwise it remains
+   NULL
+*/
+static void *null_context;
+static void *autofree_context;
+
+struct talloc_reference_handle {
+	struct talloc_reference_handle *next, *prev;
+	void *ptr;
+};
+
+typedef int (*talloc_destructor_t)(void *);
+
+struct talloc_chunk {
+	struct talloc_chunk *next, *prev;
+	struct talloc_chunk *parent, *child;
+	struct talloc_reference_handle *refs;
+	talloc_destructor_t destructor;
+	const char *name;
+	size_t size;
+	unsigned flags;
+
+	/*
+	 * "pool" has dual use:
+	 *
+	 * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool"
+	 * marks the end of the currently allocated area.
+	 *
+	 * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool"
+	 * is a pointer to the struct talloc_chunk of the pool that it was
+	 * allocated from. This way children can quickly find the pool to chew
+	 * from.
+	 */
+	void *pool;
+};
+
+/* 16 byte alignment seems to keep everyone happy */
+#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
+#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
+
+static void talloc_abort_double_free(void)
+{
+	TALLOC_ABORT("Bad talloc magic value - double free"); 
+}
+
+static void talloc_abort_unknown_value(void)
+{
+	TALLOC_ABORT("Bad talloc magic value - unknown value"); 
+}
+
+/* panic if we get a bad magic value */
+static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
+{
+	const char *pp = (const char *)ptr;
+	struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
+	if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { 
+		if (tc->flags & TALLOC_FLAG_FREE) {
+			talloc_abort_double_free();
+		} else {
+			talloc_abort_unknown_value();
+		}
+	}
+	return tc;
+}
+
+/* hook into the front of the list */
+#define _TLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+		(list) = (p); \
+		(p)->next = (p)->prev = NULL; \
+	} else { \
+		(list)->prev = (p); \
+		(p)->next = (list); \
+		(p)->prev = NULL; \
+		(list) = (p); \
+	}\
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define _TLIST_REMOVE(list, p) \
+do { \
+	if ((p) == (list)) { \
+		(list) = (p)->next; \
+		if (list) (list)->prev = NULL; \
+	} else { \
+		if ((p)->prev) (p)->prev->next = (p)->next; \
+		if ((p)->next) (p)->next->prev = (p)->prev; \
+	} \
+	if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
+} while (0)
+
+
+/*
+  return the parent chunk of a pointer
+*/
+static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (unlikely(ptr == NULL)) {
+		return NULL;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+	while (tc->prev) tc=tc->prev;
+
+	return tc->parent;
+}
+
+void *talloc_parent(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+	return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
+}
+
+/*
+  find parents name
+*/
+const char *talloc_parent_name(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_parent_chunk(ptr);
+	return tc? tc->name : NULL;
+}
+
+/*
+  A pool carries an in-pool object count count in the first 16 bytes.
+  bytes. This is done to support talloc_steal() to a parent outside of the
+  pool. The count includes the pool itself, so a talloc_free() on a pool will
+  only destroy the pool if the count has dropped to zero. A talloc_free() of a
+  pool member will reduce the count, and eventually also call free(3) on the
+  pool memory.
+
+  The object count is not put into "struct talloc_chunk" because it is only
+  relevant for talloc pools and the alignment to 16 bytes would increase the
+  memory footprint of each talloc chunk by those 16 bytes.
+*/
+
+#define TALLOC_POOL_HDR_SIZE 16
+
+static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
+{
+	return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk));
+}
+
+/*
+  Allocate from a pool
+*/
+
+static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
+					      size_t size)
+{
+	struct talloc_chunk *pool_ctx = NULL;
+	size_t space_left;
+	struct talloc_chunk *result;
+	size_t chunk_size;
+
+	if (parent == NULL) {
+		return NULL;
+	}
+
+	if (parent->flags & TALLOC_FLAG_POOL) {
+		pool_ctx = parent;
+	}
+	else if (parent->flags & TALLOC_FLAG_POOLMEM) {
+		pool_ctx = (struct talloc_chunk *)parent->pool;
+	}
+
+	if (pool_ctx == NULL) {
+		return NULL;
+	}
+
+	space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size)
+		- ((char *)pool_ctx->pool);
+
+	/*
+	 * Align size to 16 bytes
+	 */
+	chunk_size = ((size + 15) & ~15);
+
+	if (space_left < chunk_size) {
+		return NULL;
+	}
+
+	result = (struct talloc_chunk *)pool_ctx->pool;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+	VALGRIND_MAKE_MEM_UNDEFINED(result, size);
+#endif
+
+	pool_ctx->pool = (void *)((char *)result + chunk_size);
+
+	result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
+	result->pool = pool_ctx;
+
+	*talloc_pool_objectcount(pool_ctx) += 1;
+
+	return result;
+}
+
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+static inline void *__talloc(const void *context, size_t size)
+{
+	struct talloc_chunk *tc = NULL;
+
+	if (unlikely(context == NULL)) {
+		context = null_context;
+	}
+
+	if (unlikely(size >= MAX_TALLOC_SIZE)) {
+		return NULL;
+	}
+
+	if (context != NULL) {
+		tc = talloc_alloc_pool(talloc_chunk_from_ptr(context),
+				       TC_HDR_SIZE+size);
+	}
+
+	if (tc == NULL) {
+		tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
+		if (unlikely(tc == NULL)) return NULL;
+		tc->flags = TALLOC_MAGIC;
+		tc->pool  = NULL;
+	}
+
+	tc->size = size;
+	tc->destructor = NULL;
+	tc->child = NULL;
+	tc->name = NULL;
+	tc->refs = NULL;
+
+	if (likely(context)) {
+		struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
+
+		if (parent->child) {
+			parent->child->parent = NULL;
+			tc->next = parent->child;
+			tc->next->prev = tc;
+		} else {
+			tc->next = NULL;
+		}
+		tc->parent = parent;
+		tc->prev = NULL;
+		parent->child = tc;
+	} else {
+		tc->next = tc->prev = tc->parent = NULL;
+	}
+
+	return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+ * Create a talloc pool
+ */
+
+void *talloc_pool(const void *context, size_t size)
+{
+	void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE);
+	struct talloc_chunk *tc;
+
+	if (unlikely(result == NULL)) {
+		return NULL;
+	}
+
+	tc = talloc_chunk_from_ptr(result);
+
+	tc->flags |= TALLOC_FLAG_POOL;
+	tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE;
+
+	*talloc_pool_objectcount(tc) = 1;
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+	VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size);
+#endif
+
+	return result;
+}
+
+/*
+  setup a destructor to be called on free of a pointer
+  the destructor should return 0 on success, or -1 on failure.
+  if the destructor fails then the free is failed, and the memory can
+  be continued to be used
+*/
+void _talloc_set_destructor(const void *ptr, int (*destructor)(void *))
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	tc->destructor = destructor;
+}
+
+/*
+  increase the reference count on a piece of memory. 
+*/
+int talloc_increase_ref_count(const void *ptr)
+{
+	if (unlikely(!talloc_reference(null_context, ptr))) {
+		return -1;
+	}
+	return 0;
+}
+
+/*
+  helper for talloc_reference()
+
+  this is referenced by a function pointer and should not be inline
+*/
+static int talloc_reference_destructor(struct talloc_reference_handle *handle)
+{
+	struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
+	_TLIST_REMOVE(ptr_tc->refs, handle);
+	return 0;
+}
+
+/*
+   more efficient way to add a name to a pointer - the name must point to a 
+   true string constant
+*/
+static inline void _talloc_set_name_const(const void *ptr, const char *name)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	tc->name = name;
+}
+
+/*
+  internal talloc_named_const()
+*/
+static inline void *_talloc_named_const(const void *context, size_t size, const char *name)
+{
+	void *ptr;
+
+	ptr = __talloc(context, size);
+	if (unlikely(ptr == NULL)) {
+		return NULL;
+	}
+
+	_talloc_set_name_const(ptr, name);
+
+	return ptr;
+}
+
+/*
+  make a secondary reference to a pointer, hanging off the given context.
+  the pointer remains valid until both the original caller and this given
+  context are freed.
+  
+  the major use for this is when two different structures need to reference the 
+  same underlying data, and you want to be able to free the two instances separately,
+  and in either order
+*/
+void *_talloc_reference(const void *context, const void *ptr)
+{
+	struct talloc_chunk *tc;
+	struct talloc_reference_handle *handle;
+	if (unlikely(ptr == NULL)) return NULL;
+
+	tc = talloc_chunk_from_ptr(ptr);
+	handle = (struct talloc_reference_handle *)_talloc_named_const(context,
+						   sizeof(struct talloc_reference_handle),
+						   TALLOC_MAGIC_REFERENCE);
+	if (unlikely(handle == NULL)) return NULL;
+
+	/* note that we hang the destructor off the handle, not the
+	   main context as that allows the caller to still setup their
+	   own destructor on the context if they want to */
+	talloc_set_destructor(handle, talloc_reference_destructor);
+	handle->ptr = discard_const_p(void, ptr);
+	_TLIST_ADD(tc->refs, handle);
+	return handle->ptr;
+}
+
+
+/* 
+   internal talloc_free call
+*/
+static inline int _talloc_free(void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (unlikely(ptr == NULL)) {
+		return -1;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (unlikely(tc->refs)) {
+		int is_child;
+		/* check this is a reference from a child or grantchild
+		 * back to it's parent or grantparent
+		 *
+		 * in that case we need to remove the reference and
+		 * call another instance of talloc_free() on the current
+		 * pointer.
+		 */
+		is_child = talloc_is_parent(tc->refs, ptr);
+		_talloc_free(tc->refs);
+		if (is_child) {
+			return _talloc_free(ptr);
+		}
+		return -1;
+	}
+
+	if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
+		/* we have a free loop - stop looping */
+		return 0;
+	}
+
+	if (unlikely(tc->destructor)) {
+		talloc_destructor_t d = tc->destructor;
+		if (d == (talloc_destructor_t)-1) {
+			return -1;
+		}
+		tc->destructor = (talloc_destructor_t)-1;
+		if (d(ptr) == -1) {
+			tc->destructor = d;
+			return -1;
+		}
+		tc->destructor = NULL;
+	}
+
+	if (tc->parent) {
+		_TLIST_REMOVE(tc->parent->child, tc);
+		if (tc->parent->child) {
+			tc->parent->child->parent = tc->parent;
+		}
+	} else {
+		if (tc->prev) tc->prev->next = tc->next;
+		if (tc->next) tc->next->prev = tc->prev;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+
+	while (tc->child) {
+		/* we need to work out who will own an abandoned child
+		   if it cannot be freed. In priority order, the first
+		   choice is owner of any remaining reference to this
+		   pointer, the second choice is our parent, and the
+		   final choice is the null context. */
+		void *child = TC_PTR_FROM_CHUNK(tc->child);
+		const void *new_parent = null_context;
+		if (unlikely(tc->child->refs)) {
+			struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+			if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+		}
+		if (unlikely(_talloc_free(child) == -1)) {
+			if (new_parent == null_context) {
+				struct talloc_chunk *p = talloc_parent_chunk(ptr);
+				if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+			}
+			talloc_steal(new_parent, child);
+		}
+	}
+
+	tc->flags |= TALLOC_FLAG_FREE;
+
+	if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) {
+		struct talloc_chunk *pool;
+		unsigned int *pool_object_count;
+
+		pool = (tc->flags & TALLOC_FLAG_POOL)
+			? tc : (struct talloc_chunk *)tc->pool;
+
+		pool_object_count = talloc_pool_objectcount(pool);
+
+		if (*pool_object_count == 0) {
+			TALLOC_ABORT("Pool object count zero!");
+		}
+
+		*pool_object_count -= 1;
+
+		if (*pool_object_count == 0) {
+			free(pool);
+		}
+	}
+	else {
+		free(tc);
+	}
+	return 0;
+}
+
+/* 
+   move a lump of memory from one talloc context to another return the
+   ptr on success, or NULL if it could not be transferred.
+   passing NULL as ptr will always return NULL with no side effects.
+*/
+void *_talloc_steal(const void *new_ctx, const void *ptr)
+{
+	struct talloc_chunk *tc, *new_tc;
+
+	if (unlikely(!ptr)) {
+		return NULL;
+	}
+
+	if (unlikely(new_ctx == NULL)) {
+		new_ctx = null_context;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (unlikely(new_ctx == NULL)) {
+		if (tc->parent) {
+			_TLIST_REMOVE(tc->parent->child, tc);
+			if (tc->parent->child) {
+				tc->parent->child->parent = tc->parent;
+			}
+		} else {
+			if (tc->prev) tc->prev->next = tc->next;
+			if (tc->next) tc->next->prev = tc->prev;
+		}
+		
+		tc->parent = tc->next = tc->prev = NULL;
+		return discard_const_p(void, ptr);
+	}
+
+	new_tc = talloc_chunk_from_ptr(new_ctx);
+
+	if (unlikely(tc == new_tc || tc->parent == new_tc)) {
+		return discard_const_p(void, ptr);
+	}
+
+	if (tc->parent) {
+		_TLIST_REMOVE(tc->parent->child, tc);
+		if (tc->parent->child) {
+			tc->parent->child->parent = tc->parent;
+		}
+	} else {
+		if (tc->prev) tc->prev->next = tc->next;
+		if (tc->next) tc->next->prev = tc->prev;
+	}
+
+	tc->parent = new_tc;
+	if (new_tc->child) new_tc->child->parent = NULL;
+	_TLIST_ADD(new_tc->child, tc);
+
+	return discard_const_p(void, ptr);
+}
+
+
+
+/*
+  remove a secondary reference to a pointer. This undo's what
+  talloc_reference() has done. The context and pointer arguments
+  must match those given to a talloc_reference()
+*/
+static inline int talloc_unreference(const void *context, const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	struct talloc_reference_handle *h;
+
+	if (unlikely(context == NULL)) {
+		context = null_context;
+	}
+
+	for (h=tc->refs;h;h=h->next) {
+		struct talloc_chunk *p = talloc_parent_chunk(h);
+		if (p == NULL) {
+			if (context == NULL) break;
+		} else if (TC_PTR_FROM_CHUNK(p) == context) {
+			break;
+		}
+	}
+	if (h == NULL) {
+		return -1;
+	}
+
+	return _talloc_free(h);
+}
+
+/*
+  remove a specific parent context from a pointer. This is a more
+  controlled varient of talloc_free()
+*/
+int talloc_unlink(const void *context, void *ptr)
+{
+	struct talloc_chunk *tc_p, *new_p;
+	void *new_parent;
+
+	if (ptr == NULL) {
+		return -1;
+	}
+
+	if (context == NULL) {
+		context = null_context;
+	}
+
+	if (talloc_unreference(context, ptr) == 0) {
+		return 0;
+	}
+
+	if (context == NULL) {
+		if (talloc_parent_chunk(ptr) != NULL) {
+			return -1;
+		}
+	} else {
+		if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
+			return -1;
+		}
+	}
+	
+	tc_p = talloc_chunk_from_ptr(ptr);
+
+	if (tc_p->refs == NULL) {
+		return _talloc_free(ptr);
+	}
+
+	new_p = talloc_parent_chunk(tc_p->refs);
+	if (new_p) {
+		new_parent = TC_PTR_FROM_CHUNK(new_p);
+	} else {
+		new_parent = NULL;
+	}
+
+	if (talloc_unreference(new_parent, ptr) != 0) {
+		return -1;
+	}
+
+	talloc_steal(new_parent, ptr);
+
+	return 0;
+}
+
+/*
+  add a name to an existing pointer - va_list version
+*/
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	tc->name = talloc_vasprintf(ptr, fmt, ap);
+	if (likely(tc->name)) {
+		_talloc_set_name_const(tc->name, ".name");
+	}
+	return tc->name;
+}
+
+/*
+  add a name to an existing pointer
+*/
+const char *talloc_set_name(const void *ptr, const char *fmt, ...)
+{
+	const char *name;
+	va_list ap;
+	va_start(ap, fmt);
+	name = talloc_set_name_v(ptr, fmt, ap);
+	va_end(ap);
+	return name;
+}
+
+
+/*
+  create a named talloc pointer. Any talloc pointer can be named, and
+  talloc_named() operates just like talloc() except that it allows you
+  to name the pointer.
+*/
+void *talloc_named(const void *context, size_t size, const char *fmt, ...)
+{
+	va_list ap;
+	void *ptr;
+	const char *name;
+
+	ptr = __talloc(context, size);
+	if (unlikely(ptr == NULL)) return NULL;
+
+	va_start(ap, fmt);
+	name = talloc_set_name_v(ptr, fmt, ap);
+	va_end(ap);
+
+	if (unlikely(name == NULL)) {
+		_talloc_free(ptr);
+		return NULL;
+	}
+
+	return ptr;
+}
+
+/*
+  return the name of a talloc ptr, or "UNNAMED"
+*/
+const char *talloc_get_name(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) {
+		return ".reference";
+	}
+	if (likely(tc->name)) {
+		return tc->name;
+	}
+	return "UNNAMED";
+}
+
+
+/*
+  check if a pointer has the given name. If it does, return the pointer,
+  otherwise return NULL
+*/
+void *talloc_check_name(const void *ptr, const char *name)
+{
+	const char *pname;
+	if (unlikely(ptr == NULL)) return NULL;
+	pname = talloc_get_name(ptr);
+	if (likely(pname == name || strcmp(pname, name) == 0)) {
+		return discard_const_p(void, ptr);
+	}
+	return NULL;
+}
+
+
+/*
+  this is for compatibility with older versions of talloc
+*/
+void *talloc_init(const char *fmt, ...)
+{
+	va_list ap;
+	void *ptr;
+	const char *name;
+
+	/*
+	 * samba3 expects talloc_report_depth_cb(NULL, ...)
+	 * reports all talloc'ed memory, so we need to enable
+	 * null_tracking
+	 */
+	talloc_enable_null_tracking();
+
+	ptr = __talloc(NULL, 0);
+	if (unlikely(ptr == NULL)) return NULL;
+
+	va_start(ap, fmt);
+	name = talloc_set_name_v(ptr, fmt, ap);
+	va_end(ap);
+
+	if (unlikely(name == NULL)) {
+		_talloc_free(ptr);
+		return NULL;
+	}
+
+	return ptr;
+}
+
+/*
+  this is a replacement for the Samba3 talloc_destroy_pool functionality. It
+  should probably not be used in new code. It's in here to keep the talloc
+  code consistent across Samba 3 and 4.
+*/
+void talloc_free_children(void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (unlikely(ptr == NULL)) {
+		return;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	while (tc->child) {
+		/* we need to work out who will own an abandoned child
+		   if it cannot be freed. In priority order, the first
+		   choice is owner of any remaining reference to this
+		   pointer, the second choice is our parent, and the
+		   final choice is the null context. */
+		void *child = TC_PTR_FROM_CHUNK(tc->child);
+		const void *new_parent = null_context;
+		if (unlikely(tc->child->refs)) {
+			struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
+			if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+		}
+		if (unlikely(_talloc_free(child) == -1)) {
+			if (new_parent == null_context) {
+				struct talloc_chunk *p = talloc_parent_chunk(ptr);
+				if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+			}
+			talloc_steal(new_parent, child);
+		}
+	}
+
+	if ((tc->flags & TALLOC_FLAG_POOL)
+	    && (*talloc_pool_objectcount(tc) == 1)) {
+		tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE);
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+		VALGRIND_MAKE_MEM_NOACCESS(
+			tc->pool, tc->size - TALLOC_POOL_HDR_SIZE);
+#endif
+	}
+}
+
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+void *_talloc(const void *context, size_t size)
+{
+	return __talloc(context, size);
+}
+
+/*
+  externally callable talloc_set_name_const()
+*/
+void talloc_set_name_const(const void *ptr, const char *name)
+{
+	_talloc_set_name_const(ptr, name);
+}
+
+/*
+  create a named talloc pointer. Any talloc pointer can be named, and
+  talloc_named() operates just like talloc() except that it allows you
+  to name the pointer.
+*/
+void *talloc_named_const(const void *context, size_t size, const char *name)
+{
+	return _talloc_named_const(context, size, name);
+}
+
+/* 
+   free a talloc pointer. This also frees all child pointers of this 
+   pointer recursively
+
+   return 0 if the memory is actually freed, otherwise -1. The memory
+   will not be freed if the ref_count is > 1 or the destructor (if
+   any) returns non-zero
+*/
+int talloc_free(void *ptr)
+{
+	return _talloc_free(ptr);
+}
+
+
+
+/*
+  A talloc version of realloc. The context argument is only used if
+  ptr is NULL
+*/
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
+{
+	struct talloc_chunk *tc;
+	void *new_ptr;
+	bool malloced = false;
+
+	/* size zero is equivalent to free() */
+	if (unlikely(size == 0)) {
+		_talloc_free(ptr);
+		return NULL;
+	}
+
+	if (unlikely(size >= MAX_TALLOC_SIZE)) {
+		return NULL;
+	}
+
+	/* realloc(NULL) is equivalent to malloc() */
+	if (ptr == NULL) {
+		return _talloc_named_const(context, size, name);
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	/* don't allow realloc on referenced pointers */
+	if (unlikely(tc->refs)) {
+		return NULL;
+	}
+
+	/* don't shrink if we have less than 1k to gain */
+	if ((size < tc->size) && ((tc->size - size) < 1024)) {
+		tc->size = size;
+		return ptr;
+	}
+
+	/* by resetting magic we catch users of the old memory */
+	tc->flags |= TALLOC_FLAG_FREE;
+
+#if ALWAYS_REALLOC
+	new_ptr = malloc(size + TC_HDR_SIZE);
+	if (new_ptr) {
+		memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE);
+		free(tc);
+	}
+#else
+	if (tc->flags & TALLOC_FLAG_POOLMEM) {
+
+		new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
+		*talloc_pool_objectcount((struct talloc_chunk *)
+					 (tc->pool)) -= 1;
+
+		if (new_ptr == NULL) {
+			new_ptr = malloc(TC_HDR_SIZE+size);
+			malloced = true;
+		}
+
+		if (new_ptr) {
+			memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
+		}
+	}
+	else {
+		new_ptr = realloc(tc, size + TC_HDR_SIZE);
+	}
+#endif
+	if (unlikely(!new_ptr)) {	
+		tc->flags &= ~TALLOC_FLAG_FREE; 
+		return NULL; 
+	}
+
+	tc = (struct talloc_chunk *)new_ptr;
+	tc->flags &= ~TALLOC_FLAG_FREE;
+	if (malloced) {
+		tc->flags &= ~TALLOC_FLAG_POOLMEM;
+	}
+	if (tc->parent) {
+		tc->parent->child = tc;
+	}
+	if (tc->child) {
+		tc->child->parent = tc;
+	}
+
+	if (tc->prev) {
+		tc->prev->next = tc;
+	}
+	if (tc->next) {
+		tc->next->prev = tc;
+	}
+
+	tc->size = size;
+	_talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
+
+	return TC_PTR_FROM_CHUNK(tc);
+}
+
+/*
+  a wrapper around talloc_steal() for situations where you are moving a pointer
+  between two structures, and want the old pointer to be set to NULL
+*/
+void *_talloc_move(const void *new_ctx, const void *_pptr)
+{
+	const void **pptr = discard_const_p(const void *,_pptr);
+	void *ret = _talloc_steal(new_ctx, *pptr);
+	(*pptr) = NULL;
+	return ret;
+}
+
+/*
+  return the total size of a talloc pool (subtree)
+*/
+size_t talloc_total_size(const void *ptr)
+{
+	size_t total = 0;
+	struct talloc_chunk *c, *tc;
+
+	if (ptr == NULL) {
+		ptr = null_context;
+	}
+	if (ptr == NULL) {
+		return 0;
+	}
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (tc->flags & TALLOC_FLAG_LOOP) {
+		return 0;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+
+	total = tc->size;
+	for (c=tc->child;c;c=c->next) {
+		total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
+	}
+
+	tc->flags &= ~TALLOC_FLAG_LOOP;
+
+	return total;
+}
+
+/*
+  return the total number of blocks in a talloc pool (subtree)
+*/
+size_t talloc_total_blocks(const void *ptr)
+{
+	size_t total = 0;
+	struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
+
+	if (tc->flags & TALLOC_FLAG_LOOP) {
+		return 0;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+
+	total++;
+	for (c=tc->child;c;c=c->next) {
+		total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
+	}
+
+	tc->flags &= ~TALLOC_FLAG_LOOP;
+
+	return total;
+}
+
+/*
+  return the number of external references to a pointer
+*/
+size_t talloc_reference_count(const void *ptr)
+{
+	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+	struct talloc_reference_handle *h;
+	size_t ret = 0;
+
+	for (h=tc->refs;h;h=h->next) {
+		ret++;
+	}
+	return ret;
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+			    void (*callback)(const void *ptr,
+			  		     int depth, int max_depth,
+					     int is_ref,
+					     void *private_data),
+			    void *private_data)
+{
+	struct talloc_chunk *c, *tc;
+
+	if (ptr == NULL) {
+		ptr = null_context;
+	}
+	if (ptr == NULL) return;
+
+	tc = talloc_chunk_from_ptr(ptr);
+
+	if (tc->flags & TALLOC_FLAG_LOOP) {
+		return;
+	}
+
+	callback(ptr, depth, max_depth, 0, private_data);
+
+	if (max_depth >= 0 && depth >= max_depth) {
+		return;
+	}
+
+	tc->flags |= TALLOC_FLAG_LOOP;
+	for (c=tc->child;c;c=c->next) {
+		if (c->name == TALLOC_MAGIC_REFERENCE) {
+			struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c);
+			callback(h->ptr, depth + 1, max_depth, 1, private_data);
+		} else {
+			talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data);
+		}
+	}
+	tc->flags &= ~TALLOC_FLAG_LOOP;
+}
+
+static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f)
+{
+	const char *name = talloc_get_name(ptr);
+	FILE *f = (FILE *)_f;
+
+	if (is_ref) {
+		fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+		return;
+	}
+
+	if (depth == 0) {
+		fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", 
+			(max_depth < 0 ? "full " :""), name,
+			(unsigned long)talloc_total_size(ptr),
+			(unsigned long)talloc_total_blocks(ptr));
+		return;
+	}
+
+	fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", 
+		depth*4, "",
+		name,
+		(unsigned long)talloc_total_size(ptr),
+		(unsigned long)talloc_total_blocks(ptr),
+		(int)talloc_reference_count(ptr), ptr);
+
+#if 0
+	fprintf(f, "content: ");
+	if (talloc_total_size(ptr)) {
+		int tot = talloc_total_size(ptr);
+		int i;
+
+		for (i = 0; i < tot; i++) {
+			if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) {
+				fprintf(f, "%c", ((char *)ptr)[i]);
+			} else {
+				fprintf(f, "~%02x", ((char *)ptr)[i]);
+			}
+		}
+	}
+	fprintf(f, "\n");
+#endif
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
+{
+	talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
+	fflush(f);
+}
+
+/*
+  report on memory usage by all children of a pointer, giving a full tree view
+*/
+void talloc_report_full(const void *ptr, FILE *f)
+{
+	talloc_report_depth_file(ptr, 0, -1, f);
+}
+
+/*
+  report on memory usage by all children of a pointer
+*/
+void talloc_report(const void *ptr, FILE *f)
+{
+	talloc_report_depth_file(ptr, 0, 1, f);
+}
+
+/*
+  report on any memory hanging off the null context
+*/
+static void talloc_report_null(void)
+{
+	if (talloc_total_size(null_context) != 0) {
+		talloc_report(null_context, stderr);
+	}
+}
+
+/*
+  report on any memory hanging off the null context
+*/
+static void talloc_report_null_full(void)
+{
+	if (talloc_total_size(null_context) != 0) {
+		talloc_report_full(null_context, stderr);
+	}
+}
+
+/*
+  enable tracking of the NULL context
+*/
+void talloc_enable_null_tracking(void)
+{
+	if (null_context == NULL) {
+		null_context = _talloc_named_const(NULL, 0, "null_context");
+	}
+}
+
+/*
+  disable tracking of the NULL context
+*/
+void talloc_disable_null_tracking(void)
+{
+	_talloc_free(null_context);
+	null_context = NULL;
+}
+
+/*
+  enable leak reporting on exit
+*/
+void talloc_enable_leak_report(void)
+{
+	talloc_enable_null_tracking();
+	atexit(talloc_report_null);
+}
+
+/*
+  enable full leak reporting on exit
+*/
+void talloc_enable_leak_report_full(void)
+{
+	talloc_enable_null_tracking();
+	atexit(talloc_report_null_full);
+}
+
+/* 
+   talloc and zero memory. 
+*/
+void *_talloc_zero(const void *ctx, size_t size, const char *name)
+{
+	void *p = _talloc_named_const(ctx, size, name);
+
+	if (p) {
+		memset(p, '\0', size);
+	}
+
+	return p;
+}
+
+/*
+  memdup with a talloc. 
+*/
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
+{
+	void *newp = _talloc_named_const(t, size, name);
+
+	if (likely(newp)) {
+		memcpy(newp, p, size);
+	}
+
+	return newp;
+}
+
+static inline char *__talloc_strlendup(const void *t, const char *p, size_t len)
+{
+	char *ret;
+
+	ret = (char *)__talloc(t, len + 1);
+	if (unlikely(!ret)) return NULL;
+
+	memcpy(ret, p, len);
+	ret[len] = 0;
+
+	_talloc_set_name_const(ret, ret);
+	return ret;
+}
+
+/*
+  strdup with a talloc
+*/
+char *talloc_strdup(const void *t, const char *p)
+{
+	if (unlikely(!p)) return NULL;
+	return __talloc_strlendup(t, p, strlen(p));
+}
+
+/*
+  strndup with a talloc
+*/
+char *talloc_strndup(const void *t, const char *p, size_t n)
+{
+	if (unlikely(!p)) return NULL;
+	return __talloc_strlendup(t, p, n);
+}
+
+static inline char *__talloc_strlendup_append(char *s, size_t slen,
+					      const char *a, size_t alen)
+{
+	char *ret;
+
+	ret = talloc_realloc(NULL, s, char, slen + alen + 1);
+	if (unlikely(!ret)) return NULL;
+
+	/* append the string and the trailing \0 */
+	memcpy(&ret[slen], a, alen);
+	ret[slen+alen] = 0;
+
+	_talloc_set_name_const(ret, ret);
+	return ret;
+}
+
+/*
+ * Appends at the end of the string.
+ */
+char *talloc_strdup_append(char *s, const char *a)
+{
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	return __talloc_strlendup_append(s, strlen(s), a, strlen(a));
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+char *talloc_strdup_append_buffer(char *s, const char *a)
+{
+	size_t slen;
+
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	slen = talloc_get_size(s);
+	if (likely(slen > 0)) {
+		slen--;
+	}
+
+	return __talloc_strlendup_append(s, slen, a, strlen(a));
+}
+
+/*
+ * Appends at the end of the string.
+ */
+char *talloc_strndup_append(char *s, const char *a, size_t n)
+{
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	return __talloc_strlendup_append(s, strlen(s), a, n);
+}
+
+/*
+ * Appends at the end of the talloc'ed buffer,
+ * not the end of the string.
+ */
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
+{
+	size_t slen;
+
+	if (unlikely(!s)) {
+		return talloc_strdup(NULL, a);
+	}
+
+	if (unlikely(!a)) {
+		return s;
+	}
+
+	slen = talloc_get_size(s);
+	if (likely(slen > 0)) {
+		slen--;
+	}
+
+	return __talloc_strlendup_append(s, slen, a, n);
+}
+
+
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
+{
+	int len;
+	char *ret;
+	va_list ap2;
+	char c;
+
+	/* this call looks strange, but it makes it work on older solaris boxes */
+	va_copy(ap2, ap);
+	len = vsnprintf(&c, 1, fmt, ap2);
+	va_end(ap2);
+	if (unlikely(len < 0)) {
+		return NULL;
+	}
+
+	ret = (char *)__talloc(t, len+1);
+	if (unlikely(!ret)) return NULL;
+
+	va_copy(ap2, ap);
+	vsnprintf(ret, len+1, fmt, ap2);
+	va_end(ap2);
+
+	_talloc_set_name_const(ret, ret);
+	return ret;
+}
+
+
+/*
+  Perform string formatting, and return a pointer to newly allocated
+  memory holding the result, inside a memory pool.
+ */
+char *talloc_asprintf(const void *t, const char *fmt, ...)
+{
+	va_list ap;
+	char *ret;
+
+	va_start(ap, fmt);
+	ret = talloc_vasprintf(t, fmt, ap);
+	va_end(ap);
+	return ret;
+}
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+						 const char *fmt, va_list ap)
+						 PRINTF_ATTRIBUTE(3,0);
+
+static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
+						 const char *fmt, va_list ap)
+{
+	ssize_t alen;
+	va_list ap2;
+	char c;
+
+	va_copy(ap2, ap);
+	alen = vsnprintf(&c, 1, fmt, ap2);
+	va_end(ap2);
+
+	if (alen <= 0) {
+		/* Either the vsnprintf failed or the format resulted in
+		 * no characters being formatted. In the former case, we
+		 * ought to return NULL, in the latter we ought to return
+		 * the original string. Most current callers of this
+		 * function expect it to never return NULL.
+		 */
+		return s;
+	}
+
+	s = talloc_realloc(NULL, s, char, slen + alen + 1);
+	if (!s) return NULL;
+
+	va_copy(ap2, ap);
+	vsnprintf(s + slen, alen + 1, fmt, ap2);
+	va_end(ap2);
+
+	_talloc_set_name_const(s, s);
+	return s;
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved.  Good for gradually
+ * accumulating output into a string buffer. Appends at the end
+ * of the string.
+ **/
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
+{
+	if (unlikely(!s)) {
+		return talloc_vasprintf(NULL, fmt, ap);
+	}
+
+	return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap);
+}
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved. Always appends at the
+ * end of the talloc'ed buffer, not the end of the string.
+ **/
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap)
+{
+	size_t slen;
+
+	if (unlikely(!s)) {
+		return talloc_vasprintf(NULL, fmt, ap);
+	}
+
+	slen = talloc_get_size(s);
+	if (likely(slen > 0)) {
+		slen--;
+	}
+
+	return __talloc_vaslenprintf_append(s, slen, fmt, ap);
+}
+
+/*
+  Realloc @p s to append the formatted result of @p fmt and return @p
+  s, which may have moved.  Good for gradually accumulating output
+  into a string buffer.
+ */
+char *talloc_asprintf_append(char *s, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	s = talloc_vasprintf_append(s, fmt, ap);
+	va_end(ap);
+	return s;
+}
+
+/*
+  Realloc @p s to append the formatted result of @p fmt and return @p
+  s, which may have moved.  Good for gradually accumulating output
+  into a buffer.
+ */
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	s = talloc_vasprintf_append_buffer(s, fmt, ap);
+	va_end(ap);
+	return s;
+}
+
+/*
+  alloc an array, checking for integer overflow in the array size
+*/
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+	if (count >= MAX_TALLOC_SIZE/el_size) {
+		return NULL;
+	}
+	return _talloc_named_const(ctx, el_size * count, name);
+}
+
+/*
+  alloc an zero array, checking for integer overflow in the array size
+*/
+void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name)
+{
+	if (count >= MAX_TALLOC_SIZE/el_size) {
+		return NULL;
+	}
+	return _talloc_zero(ctx, el_size * count, name);
+}
+
+/*
+  realloc an array, checking for integer overflow in the array size
+*/
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name)
+{
+	if (count >= MAX_TALLOC_SIZE/el_size) {
+		return NULL;
+	}
+	return _talloc_realloc(ctx, ptr, el_size * count, name);
+}
+
+/*
+  a function version of talloc_realloc(), so it can be passed as a function pointer
+  to libraries that want a realloc function (a realloc function encapsulates
+  all the basic capabilities of an allocation library, which is why this is useful)
+*/
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size)
+{
+	return _talloc_realloc(context, ptr, size, NULL);
+}
+
+
+static int talloc_autofree_destructor(void *ptr)
+{
+	autofree_context = NULL;
+	return 0;
+}
+
+static void talloc_autofree(void)
+{
+	_talloc_free(autofree_context);
+}
+
+/*
+  return a context which will be auto-freed on exit
+  this is useful for reducing the noise in leak reports
+*/
+void *talloc_autofree_context(void)
+{
+	if (autofree_context == NULL) {
+		autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
+		talloc_set_destructor(autofree_context, talloc_autofree_destructor);
+		atexit(talloc_autofree);
+	}
+	return autofree_context;
+}
+
+size_t talloc_get_size(const void *context)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL)
+		return 0;
+
+	tc = talloc_chunk_from_ptr(context);
+
+	return tc->size;
+}
+
+/*
+  find a parent of this context that has the given name, if any
+*/
+void *talloc_find_parent_byname(const void *context, const char *name)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL) {
+		return NULL;
+	}
+
+	tc = talloc_chunk_from_ptr(context);
+	while (tc) {
+		if (tc->name && strcmp(tc->name, name) == 0) {
+			return TC_PTR_FROM_CHUNK(tc);
+		}
+		while (tc && tc->prev) tc = tc->prev;
+		if (tc) {
+			tc = tc->parent;
+		}
+	}
+	return NULL;
+}
+
+/*
+  show the parentage of a context
+*/
+void talloc_show_parents(const void *context, FILE *file)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL) {
+		fprintf(file, "talloc no parents for NULL\n");
+		return;
+	}
+
+	tc = talloc_chunk_from_ptr(context);
+	fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
+	while (tc) {
+		fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
+		while (tc && tc->prev) tc = tc->prev;
+		if (tc) {
+			tc = tc->parent;
+		}
+	}
+	fflush(file);
+}
+
+/*
+  return 1 if ptr is a parent of context
+*/
+int talloc_is_parent(const void *context, const void *ptr)
+{
+	struct talloc_chunk *tc;
+
+	if (context == NULL) {
+		return 0;
+	}
+
+	tc = talloc_chunk_from_ptr(context);
+	while (tc) {
+		if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
+		while (tc && tc->prev) tc = tc->prev;
+		if (tc) {
+			tc = tc->parent;
+		}
+	}
+	return 0;
+}
Index: /releases/0.12.0/lib/void_stack.c
===================================================================
--- /releases/0.12.0/lib/void_stack.c	(revision 175)
+++ /releases/0.12.0/lib/void_stack.c	(revision 175)
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2005,2007,2009-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/**  
+ * @file
+ */
+
+
+#include "void_stack.h"
+
+void_stack* void_stack_new(unsigned short max_size)
+{
+  void_stack* ret_val = talloc(NULL, void_stack);
+
+  if (ret_val != NULL)
+  {
+    memset(ret_val, 0, sizeof(*ret_val));
+    ret_val->elements = talloc_array(ret_val, void*, max_size);
+    if (ret_val->elements == NULL)
+    {
+      talloc_free(ret_val);
+      ret_val = NULL;
+    }
+    else
+    {
+      memset(ret_val->elements, 0, max_size*sizeof(void*));
+    
+      ret_val->max_size = max_size;
+      ret_val->top = 0;
+    }
+  }
+
+  return ret_val;
+}
+
+
+void_stack* void_stack_copy(const void_stack* v)
+{
+  unsigned int i;
+  void_stack* ret_val;
+  if(v == NULL)
+    return NULL;
+
+  ret_val = void_stack_new(v->max_size);
+  if(ret_val == NULL)
+    return NULL;
+
+  for(i = 0; i < v->top; i++)
+    ret_val->elements[i] = v->elements[i];
+  ret_val->top = v->top;
+
+  return ret_val;
+}
+
+
+void_stack* void_stack_copy_reverse(const void_stack* v)
+{
+  unsigned int i;
+  void_stack* ret_val;
+  if(v == NULL)
+    return NULL;
+
+  ret_val = void_stack_new(v->max_size);
+  if(ret_val == NULL)
+    return NULL;
+
+  for(i = 0; i < v->top; i++)
+    ret_val->elements[i] = v->elements[v->top-i-1];
+  ret_val->top = v->top;
+
+  return ret_val;
+}
+
+
+void void_stack_free(void_stack* stack)
+{
+  talloc_free(stack);
+}
+
+
+void void_stack_free_deep(void_stack* stack)
+{
+  unsigned short i;
+  for(i=0; i < stack->top; i++)
+    free(stack->elements[i]);
+  talloc_free(stack);
+}
+
+
+unsigned short void_stack_size(const void_stack* stack)
+{
+  return stack->top;
+}
+
+
+void* void_stack_pop(void_stack* stack)
+{
+  void* ret_val = NULL;
+
+  if(stack->top > 0)
+  {
+    ret_val = stack->elements[--(stack->top)];
+    stack->elements[stack->top] = NULL;
+  }
+
+  return ret_val;
+}
+
+
+bool void_stack_push(void_stack* stack, void* e)
+{
+  if(stack->top < stack->max_size)
+  {
+    stack->elements[stack->top++] = e;
+    return true;
+  }
+  else
+    return false;
+}
+
+
+const void* void_stack_cur(const void_stack* stack)
+{
+  void* ret_val = NULL;
+
+  if(stack->top > 0)
+    ret_val = stack->elements[stack->top-1];
+
+  return ret_val;
+}
+
+
+void_stack_iterator* void_stack_iterator_new(const void_stack* stack)
+{
+  void_stack_iterator* ret_val = NULL;
+  
+  if(stack != NULL)
+  {
+    ret_val = talloc(stack, void_stack_iterator);
+    if (ret_val != NULL)
+    {
+      ret_val->stack = stack;
+      ret_val->cur = 0;
+    }
+  }
+
+  return ret_val;
+}
+
+
+void void_stack_iterator_free(void_stack_iterator* iter)
+{
+  talloc_free(iter);
+}
+
+
+const void* void_stack_iterator_next(void_stack_iterator* iter)
+{
+  if(iter->cur < iter->stack->top)
+    return iter->stack->elements[iter->cur++];
+  else
+    return NULL;
+}
Index: /releases/0.12.0/lib/winsec.c
===================================================================
--- /releases/0.12.0/lib/winsec.c	(revision 175)
+++ /releases/0.12.0/lib/winsec.c	(revision 175)
@@ -0,0 +1,531 @@
+/*
+ *
+ * Copyright (C) 2005-2006,2009-2010 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team 
+ *               (see individual files under Subversion for details.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/** @file */
+
+#include "winsec.h"
+
+
+/******************************************************************************
+ * Non-talloc() interface for parsing a descriptor.
+ ******************************************************************************/
+WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len)
+{
+  return winsec_parse_desc(NULL, buf, buf_len);
+}
+
+
+/******************************************************************************
+ * Free a descriptor.  Not needed if using talloc and a parent context is freed.
+ ******************************************************************************/
+void winsec_free_descriptor(WINSEC_DESC* desc)
+{
+  talloc_free(desc);
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_DESC structure and substructures.
+ ******************************************************************************/
+WINSEC_DESC* winsec_parse_desc(void* talloc_ctx, 
+			       const uint8_t* buf, uint32_t buf_len)
+{
+  WINSEC_DESC* ret_val;
+
+  if (buf == NULL || buf_len <  WINSEC_DESC_HEADER_SIZE)
+    return NULL;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_DESC)) == NULL)
+    return NULL;
+
+  ret_val->revision = buf[0];
+  ret_val->sbz1 = buf[1];
+  ret_val->control = SVAL(buf, 0x2);
+
+  if(!(ret_val->control & WINSEC_DESC_SELF_RELATIVE))
+    fprintf(stderr, "DEBUG: NOT self-relative!\n");
+
+  ret_val->off_owner_sid = IVAL(buf, 0x4);
+  ret_val->off_grp_sid = IVAL(buf, 0x8);
+  ret_val->off_sacl = IVAL(buf, 0xC);
+  ret_val->off_dacl = IVAL(buf, 0x10);
+
+  /* A basic sanity check to ensure our offsets are within our buffer.
+   * Additional length checking is done in secondary parsing functions.
+   */
+  if((ret_val->off_owner_sid >= buf_len)
+     || (ret_val->off_grp_sid >= buf_len)
+     || (ret_val->off_sacl >= buf_len)
+     || (ret_val->off_dacl >= buf_len))
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  if(ret_val->off_owner_sid == 0)
+    ret_val->owner_sid = NULL;
+  else
+  {
+    ret_val->owner_sid = winsec_parse_dom_sid(ret_val, 
+					      buf + ret_val->off_owner_sid,
+					      buf_len - ret_val->off_owner_sid);
+    if(ret_val->owner_sid == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+
+  if(ret_val->off_grp_sid == 0) 
+    ret_val->grp_sid = NULL;
+  else
+  {
+    ret_val->grp_sid = winsec_parse_dom_sid(ret_val, buf + ret_val->off_grp_sid,
+					    buf_len - ret_val->off_grp_sid);
+    if(ret_val->grp_sid == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+
+  if((ret_val->control & WINSEC_DESC_SACL_PRESENT) && ret_val->off_sacl)
+  {
+    ret_val->sacl = winsec_parse_acl(ret_val, buf + ret_val->off_sacl,
+				     buf_len - ret_val->off_sacl);
+    if(ret_val->sacl == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+  else
+    ret_val->sacl = NULL;
+
+  if((ret_val->control & WINSEC_DESC_DACL_PRESENT) && ret_val->off_dacl != 0) 
+  {
+    ret_val->dacl = winsec_parse_acl(ret_val, buf + ret_val->off_dacl,
+				     buf_len - ret_val->off_dacl);
+    if(ret_val->dacl == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+  else
+    ret_val->dacl = NULL;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_ACL structure and all substructures.
+ ******************************************************************************/
+WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
+			     const uint8_t* buf, uint32_t buf_len)
+{
+  uint32_t i, offset;
+  WINSEC_ACL* ret_val;
+
+  /*
+   * Note that the size is always a multiple of 4 bytes due to the
+   * nature of the data structure.
+   */
+  if (buf == NULL || buf_len < 8)
+    return NULL;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_ACL)) == NULL)
+    return NULL;
+  
+  ret_val->revision = SVAL(buf, 0x0);
+  ret_val->size     = SVAL(buf, 0x2);
+  ret_val->num_aces = IVAL(buf, 0x4);
+
+  /* The num_aces can be at most around 4k because anything greater
+   * wouldn't fit in the 16 bit size even if every ace was as small as
+   * possible. 
+   */
+  if((ret_val->size > buf_len) || (ret_val->num_aces > 4095))
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  /* Even if the num_aces is zero, allocate memory as there's a difference
+   * between a non-present DACL (allow all access) and a DACL with no ACE's
+   * (allow no access).
+   */
+  if((ret_val->aces = talloc_array(ret_val, WINSEC_ACE*, 
+				   ret_val->num_aces+1)) == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  offset = 8;
+  for(i=0; i < ret_val->num_aces; i++)
+  {
+    ret_val->aces[i] = winsec_parse_ace(ret_val->aces, 
+					buf+offset, buf_len-offset);
+    if(ret_val->aces[i] == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+
+    offset += ret_val->aces[i]->size;
+    if(offset > buf_len)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+  ret_val->aces[ret_val->num_aces] = NULL;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_ACE structure and all substructures.
+ ******************************************************************************/
+WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
+			     const uint8_t* buf, uint32_t buf_len)
+{
+  uint32_t offset;
+  WINSEC_ACE* ret_val;
+
+  if(buf == NULL || buf_len < WINSEC_ACE_MIN_SIZE)
+    return NULL;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_ACE)) == NULL)
+    return NULL;
+
+  ret_val->type = buf[0];
+  ret_val->flags = buf[1];
+  ret_val->size = SVAL(buf, 0x2);
+  ret_val->access_mask = IVAL(buf, 0x4);
+
+  offset = 0x8;
+
+  /* check whether object access is present */
+  if (winsec_ace_object(ret_val->type))
+  {
+    ret_val->obj_flags = IVAL(buf, offset);
+    offset += 4;
+
+    if(ret_val->obj_flags & WINSEC_ACE_OBJECT_PRESENT)
+    {
+      ret_val->obj_guid = winsec_parse_uuid(ret_val, 
+					    buf+offset, buf_len-offset);
+      if(ret_val->obj_guid == NULL)
+      {
+	talloc_free(ret_val);
+	return NULL;
+      }
+      offset += sizeof(WINSEC_UUID);
+    }
+    else
+      ret_val->obj_guid = NULL;
+
+    if(ret_val->obj_flags & WINSEC_ACE_OBJECT_INHERITED_PRESENT)
+    {
+      ret_val->inh_guid = winsec_parse_uuid(ret_val, 
+					    buf+offset, buf_len-offset);
+      if(ret_val->inh_guid == NULL)
+      {
+	talloc_free(ret_val);
+	return NULL;
+      }
+      offset += sizeof(WINSEC_UUID);
+    }
+    else
+      ret_val->inh_guid = NULL;
+  }
+
+  ret_val->trustee = winsec_parse_dom_sid(ret_val, buf+offset, buf_len-offset);
+  if(ret_val->trustee == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_DOM_SID structure.
+ ******************************************************************************/
+WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
+				     const uint8_t* buf, uint32_t buf_len)
+{
+  uint32_t i;
+  WINSEC_DOM_SID* ret_val;
+
+  if(buf == NULL || buf_len < 8)
+    return NULL;
+
+  /*  if((ret_val = (WINSEC_DOM_SID*)zalloc(sizeof(WINSEC_DOM_SID))) == NULL)*/
+  if((ret_val = talloc(talloc_ctx, WINSEC_DOM_SID)) == NULL)
+    return NULL;
+
+  ret_val->sid_rev_num = buf[0];
+  ret_val->num_auths = buf[1];
+  memcpy(ret_val->id_auth, buf+2, 6);
+
+  /* XXX: should really issue a warning here... */
+  if (ret_val->num_auths > WINSEC_MAX_SUBAUTHS)
+    ret_val->num_auths = WINSEC_MAX_SUBAUTHS;
+
+  if(buf_len < ret_val->num_auths*sizeof(uint32_t)+8)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  
+  for(i=0; i < ret_val->num_auths; i++)
+    ret_val->sub_auths[i] = IVAL(buf, 8+i*sizeof(uint32_t));
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_UUID struct.
+ ******************************************************************************/
+WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
+			       const uint8_t* buf, uint32_t buf_len)
+{
+  WINSEC_UUID* ret_val;
+
+  if(buf == NULL || buf_len < sizeof(WINSEC_UUID))
+    return false;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_UUID)) == NULL)
+    return NULL;
+  
+  ret_val->time_low = IVAL(buf, 0x0);
+  ret_val->time_mid = SVAL(buf, 0x4);
+  ret_val->time_hi_and_version = SVAL(buf, 0x6);
+  
+  memcpy(ret_val->clock_seq, buf+0x8, 2);
+  memcpy(ret_val->node, buf+0xB, 6);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Calculates the size of a SID.
+ ******************************************************************************/
+size_t winsec_sid_size(const WINSEC_DOM_SID* sid)
+{
+  if (sid == NULL)
+    return 0;
+
+  return sid->num_auths * sizeof(uint32_t) + 8;
+}
+
+
+/******************************************************************************
+ * Compare the auth portion of two SIDs.
+ ******************************************************************************/
+int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+  int i;
+
+  if (sid1 == sid2)
+    return 0;
+  if (!sid1)
+    return -1;
+  if (!sid2)
+    return 1;
+
+  if (sid1->sid_rev_num != sid2->sid_rev_num)
+    return sid1->sid_rev_num - sid2->sid_rev_num;
+
+  for (i = 0; i < 6; i++)
+    if (sid1->id_auth[i] != sid2->id_auth[i])
+      return sid1->id_auth[i] - sid2->id_auth[i];
+
+  return 0;
+}
+
+
+/******************************************************************************
+ * Compare two SIDs.
+ ******************************************************************************/
+int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+  int i;
+
+  if (sid1 == sid2)
+    return 0;
+  if (!sid1)
+    return -1;
+  if (!sid2)
+    return 1;
+
+  /* Compare most likely different rids, first: i.e start at end */
+  if (sid1->num_auths != sid2->num_auths)
+    return sid1->num_auths - sid2->num_auths;
+
+  for (i = sid1->num_auths-1; i >= 0; --i)
+    if (sid1->sub_auths[i] != sid2->sub_auths[i])
+      return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+  return winsec_sid_compare_auth(sid1, sid2);
+}
+
+
+/******************************************************************************
+ * Compare two SIDs.
+ ******************************************************************************/
+bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+  return winsec_sid_compare(sid1, sid2) == 0;
+}
+
+
+/******************************************************************************
+ * Compares two WINSEC_DESC structures.
+ ******************************************************************************/
+bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2)
+{
+  /* Trivial cases */
+  if (!s1 && !s2)
+    return true;
+  if (!s1 || !s2)
+    return false;
+
+  /* Check top level stuff */
+  if (s1->revision != s2->revision)
+    return false;
+
+  if (s1->control != s2->control)
+    return false;
+
+  /* Check owner and group */
+  if (!winsec_sid_equal(s1->owner_sid, s2->owner_sid))
+    return false;
+
+  if (!winsec_sid_equal(s1->grp_sid, s2->grp_sid)) 
+    return false;
+
+  /* Check ACLs present in one but not the other */
+  if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
+      (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) 
+  { return false; }
+
+  /* Sigh - we have to do it the hard way by iterating over all
+     the ACEs in the ACLs */
+  if(!winsec_acl_equal(s1->dacl, s2->dacl) || !winsec_acl_equal(s1->sacl, s2->sacl)) 
+    return false;
+
+  return true;
+}
+
+
+
+/******************************************************************************
+ * Compares two WINSEC_ACL structures.
+ ******************************************************************************/
+bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2)
+{
+  unsigned int i, j;
+
+  /* Trivial cases */
+  if (!s1 && !s2) 
+    return true;
+  if (!s1 || !s2) 
+    return false;
+
+  /* Check top level stuff */
+  if (s1->revision != s2->revision)
+    return false;
+
+  if (s1->num_aces != s2->num_aces)
+    return false;
+
+  /* The ACEs could be in any order so check each ACE in s1 against 
+     each ACE in s2. */
+
+  for (i = 0; i < s1->num_aces; i++)
+  {
+    bool found = false;
+
+    for (j = 0; j < s2->num_aces; j++) 
+    {
+      if (winsec_ace_equal(s1->aces[i], s2->aces[j])) 
+      {
+	found = true;
+	break;
+      }
+    }
+
+    if (!found)
+      return false;
+  }
+
+  return true;
+}
+
+
+/******************************************************************************
+ * Compares two WINSEC_ACE structures.
+ ******************************************************************************/
+bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2)
+{
+  /* Trivial cases */
+  if (!s1 && !s2) 
+    return true;
+  if (!s1 || !s2) 
+    return false;
+
+  /* Check top level stuff */
+  if (s1->type != s2->type || s1->flags != s2->flags ||
+      s1->access_mask != s2->access_mask)
+  { return false; }
+
+  /* Check SID */
+  if (!winsec_sid_equal(s1->trustee, s2->trustee))
+    return false;
+
+  return true;
+}
+
+
+/******************************************************************************
+ * Check if ACE has OBJECT type.
+ ******************************************************************************/
+bool winsec_ace_object(uint8_t type)
+{
+  if (type == WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+      type == WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+      type == WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+      type == WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) 
+  { return true; }
+
+  return false;
+}
Index: /releases/0.12.0/src/Makefile
===================================================================
--- /releases/0.12.0/src/Makefile	(revision 175)
+++ /releases/0.12.0/src/Makefile	(revision 175)
@@ -0,0 +1,28 @@
+# $Id$
+
+################################################################################
+
+REGLOOKUP=$(BUILD_BIN)/reglookup$(BIN_EXT)
+REGLOOKUP_RECOVER=$(BUILD_BIN)/reglookup-recover$(BIN_EXT)
+OBJ=$(wildcard ../lib/*.o)
+FILES=$(REGLOOKUP) $(REGLOOKUP_RECOVER)
+
+all: $(FILES)
+
+$(REGLOOKUP): reglookup.o $(OBJ)
+	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup.o $(OBJ) $(EXTRA_OBJ)
+
+$(REGLOOKUP_RECOVER): reglookup-recover.o $(OBJ)
+	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup-recover.o $(OBJ) $(EXTRA_OBJ)
+
+reglookup.o: reglookup.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ reglookup.c
+
+reglookup-recover.o: reglookup-recover.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ reglookup-recover.c
+
+install:
+	install -m 0755 $(FILES) $(BIN_PREFIX)
+
+clean:
+	rm -f *.o
Index: /releases/0.12.0/src/common.c
===================================================================
--- /releases/0.12.0/src/common.c	(revision 175)
+++ /releases/0.12.0/src/common.c	(revision 175)
@@ -0,0 +1,338 @@
+/*
+ * This file stores code common to the command line tools.
+ * XXX: This should be converted to a proper library.
+ *
+ * Copyright (C) 2005-2008 Timothy D. Morgan
+ * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+#include <iconv.h>
+iconv_t conv_desc;
+
+const char* key_special_chars = ",\"\\/";
+const char* subfield_special_chars = ",\"\\|";
+const char* common_special_chars = ",\"\\";
+
+#define REGLOOKUP_VERSION "0.12.0"
+
+#define REGLOOKUP_EXIT_OK       0
+#define REGLOOKUP_EXIT_OSERR   71
+#define REGLOOKUP_EXIT_USAGE   64
+#define REGLOOKUP_EXIT_DATAERR 65
+#define REGLOOKUP_EXIT_NOINPUT 66
+
+
+void bailOut(int code, char* message)
+{
+  fprintf(stderr, message);
+  exit(code);
+}
+
+void printMsgs(REGFI_FILE* f)
+{
+  char* msgs = regfi_get_messages(f);
+  if(msgs != NULL)
+  {
+    fprintf(stderr, "%s", msgs);
+    free(msgs);
+  }
+}
+
+void clearMsgs(REGFI_FILE* f)
+{
+  char* msgs = regfi_get_messages(f);
+  if(msgs != NULL)
+    free(msgs);
+}
+
+
+/* Returns a newly malloc()ed string which contains original buffer,
+ * except for non-printable or special characters are quoted in hex
+ * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
+ * character.  A null terminator is added, since only ascii, not binary,
+ * is returned.
+ */
+static char* quote_buffer(const unsigned char* str, 
+			  unsigned int len, const char* special)
+{
+  unsigned int i, added_len;
+  unsigned int num_written = 0;
+
+  unsigned int buf_len = sizeof(char)*(len+1);
+  char* ret_val = NULL; 
+  char* tmp_buf;
+
+  if(buf_len > 0) 
+    ret_val = malloc(buf_len);
+  if(ret_val == NULL)
+    return NULL;
+
+  for(i=0; i<len; i++)
+  {
+    if(buf_len <= (num_written+5))
+    {
+      /* Expand the buffer by the memory consumption rate seen so far 
+       * times the amount of input left to process.  The expansion is bounded 
+       * below by a minimum safety increase, and above by the maximum possible 
+       * output string length.  This should minimize both the number of 
+       * reallocs() and the amount of wasted memory.
+       */
+      added_len = (len-i)*num_written/(i+1);
+      if((buf_len+added_len) > (len*4+1))
+	buf_len = len*4+1;
+      else
+      {
+	if (added_len < 5)
+	  buf_len += 5;
+	else
+	  buf_len += added_len;
+      }
+
+      tmp_buf = realloc(ret_val, buf_len);
+      if(tmp_buf == NULL)
+      {
+	free(ret_val);
+	return NULL;
+      }
+      ret_val = tmp_buf;
+    }
+    
+    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
+    {
+      num_written += snprintf(ret_val + num_written, buf_len - num_written,
+			      "\\x%.2X", str[i]);
+    }
+    else
+      ret_val[num_written++] = str[i];
+  }
+  ret_val[num_written] = '\0';
+
+  return ret_val;
+}
+
+
+/* Returns a newly malloc()ed string which contains original string, 
+ * except for non-printable or special characters are quoted in hex
+ * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
+ * character.
+ */
+static char* quote_string(const char* str, const char* special)
+{
+  unsigned int len;
+
+  if(str == NULL)
+    return NULL;
+
+  len = strlen(str);
+  return quote_buffer((const unsigned char*)str, len, special);
+}
+
+
+/*
+ * Convert a data value to a string for display.  Returns NULL on error,
+ * and the string to display if there is no error, or a non-fatal
+ * error.  On any error (fatal or non-fatal) occurs, (*error_msg) will
+ * be set to a newly allocated string, containing an error message.  If
+ * a memory allocation failure occurs while generating the error
+ * message, both the return value and (*error_msg) will be NULL.  It
+ * is the responsibility of the caller to free both a non-NULL return
+ * value, and a non-NULL (*error_msg).
+ */
+static char* data_to_ascii(REGFI_DATA* data, char** error_msg)
+{
+  char* ret_val;
+  char* cur_quoted;
+  char* tmp_ptr;
+  char* delim;
+  uint32_t ret_val_left, i, tmp_len;
+
+  if(data == NULL || data->size == 0)
+  {
+    *error_msg = (char*)malloc(37);
+    if(*error_msg == NULL)
+      return NULL;
+    strcpy(*error_msg, "Data pointer was NULL or size was 0.");
+    return NULL;
+  }
+  *error_msg = NULL;
+
+
+  if(data->interpreted_size == 0)
+  {
+    *error_msg = (char*)malloc(51);
+    if(*error_msg == NULL)
+      return NULL;
+    strcpy(*error_msg, "Data could not be interpreted, quoting raw buffer.");
+    return quote_buffer(data->raw, data->size, subfield_special_chars);
+  }
+
+  switch (data->type) 
+  {
+  case REG_SZ:
+    ret_val = quote_string((char*)data->interpreted.string, common_special_chars);
+    if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+
+    return ret_val;
+    break;
+
+    
+  case REG_EXPAND_SZ:
+    ret_val = quote_string((char*)data->interpreted.expand_string, 
+			   common_special_chars);
+    if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+
+    return ret_val;
+    break;
+
+  case REG_LINK:
+    ret_val = quote_string((char*)data->interpreted.link, common_special_chars);
+    if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+
+    return ret_val;
+    break;
+
+  case REG_DWORD:
+    ret_val = malloc(sizeof(char)*(8+2+1));
+    if(ret_val == NULL)
+      return NULL;
+
+    sprintf(ret_val, "0x%.8X", data->interpreted.dword);
+    return ret_val;
+    break;
+
+  case REG_DWORD_BE:
+    ret_val = malloc(sizeof(char)*(8+2+1));
+    if(ret_val == NULL)
+      return NULL;
+
+    sprintf(ret_val, "0x%.8X", data->interpreted.dword_be);
+    return ret_val;
+    break;
+
+  case REG_QWORD:
+    ret_val = malloc(sizeof(char)*(16+2+1));
+    if(ret_val == NULL)
+      return NULL;
+
+    sprintf(ret_val, "0x%.16llX", 
+	    (long long unsigned int)data->interpreted.qword);
+    return ret_val;
+    break;
+
+  case REG_MULTI_SZ:
+    ret_val_left = data->interpreted_size*4+1;
+    ret_val = malloc(ret_val_left);
+    if(ret_val == NULL)
+      return NULL;
+
+    tmp_ptr = ret_val;
+    tmp_ptr[0] = '\0';
+    delim = "";
+    for(i=0; data->interpreted.multiple_string[i] != NULL; i++)
+    {
+      cur_quoted = quote_string((char*)data->interpreted.multiple_string[i],
+				subfield_special_chars);
+      if(cur_quoted != NULL && cur_quoted[0] != '\0')
+      {
+	tmp_len = snprintf(tmp_ptr, ret_val_left, "%s%s",delim, cur_quoted);
+	tmp_ptr += tmp_len;
+	ret_val_left -= tmp_len;
+	free(cur_quoted);
+      }
+      delim = "|";
+    }
+
+    return ret_val;
+    break;
+
+    
+  case REG_NONE:
+    return quote_buffer(data->interpreted.none, data->interpreted_size,
+			common_special_chars);
+
+    break;
+
+  case REG_RESOURCE_LIST:
+    return quote_buffer(data->interpreted.resource_list, data->interpreted_size,
+			common_special_chars);
+
+    break;
+
+  case REG_FULL_RESOURCE_DESCRIPTOR:
+    return quote_buffer(data->interpreted.full_resource_descriptor, 
+			data->interpreted_size, common_special_chars);
+
+    break;
+
+  case REG_RESOURCE_REQUIREMENTS_LIST:
+    return quote_buffer(data->interpreted.resource_requirements_list,
+			data->interpreted_size, common_special_chars);
+
+    break;
+
+  case REG_BINARY:
+    return quote_buffer(data->interpreted.binary, data->interpreted_size,
+			common_special_chars);
+
+    break;
+
+  default:
+    /* This shouldn't happen, since the regfi routines won't interpret 
+     * unknown types, but just as a safety measure against library changes... 
+     */
+    *error_msg = (char*)malloc(65);
+    if(*error_msg == NULL)
+      return NULL;
+    sprintf(*error_msg,
+	    "Unrecognized registry data type (0x%.8X); quoting as binary.",
+	    data->type);
+    return quote_buffer(data->raw, data->size, common_special_chars);
+  }
+    
+  return NULL;
+}
+
+
+static char* get_quoted_keyname(const REGFI_NK_REC* nk)
+{
+  char* ret_val;
+
+  if(nk->keyname == NULL)
+    ret_val = quote_buffer(nk->keyname_raw, nk->name_length, key_special_chars);
+  else
+    ret_val = quote_string(nk->keyname, key_special_chars);
+
+  return ret_val;
+}
+
+
+static char* get_quoted_valuename(const REGFI_VK_REC* vk)
+{
+  char* ret_val;
+
+  if(vk->valuename == NULL)
+    ret_val = quote_buffer(vk->valuename_raw, vk->name_length, 
+			   key_special_chars);
+  else
+    ret_val = quote_string(vk->valuename, key_special_chars);
+
+  return ret_val;
+}
Index: /releases/0.12.0/src/reglookup-recover.c
===================================================================
--- /releases/0.12.0/src/reglookup-recover.c	(revision 175)
+++ /releases/0.12.0/src/reglookup-recover.c	(revision 175)
@@ -0,0 +1,994 @@
+/*
+ * This program attempts to recover deleted data structures in a registry hive.
+ *
+ * Copyright (C) 2008-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "talloc.h"
+#include "regfi.h"
+#include "range_list.h"
+#include "lru_cache.h"
+
+
+/* Globals, influenced by command line parameters */
+bool print_verbose = false;
+bool print_security = false;
+bool print_header = true;
+bool print_leftover = false;
+bool print_parsedraw = false;
+char* registry_file = NULL;
+
+#include "common.c"
+
+
+char* getQuotedData(int fd, uint32_t offset, uint32_t length)
+{
+  uint8_t* buf;
+  char* quoted_buf;
+  uint32_t len;
+
+  if((lseek(fd, offset, SEEK_SET)) == -1)
+    return NULL;
+
+  buf = (uint8_t*)malloc(length);
+  if(buf == NULL)
+    return NULL;
+
+  len = length;
+  if((regfi_read(fd, buf, &length) != 0) || length != len)
+  {
+    free(buf);
+    return NULL;
+  }
+
+  quoted_buf = quote_buffer(buf, length, common_special_chars);
+  free(buf);
+
+  return quoted_buf;
+}
+
+/* XXX: Somewhere in here, need to start looking for and handling classnames */
+void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
+{
+  char mtime[20];
+  time_t tmp_time[1];
+  struct tm* tmp_time_s = NULL;
+  char* quoted_name = NULL;
+  char* quoted_raw = "";
+
+  *tmp_time = regfi_nt2unix_time(&nk->mtime);
+  tmp_time_s = gmtime(tmp_time);
+  strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+
+  /* XXX: Add command line option to choose output encoding */
+  regfi_interpret_keyname(f, nk, REGFI_ENCODING_ASCII, true);
+
+  quoted_name = get_quoted_keyname(nk);
+  if (quoted_name == NULL)
+  {
+    quoted_name = malloc(1*sizeof(char));
+    if(quoted_name == NULL)
+      bailOut(REGLOOKUP_EXIT_OSERR, 
+	      "ERROR: Could not allocate sufficient memory.\n");
+    quoted_name[0] = '\0';
+
+    fprintf(stderr, "WARN: NULL key name in NK record at offset %.8X.\n",
+	    nk->offset);
+  }
+
+  if(print_parsedraw)
+    quoted_raw = getQuotedData(f->fd, nk->offset, nk->cell_size);
+
+  printf("%.8X,%.8X,KEY,%s,%s,%s,%d,,,,,,,,%s\n", nk->offset, nk->cell_size,
+	 prefix, quoted_name, mtime, nk->num_values, quoted_raw);
+  
+  if(print_parsedraw)
+    free(quoted_raw);
+  free(quoted_name);
+}
+
+
+void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
+{
+  char* quoted_value = NULL;
+  char* quoted_name = NULL;
+  char* quoted_raw = "";
+  char* conv_error = NULL;
+  const char* str_type = NULL;
+
+  /* XXX: Add command line option to choose output encoding */
+  regfi_interpret_valuename(f, vk, REGFI_ENCODING_ASCII, true);
+  
+  quoted_name = get_quoted_valuename(vk);
+  if (quoted_name == NULL)
+  { /* Value names are NULL when we're looking at the "(default)" value.
+     * Currently we just return a 0-length string to try an eliminate 
+     * ambiguity with a literal "(default)" value.  The data type of a line
+     * in the output allows one to differentiate between the parent key and
+     * this value.
+     */
+    quoted_name = strdup("");
+    if(quoted_name == NULL)
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+  }
+
+  /* XXX: Add command line option to choose output encoding */
+  if(vk->data != NULL 
+     && !regfi_interpret_data(f, REGFI_ENCODING_ASCII, vk->type, vk->data))
+  {
+    fprintf(stderr, "WARN: Error occurred while interpreting data for VK record"
+	    " at offset 0x%.8X.\n", vk->offset);
+  }
+  printMsgs(f);
+
+  quoted_value = data_to_ascii(vk->data, &conv_error);
+  if(quoted_value == NULL)
+  {
+    quoted_value = malloc(1*sizeof(char));
+    if(quoted_value == NULL)
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+    quoted_value[0] = '\0';
+
+    if(conv_error == NULL)
+      fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
+	      "Memory allocation failure likely.\n", prefix, quoted_name);
+    else if(print_verbose)
+      fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
+	      "Returned error: %s\n", prefix, quoted_name, conv_error);
+  }
+  /* XXX: should these always be printed? */
+  else if(conv_error != NULL && print_verbose)
+    fprintf(stderr, "INFO: While quoting value for '%s/%s', "
+	    "warning returned: %s\n", prefix, quoted_name, conv_error);
+
+
+  if(print_parsedraw)
+    quoted_raw = getQuotedData(f->fd, vk->offset, vk->cell_size);
+
+  str_type = regfi_type_val2str(vk->type);
+  if(str_type == NULL)
+    printf("%.8X,%.8X,VALUE,%s,%s,,,0x%.8X,%s,%d,,,,,%s\n", 
+	   vk->offset, vk->cell_size, prefix, quoted_name, 
+	   vk->type, quoted_value, vk->data_size, quoted_raw);
+  else
+    printf("%.8X,%.8X,VALUE,%s,%s,,,%s,%s,%d,,,,,%s\n", 
+	   vk->offset, vk->cell_size, prefix, quoted_name, 
+	   str_type, quoted_value, vk->data_size, quoted_raw);
+
+  if(print_parsedraw)
+    free(quoted_raw);
+  if(quoted_value != NULL)
+    free(quoted_value);
+  if(quoted_name != NULL)
+    free(quoted_name);
+  if(conv_error != NULL)
+    free(conv_error);
+}
+
+
+void printSK(REGFI_FILE* f, REGFI_SK_REC* sk)
+{
+  char* quoted_raw = NULL;
+  char* empty_str = "";
+  char* owner = regfi_get_owner(sk->sec_desc);
+  char* group = regfi_get_group(sk->sec_desc);
+  char* sacl = regfi_get_sacl(sk->sec_desc);
+  char* dacl = regfi_get_dacl(sk->sec_desc);
+
+  if(print_parsedraw)
+    quoted_raw = getQuotedData(f->fd, sk->offset, sk->cell_size);
+
+  if(owner == NULL)
+    owner = empty_str;
+  if(group == NULL)
+    group = empty_str;
+  if(sacl == NULL)
+    sacl = empty_str;
+  if(dacl == NULL)
+    dacl = empty_str;
+
+  printf("%.8X,%.8X,SK,,,,,,,,%s,%s,%s,%s,%s\n", sk->offset, sk->cell_size,
+	 owner, group, sacl, dacl, quoted_raw);
+  
+  if(owner != empty_str)
+    free(owner);
+  if(group != empty_str)
+    free(group);
+  if(sacl != empty_str)
+    free(sacl);
+  if(dacl != empty_str)
+    free(dacl);
+
+  if(print_parsedraw)
+    free(quoted_raw);
+}
+
+
+int printCell(REGFI_FILE* f, uint32_t offset)
+{
+  char* quoted_buf;
+  uint32_t cell_length;
+  bool unalloc;
+
+  if(!regfi_parse_cell(f->fd, offset, NULL, 0, &cell_length, &unalloc))
+    return 1;
+
+  quoted_buf = getQuotedData(f->fd, offset, cell_length);
+  if(quoted_buf == NULL)
+    return 2;
+
+  printf("%.8X,%.8X,RAW,,,,,,,,,,,,%s\n", offset, cell_length, quoted_buf);
+
+  free(quoted_buf);
+  return 0;
+}
+
+
+/* This function returns a properly quoted parent path or partial parent 
+ * path for a given key.  Returns NULL on error, "" if no path was available.
+ * Paths returned must be free()d.
+ */
+/* XXX: This is not terribly efficient, as it may reparse many keys 
+ *      repeatedly.  Should try to add caching.
+ */
+char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
+{
+  void_stack* path_stack = void_stack_new(REGFI_MAX_DEPTH);
+  REGFI_NK_REC* cur_ancestor;
+  char* ret_val;
+  uint32_t virt_offset, i, stack_size, ret_val_size, ret_val_used, offset;
+  int32_t max_size;
+  REGFI_BUFFER* path_element;
+  
+  /* The path_stack size limit should guarantee that we don't recurse forever. */
+  virt_offset = nk->parent_off;
+  ret_val_size = 1; /* NUL */
+  while(virt_offset != REGFI_OFFSET_NONE)
+  {
+    offset = virt_offset+REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(f, offset);
+    if(max_size < 0)
+      virt_offset = REGFI_OFFSET_NONE;
+    else
+    {
+      cur_ancestor = regfi_parse_nk(f, offset, max_size, true);
+      printMsgs(f);
+
+      if(cur_ancestor == NULL)
+	virt_offset = REGFI_OFFSET_NONE;
+      else
+      {
+	if(cur_ancestor->flags & REGFI_NK_FLAG_ROOT)
+	  virt_offset = REGFI_OFFSET_NONE;
+	else
+	  virt_offset = cur_ancestor->parent_off;
+	
+	path_element = talloc(path_stack, REGFI_BUFFER);
+	if(path_element != NULL)
+	{
+	  /* XXX: Add command line option to choose output encoding */
+	  regfi_interpret_keyname(f, cur_ancestor, REGFI_ENCODING_ASCII, true);
+	  
+	  path_element->buf = (uint8_t*)get_quoted_keyname(cur_ancestor);
+	}
+ 
+	if(path_element == NULL || path_element->buf == NULL 
+	   || !void_stack_push(path_stack, path_element))
+	{
+	  /* XXX: Need to add a warning here */
+	  regfi_free_key(cur_ancestor);
+	  void_stack_free(path_stack);
+	  return NULL;
+	}
+
+	/* Path element and preceeding delimiter
+	 * Note that this integer can't overflow since key name lengths are
+	 * 16 bits and the max depth is 512.
+	 */
+	path_element->len = strlen((char*)path_element->buf);
+	ret_val_size += path_element->len + 1;
+
+	regfi_free_key(cur_ancestor);
+      }
+    }
+  }
+  
+  stack_size = void_stack_size(path_stack);
+  ret_val_used = 0;
+  ret_val = malloc(ret_val_size);
+  if(ret_val == NULL)
+  {
+    void_stack_free(path_stack);
+    return NULL;
+  }
+  ret_val[0] = '\0';
+
+  for(i=0; i<stack_size; i++)
+  {
+    path_element = void_stack_pop(path_stack);
+    snprintf(ret_val+ret_val_used, ret_val_size-ret_val_used, 
+	     "/%s", path_element->buf);
+    ret_val_used += path_element->len + 1;
+    free(path_element->buf);
+    talloc_free(path_element);
+  }
+  void_stack_free(path_stack);
+
+  return ret_val;
+}
+
+
+static void usage(void)
+{
+  fprintf(stderr, "Usage: reglookup-recover [options] <REGISTRY_FILE>\n");
+  fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION);
+  fprintf(stderr, "Options:\n");
+  fprintf(stderr, "\t-v\t sets verbose mode.\n");
+  fprintf(stderr, "\t-h\t enables header row. (default)\n");
+  fprintf(stderr, "\t-H\t disables header row.\n");
+  fprintf(stderr, "\t-l\t enables leftover(raw) cell output.\n");
+  fprintf(stderr, "\t-L\t disables leftover(raw) cell output. (default)\n");
+  fprintf(stderr, "\t-r\t enables raw cell output for parsed cells.\n");
+  fprintf(stderr, "\t-R\t disables raw cell output for parsed cells. (default)\n");
+  fprintf(stderr, "\n");
+}
+
+
+bool removeRange(range_list* rl, uint32_t offset, uint32_t length)
+{
+  int32_t rm_idx;
+  const range_list_element* cur_elem;
+
+  rm_idx = range_list_find(rl, offset);
+  if(rm_idx < 0)
+  {
+    fprintf(stderr, "DEBUG: removeRange: rm_idx < 0; (%d)\n", rm_idx);
+    return false;
+  }
+
+  cur_elem = range_list_get(rl, rm_idx);
+  if(cur_elem == NULL)
+  {
+    fprintf(stderr, "DEBUG: removeRange: cur_elem == NULL.  rm_idx=%d\n", rm_idx);
+    return false;
+  }
+
+  if(offset > cur_elem->offset)
+  {
+    if(!range_list_split_element(rl, rm_idx, offset))
+    {
+      fprintf(stderr, "DEBUG: removeRange: first split failed\n");
+      return false;
+    }
+    rm_idx++;
+    cur_elem = range_list_get(rl, rm_idx);
+    if(cur_elem == NULL)
+    {
+      fprintf(stderr, 
+	      "DEBUG: removeRange: cur_elem == NULL after first split.  rm_idx=%d\n",
+	      rm_idx);
+      return false;
+    }
+  }
+  
+  if(offset+length < cur_elem->offset+cur_elem->length)
+  {
+    if(!range_list_split_element(rl, rm_idx, offset+length))
+    {
+      fprintf(stderr, "DEBUG: removeRange: second split failed\n");
+      return false;
+    }
+  }
+  
+  if(!range_list_remove(rl, rm_idx))
+  {
+    fprintf(stderr, "DEBUG: removeRange: remove failed\n");
+    return false;
+  }
+
+  return true;
+}
+
+
+int extractVKs(REGFI_FILE* f,
+	       range_list* unalloc_cells,
+	       range_list* unalloc_values)
+{
+  const range_list_element* cur_elem;
+  REGFI_VK_REC* vk;
+  uint32_t i, j;
+
+  for(i=0; i < range_list_size(unalloc_cells); i++)
+  {
+    printMsgs(f);
+    cur_elem = range_list_get(unalloc_cells, i);
+    for(j=0; j <= cur_elem->length; j+=8)
+    {
+      vk = regfi_parse_vk(f, cur_elem->offset+j, 
+			   cur_elem->length-j, false);
+      printMsgs(f);
+
+      if(vk != NULL)
+      {
+	if(!range_list_add(unalloc_values, vk->offset,
+			   vk->cell_size, vk))
+	{
+	  fprintf(stderr, "ERROR: Couldn't add value to unalloc_values.\n");
+	  return 20;
+	}
+	j+=vk->cell_size-8;
+      }
+    }
+  }
+
+  /* Remove value ranges from the unalloc_cells before we continue. */
+  for(i=0; i<range_list_size(unalloc_values); i++)
+  {
+    cur_elem = range_list_get(unalloc_values, i);
+    if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
+      return 30;
+  }
+
+  return 0;
+}
+
+
+int extractDataCells(REGFI_FILE* file,
+		     range_list* unalloc_cells,
+		     range_list* unalloc_values)
+{
+  const range_list_element* cur_elem;
+  REGFI_VK_REC* vk;
+  range_list* bd_cells;
+  REGFI_BUFFER data;
+  uint32_t i, j, offset, cell_length, length;
+  int32_t max_size;
+  bool unalloc;
+
+  bd_cells = range_list_new();
+  if(bd_cells == NULL)
+    return 10;
+
+  data.buf = NULL;
+  data.len = 0;
+  for(i=0; i<range_list_size(unalloc_values); i++)
+  {
+    cur_elem = range_list_get(unalloc_values, i);
+    vk = (REGFI_VK_REC*)cur_elem->data;
+    if(vk == NULL)
+      return 11;
+
+    length = vk->data_size;
+    vk->data = NULL;
+    if(vk->data_size != 0)
+    {
+      offset = vk->data_off+REGFI_REGF_SIZE;
+
+      if(vk->data_in_offset)
+	data = regfi_parse_little_data(file, vk->data_off, 
+				       length, false);
+      else
+      {
+	max_size = regfi_calc_maxsize(file, offset);
+	if(max_size >= 0 
+	   && regfi_parse_cell(file->fd, offset, NULL, 0,
+			       &cell_length, &unalloc)
+	   && (cell_length & 0x00000007) == 0
+	   && cell_length <= max_size)
+	{
+	  if(cell_length - 4 < length)
+	  {
+	    /* Multi-cell "big data" */
+
+	    /* XXX: All big data records thus far have been 16 bytes long.  
+	     *      Should we check for this precise size instead of just 
+	     *      relying upon the above check?
+	     */
+	    if (file->major_version >= 1 && file->minor_version >= 5)
+	    {
+	      /* Attempt to parse a big data record */
+	      data = regfi_load_big_data(file, offset, length, 
+					 cell_length, bd_cells, false);
+
+	      /* XXX: if this turns out NULL, should fall back to truncating cell */
+	      if(data.buf != NULL)
+	      {
+		for(j=0; j<range_list_size(bd_cells); j++)
+		{
+		  cur_elem = range_list_get(bd_cells, j);
+		  if(cur_elem == NULL)
+		    return 20;
+		  if(!range_list_has_range(unalloc_cells,
+					   cur_elem->offset, 
+					   cur_elem->length))
+		  {
+		    fprintf(stderr, 
+			    "WARN: Successfully parsed big data at offset"
+			    " 0x%.8X was rejected because some substructure"
+			    " (offset=0x%.8X) is allocated or used in other"
+			    " recovered structures.\n",
+			    offset, cur_elem->offset);
+		    talloc_free(data.buf);
+		    data.buf = NULL;
+		    data.len = 0;
+		    break;
+		  }
+		}
+		
+		if(data.buf != NULL)
+		{
+		  for(j=0; j<range_list_size(bd_cells); j++)
+		  {
+		    cur_elem = range_list_get(bd_cells, j);
+		    if(cur_elem == NULL)
+		      return 21;
+		    
+		    if(!removeRange(unalloc_cells, 
+				    cur_elem->offset,
+				    cur_elem->length))
+		    { return 22; }
+		  }
+		}
+	      }
+
+	    }
+	    else
+	    {
+	      fprintf(stderr, 
+		      "WARN: Data length (0x%.8X)"
+		      " larger than remaining cell length (0x%.8X)"
+		      " while parsing data record at offset 0x%.8X."
+		      " Truncating...\n",
+		      length, cell_length - 4, offset);
+	       length = cell_length - 4;
+	    }
+	  }
+	  
+	  /* Typical 1-cell data */
+	  if(range_list_has_range(unalloc_cells, offset, length))
+	  {
+	    data = regfi_parse_data(file, offset, length, false);
+	    if(data.buf != NULL)
+	      if(!removeRange(unalloc_cells, offset, length))
+		return 30;
+	  }
+	}
+      }
+      /* XXX: Need to come up with a different way to link these so the
+       *      vk->data item can be removed from the structure.
+       */
+      vk->data = regfi_buffer_to_data(data);
+      talloc_steal(vk, vk->data);
+    }
+  }
+
+  range_list_free(bd_cells);
+  return 0;
+}
+
+
+/* NOTE: unalloc_keys should be an empty range_list. */
+int extractKeys(REGFI_FILE* f, 
+		range_list* unalloc_cells, 
+		range_list* unalloc_keys)
+{
+  const range_list_element* cur_elem;
+  REGFI_NK_REC* key;
+  uint32_t i, j;
+  int error_code = 0;
+
+  for(i=0; i < range_list_size(unalloc_cells); i++)
+  {
+    printMsgs(f);
+    cur_elem = range_list_get(unalloc_cells, i);
+    for(j=0; cur_elem->length > REGFI_NK_MIN_LENGTH 
+	  && j <= cur_elem->length-REGFI_NK_MIN_LENGTH; j+=8)
+    {
+      key = regfi_parse_nk(f, cur_elem->offset+j,
+			   cur_elem->length-j, false);
+      printMsgs(f);
+
+      if(key != NULL)
+      {
+	if(!range_list_add(unalloc_keys, key->offset, 
+			   key->cell_size, key))
+	{
+	  fprintf(stderr, "ERROR: Couldn't add key to unalloc_keys.\n");
+	  error_code = 20;
+	  goto fail;
+	}
+	talloc_steal(unalloc_keys, key);
+	j+=key->cell_size-8;
+      }
+    }
+  }
+
+  for(i=0; i<range_list_size(unalloc_keys); i++)
+  {
+    cur_elem = range_list_get(unalloc_keys, i);
+    if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
+    {
+      error_code = 30;
+      goto fail;
+    }
+  }
+
+  return 0;
+
+ fail:
+  regfi_free_key(key);
+  return error_code;
+}
+
+int extractValueLists(REGFI_FILE* f,
+		      range_list* unalloc_cells,
+		      range_list* unalloc_keys,
+		      range_list* unalloc_linked_values)
+{
+  REGFI_NK_REC* nk;
+  REGFI_VK_REC* vk;
+  const range_list_element* cur_elem;
+  uint32_t i, j, num_keys, off, values_length;
+  int32_t max_size;
+
+  num_keys=range_list_size(unalloc_keys);
+  for(i=0; i<num_keys; i++)
+  {
+    cur_elem = range_list_get(unalloc_keys, i);
+    if(cur_elem == NULL)
+      return 10;
+    nk = cur_elem->data;
+
+    if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE))
+    {
+      off = nk->values_off + REGFI_REGF_SIZE;
+      max_size = regfi_calc_maxsize(f, off);
+      if(max_size >= 0)
+      {
+	nk->values = regfi_load_valuelist(f, off, nk->num_values, 
+					  max_size, false);
+	if(nk->values != NULL && nk->values->elements != NULL)
+	{
+	  /* Number of elements in the value list may be shorter than advertised 
+	   * by NK record due to cell truncation.  We'll consider this valid and 
+	   * only throw out the whole value list if it bleeds into an already 
+	   * parsed structure.
+	   */
+	  values_length = (nk->values->num_values+1)*sizeof(uint32_t);
+	  if(values_length != (values_length & 0xFFFFFFF8))
+	    values_length = (values_length & 0xFFFFFFF8) + 8;
+
+	  if(!range_list_has_range(unalloc_cells, off, values_length))
+	  { /* We've parsed a values-list which isn't in the unallocated list,
+	     * so prune it.
+	     */
+	    talloc_free(nk->values);
+	    nk->values = NULL;
+	  }
+	  else
+	  { /* Values-list was recovered.  Remove from unalloc_cells and 
+	     * inspect values. 
+	     */
+	    if(!removeRange(unalloc_cells, off, values_length))
+	      return 20;
+
+	    for(j=0; j < nk->values->num_values; j++)
+	    {
+	      /* Don't bother to restrict cell length here, since we'll
+	       * check our unalloc_cells range_list later. 
+	       */
+	      vk = regfi_parse_vk(f, nk->values->elements[j]+REGFI_REGF_SIZE,
+				  0x7FFFFFFF, false);
+	      printMsgs(f);
+	      
+	      if(vk != NULL)
+	      {
+		if(range_list_has_range(unalloc_cells, 
+					vk->offset, vk->cell_size))
+		{
+		  if(!range_list_add(unalloc_linked_values, vk->offset,
+				     vk->cell_size, vk))
+		  {
+		    talloc_free(vk);
+		    return 30;
+		  }
+
+		  if(!removeRange(unalloc_cells, vk->offset, vk->cell_size))
+		    return 40;
+		}
+		else
+		  talloc_free(vk);
+	      }
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  return 0;
+}
+
+
+
+/* NOTE: unalloc_sks should be an empty range_list. */
+int extractSKs(REGFI_FILE* f, 
+	       range_list* unalloc_cells,
+	       range_list* unalloc_sks)
+{
+  const range_list_element* cur_elem;
+  REGFI_SK_REC* sk;
+  uint32_t i, j;
+
+  for(i=0; i < range_list_size(unalloc_cells); i++)
+  {
+    printMsgs(f);
+    cur_elem = range_list_get(unalloc_cells, i);
+    for(j=0; j <= cur_elem->length; j+=8)
+    {
+      sk = regfi_parse_sk(f, cur_elem->offset+j, 
+			  cur_elem->length-j, false);
+      printMsgs(f);
+
+      if(sk != NULL)
+      {
+	if(!range_list_add(unalloc_sks, sk->offset,
+			   sk->cell_size, sk))
+	{
+	  fprintf(stderr, "ERROR: Couldn't add sk to unalloc_sks.\n");
+	  return 20;
+	}
+	talloc_steal(unalloc_sks, sk);
+	j+=sk->cell_size-8;
+      }
+    }
+  }
+
+  for(i=0; i<range_list_size(unalloc_sks); i++)
+  {
+    cur_elem = range_list_get(unalloc_sks, i);
+    if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
+      return 30;
+  }
+
+  return 0;
+}
+
+
+int main(int argc, char** argv)
+{ 
+  REGFI_FILE* f;
+  const range_list_element* cur_elem;
+  range_list* unalloc_cells;
+  range_list* unalloc_keys;
+  range_list* unalloc_linked_values;
+  range_list* unalloc_values;
+  range_list* unalloc_sks;
+  char** parent_paths;
+  char* tmp_name;
+  char* tmp_path;
+  REGFI_NK_REC* tmp_key;
+  REGFI_VK_REC* tmp_value;
+  uint32_t argi, arge, i, j, ret, num_unalloc_keys;
+  
+  /* Process command line arguments */
+  if(argc < 2)
+  {
+    usage();
+    bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n");
+  }
+  
+  arge = argc-1;
+  for(argi = 1; argi < arge; argi++)
+  {
+    if (strcmp("-v", argv[argi]) == 0)
+      print_verbose = true;
+    else if (strcmp("-h", argv[argi]) == 0)
+      print_header = true;
+    else if (strcmp("-H", argv[argi]) == 0)
+      print_header = false;
+    else if (strcmp("-l", argv[argi]) == 0)
+      print_leftover = true;
+    else if (strcmp("-L", argv[argi]) == 0)
+      print_leftover = false;
+    else if (strcmp("-r", argv[argi]) == 0)
+      print_parsedraw = true;
+    else if (strcmp("-R", argv[argi]) == 0)
+      print_parsedraw = false;
+    else
+    {
+      usage();
+      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
+      bailOut(REGLOOKUP_EXIT_USAGE, "");
+    }
+  }
+  /*test_offset = strtol(argv[argi++], NULL, 16);*/
+
+  if((registry_file = strdup(argv[argi])) == NULL)
+    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
+
+  f = regfi_open(registry_file);
+  if(f == NULL)
+  {
+    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
+    bailOut(REGLOOKUP_EXIT_NOINPUT, "");
+  }
+  if(print_verbose)
+    regfi_set_message_mask(f, REGFI_MSG_ERROR|REGFI_MSG_WARN|REGFI_MSG_INFO);
+  else
+    regfi_set_message_mask(f, REGFI_MSG_ERROR);
+
+  if(print_header)
+    printf("OFFSET,REC_LENGTH,REC_TYPE,PATH,NAME,"
+	   "NK_MTIME,NK_NVAL,VK_TYPE,VK_VALUE,VK_DATA_LEN,"
+	   "SK_OWNER,SK_GROUP,SK_SACL,SK_DACL,RAW_CELL\n");
+
+  unalloc_cells = regfi_parse_unalloc_cells(f);
+  if(unalloc_cells == NULL)
+  {
+    fprintf(stderr, "ERROR: Could not obtain list of unallocated cells.\n");
+    return 1;
+  }
+
+  unalloc_keys = range_list_new();
+  if(unalloc_keys == NULL)
+    return 10;
+
+  unalloc_linked_values = range_list_new();
+  if(unalloc_linked_values == NULL)
+    return 10;
+
+  unalloc_values = range_list_new();
+  if(unalloc_values == NULL)
+    return 10;
+
+  unalloc_sks = range_list_new();
+  if(unalloc_sks == NULL)
+    return 10;
+
+  ret = extractKeys(f, unalloc_cells, unalloc_keys);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractKeys() failed with %d.\n", ret);
+    return ret;
+  }
+
+  ret = extractValueLists(f, unalloc_cells, unalloc_keys,unalloc_linked_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractValueLists() failed with %d.\n", ret);
+    return ret;
+  }
+
+  /* Carve any orphan values */
+  ret = extractVKs(f, unalloc_cells, unalloc_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractVKs() failed with %d.\n", ret);
+    return ret;
+  }
+
+  /* Carve any data associated with VK records */
+  ret = extractDataCells(f, unalloc_cells, unalloc_linked_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret);
+    return ret;
+  }
+  ret = extractDataCells(f, unalloc_cells, unalloc_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret);
+    return ret;
+  }
+  
+  /* Carve any SK records */
+  ret = extractSKs(f, unalloc_cells, unalloc_sks);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractSKs() failed with %d.\n", ret);
+    return ret;
+  }
+
+  /* Now that we're done carving, associate recovered keys with parents, 
+   * if at all possible.
+   */
+  num_unalloc_keys = range_list_size(unalloc_keys);
+  parent_paths = (char**)malloc(sizeof(char*)*num_unalloc_keys);
+  if(parent_paths == NULL)
+    return 10;
+
+  for(i=0; i < num_unalloc_keys; i++)
+  {
+    cur_elem = range_list_get(unalloc_keys, i);
+    tmp_key = (REGFI_NK_REC*)cur_elem->data;
+
+    if(tmp_key == NULL)
+      return 20;
+    
+    parent_paths[i] = getParentPath(f, tmp_key);
+    if(parent_paths[i] == NULL)
+      return 20;
+  }
+  
+  /* Now start the output */
+  for(i=0; i < num_unalloc_keys; i++)
+  {
+    cur_elem = range_list_get(unalloc_keys, i);
+    tmp_key = (REGFI_NK_REC*)cur_elem->data;
+
+    printKey(f, tmp_key, parent_paths[i]);
+    if(tmp_key->num_values > 0 && tmp_key->values != NULL)
+    {
+      /* XXX: Add command line option to choose output encoding */
+      regfi_interpret_keyname(f, tmp_key, REGFI_ENCODING_ASCII, true);
+
+      tmp_name = get_quoted_keyname(tmp_key);
+      tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2);
+      if(tmp_path == NULL)
+      {
+	free(tmp_name);
+	return 10;
+      }
+
+      sprintf(tmp_path, "%s/%s", parent_paths[i], tmp_name);
+      for(j=0; j < tmp_key->values->num_values; j++)
+      {
+	tmp_value = 
+	  (REGFI_VK_REC*)range_list_find_data(unalloc_linked_values, 
+					      tmp_key->values->elements[j]
+					      + REGFI_REGF_SIZE);
+	if(tmp_value != NULL)
+	  printValue(f, tmp_value, tmp_path);
+      }
+      free(tmp_path);
+      free(tmp_name);
+      free(parent_paths[i]);
+    }
+  }
+  free(parent_paths);
+
+  /* Print out orphaned values */
+  for(i=0; i < range_list_size(unalloc_values); i++)
+  {
+    cur_elem = range_list_get(unalloc_values, i);
+    tmp_value = (REGFI_VK_REC*)cur_elem->data; 
+
+    printValue(f, tmp_value, "");
+  }
+  
+  if(print_leftover)
+  {
+    for(i=0; i < range_list_size(unalloc_cells); i++)
+    {
+      cur_elem = range_list_get(unalloc_cells, i);
+      printCell(f, cur_elem->offset);
+    }
+  }
+
+  range_list_free(unalloc_cells);
+  range_list_free(unalloc_keys);
+  range_list_free(unalloc_linked_values);
+  range_list_free(unalloc_values);
+  range_list_free(unalloc_sks);
+
+  return 0;
+}
Index: /releases/0.12.0/src/reglookup.c
===================================================================
--- /releases/0.12.0/src/reglookup.c	(revision 175)
+++ /releases/0.12.0/src/reglookup.c	(revision 175)
@@ -0,0 +1,680 @@
+/*
+ * A utility to read a Windows NT and later registry files.
+ *
+ * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2010 Tobias Mueller (portions of '-i' code)
+ * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include "regfi.h"
+#include "void_stack.h"
+
+/* Globals, influenced by command line parameters */
+bool print_value_mtime = false;
+bool print_verbose = false;
+bool print_security = false;
+bool print_header = true;
+bool path_filter_enabled = false;
+bool type_filter_enabled = false;
+char* path_filter = NULL;
+int type_filter;
+char* registry_file = NULL;
+
+/* Other globals */
+REGFI_FILE* f;
+
+
+/* XXX: A hack to share some functions with reglookup-recover.c.
+ *      Should move these into a proper library at some point.
+ */
+#include "common.c"
+
+
+void printValue(REGFI_ITERATOR* iter, const REGFI_VK_REC* vk, char* prefix)
+{
+  REGFI_DATA* data;
+  char* quoted_value = NULL;
+  char* quoted_name = NULL;
+  char* conv_error = NULL;
+  const char* str_type = NULL;
+  char mtime[20];
+  time_t tmp_time[1];
+  struct tm* tmp_time_s = NULL;
+
+  quoted_name = get_quoted_valuename(vk);
+  if (quoted_name == NULL)
+  { /* Value names are NULL when we're looking at the "(default)" value.
+     * Currently we just return a 0-length string to try an eliminate 
+     * ambiguity with a literal "(default)" value.  The data type of a line
+     * in the output allows one to differentiate between the parent key and
+     * this value.
+     */
+    quoted_name = malloc(1*sizeof(char));
+    if(quoted_name == NULL)
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+    quoted_name[0] = '\0';
+  }
+  
+  data = regfi_iterator_fetch_data(iter, vk);
+
+  printMsgs(iter->f);
+  if(data != NULL)
+  {
+    quoted_value = data_to_ascii(data, &conv_error);
+    if(quoted_value == NULL)
+    {
+      if(conv_error == NULL)
+	fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
+		"Memory allocation failure likely.\n", prefix, quoted_name);
+      else
+	fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
+		"Returned error: %s\n", prefix, quoted_name, conv_error);
+    }
+    else if(conv_error != NULL)
+      fprintf(stderr, "WARN: While quoting value for '%s/%s', "
+	      "warning returned: %s\n", prefix, quoted_name, conv_error);
+    regfi_free_data(data);
+  }
+
+  if(print_value_mtime)
+  {
+    *tmp_time = regfi_nt2unix_time(&iter->cur_key->mtime);
+    tmp_time_s = gmtime(tmp_time);
+    strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+  }
+  else
+    mtime[0] = '\0';
+
+  str_type = regfi_type_val2str(vk->type);
+  if(print_security)
+  {
+    if(str_type == NULL)
+      printf("%s/%s,0x%.8X,%s,%s,,,,\n", prefix, quoted_name,
+	     vk->type, quoted_value, mtime);
+    else
+      printf("%s/%s,%s,%s,%s,,,,\n", prefix, quoted_name,
+	     str_type, quoted_value, mtime);
+  }
+  else
+  {
+    if(str_type == NULL)
+      printf("%s/%s,0x%.8X,%s,%s\n", prefix, quoted_name,
+	     vk->type, quoted_value, mtime);
+    else
+      printf("%s/%s,%s,%s,%s\n", prefix, quoted_name,
+	     str_type, quoted_value, mtime);
+  }
+
+  if(quoted_value != NULL)
+    free(quoted_value);
+  if(quoted_name != NULL)
+    free(quoted_name);
+  if(conv_error != NULL)
+    free(conv_error);
+}
+
+
+char** splitPath(const char* s)
+{
+  char** ret_val;
+  const char* cur = s;
+  char* next = NULL;
+  char* copy;
+  uint32_t ret_cur = 0;
+
+  ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**));
+  if (ret_val == NULL)
+    return NULL;
+  ret_val[0] = NULL;
+
+  /* We return a well-formed, 0-length, path even when input is icky. */
+  if (s == NULL)
+    return ret_val;
+  
+  while((next = strchr(cur, '/')) != NULL)
+  {
+    if ((next-cur) > 0)
+    {
+      copy = (char*)malloc((next-cur+1)*sizeof(char));
+      if(copy == NULL)
+	bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
+	  
+      memcpy(copy, cur, next-cur);
+      copy[next-cur] = '\0';
+      ret_val[ret_cur++] = copy;
+      if(ret_cur < (REGFI_MAX_DEPTH+1+1))
+	ret_val[ret_cur] = NULL;
+      else
+	bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
+    }
+    cur = next+1;
+  }
+
+  /* Grab last element, if path doesn't end in '/'. */
+  if(strlen(cur) > 0)
+  {
+    copy = strdup(cur);
+    ret_val[ret_cur++] = copy;
+    if(ret_cur < (REGFI_MAX_DEPTH+1+1))
+      ret_val[ret_cur] = NULL;
+    else
+      bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
+  }
+
+  return ret_val;
+}
+
+
+void freePath(char** path)
+{
+  uint32_t i;
+
+  if(path == NULL)
+    return;
+
+  for(i=0; path[i] != NULL; i++)
+    free(path[i]);
+
+  free(path);
+}
+
+
+/* Returns a quoted path from an iterator's stack */
+char* iter2Path(REGFI_ITERATOR* i)
+{
+  const REGFI_ITER_POSITION* cur;
+  const REGFI_NK_REC* tmp_key;
+  uint32_t buf_left = 127;
+  uint32_t buf_len = buf_left+1;
+  uint32_t name_len = 0;
+  uint32_t grow_amt;
+  char* buf;
+  char* new_buf;
+  char* name;
+  void_stack_iterator* iter;
+  
+  buf = (char*)malloc((buf_len)*sizeof(char));
+  if (buf == NULL)
+    return NULL;
+  buf[0] = '\0';
+
+  iter = void_stack_iterator_new(i->key_positions);
+  if (iter == NULL)
+  {
+    free(buf);
+    return NULL;
+  }
+
+  /* skip root element */
+  if(void_stack_size(i->key_positions) < 1)
+  {
+    buf[0] = '/';
+    buf[1] = '\0';
+    return buf;
+  }
+  cur = void_stack_iterator_next(iter);
+
+  do
+  {
+    cur = void_stack_iterator_next(iter);
+    if (cur == NULL)
+      tmp_key = i->cur_key;
+    else
+      tmp_key = cur->nk;
+
+    name = get_quoted_keyname(tmp_key);
+
+    buf[buf_len-buf_left-1] = '/';
+    buf_left -= 1;
+    name_len = strlen(name);
+    if(name_len+1 > buf_left)
+    {
+      grow_amt = (uint32_t)(buf_len/2);
+      buf_len += name_len+1+grow_amt-buf_left;
+      if((new_buf = realloc(buf, buf_len)) == NULL)
+      {
+	free(name);
+	free(buf);
+	free(iter);
+	return NULL;
+      }
+      buf = new_buf;
+      buf_left = grow_amt + name_len + 1;
+    }
+    strncpy(buf+(buf_len-buf_left-1), name, name_len);
+    buf_left -= name_len;
+    buf[buf_len-buf_left-1] = '\0';
+    free(name);
+  } while(cur != NULL);
+
+  return buf;
+}
+
+
+void printValueList(REGFI_ITERATOR* iter, char* prefix)
+{
+  REGFI_VK_REC* value;
+
+  value = regfi_iterator_first_value(iter);
+  while(value != NULL)
+  {
+    if(!type_filter_enabled || (value->type == type_filter))
+      printValue(iter, value, prefix);
+    regfi_free_value(value);
+    value = regfi_iterator_next_value(iter);
+    printMsgs(iter->f);
+  }
+}
+
+
+void printKey(REGFI_ITERATOR* iter, char* full_path)
+{
+  static char empty_str[1] = "";
+  char* owner = NULL;
+  char* group = NULL;
+  char* sacl = NULL;
+  char* dacl = NULL;
+  char* quoted_classname;
+  char mtime[20];
+  time_t tmp_time[1];
+  struct tm* tmp_time_s = NULL;
+  const REGFI_SK_REC* sk;
+  const REGFI_NK_REC* k = regfi_iterator_cur_key(iter);
+  REGFI_CLASSNAME* classname;
+
+  *tmp_time = regfi_nt2unix_time(&k->mtime);
+  tmp_time_s = gmtime(tmp_time);
+  strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+
+  if(print_security && (sk=regfi_iterator_cur_sk(iter)))
+  {
+    owner = regfi_get_owner(sk->sec_desc);
+    group = regfi_get_group(sk->sec_desc);
+    sacl = regfi_get_sacl(sk->sec_desc);
+    dacl = regfi_get_dacl(sk->sec_desc);
+    if(owner == NULL)
+      owner = empty_str;
+    if(group == NULL)
+      group = empty_str;
+    if(sacl == NULL)
+      sacl = empty_str;
+    if(dacl == NULL)
+      dacl = empty_str;
+
+    classname = regfi_iterator_fetch_classname(iter, k);
+    printMsgs(iter->f);
+    if(classname != NULL)
+    {
+      if(classname->interpreted == NULL)
+      {
+	fprintf(stderr, "WARN: Could not convert class name"
+		" charset for key '%s'.  Quoting raw...\n", full_path);
+	quoted_classname = quote_buffer(classname->raw, classname->size,
+					key_special_chars);
+      }
+      else
+	quoted_classname = quote_string(classname->interpreted, 
+					key_special_chars);
+
+      if(quoted_classname == NULL)
+      {
+	fprintf(stderr, "ERROR: Could not quote classname"
+		" for key '%s' due to unknown error.\n", full_path);
+	quoted_classname = empty_str;
+      }
+    }
+    else
+      quoted_classname = empty_str;
+    regfi_free_classname(classname);
+
+    printMsgs(iter->f);
+    printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime, 
+	   owner, group, sacl, dacl, quoted_classname);
+
+    if(owner != empty_str)
+      free(owner);
+    if(group != empty_str)
+      free(group);
+    if(sacl != empty_str)
+      free(sacl);
+    if(dacl != empty_str)
+      free(dacl);
+    if(quoted_classname != empty_str)
+      free(quoted_classname);
+  }
+  else
+    printf("%s,KEY,,%s\n", full_path, mtime);
+}
+
+
+void printKeyTree(REGFI_ITERATOR* iter)
+{
+  const REGFI_NK_REC* root = NULL;
+  const REGFI_NK_REC* cur = NULL;
+  REGFI_NK_REC* sub = NULL;
+  char* path = NULL;
+  int key_type = regfi_type_str2val("KEY");
+  bool print_this = true;
+
+  root = cur = regfi_iterator_cur_key(iter);
+  sub = regfi_iterator_first_subkey(iter);
+  printMsgs(iter->f);
+
+  if(root == NULL)
+    bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: root cannot be NULL.\n");
+  
+  do
+  {
+    if(print_this)
+    {
+      path = iter2Path(iter);
+      if(path == NULL)
+	bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not construct iterator's path.\n");
+
+      if(!type_filter_enabled || (key_type == type_filter))
+	printKey(iter, path);
+      if(!type_filter_enabled || (key_type != type_filter))
+	printValueList(iter, path);
+      
+      free(path);
+    }
+    
+    if(sub == NULL)
+    {
+      if(cur != root)
+      {
+	/* We're done with this sub-tree, going up and hitting other branches. */
+	if(!regfi_iterator_up(iter))
+	{
+	  printMsgs(iter->f);
+	  bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator upward.\n");
+	}
+
+	cur = regfi_iterator_cur_key(iter);
+	if(cur == NULL)
+	{
+	  printMsgs(iter->f);
+	  bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: unexpected NULL for key.\n");
+	}
+	
+	sub = regfi_iterator_next_subkey(iter);
+      }
+      print_this = false;
+    }
+    else
+    { /* We have unexplored sub-keys.  
+       * Let's move down and print this first sub-tree out. 
+       */
+      if(!regfi_iterator_down(iter))
+      {
+	printMsgs(iter->f);
+	bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator downward.\n");
+      }
+
+      cur = regfi_iterator_cur_key(iter);
+      regfi_free_key(sub);
+      sub = regfi_iterator_first_subkey(iter);
+      print_this = true;
+    }
+    printMsgs(iter->f);
+  } while(!((cur == root) && (sub == NULL)));
+
+  if(print_verbose)
+    fprintf(stderr, "INFO: Finished printing key tree.\n");
+}
+
+
+/* XXX: What if there is BOTH a value AND a key with that name?? 
+ *      What if there are multiple keys/values with the same name?? 
+ */
+/*
+ * Returns 0 if path was not found.
+ * Returns 1 if path was found as value.
+ * Returns 2 if path was found as key.
+ * Returns less than 0 on other error.
+ */
+int retrievePath(REGFI_ITERATOR* iter, char** path)
+{
+  REGFI_VK_REC* value;
+  char* tmp_path_joined;
+  const char** tmp_path;
+  uint32_t i;
+  
+  if(path == NULL)
+    return -1;
+
+  /* One extra for any value at the end, and one more for NULL */
+  tmp_path = (const char**)malloc(sizeof(const char**)*(REGFI_MAX_DEPTH+1+1));
+  if(tmp_path == NULL)
+    return -2;
+
+  /* Strip any potential value name at end of path */
+  for(i=0; 
+      (path[i] != NULL) && (path[i+1] != NULL) && (i < REGFI_MAX_DEPTH+1);
+      i++)
+  { tmp_path[i] = path[i]; }
+  tmp_path[i] = NULL;
+
+  if(print_verbose)
+    fprintf(stderr, "INFO: Attempting to retrieve specified path: %s\n",
+	    path_filter);
+
+  /* Special check for '/' path filter */
+  if(path[0] == NULL)
+  {
+    if(print_verbose)
+      fprintf(stderr, "INFO: Found final path element as root key.\n");
+    free(tmp_path);
+    return 2;
+  }
+
+  if(!regfi_iterator_walk_path(iter, tmp_path))
+  {
+    printMsgs(iter->f);
+    free(tmp_path);
+    return 0;
+  }
+
+  if(regfi_iterator_find_value(iter, path[i]))
+  {
+    if(print_verbose)
+      fprintf(stderr, "INFO: Found final path element as value.\n");
+
+    value = regfi_iterator_cur_value(iter);
+    printMsgs(iter->f);
+    tmp_path_joined = iter2Path(iter);
+
+    if((value == NULL) || (tmp_path_joined == NULL))
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Unexpected error before printValue.\n");
+
+    if(!type_filter_enabled || (value->type == type_filter))
+      printValue(iter, value, tmp_path_joined);
+
+    regfi_free_value(value);
+    free(tmp_path);
+    free(tmp_path_joined);
+    return 1;
+  }
+  else if(regfi_iterator_find_subkey(iter, path[i]))
+  {
+    printMsgs(iter->f);
+    if(print_verbose)
+      fprintf(stderr, "INFO: Found final path element as key.\n");
+
+    if(!regfi_iterator_down(iter))
+    {
+      printMsgs(iter->f);
+      bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
+    }
+
+    return 2;
+  }
+  printMsgs(iter->f);
+
+  if(print_verbose)
+    fprintf(stderr, "INFO: Could not find last element of path.\n");
+
+  return 0;
+}
+
+
+static void usage(void)
+{
+  fprintf(stderr, "Usage: reglookup [-v] [-s]"
+	  " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
+	  " <REGISTRY_FILE>\n");
+  fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION);
+  fprintf(stderr, "Options:\n");
+  fprintf(stderr, "\t-v\t sets verbose mode.\n");
+  fprintf(stderr, "\t-h\t enables header row. (default)\n");
+  fprintf(stderr, "\t-H\t disables header row.\n");
+  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
+  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
+  fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
+  fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
+  fprintf(stderr, "\t-i\t includes parent key modification times with child values.\n");
+  fprintf(stderr, "\n");
+}
+
+
+int main(int argc, char** argv)
+{
+  char** path = NULL;
+  REGFI_ITERATOR* iter;
+  int retr_path_ret;
+  uint32_t argi, arge;
+
+  /* Process command line arguments */
+  if(argc < 2)
+  {
+    usage();
+    bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n");
+  }
+  
+  arge = argc-1;
+  for(argi = 1; argi < arge; argi++)
+  {
+    if (strcmp("-p", argv[argi]) == 0)
+    {
+      if(++argi >= arge)
+      {
+	usage();
+	bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-p' option requires parameter.\n");
+      }
+      if((path_filter = strdup(argv[argi])) == NULL)
+	bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
+
+      path_filter_enabled = true;
+    }
+    else if (strcmp("-t", argv[argi]) == 0)
+    {
+      if(++argi >= arge)
+      {
+	usage();
+	bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-t' option requires parameter.\n");
+      }
+      if((type_filter = regfi_type_str2val(argv[argi])) < 0)
+      {
+	fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
+	bailOut(REGLOOKUP_EXIT_USAGE, "");
+      }
+      type_filter_enabled = true;
+    }
+    else if (strcmp("-h", argv[argi]) == 0)
+      print_header = true;
+    else if (strcmp("-H", argv[argi]) == 0)
+      print_header = false;
+    else if (strcmp("-s", argv[argi]) == 0)
+      print_security = true;
+    else if (strcmp("-S", argv[argi]) == 0)
+      print_security = false;
+    else if (strcmp("-v", argv[argi]) == 0)
+      print_verbose = true;
+    else if (strcmp("-i", argv[argi]) == 0)
+      print_value_mtime = true;
+    else
+    {
+      usage();
+      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
+      bailOut(REGLOOKUP_EXIT_USAGE, "");
+    }
+  }
+  if((registry_file = strdup(argv[argi])) == NULL)
+    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
+
+  f = regfi_open(registry_file);
+  if(f == NULL)
+  {
+    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
+    bailOut(REGLOOKUP_EXIT_NOINPUT, "");
+  }
+
+  if(print_verbose)
+    regfi_set_message_mask(f, REGFI_MSG_INFO|REGFI_MSG_WARN|REGFI_MSG_ERROR);
+
+  /* XXX: add command line option to choose output encoding */
+  iter = regfi_iterator_new(f, REGFI_ENCODING_ASCII);
+  if(iter == NULL)
+  {
+    printMsgs(f);
+    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Couldn't create registry iterator.\n");
+  }
+
+  if(print_header)
+  {
+    if(print_security)
+      printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL,CLASS\n");
+    else
+      printf("PATH,TYPE,VALUE,MTIME\n");
+  }
+
+  if(path_filter_enabled && path_filter != NULL)
+    path = splitPath(path_filter);
+
+  if(path != NULL)
+  {
+    retr_path_ret = retrievePath(iter, path);
+    printMsgs(iter->f);
+    freePath(path);
+
+    if(retr_path_ret == 0)
+      fprintf(stderr, "WARN: Specified path '%s' not found.\n", path_filter);
+    else if (retr_path_ret == 2)
+      printKeyTree(iter);
+    else if(retr_path_ret < 0)
+    {
+      fprintf(stderr, "ERROR: retrievePath() returned %d.\n", 
+	      retr_path_ret);
+      bailOut(REGLOOKUP_EXIT_DATAERR,
+	      "ERROR: Unknown error occurred in retrieving path.\n");
+    }
+  }
+  else
+    printKeyTree(iter);
+
+  regfi_iterator_free(iter);
+  regfi_close(f);
+
+  return 0;
+}
