Index: releases/0.9.0/INSTALL
===================================================================
--- releases/0.9.0/INSTALL	(revision 123)
+++ releases/0.9.0/INSTALL	(revision 123)
@@ -0,0 +1,45 @@
+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
+
+
+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.
Index: releases/0.9.0/LICENSE
===================================================================
--- releases/0.9.0/LICENSE	(revision 123)
+++ releases/0.9.0/LICENSE	(revision 123)
@@ -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.9.0/Makefile
===================================================================
--- releases/0.9.0/Makefile	(revision 123)
+++ releases/0.9.0/Makefile	(revision 123)
@@ -0,0 +1,55 @@
+# $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=gnu89 -pedantic -Wall -ggdb
+INC=-I/usr/local/include
+LIB=-L/usr/local/lib -lm
+
+UNAME := $(shell uname)
+ifneq ($(UNAME),Linux) 	
+  LIB:=$(LIB) -liconv
+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.9.0/bin/Makefile
===================================================================
--- releases/0.9.0/bin/Makefile	(revision 123)
+++ releases/0.9.0/bin/Makefile	(revision 123)
@@ -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.9.0/bin/reglookup-timeline
===================================================================
--- releases/0.9.0/bin/reglookup-timeline	(revision 123)
+++ releases/0.9.0/bin/reglookup-timeline	(revision 123)
@@ -0,0 +1,49 @@
+#!/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 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] <REGISTRY_FILE> [<REGISTRY_FILE> ...]" 1>&2
+  echo "   -H  Omit header line" 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
+
+if [ "$PRINT_HEADER" = "true" ]; then
+  echo "MTIME,FILE,PATH"
+fi
+
+for F in $@; do
+  reglookup -t KEY -H "$F" | awk -F',' '{ printf "%s,'"$F"',%s\n",$4,$1; }'
+done | sort
Index: releases/0.9.0/doc/Makefile
===================================================================
--- releases/0.9.0/doc/Makefile	(revision 123)
+++ releases/0.9.0/doc/Makefile	(revision 123)
@@ -0,0 +1,33 @@
+# $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:
+	docbook2x-man --to-stdout reglookup.1.docbook > man/man1/reglookup.1
+	docbook2x-man --to-stdout reglookup-timeline.1.docbook > man/man1/reglookup-timeline.1
+	docbook2x-man --to-stdout reglookup-recover.1.docbook > 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.9.0/doc/devel/TODO
===================================================================
--- releases/0.9.0/doc/devel/TODO	(revision 123)
+++ releases/0.9.0/doc/devel/TODO	(revision 123)
@@ -0,0 +1,64 @@
+$Id$
+
+If you are interested in contributing to this project, here's a few
+things you could look into:
+
+ - Currently there is no way on the command line to search for exotic 
+   paths/types.  For instance, if reglookup encounters an unknown VK
+   type, it just prints it out in Hex.  However, if you wanted to search
+   specifically for that type, there is no way to do it.  Similarly, it
+   isn't possible to specify certain binary or weird characters in
+   paths.  Reglookup should take the user path and unquote each path
+   component using the \xQQ syntax prior to searching.
+
+ - Testing, testing, and more testing.  reglookup needs to be tested on 
+   NT/XP/2k3/Vista.  A regression test suite would be nice too.  Some 
+   thoughts on this include a script which randomly fuzzes an existing
+   registry file, and tries to detect crashes of reglookup when parsing
+   it.  Another test script might randomly truncate an existing registry
+   file, which will help improve reglookup's parsing on fragmentary
+   files.
+
+ - Build system.  I do not wish to use automake/autoconf in this
+   project.  I have also now decided that make is painful to use for
+   everything.  I'd like to switch to a suite of shell scripts driven by
+   minimal make rules.  If you got any ideas on this, shoot them my way.
+
+ - Unicode support still needs improvement.  While parsing strings seems
+   to be decent, UTF-8 output would be nice.
+
+ - The interface between reglookup.c and regfi.c is much better than it
+   used to be, but the iter2Stack function needs to be moved into the 
+   library, which means the \xQQ quoting syntax will have to go with it.
+   This syntax will need to be more carefully documented if it's going 
+   to be a part of the library.
+
+ - NK/VK/SK record caching.  Right now, HBINs and perhaps SK records are 
+   cached, but it's pretty haphazard, and NK/VK records are repeatedly
+   re-parsed.  A generic caching library should be introduced which can
+   cache many of these records with a specific memory limit in mind.  
+   I think this will speed things up greatly.
+
+ - It might be nice to have a way to filter results by security 
+   descriptor information.  Maybe by MTIME as well.
+
+ - Documentation.  The security descriptor output format needs to be 
+   documented.  Also, function contracts should be added to the 
+   lower-level functions of regfi.c.
+
+ - The stuff in smb_deps.h and smb_deps.c needs to be cleaned up.  The
+   eventual goal is to have it all either integrated into regfi, or to
+   be eliminated, or broken out into small supporting libraries, as
+   necessary.  It is currently just a jumble of old Samba code that I
+   haven't decided where to put yet.
+
+ - At least one user reported that they use reglookup on a Windows host 
+   through Cygwin, but after version 0.3.0 came out, the dependency on
+   libiconv caused that to break.  libiconv seems to be a portability
+   issue on other platforms as well.  However, it's interface is a POSIX
+   standard, and I think I'd like to keep it around.  Perhaps it would 
+   be nice if reglookup could be cross-compiled using MinGW.  Then a 
+   binary could be distributed for that platform.  This app was never 
+   meant for Windows though, so this isn't a high priority.
+
+ - Grep through the source for 'XXX', and you'll find more.
Index: releases/0.9.0/doc/devel/references.txt
===================================================================
--- releases/0.9.0/doc/devel/references.txt	(revision 123)
+++ releases/0.9.0/doc/devel/references.txt	(revision 123)
@@ -0,0 +1,30 @@
+- The Windows NT Registry File Format
+  (A work in progress to support this tool.)
+  http://sentinelchicken.com/research/registry_format/
+
+- Recovering Deleted Data From the Windows Registry
+  (The research that is implemented as a PoC in reglookup-recover.)
+  http://sentinelchicken.com/research/registry_recovery/
+
+- Petter Nordahl-Hagen.  Windows NT registry file format description.
+  (The file 'winntreg.txt' included in this distribution is derived from this.)
+  http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt
+
+- Some useful information on how Windows reads from and writes to registry
+  hives:
+  http://www.microsoft.com/technet/archive/winntas/tips/winntmag/inreg.mspx
+
+- Registry key, value, and depth limits:
+  http://msdn2.microsoft.com/en-us/library/ms724872.aspx
+
+- Misc references for windows registry permissions and ownership:
+  http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
+  http://technet2.microsoft.com/WindowsServer/en/library/86cf2457-4f17-43f8-a2ab-7f4e2e5659091033.mspx?mfr=true
+  http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
+
+- ACL/ACE flags information
+  http://support.microsoft.com/kb/220167
+  http://msdn2.microsoft.com/en-us/library/aa772242.aspx
+
+- Info on SAM hive, syskey, and hash extraction (with tools bkhive and samdump2):
+  http://www.studenti.unina.it/~ncuomo/syskey/
Index: releases/0.9.0/doc/devel/winntreg.txt
===================================================================
--- releases/0.9.0/doc/devel/winntreg.txt	(revision 123)
+++ releases/0.9.0/doc/devel/winntreg.txt	(revision 123)
@@ -0,0 +1,272 @@
+The windows NT registry has 2 different blocks, where one can occur many
+times...
+
+the "regf"-Block
+================
+ 
+"regf" is obviosly the abbreviation for "Registry file". "regf" is the
+signature of the header-block which is always 4kb in size, although only
+the first 64 bytes seem to be used and a checksum is calculated over
+the first 0x200 bytes only!
+
+Offset            Size      Contents
+0x00000000      D-Word      ID: ASCII-"regf" = 0x66676572
+0x00000004      D-Word      ???? //see struct REGF
+0x00000008      D-Word      ???? Always the same value as at 0x00000004
+0x0000000C      Q-Word      last modify date in WinNT date-format
+0x00000014      D-Word      1
+0x00000018      D-Word      3
+0x0000001C      D-Word      0
+0x00000020      D-Word      1
+0x00000024      D-Word      Offset of 1st key record
+0x00000028      D-Word      Size of the data-blocks (Filesize-4kb)
+0x0000002C      D-Word      1
+0x000001FC      D-Word      Sum of all D-Words from 0x00000000 to
+                            0x000001FB  //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I dont know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset      Size      Contents
+0x0000      D-Word      ID: ASCII-"hbin" = 0x6E696268
+0x0004      D-Word      Offset from the 1st hbin-Block
+0x0008      D-Word      Offset to the next hbin-Block
+0x001C      D-Word      Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I dont know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset      Size      Contents
+0x0000      D-Word      Data-block size    //this size must be a
+multiple of 8. Nigel
+0x0004      ????      Data
+ 
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+That does not seem to be true. All block lengths seem to be negative! 
+(Richard Sharpe) 
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+(That also seems incorrect, in that the block size if a multiple of 8.
+That is, the block, including the 4 byte header, is always a multiple of
+8 bytes. Richard Sharpe.)
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+      The nk-record can be treated as a kombination of tree-record and
+      key-record of the win 95 registry.
+
+lf-Record
+
+      The lf-record is the counterpart to the RGKN-record (the
+      hash-function)
+
+vk-Record
+
+      The vk-record consists information to a single value.
+
+sk-Record
+
+      sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+      The value-lists contain information about which values are inside a
+      sub-key and dont have a header.
+
+Datas
+
+      The datas of the registry are (like the value-list) stored without a
+      header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"nk" = 0x6B6E
+0x0002      Word      for the root-key: 0x2C, otherwise 0x20  //key symbolic links 0x10. Nigel
+0x0004      Q-Word      write-date/time in windows nt notation
+0x000C      D-Word      UNKNOWN // added by TDM
+0x0010      D-Word      Offset of Owner/Parent key
+0x0014      D-Word      number of sub-Keys
+0x0018      D-Word      UNKNOWN // added by TDM
+0x001C      D-Word      Offset of the sub-key lf-Records
+0x0020      D-Word      UNKNOWN // added by TDM
+0x0024      D-Word      number of values
+0x0028      D-Word      Offset of the Value-List
+0x002C      D-Word      Offset of the sk-Record
+
+0x0030      D-Word      Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044      D-Word      Unused (data-trash)  //some kind of run time index. Does not appear to be important. Nigel
+0x0048      Word      name-length
+0x004A      Word      class-name length
+0x004C      ????      key-name
+
+the Value-List
+==============
+Offset      Size      Contents
+0x0000      D-Word      Offset 1st Value
+0x0004      D-Word      Offset 2nd Value
+0x????      D-Word      Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"vk" = 0x6B76
+0x0002      Word      name length
+0x0004      D-Word      length of the data   //if top bit is set when offset contains data. Nigel
+0x0008      D-Word      Offset of Data
+0x000C      D-Word      Type of value
+0x0010      Word      Flag
+0x0012      Word      Unused (data-trash)
+0x0014      ????      Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert      Beteutung
+0x0001      RegSZ:             character string (in UNICODE!)
+0x0002      ExpandSZ:   string with "%var%" expanding (UNICODE!)
+0x0003      RegBin:           raw-binary value
+0x0004      RegDWord:   Dword
+0x0007      RegMultiSZ:      multiple strings, seperated with 0
+                  (UNICODE!)
+
+The "lf"/"lh"/"ri"-record (hash list header)
+===============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"lf" = 0x666C  (or "lh" or "ri")
+0x0002      Word      number of keys
+0x0004      ????      Hash-Records
+
+Hash-Record
+===========
+Offset      Size      Contents
+0x0000      D-Word      Offset of corresponding "nk"-Record
+0x0004      D-Word      ASCII: the first 4 characters of the key-name, padded with 0-s. Case sensitive! 
+			(the hash value may be computed differently for the various header types 
+			 "lf"/"lh"/"ri"/etc)
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the 
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+(This is just a self-relative security descriptor in the data. R Sharpe.) 
+
+
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"sk" = 0x6B73
+0x0002      Word      Unused
+0x0004      D-Word      Offset of previous "sk"-Record
+0x0008      D-Word      Offset of next "sk"-Record
+0x000C      D-Word      usage-counter
+0x0010      D-Word      Size of "sk"-record in bytes
+????                                             //standard self
+relative security desciptor. Nigel
+????  ????      Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I dont know how accurate it really is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+                      +----->+------------+
+| first sub |   |                      |      | Usage cnt. |
+| name      |   |  +-->+------------+  |      | length     |
+| value     |   |  |   | next rec.  |  |      | text       |------->+-------+
++-----------+   |  |   | name rec.  |--+      +------------+        | xxxxx |
+   +------------+  |   | value rec. |-------->+------------+        +-------+
+   v               |   +------------+         | Usage cnt. |
++-----------+      |                          | length     |
+| next rec. |      |                          | text       |------->+-------+
+| first sub |------+                          +------------+        | xxxxx |
+| name      |                                                       +-------+
+| value     |
++-----------+    
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+   
++---------------------------------------------------------------+
+|                                                               |
+v                                                               |
++---------+     +---------->+-----------+  +----->+---------+   |
+| "nk"    |     |           | lf-rec.   |  |      | nk-rec. |   |
+| ID      |     |           | # of keys |  |      | parent  |---+
+| Date    |     |           | 1st key   |--+      | ....    |
+| parent  |     |           +-----------+         +---------+
+| suk-keys|-----+
+| values  |--------------------->+----------+
+| SK-rec. |---------------+      | 1. value |--> +----------+
+| class   |--+            |      +----------+    | vk-rec.  |
++---------+  |            |                      | ....     |
+             v            |                      | data     |--> +-------+
+      +------------+      |                      +----------+    | xxxxx |
+      | Class name |      |                                      +-------+
+      +------------+      |
+                          v
+          +---------+    +---------+
+   +----->| next sk |--->| Next sk |--+
+   |  +---| prev sk |<---| prev sk |  |
+   |  |   | ....    |    | ...     |  |
+   |  |   +---------+    +---------+  |
+   |  |                    ^          |
+   |  |                    |          |
+   |  +--------------------+          |
+   +----------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps....  (Although it was *fun* for me to uncover this things,
+                  it took me several sleepless nights ;)
+
+            B.D.
Index: releases/0.9.0/doc/reglookup-recover.1.docbook
===================================================================
--- releases/0.9.0/doc/reglookup-recover.1.docbook	(revision 123)
+++ releases/0.9.0/doc/reglookup-recover.1.docbook	(revision 123)
@@ -0,0 +1,235 @@
+<?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.
+        print them out to stdout 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. (Currently does little to nothing.)
+	  </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.9.0/doc/reglookup-timeline.1.docbook
===================================================================
--- releases/0.9.0/doc/reglookup-timeline.1.docbook	(revision 123)
+++ releases/0.9.0/doc/reglookup-timeline.1.docbook	(revision 123)
@@ -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.9.0/doc/reglookup.1.docbook
===================================================================
--- releases/0.9.0/doc/reglookup.1.docbook	(revision 123)
+++ releases/0.9.0/doc/reglookup.1.docbook	(revision 123)
@@ -0,0 +1,358 @@
+<?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/2K/XP/2K3/Vista registries, 
+        though your mileage may vary.
+    </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>-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 four additional columns to output containing 
+	    information from key security descriptors.  The columns 
+	    are: owner, group, sacl, dacl.
+	    (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. (Currently does little to nothing.)
+	  </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>
+      Verbose output is not working.
+    </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.9.0/include/byteorder.h
===================================================================
--- releases/0.9.0/include/byteorder.h	(revision 123)
+++ releases/0.9.0/include/byteorder.h	(revision 123)
@@ -0,0 +1,177 @@
+/* 
+ * 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
+
+/*
+   This file implements macros for machine independent short and 
+   int manipulation
+
+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 that is in the local machines byte order, and you
+want to do it with only the assumption that uint16 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 value at offset 25 in a buffer you do this:
+
+char *buffer = foo_bar();
+uint16 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 :-)
+
+*/
+
+#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)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(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 and int32
+   being correct 
+*/
+
+/* get single value from an SMB buffer */
+#define SVAL(buf,pos) (*(const uint16 *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32 *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVALS(buf,pos) (*(const int16 *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32 *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32 *)((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)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32)(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.9.0/include/lru_cache.h
===================================================================
--- releases/0.9.0/include/lru_cache.h	(revision 123)
+++ releases/0.9.0/include/lru_cache.h	(revision 123)
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 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$
+ */
+
+#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>
+
+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;
+};
+
+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 free_data;
+} lru_cache;
+
+
+lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret, bool free_data);
+void lru_cache_destroy(lru_cache* ht);
+
+/* Returns a pointer to the old, replaced data stored at index.  
+ * Returns NULL if no entry was overwritten.
+ */
+bool lru_cache_update(lru_cache* ht, const void* index, 
+		      uint32_t index_len, void* data);
+
+/* Returns 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);
+
+/* Removes entry from table at index.
+ * Returns pointer to data that was there previously.  
+ * Returns 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.9.0/include/range_list.h
===================================================================
--- releases/0.9.0/include/range_list.h	(revision 123)
+++ releases/0.9.0/include/range_list.h	(revision 123)
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2008 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 <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifndef _RANGE_LIST_H
+#define _RANGE_LIST_H
+
+
+typedef struct _range_list_element
+{
+  uint32_t offset;
+  uint32_t length;
+  void* data;
+} range_list_element;
+
+
+typedef struct _range_list
+{
+  range_list_element** elements;
+  uint32_t elem_alloced;
+  uint32_t size;
+} range_list;
+
+
+/* range_list_new(): 
+ *  Allocates a new range_list.
+ *
+ * Returns: 
+ *  A newly allocated range_list, or NULL if an error occurred.
+ */
+range_list* range_list_new();
+
+
+/* range_list_free(): 
+ *  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.
+ *
+ * Arguments:
+ *  rl -- the range_list to be free()d.
+ */
+void range_list_free(range_list* rl);
+
+
+/* range_list_size(): 
+ *  Query the current number of elements on a range_list
+ *
+ * Arguments:
+ *  rl -- the range_list to query
+ *
+ * Returns:
+ *  The number of elements currently in the list.
+ */
+uint32_t range_list_size(const range_list* rl);
+
+
+/* range_list_add():
+ *  Adds an element to the range_list.  
+ *  The new element must not overlap with others.
+ *  NOTE: this is a slow operation.
+ *
+ * Arguments:
+ *  rl     -- the range list to update
+ *  offset -- the starting point for the range
+ *  length -- the length of the range
+ *  data   -- misc data associated with this range element
+ * Returns:
+ *  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);
+
+
+/* range_list_remove():
+ *  Removes an element from the list.  The element data structure will be 
+ *  freed, but the data property will not be.
+ *
+ * Arguments:
+ *  rl     -- the range_list to modify
+ *  index  -- the element index to remove
+ *
+ * Returns:
+ *  true if the element was successfully removed, false otherwise.
+ */
+bool range_list_remove(range_list* rl, uint32_t index);
+
+
+/* range_list_get():
+ *  Retrieves the element for a given index.
+ *
+ * Arguments:
+ *  rl    -- the range_list being queried.
+ *  index -- the element index desired.
+ * 
+ * Returns:
+ *  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);
+
+
+/* range_list_find():
+ *  Attempts to find the unique element whose range encompasses offset.
+ *
+ * Arguments:
+ *  rl     -- the range_list being queried.
+ *  offset -- the location for which an element is desired.
+ *
+ * Returns:
+ *  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);
+
+
+/* range_list_find_data():
+ *  Same as range_list_find(), but returns the data associated with an element.
+ *
+ * Arguments:
+ *  rl     -- the range_list being queried.
+ *  offset -- the address to search for in the ranges
+ *
+ * Returns:
+ *  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 if the data
+ *        element was never set.
+ */
+void* range_list_find_data(const range_list* rl, uint32_t offset);
+
+
+/* range_list_split_element():
+ *  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.
+ *
+ * Arguments:
+ *  rl     -- the range_list to modify
+ *  index  -- the index of the element to be split
+ *  offset -- the at which the element will be split
+ *
+ * Returns:
+ *  true if the element was successfully split, false otherwise.
+ *   
+ *
+ */
+bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset);
+
+
+/* range_list_has_range():
+ *  Determines whether or not a specified range exists contiguously within the
+ *  range_list.
+ *
+ * Arguments:
+ *  rl     -- the range_list to search
+ *  start  -- the offset at the beginning of the range
+ *  length -- the length of the range
+ *
+ * Returns:
+ *  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.9.0/include/regfi.h
===================================================================
--- releases/0.9.0/include/regfi.h	(revision 123)
+++ releases/0.9.0/include/regfi.h	(revision 123)
@@ -0,0 +1,403 @@
+/*
+ * 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
+ *
+ * Unix SMB/CIFS implementation.
+ * Windows NT registry I/O library
+ *
+ * Copyright (C) 2005-2008 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 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$
+ */
+
+/************************************************************
+ * Most of this information was obtained from 
+ * http://www.wednesday.demon.co.uk/dosreg.html
+ * Thanks Nigel!
+ ***********************************************************/
+
+#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 <assert.h>
+
+#include "smb_deps.h"
+#include "void_stack.h"
+#include "range_list.h"
+#include "lru_cache.h"
+
+/******************************************************************************/
+/* Macros */
+ 
+/* 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 REGF_BLOCKSIZE		   0x1000
+#define REGF_ALLOC_BLOCK	   0x1000 /* Minimum allocation unit for HBINs */
+#define REGF_MAX_DEPTH		   512
+
+/* header sizes for various records */
+#define REGF_MAGIC_SIZE		   4
+#define HBIN_MAGIC_SIZE		   4
+#define HBIN_HEADER_REC_SIZE	   0x20
+#define REC_HDR_SIZE		   2
+
+#define REGF_OFFSET_NONE           0xffffffff
+#define REGFI_NK_MIN_LENGTH        0x4C
+#define REGFI_VK_MIN_LENGTH        0x14
+#define REGFI_SK_MIN_LENGTH        0x14
+#define REGFI_HASH_LIST_MIN_LENGTH 0x4
+
+/* Constants used for validation */
+ /* 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 VK_FLAG_NAME_PRESENT	   0x0001
+#define VK_DATA_IN_OFFSET	   0x80000000
+#define VK_MAX_DATA_LENGTH         1024*1024
+
+/* NK record types */
+#define NK_TYPE_LINKKEY		   0x0010
+#define NK_TYPE_NORMALKEY	   0x0020
+#define NK_TYPE_ROOTKEY		   0x002c
+ /* TODO: Unknown type that shows up in Vista registries */
+#define NK_TYPE_UNKNOWN1           0x1020
+
+
+/* HBIN block */
+typedef struct regf_hbin 
+{
+  uint32 file_off;       /* my offset in the registry file */
+  uint32 ref_count;      /* how many active records are pointing to this
+                          * block (not used currently) 
+			  */
+  
+  uint32 first_hbin_off; /* offset from first hbin block */
+  uint32 block_size;     /* block size of this block 
+                          * Should be a multiple of 4096 (0x1000)
+			  */
+  uint32 next_block;     /* relative offset to next block.  
+			  * NOTE: This value may be unreliable!
+			  */
+
+  uint8 magic[HBIN_MAGIC_SIZE]; /* "hbin" */
+} REGF_HBIN;
+
+
+/* Hash List -- list of key offsets and hashed names for consistency */
+typedef struct 
+{
+  uint32 nk_off;
+  uint32 hash;
+} REGF_HASH_LIST_ELEM;
+
+
+typedef struct 
+{
+  uint32 offset;	/* Real offset of this record's cell in the file */
+  uint32 cell_size;	 /* ((start_offset - end_offset) & 0xfffffff8) */
+  REGF_HBIN* hbin;       /* pointer to HBIN record (in memory) containing 
+			  * this nk record 
+			  */
+  uint32 hbin_off;	 /* offset from beginning of this hbin block */
+  REGF_HASH_LIST_ELEM* hashes;
+  
+  uint8 magic[REC_HDR_SIZE];
+  uint16 num_keys;
+} REGF_HASH_LIST;
+
+
+/* Key Value */
+typedef struct 
+{
+  uint32 offset;	/* Real offset of this record's cell in the file */
+  uint32 cell_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+
+  REGF_HBIN* hbin;	/* pointer to HBIN record (in memory) containing 
+			 * this nk record 
+			 */
+  uint8* data;
+  uint16 name_length;
+  char*  valuename;
+  uint32 hbin_off;	/* offset from beginning of this hbin block */
+  
+  uint32 data_size;
+  uint32 data_off;      /* offset of data cell (virtual) */
+  uint32 type;
+  uint8  magic[REC_HDR_SIZE];
+  uint16 flag;
+  uint16 unknown1;
+  bool data_in_offset;
+} REGF_VK_REC;
+
+
+/* Key Security */
+struct _regf_sk_rec;
+
+typedef struct _regf_sk_rec 
+{
+  uint32 offset;        /* Real file offset of this record */
+  uint32 cell_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+
+  SEC_DESC* sec_desc;
+  uint32 hbin_off;	/* offset from beginning of this hbin block */
+  
+  uint32 sk_off;	/* offset parsed from NK record used as a key
+			 * to lookup reference to this SK record 
+			 */
+  
+  uint32 prev_sk_off;
+  uint32 next_sk_off;
+  uint32 ref_count;
+  uint32 desc_size;     /* size of security descriptor */
+  uint16 unknown_tag;
+  uint8  magic[REC_HDR_SIZE];
+} REGF_SK_REC;
+
+
+/* Key Name */
+typedef struct
+{
+  uint32 offset;	/* Real offset of this record's cell in the file */
+  uint32 cell_size;	/* Actual or estimated length of the cell.  
+			 * Always in multiples of 8. 
+			 */
+
+  /* link in the other records here */
+  REGF_VK_REC** values;
+  REGF_HASH_LIST* subkeys;
+  
+  /* header information */
+  /* XXX: should we be looking for types other than the root key type? */
+  uint16 key_type;
+  uint8  magic[REC_HDR_SIZE];
+  NTTIME mtime;
+  uint16 name_length;
+  uint16 classname_length;
+  char* classname;
+  char* keyname;
+  uint32 parent_off;	/* back pointer in registry hive */
+  uint32 classname_off;	
+  
+  /* max lengths */
+  uint32 max_bytes_subkeyname;	    /* max subkey name * 2 */
+  uint32 max_bytes_subkeyclassname; /* max subkey classname length (as if) */
+  uint32 max_bytes_valuename;	    /* max valuename * 2 */
+  uint32 max_bytes_value;           /* max value data size */
+  
+  /* unknowns */
+  uint32 unknown1;
+  uint32 unknown2;
+  uint32 unknown3;
+  uint32 unk_index;		    /* nigel says run time index ? */
+  
+  /* children */
+  uint32 num_subkeys;
+  uint32 subkeys_off;	/* hash records that point to NK records */	
+  uint32 num_values;
+  uint32 values_off;	/* value lists which point to VK records */
+  uint32 sk_off;	/* offset to SK record */  
+} REGF_NK_REC;
+
+
+
+/* REGF block */
+typedef struct 
+{
+  /* run time information */
+  int fd;	  /* file descriptor */
+  /* For sanity checking (not part of the registry header) */
+  uint32 file_length;
+  void* mem_ctx;  /* memory context for run-time file access information */
+
+  /* Experimental hbin lists */
+  range_list* hbins;
+
+  /* file format information */  
+  uint8  magic[REGF_MAGIC_SIZE];/* "regf" */
+  NTTIME mtime;
+  uint32 data_offset;		/* offset to record in the first (or any?) 
+				 * hbin block 
+				 */
+  uint32 last_block;		/* offset to last hbin block in file */
+
+  uint32 checksum;		/* Stored checksum. */
+  uint32 computed_checksum;     /* Our own calculation of the checksum.
+				 * (XOR of bytes 0x0000 - 0x01FB) 
+				 */
+  
+  /* unknown data structure values */
+  uint32 unknown1;
+  uint32 unknown2;
+  uint32 unknown3;
+  uint32 unknown4;
+  uint32 unknown5;
+  uint32 unknown6;
+  uint32 unknown7;
+} REGF_FILE;
+
+
+
+typedef struct 
+{
+  REGF_FILE* f;
+  void_stack* key_positions;
+  lru_cache* sk_recs;
+  REGF_NK_REC* cur_key;
+  uint32 cur_subkey;
+  uint32 cur_value;
+} REGFI_ITERATOR;
+
+
+typedef struct 
+{
+  REGF_NK_REC* nk;
+  uint32 cur_subkey;
+  /* We could store a cur_value here as well, but didn't see 
+   * the use in it right now.
+   */
+} REGFI_ITER_POSITION;
+
+
+/******************************************************************************/
+/* Function Declarations */
+/*  Main API */
+const char*           regfi_type_val2str(unsigned int val);
+int                   regfi_type_str2val(const char* str);
+
+char*                 regfi_get_sacl(SEC_DESC* sec_desc);
+char*                 regfi_get_dacl(SEC_DESC* sec_desc);
+char*                 regfi_get_owner(SEC_DESC* sec_desc);
+char*                 regfi_get_group(SEC_DESC* sec_desc);
+
+REGF_FILE*            regfi_open(const char* filename);
+int                   regfi_close(REGF_FILE* r);
+
+REGFI_ITERATOR*       regfi_iterator_new(REGF_FILE* fh);
+void                  regfi_iterator_free(REGFI_ITERATOR* i);
+bool                  regfi_iterator_down(REGFI_ITERATOR* i);
+bool                  regfi_iterator_up(REGFI_ITERATOR* i);
+bool                  regfi_iterator_to_root(REGFI_ITERATOR* i);
+
+bool                  regfi_iterator_find_subkey(REGFI_ITERATOR* i, 
+						 const char* subkey_name);
+bool                  regfi_iterator_walk_path(REGFI_ITERATOR* i, 
+					       const char** path);
+const REGF_NK_REC*    regfi_iterator_cur_key(REGFI_ITERATOR* i);
+const REGF_SK_REC*    regfi_iterator_cur_sk(REGFI_ITERATOR* i);
+const REGF_NK_REC*    regfi_iterator_first_subkey(REGFI_ITERATOR* i);
+const REGF_NK_REC*    regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
+const REGF_NK_REC*    regfi_iterator_next_subkey(REGFI_ITERATOR* i);
+
+bool                  regfi_iterator_find_value(REGFI_ITERATOR* i, 
+						const char* value_name);
+const REGF_VK_REC*    regfi_iterator_first_value(REGFI_ITERATOR* i);
+const REGF_VK_REC*    regfi_iterator_cur_value(REGFI_ITERATOR* i);
+const REGF_VK_REC*    regfi_iterator_next_value(REGFI_ITERATOR* i);
+
+/************************************/
+/*  Low-layer data structure access */
+/************************************/
+REGF_FILE*            regfi_parse_regf(int fd, bool strict);
+REGF_HBIN*            regfi_parse_hbin(REGF_FILE* file, uint32 offset, 
+				       bool strict);
+
+
+/* regfi_parse_nk: Parses an NK record.
+ *
+ * Arguments:
+ *   f        -- the registry file structure
+ *   offset   -- the offset of the cell (not the record) to be parsed.
+ *   max_size -- the maximum size the NK cell could be. (for validation)
+ *   strict   -- if true, rejects any malformed records.  Otherwise,
+ *               tries to minimally validate integrity.
+ * Returns:
+ *   A newly allocated NK record structure, or NULL on failure.
+ */
+REGF_NK_REC*          regfi_parse_nk(REGF_FILE* file, uint32 offset, 
+				     uint32 max_size, bool strict);
+
+
+/* Private Functions */
+REGF_NK_REC*          regfi_rootkey(REGF_FILE* file);
+void                  regfi_key_free(REGF_NK_REC* nk);
+uint32                regfi_read(int fd, uint8* buf, uint32* length);
+
+
+
+/****************/
+/* Experimental */
+/****************/
+REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict);
+
+REGF_HASH_LIST* regfi_load_hashlist(REGF_FILE* file, uint32 offset, 
+				    uint32 num_keys, uint32 max_size, 
+				    bool strict);
+
+REGF_VK_REC** regfi_load_valuelist(REGF_FILE* file, uint32 offset, 
+				   uint32 num_values, uint32 max_size, 
+				   bool strict);
+
+REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset, 
+			    uint32 max_size, bool strict);
+
+uint8* regfi_parse_data(REGF_FILE* file, uint32 offset, 
+			uint32 length, bool strict);
+
+REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, bool strict);
+
+range_list* regfi_parse_unalloc_cells(REGF_FILE* file);
+
+REGF_HBIN* regfi_lookup_hbin(REGF_FILE* file, uint32 offset);
+
+bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
+		      uint32* cell_length, bool* unalloc);
+
+#endif	/* _REGFI_H */
Index: releases/0.9.0/include/smb_deps.h
===================================================================
--- releases/0.9.0/include/smb_deps.h	(revision 123)
+++ releases/0.9.0/include/smb_deps.h	(revision 123)
@@ -0,0 +1,315 @@
+/*
+ * This file contains miscellaneous pieces of code which regfio.c
+ * depends upon, from the Samba Subversion tree.  See:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * Copyright (C) 2005 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$
+ */
+
+#include <stdlib.h>
+#include <stdbool.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 "byteorder.h"
+
+#define DEBUG(lvl,body) 0
+
+void* zalloc(size_t size);
+void* zcalloc(size_t size, unsigned int count);
+
+/* From includes.h */
+
+#define uint8 unsigned char
+#define int16 short
+#define uint16 unsigned short
+#define int32 int
+#define uint32 unsigned int
+
+#define SMB_STRUCT_STAT struct stat
+#define QSORT_CAST (int (*)(const void *, const void *))
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+extern int DEBUGLEVEL;
+
+#define DLIST_ADD(list, p) \
+{ \
+        if (!(list)) { \
+		(list) = (p); \
+		(p)->next = (p)->prev = NULL; \
+	} else { \
+		(list)->prev = (p); \
+		(p)->next = (list); \
+		(p)->prev = NULL; \
+		(list) = (p); \
+	}\
+}
+
+/* End of stuff from includes.h */
+
+/* From smb.h */
+
+#define MAXSUBAUTHS 15
+
+typedef struct sid_info
+{
+  uint8  sid_rev_num;             /**< SID revision number */
+  uint8  num_auths;               /**< Number of sub-authorities */
+  uint8  id_auth[6];              /**< Identifier Authority */
+  /*
+   *  Pointer to sub-authorities.
+   *
+   * @note The values in these uint32's are in *native* byteorder, not
+   * neccessarily little-endian...... JRA.
+   */
+  uint32 sub_auths[MAXSUBAUTHS];
+} DOM_SID;
+
+typedef struct nttime_info
+{
+  uint32 low;
+  uint32 high;
+} NTTIME;
+
+/* End of stuff from smb.h */
+
+/* From smb_macros.h */
+
+#define TALLOC_ZERO_P(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define SMB_MALLOC_P(type) (type *)malloc_(sizeof(type))
+#define TALLOC_ARRAY(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
+#define TALLOC_ZERO_ARRAY(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
+
+/* End of stuff from smb_macros.h */
+
+/* From ntdomain.h */
+
+struct uuid {
+       uint32 time_low;
+       uint16 time_mid;
+       uint16 time_hi_and_version;
+       uint8  clock_seq[2];
+       uint8  node[6];
+};
+
+typedef struct _prs_struct {
+	bool io; /* parsing in or out of data stream */
+	/* 
+	 * If the (incoming) data is big-endian. On output we are
+	  * always little-endian.
+	   */ 
+	   bool bigendian_data;
+	   uint8 align; /* data alignment */
+	   bool is_dynamic; /* Do we own this memory or not ? */
+	   uint32 data_offset; /* Current working offset into data. */
+	   uint32 buffer_size; /* Current allocated size of the buffer. */
+	   uint32 grow_size; /* size requested via prs_grow() calls */
+	   char *data_p; /* The buffer itself. */
+	   void *mem_ctx; /* When unmarshalling, use this.... */
+} prs_struct;
+
+#define MARSHALL 0
+#define UNMARSHALL 1
+
+#define RPC_LITTLE_ENDIAN  0
+#define RPC_PARSE_ALIGN    4
+
+/* End of stuff from ntdomain.h */
+
+
+/* From lib/time.c */
+
+#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))
+
+void unix_to_nt_time(NTTIME* nt, time_t t);
+time_t nt_time_to_unix(const NTTIME* nt);
+
+/* End of stuff from lib/time.c */
+
+/* From rpc_dce.h */
+
+#define MAX_PDU_FRAG_LEN 0x10b8 /* this is what w2k sets */
+
+/* End of stuff from rpc_dce.h */
+
+/* From parse_prs.h */
+
+bool prs_grow(prs_struct *ps, uint32 extra_space);
+bool prs_align(prs_struct *ps);
+bool prs_init(prs_struct *ps, uint32 size, void *ctx, bool io);
+char *prs_mem_get(prs_struct *ps, uint32 extra_size);
+bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32);
+bool prs_uint32s(const char *name, prs_struct *ps, 
+		 int depth, uint32 *data32s, int len);
+bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16);
+bool prs_uint16_pre(const char *name, prs_struct *ps, int depth, 
+		    uint16 *data16, uint32 *offset);
+bool prs_uint16_post(const char *name, prs_struct *ps, int depth, 
+		     uint16 *data16, uint32 ptr_uint16, uint32 start_offset);
+bool prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8);
+bool prs_uint8s(const char *name, prs_struct *ps, int depth, 
+		uint8* data8s, int len);
+bool prs_set_offset(prs_struct *ps, uint32 offset);
+
+/* End of stuff from parse_prs.h */
+
+
+/* From rpc_secdesc.h */
+
+typedef struct security_info_info
+{
+	uint32 mask;
+
+} SEC_ACCESS;
+
+typedef struct security_ace_info
+{
+	uint8 type;  /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+	uint8 flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+	uint16 size;
+
+	SEC_ACCESS info;
+
+	/* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+	uint32  obj_flags; /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+	struct uuid obj_guid;  /* object GUID */
+	struct uuid inh_guid;  /* inherited object GUID */		
+        /* eof object stuff */
+
+	DOM_SID trustee;
+
+} SEC_ACE;
+
+typedef struct security_acl_info
+{
+	uint16 revision; /* 0x0003 */
+	uint16 size; /* size in bytes of the entire ACL structure */
+	uint32 num_aces; /* number of Access Control Entries */
+
+	SEC_ACE *ace;
+
+} SEC_ACL;
+
+typedef struct security_descriptor_info
+{
+	uint16 revision; /* 0x0001 */
+	uint16 type;     /* SEC_DESC_xxxx flags */
+
+	uint32 off_owner_sid; /* offset to owner sid */
+	uint32 off_grp_sid  ; /* offset to group sid */
+	uint32 off_sacl     ; /* offset to system list of permissions */
+	uint32 off_dacl     ; /* offset to list of permissions */
+
+	SEC_ACL *dacl; /* user ACL */
+	SEC_ACL *sacl; /* system ACL */
+	DOM_SID *owner_sid; 
+	DOM_SID *grp_sid;
+
+} SEC_DESC;
+
+/* End of stuff from rpc_secdesc.h */
+
+
+/* From pstring.h */
+
+#define FSTRING_LEN 256
+typedef char fstring[FSTRING_LEN];
+
+/* End of stuff from pstring.h */
+
+
+/* From rpc_secdes.h */
+
+#define SEC_DESC_DACL_PRESENT		0x0004
+#define SEC_DESC_SACL_PRESENT		0x0010
+#define  SEC_DESC_HEADER_SIZE (2 * sizeof(uint16) + 4 * sizeof(uint32))
+   /* thanks for Jim McDonough <jmcd@us.ibm.com> */
+#define SEC_ACE_OBJECT_PRESENT        0x00000001 
+#define SEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002
+
+#define SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT	0x5
+#define SEC_ACE_TYPE_ACCESS_DENIED_OBJECT     	0x6
+#define SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT      	0x7
+#define SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT	0x8
+
+/* End of stuff from rpc_secdes.h */
+
+/* From rpc_parse/parse_misc.c */
+
+bool smb_io_uuid(const char *desc, struct uuid *uuid, 
+		 prs_struct *ps, int depth);
+bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth);
+bool smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth);
+
+/* End of stuff from rpc_parse/parse_misc.c */
+
+/* From lib/util_sid.c */
+
+size_t sid_size(const DOM_SID *sid);
+int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2);
+int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2);
+bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2);
+
+/* End of stuff from lib/util_sid.c */
+
+/* From lib/secace.c */
+
+bool sec_ace_object(uint8 type);
+
+/* End of stuff from lib/secace.c */
+
+/* From rpc_parse/parse_sec.c */
+
+bool sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth);
+bool sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth);
+bool sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth);
+bool sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth);
+
+/* End of stuff from rpc_parse/parse_sec.c */
+
+/* From lib/secace.c */
+
+bool sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2);
+
+/* End of stuff from lib/secace.c */
+
+/* From lib/secacl.c */
+
+bool sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2);
+
+/* End of stuff from lib/secacl.c */
+
+/* From lib/secdesc.c */
+
+bool sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2);
+
+/* End of stuff from lib/secdesc.c */
Index: releases/0.9.0/include/void_stack.h
===================================================================
--- releases/0.9.0/include/void_stack.h	(revision 123)
+++ releases/0.9.0/include/void_stack.h	(revision 123)
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2005,2007 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 <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#ifndef _VOID_STACK_H
+#define _VOID_STACK_H
+
+typedef struct _void_stack
+{
+  void** elements;
+  unsigned short max_size;
+  unsigned short top;
+} void_stack;
+
+typedef struct _void_stack_iterator
+{
+  const void_stack* stack;
+  unsigned short cur;
+} void_stack_iterator;
+
+
+/* void_stack_new(): 
+ *  Allocates a new void_stack.
+ *
+ * Arguments:
+ *  max_size -- the maxiumum number of elements 
+ *              which may be pushed onto the stack.
+ *
+ * Returns: 
+ *  a pointer to the newly allocated void_stack, or NULL if an error occurred.
+ */
+void_stack* void_stack_new(unsigned short max_size);
+
+
+/* void_stack_copy(): 
+ *  Makes a shallow copy of void_stack.
+ *
+ * Arguments:
+ *  v -- the stack to make a copy of.
+ *
+ * Returns:
+ *  a pointer to the duplicate void_stack, or NULL If an error occurred.
+ */
+void_stack* void_stack_copy(const void_stack* v);
+
+
+/* void_stack_copy_reverse(): 
+ *  Makes a shallow copy of void_stack in reverse order.
+ *
+ * Arguments:
+ *  v -- the stack to make a copy of.
+ *
+ * Returns:
+ *  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);
+
+
+/* void_stack_free(): 
+ *  Frees the memory associated with a void_stack, but not the elements held
+ *  on the stack.
+ *
+ * Arguments:
+ *  stack -- the stack to be free()d.
+ */
+void void_stack_free(void_stack* stack);
+
+
+/* void_stack_free_deep(): 
+ *  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.
+ *
+ * Arguments:
+ *  stack -- the stack to be free()d.
+ */
+void void_stack_free_deep(void_stack* stack);
+
+
+/* void_stack_size(): 
+ *  Query the current number of elements on a void_stack()
+ *
+ * Arguments:
+ *  stack -- the void_stack to query
+ *
+ * Returns:
+ *  the number of elements currently on the stack.
+ */
+unsigned short void_stack_size(const void_stack* stack);
+
+
+/* void_stack_pop():
+ *  Removes the top element on a void_stack and returns a reference to it.
+ *
+ * Arguments:
+ *  stack -- the void_stack to pop
+ *
+ * Returns:
+ *  a pointer to the popped stack element, or NULL if no elements exist on 
+ *  the stack.
+ */
+void* void_stack_pop(void_stack* stack);
+
+
+/* void_stack_push():
+ *  Puts a new element on the top of a void_stack.
+ *
+ * Arguments:
+ *  stack -- the void_stack being modified.
+ *
+ *  e     -- the element to be added
+ *
+ * Returns:
+ *  true if the element was successfully added, false otherwise.
+ */
+bool void_stack_push(void_stack* stack, void* e);
+
+
+/* void_stack_cur():
+ *  Returns a pointer to the current element on the top of the stack.
+ *
+ * Arguments:
+ *  stack -- the void_stack being queried.
+ *
+ * Returns:
+ *  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);
+
+
+/* void_stack_iterator_new():
+ *  Creates a new iterator for the specified void_stack.
+ *
+ * Arguments:
+ *  stack -- the void_stack to be referenced by the new iterator
+ *
+ * Returns:
+ *  a new void_stack_iterator, or NULL if an error occurred.
+ */
+void_stack_iterator* void_stack_iterator_new(const void_stack* stack);
+
+
+/* void_stack_iterator_free():
+ *  Frees a void_stack_iterator.  Does not affect the void_stack referenced
+ *  by the iterator.
+ *
+ * Arguments:
+ *  iter -- the void_stack_iterator to be free()d.
+ */
+void void_stack_iterator_free(void_stack_iterator* iter);
+
+
+/* void_stack_iterator_next():
+ *  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).
+ *
+ * Arguments:
+ *  iter -- the void_stack_iterator used to lookup the next element.
+ *
+ * Returns:
+ *  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.9.0/lib/Makefile
===================================================================
--- releases/0.9.0/lib/Makefile	(revision 123)
+++ releases/0.9.0/lib/Makefile	(revision 123)
@@ -0,0 +1,25 @@
+# $Id: Makefile 30 2005-07-16 14:31:27Z tim $
+
+################################################################################
+
+FILES=regfi.o smb_deps.o void_stack.o range_list.o lru_cache.o
+
+all: $(FILES)
+
+regfi.o: regfi.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ regfi.c
+
+smb_deps.o: smb_deps.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ smb_deps.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
+
+clean:
+	rm -f $(FILES)
Index: releases/0.9.0/lib/lru_cache.c
===================================================================
--- releases/0.9.0/lib/lru_cache.c	(revision 123)
+++ releases/0.9.0/lib/lru_cache.c	(revision 123)
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2008 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 "../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, bool free_data)
+{
+  lru_cache* ret_val;
+
+  ret_val = (lru_cache*)malloc(sizeof(lru_cache));
+  if(ret_val == NULL)
+    return NULL;
+
+  if(max_keys == 0)
+    ret_val->num_buckets = 2048;
+  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 
+    = (lru_cache_element**)malloc(sizeof(lru_cache_element*) 
+				  * ret_val->num_buckets);
+  if(ret_val->table == NULL)
+  {
+    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->free_data = free_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)
+{
+  lru_cache_element* cur;
+  lru_cache_element* last = NULL;
+  
+  for(cur=ht->oldest; cur != NULL; last=cur,cur=cur->newer)
+  {
+    if(last != NULL)
+    {
+      if(ht->free_data)
+	free(last->data);
+      free(last->index);
+      free(last);
+    }
+  }
+  free(ht->table);
+  ht->secret = 0;
+  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->free_data)
+      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->free_data)
+	free(e->data);
+
+      tmp_index = realloc(e->index, index_len);
+      if(tmp_index == NULL)
+      {
+	free(e->index);
+	free(e);
+	return false;
+      }
+    }
+    else
+    { /* Brand new element because we have room to spare. */
+
+      e = (lru_cache_element*)malloc(sizeof(lru_cache_element));
+      if(e == NULL)
+	return false;
+      
+      e->index = malloc(index_len);
+      if(e->index == NULL)
+      {
+	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;
+
+  /* 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;
+
+  if(ht->free_data)
+    free(cur->data);
+
+  /* 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;
+
+  free(cur->index);
+  free(cur);
+  
+  /* Removing entry, decrement counters. */
+  ht->num_keys--;
+  
+  return true;
+}
Index: releases/0.9.0/lib/range_list.c
===================================================================
--- releases/0.9.0/lib/range_list.c	(revision 123)
+++ releases/0.9.0/lib/range_list.c	(revision 123)
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2008 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 <math.h>
+#include "../include/range_list.h"
+
+
+/*******************/
+/* Private symbols */
+/*******************/
+#define RANGE_LIST_ALLOC_SIZE 256
+
+#if 0
+#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 = (range_list_element**)realloc(rl->elements, 
+					(rl->elem_alloced+RANGE_LIST_ALLOC_SIZE)
+					* sizeof(range_list_element*));
+    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 = (range_list*)malloc(sizeof(range_list));
+  if(rl == NULL)
+    return NULL;
+
+  rl->elements = (range_list_element**)malloc(sizeof(range_list_element*)
+					      * RANGE_LIST_ALLOC_SIZE);
+
+  if(rl->elements == NULL)
+  {
+    free(rl);
+    return NULL;
+  }
+
+  rl->elem_alloced = RANGE_LIST_ALLOC_SIZE;
+  rl->size = 0;
+
+  return rl;
+}
+
+
+void range_list_free(range_list* rl)
+{
+  uint32_t i;
+
+  if(rl == NULL)
+    return;
+
+  for(i=0; i < rl->size; i++)
+    free(rl->elements[i]);
+
+  free(rl->elements);
+  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 = (range_list_element*)malloc(sizeof(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))
+  {
+    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;
+
+  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 = (range_list_element**)realloc(rl->elements, 
+					(rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE)
+					* sizeof(range_list_element*));
+    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((offset < rl->elements[0]->offset)
+     || (offset > rl->elements[rl->size-1]->offset 
+	 + rl->elements[rl->size-1]->length))
+    return -1;
+
+  prev_idx = range_list_find_previous(rl, offset);
+  elem = rl->elements[prev_idx];
+  if(offset < elem->offset+elem->length)
+    return prev_idx;
+
+  return -2;
+}
+
+
+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 = (range_list_element*)malloc(sizeof(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))
+  {
+    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.9.0/lib/regfi.c
===================================================================
--- releases/0.9.0/lib/regfi.c	(revision 123)
+++ releases/0.9.0/lib/regfi.c	(revision 123)
@@ -0,0 +1,1861 @@
+/*
+ * 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
+ *
+ * Unix SMB/CIFS implementation.
+ * Windows NT registry I/O library
+ *
+ * Copyright (C) 2005-2008 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$
+ */
+
+#include "../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"};
+
+
+/* Returns NULL on error */
+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 parsing functions  */
+
+const char* regfi_ace_type2str(uint8 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 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 i;
+  uint8 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';
+
+  /* XXX: what was this old VI flag for??
+     XXX: Is this check right?  0xF == 1|2|4|8, which makes it redundant...
+  if (flags == 0xF) {
+    if (some) strcat(flg_output, " ");
+    some = 1;
+    strcat(flg_output, "VI");
+  }
+  */
+
+  return ret_val;
+}
+
+
+char* regfi_ace_perms2str(uint32 perms)
+{
+  uint32 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(DOM_SID* sid)
+{
+  uint32 i, size = MAXSUBAUTHS*11 + 24;
+  uint32 left = size;
+  uint8 comps = sid->num_auths;
+  char* ret_val = malloc(size);
+  
+  if(ret_val == NULL)
+    return NULL;
+
+  if(comps > MAXSUBAUTHS)
+    comps = MAXSUBAUTHS;
+
+  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(SEC_ACL* acl)
+{
+  uint32 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->ace[i].trustee);
+    type_str = regfi_ace_type2str(acl->ace[i].type);
+    perms_str = regfi_ace_perms2str(acl->ace[i].info.mask);
+    flags_str = regfi_ace_flags2str(acl->ace[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);
+	failed = true;
+      }
+      else
+      {
+	ret_val = tmp_val;
+	size += snprintf(ret_val+size, extra, "%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(SEC_DESC *sec_desc)
+{
+  if (sec_desc->sacl)
+    return regfi_get_acl(sec_desc->sacl);
+  else
+    return NULL;
+}
+
+
+char* regfi_get_dacl(SEC_DESC *sec_desc)
+{
+  if (sec_desc->dacl)
+    return regfi_get_acl(sec_desc->dacl);
+  else
+    return NULL;
+}
+
+
+char* regfi_get_owner(SEC_DESC *sec_desc)
+{
+  return regfi_sid2str(sec_desc->owner_sid);
+}
+
+
+char* regfi_get_group(SEC_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 regfi_read(int fd, uint8* buf, uint32* length)
+{
+  uint32 rsize = 0;
+  uint32 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 offset, uint8* hdr, uint32 hdr_len,
+		      uint32* cell_length, bool* unalloc)
+{
+  uint32 length;
+  int32 raw_length;
+  uint8 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(REGF_HBIN* hbin, uint32 offset)
+{
+  if(!hbin)
+    return false;
+
+  if((offset > hbin->first_hbin_off) 
+     && (offset < (hbin->first_hbin_off + hbin->block_size)))
+    return true;
+		
+  return false;
+}
+
+
+
+/*******************************************************************
+ * Given a virtual offset, and receive the correpsonding HBIN 
+ * block for it.  NULL if one doesn't exist.
+ *******************************************************************/
+REGF_HBIN* regfi_lookup_hbin(REGF_FILE* file, uint32 offset)
+{
+  return (REGF_HBIN*)range_list_find_data(file->hbins, offset+REGF_BLOCKSIZE);
+}
+
+
+
+/*******************************************************************
+ *******************************************************************/
+REGF_HASH_LIST* regfi_load_hashlist(REGF_FILE* file, uint32 offset, 
+				    uint32 num_keys, uint32 max_size, 
+				    bool strict)
+{
+  REGF_HASH_LIST* ret_val;
+  uint32 i, cell_length, length;
+  uint8* hashes;
+  uint8 buf[REGFI_HASH_LIST_MIN_LENGTH];
+  bool unalloc;
+
+  if(!regfi_parse_cell(file->fd, offset, buf, REGFI_HASH_LIST_MIN_LENGTH, 
+		       &cell_length, &unalloc))
+    return NULL;
+
+  ret_val = (REGF_HASH_LIST*)zalloc(sizeof(REGF_HASH_LIST));
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->offset = offset;
+  if(cell_length > max_size)
+  {
+    if(strict)
+      return NULL;
+    cell_length = max_size & 0xFFFFFFF8;
+  }
+  ret_val->cell_size = cell_length;
+
+  if((buf[0] != 'l' || buf[1] != 'f') && (buf[0] != 'l' || buf[1] != 'h')
+     && (buf[0] != 'r' || buf[1] != 'i'))
+  {
+    /*printf("DEBUG: lf->header=%c%c\n", buf[0], buf[1]);*/
+    free(ret_val);
+    return NULL;
+  }
+
+  if(buf[0] == 'r' && buf[1] == 'i')
+  {
+    fprintf(stderr, "WARNING: ignoring encountered \"ri\" record.\n");
+    free(ret_val);
+    return NULL;
+  }
+
+  ret_val->magic[0] = buf[0];
+  ret_val->magic[1] = buf[1];
+
+  ret_val->num_keys = SVAL(buf, 0x2);
+  if(num_keys != ret_val->num_keys)
+  {
+    if(strict)
+    {
+      free(ret_val);
+      return NULL;
+    }
+    /* XXX: Not sure which should be authoritative, the number from the 
+     *      NK record, or the number in the hash list.  Go with the larger
+     *      of the two to ensure all keys are found.  Note the length checks
+     *      on the cell later ensure that there won't be any critical errors.
+     */
+    if(num_keys < ret_val->num_keys)
+      num_keys = ret_val->num_keys;
+    else
+      ret_val->num_keys = num_keys;
+  }
+
+  if(cell_length - REGFI_HASH_LIST_MIN_LENGTH - sizeof(uint32) 
+     < ret_val->num_keys*sizeof(REGF_HASH_LIST_ELEM))
+    return NULL;
+
+  length = sizeof(REGF_HASH_LIST_ELEM)*ret_val->num_keys;
+  ret_val->hashes = (REGF_HASH_LIST_ELEM*)zalloc(length);
+  if(ret_val->hashes == NULL)
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  hashes = (uint8*)zalloc(length);
+  if(hashes == NULL)
+  {
+    free(ret_val->hashes);
+    free(ret_val);
+    return NULL;
+  }
+
+  if(regfi_read(file->fd, hashes, &length) != 0
+     || length != sizeof(REGF_HASH_LIST_ELEM)*ret_val->num_keys)
+  {
+    free(ret_val->hashes);
+    free(ret_val);
+    return NULL;
+  }
+
+  for (i=0; i < ret_val->num_keys; i++)
+  {
+    ret_val->hashes[i].nk_off = IVAL(hashes, i*sizeof(REGF_HASH_LIST_ELEM));
+    ret_val->hashes[i].hash = IVAL(hashes, i*sizeof(REGF_HASH_LIST_ELEM)+4);
+  }
+  free(hashes);
+
+  return ret_val;
+}
+
+
+
+/*******************************************************************
+ *******************************************************************/
+REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, bool strict)
+{
+  REGF_SK_REC* ret_val;
+  uint32 cell_length, length;
+  prs_struct ps;
+  uint8 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))
+    return NULL;
+   
+  if(sk_header[0] != 's' || sk_header[1] != 'k')
+    return NULL;
+  
+  ret_val = (REGF_SK_REC*)zalloc(sizeof(REGF_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 != (ret_val->cell_size & 0xFFFFFFF8)))
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  ret_val->magic[0] = sk_header[0];
+  ret_val->magic[1] = sk_header[1];
+
+  /* XXX: Can additional validation be added here? */
+  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->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  /* XXX: need to get rid of this, but currently the security descriptor
+   * code depends on the ps structure.
+   */
+  if(!prs_init(&ps, ret_val->desc_size, NULL, UNMARSHALL))
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  length = ret_val->desc_size;
+  if(regfi_read(file->fd, (uint8*)ps.data_p, &length) != 0 
+     || length != ret_val->desc_size)
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  if (!sec_io_desc("sec_desc", &ret_val->sec_desc, &ps, 0))
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  free(ps.data_p);
+
+  return ret_val;
+}
+
+
+uint32* regfi_parse_valuelist(REGF_FILE* file, uint32 offset, 
+			      uint32 num_values, bool strict)
+{
+  uint32* ret_val;
+  uint32 i, cell_length, length, read_len;
+  bool unalloc;
+
+  if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
+    return NULL;
+
+  if(cell_length != (cell_length & 0xFFFFFFF8))
+  {
+    if(strict)
+      return NULL;
+    cell_length = cell_length & 0xFFFFFFF8;
+  }
+  if((num_values * sizeof(uint32)) > cell_length-sizeof(uint32))
+    return NULL;
+
+  read_len = num_values*sizeof(uint32);
+  ret_val = (uint32*)malloc(read_len);
+  if(ret_val == NULL)
+    return NULL;
+
+  length = read_len;
+  if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0) || length != read_len)
+  {
+    free(ret_val);
+    return NULL;
+  }
+  
+  for(i=0; i < num_values; i++)
+  {
+    /* Fix endianness */
+    ret_val[i] = IVAL(&ret_val[i], 0);
+
+    /* Validate the first num_values values to ensure they make sense */
+    if(strict)
+    {
+      if((ret_val[i] + REGF_BLOCKSIZE > file->file_length)
+	 || ((ret_val[i] & 0xFFFFFFF8) != ret_val[i]))
+      {
+	free(ret_val);
+	return NULL;
+      }
+    }
+  }
+
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ * If !strict, the list may contain NULLs, VK records may point to NULL.
+ ******************************************************************************/
+REGF_VK_REC** regfi_load_valuelist(REGF_FILE* file, uint32 offset, 
+				   uint32 num_values, uint32 max_size, 
+				   bool strict)
+{
+  REGF_VK_REC** ret_val;
+  REGF_HBIN* hbin;
+  uint32 i, vk_offset, vk_max_length, usable_num_values;
+  uint32* voffsets;
+
+  if((num_values+1) * sizeof(uint32) > max_size)
+  {
+    if(strict)
+      return NULL;
+    usable_num_values = max_size/sizeof(uint32) - sizeof(uint32);
+  }
+  else
+    usable_num_values = num_values;
+
+  voffsets = regfi_parse_valuelist(file, offset, usable_num_values, strict);
+  if(voffsets == NULL)
+    return NULL;
+
+  ret_val = (REGF_VK_REC**)zalloc(sizeof(REGF_VK_REC*) * num_values);
+  if(ret_val == NULL)
+  {
+    free(voffsets);
+    return NULL;
+  }
+  
+  for(i=0; i < usable_num_values; i++)
+  {
+    hbin = regfi_lookup_hbin(file, voffsets[i]);
+    if(!hbin)
+    {
+      free(voffsets);
+      free(ret_val);
+      return NULL;
+    }
+    
+    vk_offset =  voffsets[i] + REGF_BLOCKSIZE;
+    vk_max_length = hbin->block_size - vk_offset + sizeof(uint32);
+    ret_val[i] = regfi_parse_vk(file, vk_offset, vk_max_length, strict);
+    if(ret_val[i] == NULL)
+    { /* If we're being strict, throw out the whole list.
+       * Otherwise, let it be NULL.
+       */
+      if(strict)
+      {
+	free(voffsets);
+	free(ret_val);
+	return NULL;
+      }
+    }
+  }
+
+  free(voffsets);
+  return ret_val;
+}
+
+
+
+/*******************************************************************
+ * XXX: Need to add full key caching using a 
+ *      custom cache structure.
+ *******************************************************************/
+REGF_NK_REC* regfi_load_key(REGF_FILE* file, uint32 offset, bool strict)
+{
+  REGF_HBIN* hbin;
+  REGF_HBIN* sub_hbin;
+  REGF_NK_REC* nk;
+  uint32 max_length, off;
+
+  hbin = regfi_lookup_hbin(file, offset-REGF_BLOCKSIZE);
+  if (hbin == NULL) 
+    return NULL;
+
+  /* get the initial nk record */
+  max_length = hbin->block_size + hbin->file_off - offset;
+  if ((nk = regfi_parse_nk(file, offset, max_length, true)) == NULL)
+    return NULL;
+
+  /* fill in values */
+  if(nk->num_values && (nk->values_off!=REGF_OFFSET_NONE)) 
+  {
+    sub_hbin = hbin;
+    if(!regfi_offset_in_hbin(hbin, nk->values_off)) 
+      sub_hbin = regfi_lookup_hbin(file, nk->values_off);
+    
+    if(sub_hbin == NULL)
+    {
+      if(strict)
+      {
+	free(nk);
+	return NULL;
+      }
+      else
+	nk->values = NULL;
+    }
+    else
+    {
+      off = nk->values_off + REGF_BLOCKSIZE;
+      max_length = sub_hbin->block_size + sub_hbin->file_off - off;
+      nk->values = regfi_load_valuelist(file, off, nk->num_values, max_length, 
+					true);
+      if(strict && nk->values == NULL)
+      {
+	free(nk);
+	return NULL;
+      }
+    }
+  }
+
+  /* now get subkeys */
+  if(nk->num_subkeys && (nk->subkeys_off != REGF_OFFSET_NONE)) 
+  {
+    sub_hbin = hbin;
+    if(!regfi_offset_in_hbin(hbin, nk->subkeys_off))
+      sub_hbin = regfi_lookup_hbin(file, nk->subkeys_off);
+
+    if (sub_hbin == NULL) 
+    {
+      if(strict)
+      {
+	regfi_key_free(nk);
+	return NULL;
+      }
+      else
+	nk->subkeys = NULL;
+    }
+    else
+    {
+      off = nk->subkeys_off + REGF_BLOCKSIZE;
+      max_length = sub_hbin->block_size + sub_hbin->file_off - off;
+      nk->subkeys = regfi_load_hashlist(file, off, nk->num_subkeys, 
+					max_length, true);
+      if(nk->subkeys == NULL)
+      {
+	/* XXX: Temporary hack to get around 'ri' records */
+	nk->num_subkeys = 0;
+      }
+    }
+  }
+
+  return nk;
+}
+
+
+/******************************************************************************
+
+ ******************************************************************************/
+static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
+			       uint32* root_offset)
+{
+  uint8 tmp[4];
+  int32 record_size;
+  uint32 length, hbin_offset = 0;
+  REGF_NK_REC* nk = NULL;
+  bool found = false;
+
+  for(record_size=0; !found && (hbin_offset < hbin_size); )
+  {
+    if(lseek(file->fd, offset+hbin_offset, SEEK_SET) == -1)
+      return false;
+    
+    length = 4;
+    if((regfi_read(file->fd, tmp, &length) != 0) || length != 4)
+      return false;
+    record_size = IVALS(tmp, 0);
+
+    if(record_size < 0)
+    {
+      record_size = record_size*(-1);
+      nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
+      if(nk != NULL)
+      {
+	if(nk->key_type == NK_TYPE_ROOTKEY)
+	{
+	  found = true;
+	  *root_offset = nk->offset;
+	}
+	free(nk);
+      }
+    }
+
+    hbin_offset += record_size;
+  }
+
+  return found;
+}
+
+
+/*******************************************************************
+ * Open the registry file and then read in the REGF block to get the
+ * first hbin offset.
+ *******************************************************************/
+REGF_FILE* regfi_open(const char* filename)
+{
+  REGF_FILE* rb;
+  REGF_HBIN* hbin = NULL;
+  uint32 hbin_off;
+  int fd;
+  bool rla;
+
+  /* open an existing file */
+  if ((fd = open(filename, O_RDONLY)) == -1) 
+  {
+    /* DEBUG(0,("regfi_open: failure to open %s (%s)\n", filename, strerror(errno)));*/
+    return NULL;
+  }
+  
+  /* read in an existing file */
+  if ((rb = regfi_parse_regf(fd, true)) == NULL) 
+  {
+    /* DEBUG(0,("regfi_open: Failed to read initial REGF block\n"));*/
+    close(fd);
+    return NULL;
+  }
+  
+  rb->hbins = range_list_new();
+  if(rb->hbins == NULL)
+  {
+    range_list_free(rb->hbins);
+    close(fd);
+    free(rb);
+    return NULL;
+  }
+  
+  rla = true;
+  hbin_off = REGF_BLOCKSIZE;
+  hbin = regfi_parse_hbin(rb, hbin_off, true);
+  while(hbin && rla)
+  {
+    hbin_off = hbin->file_off + hbin->block_size;
+    rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
+    hbin = regfi_parse_hbin(rb, hbin_off, true);
+  }
+
+  /* success */
+  return rb;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+int regfi_close( REGF_FILE *file )
+{
+  int fd;
+  uint32 i;
+
+  /* nothing to do if there is no open file */
+  if ((file == NULL) || (file->fd == -1))
+    return 0;
+
+  fd = file->fd;
+  file->fd = -1;
+  for(i=0; i < range_list_size(file->hbins); i++)
+    free(range_list_get(file->hbins, i)->data);
+  range_list_free(file->hbins);
+
+  free(file);
+
+  return close(fd);
+}
+
+
+/******************************************************************************
+ * There should be only *one* root key in the registry file based 
+ * on my experience.  --jerry
+ *****************************************************************************/
+REGF_NK_REC* regfi_rootkey(REGF_FILE *file)
+{
+  REGF_NK_REC* nk = NULL;
+  REGF_HBIN*   hbin;
+  uint32       root_offset, i, num_hbins;
+  
+  if(!file)
+    return NULL;
+
+  /* Scan through the file one HBIN block at a time looking 
+     for an NK record with a type == 0x002c.
+     Normally this is the first nk record in the first hbin 
+     block (but I'm not assuming that for now) */
+
+  num_hbins = range_list_size(file->hbins);
+  for(i=0; i < num_hbins; i++)
+  {
+    hbin = (REGF_HBIN*)range_list_get(file->hbins, i)->data;
+    if(regfi_find_root_nk(file, hbin->file_off+HBIN_HEADER_REC_SIZE, 
+			  hbin->block_size-HBIN_HEADER_REC_SIZE, &root_offset))
+    {
+      nk = regfi_load_key(file, root_offset, true);
+      break;
+    }
+  }
+
+  return nk;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_key_free(REGF_NK_REC* nk)
+{
+  uint32 i;
+  
+  if((nk->values != NULL) && (nk->values_off!=REGF_OFFSET_NONE))
+  {
+    for(i=0; i < nk->num_values; i++)
+    {
+      if(nk->values[i]->valuename != NULL)
+	free(nk->values[i]->valuename);
+      if(nk->values[i]->data != NULL)
+	free(nk->values[i]->data);
+      free(nk->values[i]);
+    }
+    free(nk->values);
+  }
+
+  if(nk->keyname != NULL)
+    free(nk->keyname);
+  if(nk->classname != NULL)
+    free(nk->classname);
+
+  /* XXX: not freeing hbin because these are cached.  This needs to be reviewed. */
+  /* XXX: not freeing sec_desc because these are cached.  This needs to be reviewed. */
+  free(nk);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_ITERATOR* regfi_iterator_new(REGF_FILE* fh)
+{
+  REGF_NK_REC* root;
+  REGFI_ITERATOR* ret_val = (REGFI_ITERATOR*)malloc(sizeof(REGFI_ITERATOR));
+  if(ret_val == NULL)
+    return NULL;
+
+  root = regfi_rootkey(fh);
+  if(root == NULL)
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  ret_val->key_positions = void_stack_new(REGF_MAX_DEPTH);
+  if(ret_val->key_positions == NULL)
+  {
+    free(ret_val);
+    free(root);
+    return NULL;
+  }
+
+  /* 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.
+   */
+  ret_val->sk_recs = lru_cache_create(127, 0x15DEAD05^time(NULL)
+				           ^(getpid()<<16)^(getppid()<<8),
+				      true);
+
+  ret_val->f = fh;
+  ret_val->cur_key = root;
+  ret_val->cur_subkey = 0;
+  ret_val->cur_value = 0;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_iterator_free(REGFI_ITERATOR* i)
+{
+  REGFI_ITER_POSITION* cur;
+
+  if(i->cur_key != NULL)
+    regfi_key_free(i->cur_key);
+
+  while((cur = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions)) != NULL)
+  {
+    regfi_key_free(cur->nk);
+    free(cur);
+  }
+  
+  lru_cache_destroy(i->sk_recs);
+
+  free(i);
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+/* XXX: some way of indicating reason for failure should be added. */
+bool regfi_iterator_down(REGFI_ITERATOR* i)
+{
+  REGF_NK_REC* subkey;
+  REGFI_ITER_POSITION* pos;
+
+  pos = (REGFI_ITER_POSITION*)malloc(sizeof(REGFI_ITER_POSITION));
+  if(pos == NULL)
+    return false;
+
+  subkey = (REGF_NK_REC*)regfi_iterator_cur_subkey(i);
+  if(subkey == NULL)
+  {
+    free(pos);
+    return false;
+  }
+
+  pos->nk = i->cur_key;
+  pos->cur_subkey = i->cur_subkey;
+  if(!void_stack_push(i->key_positions, pos))
+  {
+    free(pos);
+    regfi_key_free(subkey);
+    return false;
+  }
+
+  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_key_free(i->cur_key);
+  i->cur_key = pos->nk;
+  i->cur_subkey = pos->cur_subkey;
+  i->cur_value = 0;
+  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)
+{
+  REGF_NK_REC* subkey;
+  bool found = false;
+  uint32 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 = (REGF_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_key_free(subkey);
+      subkey = (REGF_NK_REC*)regfi_iterator_next_subkey(i);
+    }
+  }
+
+  if(found == false)
+  {
+    i->cur_subkey = old_subkey;
+    return false;
+  }
+
+  regfi_key_free(subkey);
+  return true;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
+{
+  uint32 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 REGF_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
+{
+  return i->cur_key;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGF_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
+{
+  REGF_SK_REC* ret_val;
+  REGF_HBIN* hbin;
+  uint32 max_length, off;
+
+  if(i->cur_key == NULL)
+    return NULL;
+  
+  /* First look if we have already parsed it */
+  if((i->cur_key->sk_off!=REGF_OFFSET_NONE)
+     && !(ret_val =(REGF_SK_REC*)lru_cache_find(i->sk_recs, 
+						&i->cur_key->sk_off, 4)))
+  {
+    hbin = regfi_lookup_hbin(i->f, i->cur_key->sk_off);
+
+    if(hbin == NULL)
+      return NULL;
+
+    off = i->cur_key->sk_off + REGF_BLOCKSIZE;
+    max_length = hbin->block_size + hbin->file_off - off;
+    ret_val = regfi_parse_sk(i->f, off, max_length, true);
+    if(ret_val == NULL)
+      return NULL;
+
+    ret_val->sk_off = i->cur_key->sk_off;
+    lru_cache_update(i->sk_recs, &i->cur_key->sk_off, 4, ret_val);
+  }
+
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGF_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
+{
+  i->cur_subkey = 0;
+  return regfi_iterator_cur_subkey(i);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGF_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
+{
+  uint32 nk_offset;
+
+  /* see if there is anything left to report */
+  if (!(i->cur_key) || (i->cur_key->subkeys_off==REGF_OFFSET_NONE)
+      || (i->cur_subkey >= i->cur_key->num_subkeys))
+    return NULL;
+
+  nk_offset = i->cur_key->subkeys->hashes[i->cur_subkey].nk_off;
+  
+  return regfi_load_key(i->f, nk_offset+REGF_BLOCKSIZE, true);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+/* XXX: some way of indicating reason for failure should be added. */
+const REGF_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
+{
+  const REGF_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)
+{
+  const REGF_VK_REC* cur;
+  bool found = false;
+
+  /* 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
+      cur = regfi_iterator_next_value(i);
+  }
+
+  return found;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGF_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
+{
+  i->cur_value = 0;
+  return regfi_iterator_cur_value(i);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGF_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
+{
+  REGF_VK_REC* ret_val = NULL;
+  if(i->cur_value < i->cur_key->num_values)
+    ret_val = i->cur_key->values[i->cur_value];
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGF_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
+{
+  const REGF_VK_REC* ret_val;
+
+  i->cur_value++;
+  ret_val = regfi_iterator_cur_value(i);
+  if(ret_val == NULL)
+    i->cur_value--;
+
+  return ret_val;
+}
+
+
+
+/*******************************************************************
+ * Computes the checksum of the registry file header.
+ * buffer must be at least the size of an regf header (4096 bytes).
+ *******************************************************************/
+static uint32 regfi_compute_header_checksum(uint8* buffer)
+{
+  uint32 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.
+ *******************************************************************/
+REGF_FILE* regfi_parse_regf(int fd, bool strict)
+{
+  uint8 file_header[REGF_BLOCKSIZE];
+  uint32 length;
+  uint32 file_length;
+  struct stat sbuf;
+  REGF_FILE* ret_val;
+
+  /* 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 < REGF_BLOCKSIZE+REGF_ALLOC_BLOCK)
+    return NULL;
+
+  ret_val = (REGF_FILE*)zalloc(sizeof(REGF_FILE));
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->fd = fd;
+  ret_val->file_length = file_length;
+
+  length = REGF_BLOCKSIZE;
+  if((regfi_read(fd, file_header, &length)) != 0 
+     || length != REGF_BLOCKSIZE)
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  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))
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  memcpy(ret_val->magic, file_header, 4);
+  if(strict && (memcmp(ret_val->magic, "regf", 4) != 0))
+  {
+    free(ret_val);
+    return NULL;
+  }
+  
+  ret_val->unknown1 = IVAL(file_header, 0x4);
+  ret_val->unknown2 = IVAL(file_header, 0x8);
+
+  ret_val->mtime.low = IVAL(file_header, 0xC);
+  ret_val->mtime.high = IVAL(file_header, 0x10);
+
+  ret_val->unknown3 = IVAL(file_header, 0x14);
+  ret_val->unknown4 = IVAL(file_header, 0x18);
+  ret_val->unknown5 = IVAL(file_header, 0x1C);
+  ret_val->unknown6 = IVAL(file_header, 0x20);
+  
+  ret_val->data_offset = IVAL(file_header, 0x24);
+  ret_val->last_block = IVAL(file_header, 0x28);
+
+  ret_val->unknown7 = IVAL(file_header, 0x2C);
+
+  return ret_val;
+}
+
+
+
+/*******************************************************************
+ * Given real file offset, read and parse the hbin at that location
+ * along with it's associated cells.
+ *******************************************************************/
+/* XXX: Need a way to return types of errors.
+ */
+REGF_HBIN* regfi_parse_hbin(REGF_FILE* file, uint32 offset, bool strict)
+{
+  REGF_HBIN *hbin;
+  uint8 hbin_header[HBIN_HEADER_REC_SIZE];
+  uint32 length;
+  
+  if(offset >= file->file_length)
+    return NULL;
+
+  if(lseek(file->fd, offset, SEEK_SET) == -1)
+    return NULL;
+
+  length = HBIN_HEADER_REC_SIZE;
+  if((regfi_read(file->fd, hbin_header, &length) != 0) 
+     || length != HBIN_HEADER_REC_SIZE)
+    return NULL;
+
+
+  if(lseek(file->fd, offset, SEEK_SET) == -1)
+    return NULL;
+
+  if(!(hbin = (REGF_HBIN*)zalloc(sizeof(REGF_HBIN)))) 
+    return NULL;
+  hbin->file_off = offset;
+
+  memcpy(hbin->magic, hbin_header, 4);
+  if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
+  {
+    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)
+  {
+    free(hbin);
+    return NULL;
+  }
+
+  return hbin;
+}
+
+
+
+REGF_NK_REC* regfi_parse_nk(REGF_FILE* file, uint32 offset, 
+			    uint32 max_size, bool strict)
+{
+  uint8 nk_header[REGFI_NK_MIN_LENGTH];
+  REGF_NK_REC* ret_val;
+  uint32 length;
+  uint32 cell_length;
+  bool unalloc = false;
+
+  if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
+		       &cell_length, &unalloc))
+     return NULL;
+ 
+  /* A bit of validation before bothering to allocate memory */
+  if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
+  {
+    /* XXX: Deal with subkey-lists that reference other subkey-lists
+     *      (e.g. 'ri' records). 
+     */
+    return NULL;
+  }
+
+  ret_val = (REGF_NK_REC*)zalloc(sizeof(REGF_NK_REC));
+  if(ret_val == NULL)
+    return 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 != (ret_val->cell_size & 0xFFFFFFF8)))
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  ret_val->magic[0] = nk_header[0x0];
+  ret_val->magic[1] = nk_header[0x1];
+  ret_val->key_type = SVAL(nk_header, 0x2);
+  if((ret_val->key_type != NK_TYPE_NORMALKEY)
+     && (ret_val->key_type != NK_TYPE_ROOTKEY) 
+     && (ret_val->key_type != NK_TYPE_LINKKEY)
+     && (ret_val->key_type != NK_TYPE_UNKNOWN1))
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  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);
+  /* XXX: currently we do nothing with class names.  Need to investigate. */
+  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);
+
+  if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
+  {
+    if(strict)
+    {
+      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 = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
+  if(ret_val->keyname == NULL)
+  {
+    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*)ret_val->keyname, &length) != 0)
+     || length != ret_val->name_length)
+  {
+    free(ret_val->keyname);
+    free(ret_val);
+    return NULL;
+  }
+  ret_val->keyname[ret_val->name_length] = '\0';
+
+  return ret_val;
+}
+
+
+
+/*******************************************************************
+ *******************************************************************/
+REGF_VK_REC* regfi_parse_vk(REGF_FILE* file, uint32 offset, 
+			    uint32 max_size, bool strict)
+{
+  REGF_VK_REC* ret_val;
+  uint8 vk_header[REGFI_VK_MIN_LENGTH];
+  uint32 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))
+    return NULL;
+
+  ret_val = (REGF_VK_REC*)zalloc(sizeof(REGF_VK_REC));
+  if(ret_val == NULL)
+    return 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_VK_MIN_LENGTH) 
+     || ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8))
+  {
+    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'))
+  {
+    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 & ~VK_DATA_IN_OFFSET;
+  ret_val->data_in_offset = (bool)(raw_data_size & VK_DATA_IN_OFFSET);
+  ret_val->data_off = IVAL(vk_header, 0x8);
+  ret_val->type = IVAL(vk_header, 0xC);
+  ret_val->flag = SVAL(vk_header, 0x10);
+  ret_val->unknown1 = SVAL(vk_header, 0x12);
+
+  if(ret_val->flag & VK_FLAG_NAME_PRESENT)
+  {
+    if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
+    {
+      if(strict)
+      {
+	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 = (char*)zalloc(sizeof(char)*(ret_val->name_length+1));
+    if(ret_val->valuename == NULL)
+    {
+      free(ret_val);
+      return NULL;
+    }
+
+    length = ret_val->name_length;
+    if((regfi_read(file->fd, (uint8*)ret_val->valuename, &length) != 0)
+       || length != ret_val->name_length)
+    {
+      free(ret_val->valuename);
+      free(ret_val);
+      return NULL;
+    }
+    ret_val->valuename[ret_val->name_length] = '\0';
+  }
+  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;
+  }
+
+  if(ret_val->data_size == 0)
+    ret_val->data = NULL;
+  else
+  {
+    ret_val->data = regfi_parse_data(file, ret_val->data_off+REGF_BLOCKSIZE,
+				     raw_data_size, strict);
+    if(strict && (ret_val->data == NULL))
+    {
+      free(ret_val->valuename);
+      free(ret_val);
+      return NULL;
+    }
+  }
+
+  return ret_val;
+}
+
+
+uint8* regfi_parse_data(REGF_FILE* file, uint32 offset, uint32 length, bool strict)
+{
+  uint8* ret_val;
+  uint32 read_length, cell_length;
+  uint8 i;
+  bool unalloc;
+
+  /* The data is stored in the offset if the size <= 4 */
+  if (length & VK_DATA_IN_OFFSET)
+  {
+    length = length & ~VK_DATA_IN_OFFSET;
+    if(length > 4)
+      return NULL;
+
+    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
+      return NULL;
+
+    offset = offset - REGF_BLOCKSIZE;
+    for(i = 0; i < length; i++)
+      ret_val[i] = (uint8)((offset >> i*8) & 0xFF);
+  }
+  else
+  {
+    if(!regfi_parse_cell(file->fd, offset, NULL, 0,
+			 &cell_length, &unalloc))
+      return NULL;
+
+    if((cell_length & 0xFFFFFFF8) != cell_length)
+      return NULL;
+
+    if(cell_length - 4 < length)
+    {
+      /* XXX: This strict condition has been triggered in multiple registries.
+       *      Not sure the cause, but the data length values are very large,
+       *      such as 53392.
+       */
+      if(strict)
+	return NULL;
+      else
+	length = cell_length - 4;
+    }
+
+    /* XXX: There is currently no check to ensure the data 
+     *      cell doesn't cross HBIN boundary.
+     */
+
+    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
+      return NULL;
+
+    read_length = length;
+    if((regfi_read(file->fd, ret_val, &read_length) != 0) 
+       || read_length != length)
+    {
+      free(ret_val);
+      return NULL;
+    }
+  }
+
+  return ret_val;
+}
+
+
+range_list* regfi_parse_unalloc_cells(REGF_FILE* file)
+{
+  range_list* ret_val;
+  REGF_HBIN* hbin;
+  const range_list_element* hbins_elem;
+  uint32 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 = (REGF_HBIN*)hbins_elem->data;
+
+    curr_off = HBIN_HEADER_REC_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 & 0xFFFFFFF8) != cell_len))
+	/* XXX: should report an error here. */
+	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;
+}
Index: releases/0.9.0/lib/smb_deps.c
===================================================================
--- releases/0.9.0/lib/smb_deps.c	(revision 123)
+++ releases/0.9.0/lib/smb_deps.c	(revision 123)
@@ -0,0 +1,1273 @@
+/*
+ * This file contains miscellaneous pieces of code which regfio.c
+ * depends upon, from the Samba Subversion tree.  See:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * Copyright (C) 2005-2006 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$
+ */
+
+#include "../include/smb_deps.h"
+
+
+/* These act as replacements for numerous Samba memory allocation
+ *   functions. 
+ */
+void* zalloc(size_t size)
+{
+  void* ret_val = NULL;
+  if((ret_val = (void*)malloc(size)) != NULL)
+    memset(ret_val, 0, size);
+  return ret_val;
+}
+
+void* zcalloc(size_t size, unsigned int count)
+{
+  return zalloc(size*count);
+}
+
+/* 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 unix_to_nt_time(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)(d * (1.0/(4.0*(double)(1<<30))));
+  nt->low  = (uint32)(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 nt_time_to_unix(const 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 */
+
+/* From parse_prs.c */
+
+/*******************************************************************
+ Attempt, if needed, to grow a data buffer.
+ Also depends on the data stream mode (io).
+ ********************************************************************/
+bool prs_grow(prs_struct *ps, uint32 extra_space)
+{
+  uint32 new_size;
+  char *new_data;
+  
+  ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
+  
+  if(ps->data_offset + extra_space <= ps->buffer_size)
+    return true;
+  
+  /*
+   * We cannot grow the buffer if we're not reading
+   * into the prs_struct, or if we don't own the memory.
+   */
+  
+  if(ps->io || !ps->is_dynamic)
+    return false;
+  
+  /*
+   * Decide how much extra space we really need.
+   */
+  extra_space -= (ps->buffer_size - ps->data_offset);
+  if(ps->buffer_size == 0) 
+  {
+    /*
+     * Ensure we have at least a PDU's length, or extra_space, 
+     * whichever is greater.
+     */  
+    new_size = MAX(MAX_PDU_FRAG_LEN,extra_space);
+    
+    if((new_data = zalloc(new_size)) == NULL)
+      return false;
+  } 
+  else 
+  {
+    /*
+     * If the current buffer size is bigger than the space needed, just 
+     * double it, else add extra_space.
+     */
+    new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);		
+    
+    if ((new_data = (char*)realloc(ps->data_p, new_size)) == NULL)
+      return false;
+    
+    memset(&new_data[ps->buffer_size], '\0', 
+	   (size_t)(new_size - ps->buffer_size));
+  }
+  ps->buffer_size = new_size;
+  ps->data_p = new_data;
+  
+  return true;
+}
+
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+bool prs_align(prs_struct *ps)
+{
+  uint32 mod = ps->data_offset & (ps->align-1);
+  
+  if (ps->align != 0 && mod != 0) 
+  {
+    uint32 extra_space = (ps->align - mod);
+    if(!prs_grow(ps, extra_space))
+      return false;
+    memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+    ps->data_offset += extra_space;
+  }
+  
+  return true;
+}
+
+
+/**
+ * Initialise an expandable parse structure.
+ *
+ * @param size Initial buffer size.  If >0, a new buffer will be
+ * created with malloc().
+ *
+ * @return false if allocation fails, otherwise true.
+ **/
+
+bool prs_init(prs_struct *ps, uint32 size, void *ctx, bool io)
+{
+  if(ps == NULL)
+    return false;
+  memset(ps, 0, sizeof(prs_struct));
+
+  ps->io = io;
+  ps->bigendian_data = RPC_LITTLE_ENDIAN;
+  ps->align = RPC_PARSE_ALIGN;
+  ps->is_dynamic = false;
+  ps->data_offset = 0;
+  ps->buffer_size = 0;
+  ps->data_p = NULL;
+  ps->mem_ctx = ctx;
+  
+  if (size != 0) 
+  {
+    ps->buffer_size = size;
+    if((ps->data_p = (char *)zalloc((size_t)size)) == NULL)
+      return false;
+
+    ps->is_dynamic = true; /* We own this memory. */
+  }
+  
+  return true;
+}
+
+
+char *prs_mem_get(prs_struct *ps, uint32 extra_size)
+{
+  if(ps->io) 
+  {
+    /*
+     * If reading, ensure that we can read the requested size item.
+     */
+    if (ps->data_offset + extra_size > ps->buffer_size)
+      return NULL;
+  } 
+  else 
+  {
+    /*
+     * Writing - grow the buffer if needed.
+     */
+    if(!prs_grow(ps, extra_size))
+      return NULL;
+  }
+
+  return &ps->data_p[ps->data_offset];
+}
+
+
+/*******************************************************************
+ Stream a uint32.
+ ********************************************************************/
+bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32)
+{
+  char *q = prs_mem_get(ps, sizeof(uint32));
+  if (q == NULL)
+    return false;
+  
+  if (ps->io) 
+  {
+    if (ps->bigendian_data)
+      *data32 = RIVAL(q,0);
+    else
+      *data32 = IVAL(q,0);
+  } 
+  else 
+  {
+    if (ps->bigendian_data)
+      RSIVAL(q,0,*data32);
+    else
+      SIVAL(q,0,*data32);
+  }
+  ps->data_offset += sizeof(uint32);
+  
+  return true;
+}
+
+
+/******************************************************************
+ Stream an array of uint32s. Length is number of uint32s.
+ ********************************************************************/
+bool prs_uint32s(const char *name, prs_struct *ps, 
+		 int depth, uint32 *data32s, int len)
+{
+  int i;
+  char *q = prs_mem_get(ps, len * sizeof(uint32));
+  if (q == NULL)
+    return false;
+  
+  if (ps->io) 
+  {
+    if (ps->bigendian_data) 
+    {
+      for (i = 0; i < len; i++)
+	data32s[i] = RIVAL(q, 4*i);
+    } 
+    else 
+    {
+      for (i = 0; i < len; i++)
+	data32s[i] = IVAL(q, 4*i);
+    }
+  } 
+  else 
+  {
+    if (ps->bigendian_data) 
+    {
+      for (i = 0; i < len; i++)
+	RSIVAL(q, 4*i, data32s[i]);
+    } 
+    else 
+    {
+      for (i = 0; i < len; i++)
+	SIVAL(q, 4*i, data32s[i]);
+    }
+  }
+  ps->data_offset += (len * sizeof(uint32));
+  
+  return true;
+}
+
+
+/*******************************************************************
+ Stream a uint16.
+ ********************************************************************/
+bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16)
+{
+  char *q = prs_mem_get(ps, sizeof(uint16));
+  if (q == NULL)
+    return false;
+  
+  if (ps->io) 
+  {
+    if (ps->bigendian_data)
+      *data16 = RSVAL(q,0);
+    else
+      *data16 = SVAL(q,0);
+  } 
+  else 
+  {
+    if (ps->bigendian_data)
+      RSSVAL(q,0,*data16);
+    else
+      SSVAL(q,0,*data16);
+  }
+  ps->data_offset += sizeof(uint16);
+  
+  return true;
+}
+
+
+/*******************************************************************
+ prs_uint16 wrapper. Call this and it sets up a pointer to where the
+ uint16 should be stored, or gets the size if reading.
+ ********************************************************************/
+bool prs_uint16_pre(const char *name, prs_struct *ps, int depth, 
+		    uint16 *data16, uint32 *offset)
+{
+  *offset = ps->data_offset;
+  if (ps->io) 
+  {
+    /* reading. */
+    return prs_uint16(name, ps, depth, data16);
+  } 
+  else 
+  {
+    char *q = prs_mem_get(ps, sizeof(uint16));
+    if(q ==NULL)
+      return false;
+    ps->data_offset += sizeof(uint16);
+  }
+  return true;
+}
+
+
+/*******************************************************************
+ prs_uint16 wrapper.  call this and it retrospectively stores the size.
+ does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+bool prs_uint16_post(const char *name, prs_struct *ps, int depth, 
+		     uint16 *data16, uint32 ptr_uint16, uint32 start_offset)
+{
+  if (!ps->io) 
+  {
+    /* 
+     * Writing - temporarily move the offset pointer.
+     */
+    uint16 data_size = ps->data_offset - start_offset;
+    uint32 old_offset = ps->data_offset;
+    
+    ps->data_offset = ptr_uint16;
+    if(!prs_uint16(name, ps, depth, &data_size)) 
+    {
+      ps->data_offset = old_offset;
+      return false;
+    }
+    ps->data_offset = old_offset;
+  } 
+  else 
+    ps->data_offset = start_offset + (uint32)(*data16);
+
+  return true;
+}
+
+
+/*******************************************************************
+ Stream a uint8.
+ ********************************************************************/
+bool prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8)
+{
+  char *q = prs_mem_get(ps, 1);
+  if (q == NULL)
+    return false;
+  
+  if (ps->io)
+    *data8 = CVAL(q,0);
+  else
+    SCVAL(q,0,*data8);
+  
+  ps->data_offset += 1;
+  
+  return true;
+}
+
+
+/******************************************************************
+ Stream an array of uint8s. Length is number of uint8s.
+ ********************************************************************/
+bool prs_uint8s(const char *name, prs_struct *ps, int depth, 
+		uint8* data8s, int len)
+{
+  int i;
+  char *q = prs_mem_get(ps, len);
+  if (q == NULL)
+    return false;
+  
+  if (ps->io) 
+  {
+    for (i = 0; i < len; i++)
+      data8s[i] = CVAL(q,i);
+  } 
+  else 
+  {
+    for (i = 0; i < len; i++)
+      SCVAL(q, i, data8s[i]);
+  }
+  
+  ps->data_offset += len;
+  
+  return true;
+}
+
+
+/*******************************************************************
+ Set the current offset (external interface).
+ ********************************************************************/
+bool prs_set_offset(prs_struct *ps, uint32 offset)
+{
+  if(offset <= ps->data_offset) 
+  {
+    ps->data_offset = offset;
+    return true;
+  }
+  
+  if(!prs_grow(ps, offset - ps->data_offset))
+    return false;
+  
+  ps->data_offset = offset;
+  return true;
+}
+
+/* End of stuff from parse_prs.c */
+
+/* From rpc_parse/parse_misc.c */
+
+/*******************************************************************
+ Reads or writes a struct uuid
+********************************************************************/
+bool smb_io_uuid(const char *desc, struct uuid *uuid, 
+		 prs_struct *ps, int depth)
+{
+  if (uuid == NULL)
+    return false;
+  depth++;
+  
+  if(!prs_uint32 ("data   ", ps, depth, &uuid->time_low))
+    return false;
+  if(!prs_uint16 ("data   ", ps, depth, &uuid->time_mid))
+    return false;
+  if(!prs_uint16 ("data   ", ps, depth, &uuid->time_hi_and_version))
+    return false;
+  
+  if(!prs_uint8s ("data   ", ps, depth, 
+		  uuid->clock_seq, sizeof(uuid->clock_seq)))
+    return false;
+
+  if(!prs_uint8s ("data   ", ps, depth, uuid->node, sizeof(uuid->node)))
+    return false;
+  
+  return true;
+}
+
+
+/*******************************************************************
+ Reads or writes an NTTIME structure.
+********************************************************************/
+bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth)
+{
+  if (nttime == NULL)
+    return false;
+  depth++;
+
+  if(!prs_align(ps))
+    return false;
+	
+  if(!prs_uint32("low ", ps, depth, &nttime->low)) /* low part */
+    return false;
+  if(!prs_uint32("high", ps, depth, &nttime->high)) /* high part */
+    return false;
+
+  return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a DOM_SID structure.
+********************************************************************/
+bool smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth)
+{
+  int i;
+
+  if (sid == NULL)
+    return false;
+  depth++;
+
+  if(!prs_uint8 ("sid_rev_num", ps, depth, &sid->sid_rev_num))
+    return false;
+
+  if(!prs_uint8 ("num_auths  ", ps, depth, &sid->num_auths))
+    return false;
+
+  for (i = 0; i < 6; i++)
+  {
+    fstring tmp;
+    snprintf(tmp, sizeof(tmp) - 1, "id_auth[%d] ", i);
+    if(!prs_uint8 (tmp, ps, depth, &sid->id_auth[i]))
+      return false;
+  }
+
+  /* oops! XXXX should really issue a warning here... */
+  if (sid->num_auths > MAXSUBAUTHS)
+    sid->num_auths = MAXSUBAUTHS;
+
+  if(!prs_uint32s("sub_auths ", ps, depth, 
+		  sid->sub_auths, sid->num_auths))
+  { return false; }
+
+  return true;
+}
+
+/* End of stuff from rpc_parse/parse_misc.c */
+
+/* From lib/util_sid.c */
+
+/*****************************************************************
+ Calculates size of a sid.
+*****************************************************************/  
+size_t sid_size(const DOM_SID *sid)
+{
+  if (sid == NULL)
+    return 0;
+
+  return sid->num_auths * sizeof(uint32) + 8;
+}
+
+
+/*****************************************************************
+ Compare the auth portion of two sids.
+*****************************************************************/  
+int sid_compare_auth(const DOM_SID *sid1, const 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 sid_compare(const DOM_SID *sid1, const 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 sid_compare_auth(sid1, sid2);
+}
+
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/  
+bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+  return sid_compare(sid1, sid2) == 0;
+}
+
+/* End of stuff from lib/util_sid.c */
+
+/* From lib/secace.c */
+
+/*******************************************************************
+ Check if ACE has OBJECT type.
+********************************************************************/
+
+bool sec_ace_object(uint8 type)
+{
+  if (type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+      type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+      type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+      type == SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) {
+    return true;
+  }
+  return false;
+}
+
+/* End of stuff from lib/secace.c */
+
+/* From rpc_parse/parse_sec.c */
+
+/*******************************************************************
+ Reads or writes a SEC_ACCESS structure.
+********************************************************************/
+bool sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth)
+{
+  if (t == NULL)
+    return false;
+
+  depth++;
+	
+  if(!prs_uint32("mask", ps, depth, &t->mask))
+    return false;
+
+  return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a SEC_ACE structure.
+********************************************************************/
+bool sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth)
+{
+  uint32 old_offset;
+  uint32 offset_ace_size;
+
+  if (psa == NULL)
+    return false;
+
+  depth++;
+	
+  old_offset = ps->data_offset;
+
+  if(!prs_uint8("type ", ps, depth, &psa->type))
+    return false;
+
+  if(!prs_uint8("flags", ps, depth, &psa->flags))
+    return false;
+
+  if(!prs_uint16_pre("size ", ps, depth, &psa->size, &offset_ace_size))
+    return false;
+
+  if(!sec_io_access("info ", &psa->info, ps, depth))
+    return false;
+
+  /* check whether object access is present */
+  if (!sec_ace_object(psa->type)) 
+  {
+    if (!smb_io_dom_sid("trustee  ", &psa->trustee , ps, depth))
+      return false;
+  } 
+  else 
+  {
+    if (!prs_uint32("obj_flags", ps, depth, &psa->obj_flags))
+      return false;
+
+    if (psa->obj_flags & SEC_ACE_OBJECT_PRESENT)
+      if (!smb_io_uuid("obj_guid", &psa->obj_guid, ps,depth))
+	return false;
+
+    if (psa->obj_flags & SEC_ACE_OBJECT_INHERITED_PRESENT)
+      if (!smb_io_uuid("inh_guid", &psa->inh_guid, ps,depth))
+	return false;
+
+    if(!smb_io_dom_sid("trustee  ", &psa->trustee , ps, depth))
+      return false;
+  }
+
+  /* Theorectically an ACE can have a size greater than the
+   * sum of its components. When marshalling, pad with extra null bytes 
+   * up to the
+   * correct size. 
+   */
+  if (!ps->io && (psa->size > ps->data_offset - old_offset)) 
+  {
+    uint32 extra_len = psa->size - (ps->data_offset - old_offset);
+    uint32 i;
+    uint8 c = 0;
+
+    for (i = 0; i < extra_len; i++) 
+    {
+      if (!prs_uint8("ace extra space", ps, depth, &c))
+	return false;
+    }
+  }
+
+  if(!prs_uint16_post("size ", ps, depth, &psa->size, 
+		      offset_ace_size, old_offset))
+  { return false; }
+
+  return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a SEC_ACL structure.  
+
+ First of the xx_io_xx functions that allocates its data structures
+ for you as it reads them.
+********************************************************************/
+bool sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth)
+{
+  unsigned int i;
+  uint32 old_offset;
+  uint32 offset_acl_size;
+  SEC_ACL* psa;
+
+  /*
+   * Note that the size is always a multiple of 4 bytes due to the
+   * nature of the data structure.  Therefore the prs_align() calls
+   * have been removed as they through us off when doing two-layer
+   * marshalling such as in the printing code (RPC_BUFFER).  --jerry
+   */
+
+  if (ppsa == NULL || ps == NULL)
+    return false;
+
+  psa = *ppsa;
+
+  if(ps->io && psa == NULL) 
+  {
+    /*
+     * This is a read and we must allocate the stuct to read into.
+     */
+    if((psa = (SEC_ACL*)zalloc(sizeof(SEC_ACL))) == NULL)
+      return false;
+    *ppsa = psa;
+  }
+
+  depth++;	
+  old_offset = ps->data_offset;
+
+  if(!prs_uint16("revision", ps, depth, &psa->revision)
+     || !prs_uint16_pre("size     ", ps, depth, &psa->size, &offset_acl_size)
+     || !prs_uint32("num_aces ", ps, depth, &psa->num_aces))
+  {
+    free(psa);
+    *ppsa = NULL;
+    return false;
+  }
+
+  if (ps->io) 
+  {
+    /*
+     * 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((psa->ace = (SEC_ACE*)zcalloc(sizeof(SEC_ACE), psa->num_aces+1)) == NULL)
+    {
+      free(psa);
+      *ppsa = NULL;
+      return false;
+    }
+  }
+
+  for (i = 0; i < psa->num_aces; i++) 
+  {
+    fstring tmp;
+    snprintf(tmp, sizeof(tmp)-1, "ace_list[%02d]: ", i);
+    if(!sec_io_ace(tmp, &psa->ace[i], ps, depth))
+    {
+      free(psa);
+      *ppsa = NULL;
+      return false;
+    }
+  }
+
+  /* Theoretically an ACL can have a size greater than the
+   *  sum of its components. When marshalling, pad with extra null 
+   *  bytes up to the
+   *  correct size. 
+   */
+  if (!ps->io && (psa->size > ps->data_offset - old_offset)) 
+  {
+    uint32 extra_len = psa->size - (ps->data_offset - old_offset);
+    uint8 c = 0;
+
+    for (i = 0; i < extra_len; i++) 
+    {
+      if (!prs_uint8("acl extra space", ps, depth, &c))
+      {
+	free(psa);
+	*ppsa = NULL;
+	return false;
+      }
+    }
+  }
+
+  if(!prs_uint16_post("size     ", ps, depth, &psa->size, 
+		      offset_acl_size, old_offset))
+  { 
+    free(psa);
+    *ppsa = NULL;
+    return false; 
+  }
+
+  return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a SEC_DESC structure.
+ If reading and the *ppsd = NULL, allocates the structure.
+********************************************************************/
+bool sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth)
+{
+  uint32 old_offset;
+  uint32 max_offset = 0; /* after we're done, move offset to end */
+  uint32 tmp_offset = 0;
+
+  SEC_DESC *psd;
+
+  if (ppsd == NULL || ps == NULL)
+    return false;
+
+  psd = *ppsd;
+  if (psd == NULL) 
+  {
+    if(ps->io) 
+    {
+      if((psd = (SEC_DESC*)zalloc(sizeof(SEC_DESC))) == NULL)
+	return false;
+      *ppsd = psd;
+    } 
+    else 
+    {
+      /* Marshalling - just ignore. */
+      return true;
+    }
+  }
+
+  depth++;
+
+  /* start of security descriptor stored for back-calc offset purposes */
+  old_offset = ps->data_offset;
+
+  if(!prs_uint16("revision ", ps, depth, &psd->revision)
+     || !prs_uint16("type     ", ps, depth, &psd->type))
+  {
+    free(psd);
+    *ppsd = NULL;
+    return false;
+  }
+
+  if (!ps->io)
+  {
+    uint32 offset = SEC_DESC_HEADER_SIZE;
+
+    /*
+     * Work out the offsets here, as we write it out.
+     */
+
+    if (psd->sacl != NULL) 
+    {
+      psd->off_sacl = offset;
+      offset += psd->sacl->size;
+    } 
+    else
+      psd->off_sacl = 0;
+
+    if (psd->dacl != NULL) 
+    {
+      psd->off_dacl = offset;
+      offset += psd->dacl->size;
+    } 
+    else 
+      psd->off_dacl = 0;
+
+    if (psd->owner_sid != NULL) 
+    {
+      psd->off_owner_sid = offset;
+      offset += sid_size(psd->owner_sid);
+    } 
+    else
+      psd->off_owner_sid = 0;
+
+    if (psd->grp_sid != NULL) 
+    {
+      psd->off_grp_sid = offset;
+      offset += sid_size(psd->grp_sid);
+    } 
+    else
+      psd->off_grp_sid = 0;
+  }
+
+  if(!prs_uint32("off_owner_sid", ps, depth, &psd->off_owner_sid)
+     || !prs_uint32("off_grp_sid  ", ps, depth, &psd->off_grp_sid)
+     || !prs_uint32("off_sacl     ", ps, depth, &psd->off_sacl)
+     || !prs_uint32("off_dacl     ", ps, depth, &psd->off_dacl))
+  {
+    free(psd);
+    *ppsd = NULL;    
+    return false;
+  }
+  max_offset = MAX(max_offset, ps->data_offset);
+
+  if (psd->off_owner_sid != 0) 
+  {
+    tmp_offset = ps->data_offset;
+    if(!prs_set_offset(ps, old_offset + psd->off_owner_sid))
+    {
+      free(psd);
+      *ppsd = NULL;
+      return false;
+    }
+
+    if (ps->io) 
+    {
+      /* reading */
+      if((psd->owner_sid = (DOM_SID*)zalloc(sizeof(DOM_SID))) == NULL)
+      {
+	free(psd);
+	*ppsd = NULL;
+	return false;
+      }
+    }
+
+    if(!smb_io_dom_sid("owner_sid ", psd->owner_sid , ps, depth))
+    {
+      if(ps->io)
+	free(psd->owner_sid);
+      free(psd);
+      *ppsd = NULL;
+      return false;
+    }
+
+    max_offset = MAX(max_offset, ps->data_offset);
+
+    if (!prs_set_offset(ps,tmp_offset))
+    {
+      if(ps->io)
+	free(psd->owner_sid);
+      free(psd);
+      *ppsd = NULL;
+      return false;
+    }
+  }
+
+  if (psd->off_grp_sid != 0) 
+  {
+    tmp_offset = ps->data_offset;
+    if(!prs_set_offset(ps, old_offset + psd->off_grp_sid))
+    {
+      if(ps->io)
+	free(psd->owner_sid);
+      free(psd);
+      *ppsd = NULL;
+      return false;
+    }
+
+    if (ps->io) 
+    {
+      /* reading */
+      if((psd->grp_sid = (DOM_SID*)zalloc(sizeof(DOM_SID))) == NULL)
+      {
+	free(psd->owner_sid);
+	free(psd);
+	*ppsd = NULL;
+	return false;
+      }
+    }
+
+    if(!smb_io_dom_sid("grp_sid", psd->grp_sid, ps, depth))
+    {
+      if(ps->io)
+      {
+	free(psd->grp_sid);
+	free(psd->owner_sid);
+      }
+      free(psd);
+      *ppsd = NULL;
+      return false;
+    }
+			
+    max_offset = MAX(max_offset, ps->data_offset);
+
+    if (!prs_set_offset(ps,tmp_offset))
+    {
+      if(ps->io)
+      {
+	free(psd->grp_sid);
+	free(psd->owner_sid);
+      }
+      free(psd);
+      *ppsd = NULL;
+      return false;
+    }
+  }
+
+  if ((psd->type & SEC_DESC_SACL_PRESENT) && psd->off_sacl) 
+  {
+    tmp_offset = ps->data_offset;
+    if(!prs_set_offset(ps, old_offset + psd->off_sacl)
+       || !sec_io_acl("sacl", &psd->sacl, ps, depth))
+    {
+      if(ps->io)
+      {
+	free(psd->grp_sid);
+	free(psd->owner_sid);
+      }
+      free(psd);
+      *ppsd = NULL;
+      return false;
+    }
+    max_offset = MAX(max_offset, ps->data_offset);
+    if (!prs_set_offset(ps,tmp_offset))
+    {
+      if(ps->io)
+      {
+	free(psd->grp_sid);
+	free(psd->owner_sid);
+      }
+      free(psd);
+      *ppsd = NULL;
+      return false;
+    }
+  }
+
+  if ((psd->type & SEC_DESC_DACL_PRESENT) && psd->off_dacl != 0) 
+  {
+    tmp_offset = ps->data_offset;
+    if(!prs_set_offset(ps, old_offset + psd->off_dacl)
+       || !sec_io_acl("dacl", &psd->dacl, ps, depth))
+    {
+      if(ps->io)
+      {
+	free(psd->grp_sid);
+	free(psd->owner_sid);
+      }
+      free(psd);
+      *ppsd = NULL;
+      return false;
+    }
+    max_offset = MAX(max_offset, ps->data_offset);
+    if (!prs_set_offset(ps,tmp_offset))
+    {
+      if(ps->io)
+      {
+	free(psd->grp_sid);
+	free(psd->owner_sid);
+      }
+      free(psd);
+      *ppsd = NULL;
+      return false;
+    }
+  }
+
+  if(!prs_set_offset(ps, max_offset))
+  {
+      if(ps->io)
+      {
+	free(psd->grp_sid);
+	free(psd->owner_sid);
+      }
+      free(psd);
+      *ppsd = NULL;
+      return false;
+  }
+
+  return true;
+}
+
+/* End of stuff from rpc_parse/parse_sec.c */
+
+/* From lib/secace.c */
+
+/*******************************************************************
+ Compares two SEC_ACE structures
+********************************************************************/
+bool sec_ace_equal(SEC_ACE *s1, SEC_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->info.mask != s2->info.mask) 
+  { return false; }
+
+  /* Check SID */
+  if (!sid_equal(&s1->trustee, &s2->trustee))
+    return false;
+
+  return true;
+}
+
+/* End of stuff from lib/secace.c */
+
+/* From lib/secacl.c */
+
+/*******************************************************************
+ Compares two SEC_ACL structures
+********************************************************************/
+
+bool sec_acl_equal(SEC_ACL *s1, SEC_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 (sec_ace_equal(&s1->ace[i], &s2->ace[j])) 
+      {
+	found = true;
+	break;
+      }
+    }
+
+    if (!found)
+      return false;
+  }
+
+  return true;
+}
+
+/* End of stuff from lib/secacl.c */
+
+/* From lib/secdesc.c */
+
+/*******************************************************************
+ Compares two SEC_DESC structures
+********************************************************************/
+bool sec_desc_equal(SEC_DESC *s1, SEC_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->type!= s2->type)
+    return false;
+
+  /* Check owner and group */
+  if (!sid_equal(s1->owner_sid, s2->owner_sid))
+    return false;
+
+  if (!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(!sec_acl_equal(s1->dacl, s2->dacl) || !sec_acl_equal(s1->sacl, s2->sacl)) 
+    return false;
+
+  return true;
+}
+
+/* End of stuff from lib/secdesc.c */
Index: releases/0.9.0/lib/void_stack.c
===================================================================
--- releases/0.9.0/lib/void_stack.c	(revision 123)
+++ releases/0.9.0/lib/void_stack.c	(revision 123)
@@ -0,0 +1,180 @@
+/*
+ * This is a really simple implementation of a stack which stores chunks
+ * of memory of any type.  It still needs work to eliminate memory
+ * leaks. 
+ *
+ * Copyright (C) 2005,2007 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 "../include/void_stack.h"
+
+void_stack* void_stack_new(unsigned short max_size)
+{
+  void_stack* ret_val = (void_stack*)malloc(sizeof(void_stack));
+
+  if (ret_val != NULL)
+  {
+    memset(ret_val, 0, sizeof(*ret_val));
+    ret_val->elements = (void**)malloc(max_size*sizeof(void*));
+    if (ret_val->elements == NULL)
+    {
+      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)
+{
+  free(stack->elements);
+  free(stack);
+}
+
+
+void void_stack_free_deep(void_stack* stack)
+{
+  unsigned short i;
+  for(i=0; i < stack->top; i++)
+    free(stack->elements[i]);
+  free(stack->elements);
+  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 = (void_stack_iterator*)malloc(sizeof(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)
+{
+  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.9.0/src/Makefile
===================================================================
--- releases/0.9.0/src/Makefile	(revision 123)
+++ releases/0.9.0/src/Makefile	(revision 123)
@@ -0,0 +1,28 @@
+# $Id$
+
+################################################################################
+
+REGLOOKUP=$(BUILD_BIN)/reglookup
+REGLOOKUP_RECOVER=$(BUILD_BIN)/reglookup-recover
+OBJ=$(wildcard ../lib/*.o)
+FILES=$(REGLOOKUP) $(REGLOOKUP_RECOVER)
+
+all: $(FILES)
+
+$(REGLOOKUP): reglookup.o $(OBJ)
+	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup.o $(OBJ)
+
+$(REGLOOKUP_RECOVER): reglookup-recover.o $(OBJ)
+	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup-recover.o $(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.9.0/src/common.c
===================================================================
--- releases/0.9.0/src/common.c	(revision 123)
+++ releases/0.9.0/src/common.c	(revision 123)
@@ -0,0 +1,376 @@
+/*
+ * 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.9.0"
+
+
+void bailOut(int code, char* message)
+{
+  fprintf(stderr, message);
+  exit(code);
+}
+
+
+/* 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 = malloc(buf_len);
+  char* tmp_buf;
+
+  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 from UTF-16LE to ASCII.  Accepts a Unicode buffer, uni, and
+ * it's length, uni_max.  Writes ASCII to the buffer ascii, whose size
+ * is ascii_max.  Writes at most (ascii_max-1) bytes to ascii, and null
+ * terminates the string.  Returns the length of the string stored in
+ * ascii.  On error, returns a negative errno code.
+ */
+static int uni_to_ascii(unsigned char* uni, char* ascii, 
+			unsigned int uni_max, unsigned int ascii_max)
+{
+  char* inbuf = (char*)uni;
+  char* outbuf = ascii;
+  size_t in_len = (size_t)uni_max;
+  size_t out_len = (size_t)(ascii_max-1);
+  int ret;
+
+  /* Set up conversion descriptor. */
+  conv_desc = iconv_open("US-ASCII", "UTF-16LE");
+
+  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 strlen(ascii);
+}
+
+
+/*
+ * 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(unsigned char* datap, uint32 len, uint32 type, 
+			   char** error_msg)
+{
+  char* asciip;
+  char* ascii;
+  unsigned char* cur_str;
+  char* cur_ascii;
+  char* cur_quoted;
+  char* tmp_err;
+  const char* str_type;
+  uint32 i;
+  uint32 cur_str_len;
+  uint32 ascii_max, cur_str_max;
+  uint32 str_rem, cur_str_rem, alen;
+  int ret_err;
+  unsigned short num_nulls;
+
+  if(datap == NULL)
+  {
+    *error_msg = (char*)malloc(24);
+    if(*error_msg == NULL)
+      return NULL;
+    strcpy(*error_msg, "Data pointer was NULL.");
+    return NULL;
+  }
+  *error_msg = NULL;
+
+  switch (type) 
+  {
+  case REG_SZ:
+  case REG_EXPAND_SZ:
+    /* REG_LINK is a symbolic link, stored as a unicode string. */
+  case REG_LINK:
+    ascii_max = sizeof(char)*(len+1);
+    ascii = malloc(ascii_max);
+    if(ascii == NULL)
+      return NULL;
+    
+    /* Sometimes values have binary stored in them.  If the unicode
+     * conversion fails, just quote it raw.
+     */
+    ret_err = uni_to_ascii(datap, ascii, len, ascii_max);
+    if(ret_err < 0)
+    {
+      tmp_err = strerror(-ret_err);
+      str_type = regfi_type_val2str(type);
+      *error_msg = (char*)malloc(65+strlen(str_type)+strlen(tmp_err)+1);
+      if(*error_msg == NULL)
+      {
+	free(ascii);
+	return NULL;
+      }
+      sprintf(*error_msg, "Unicode conversion failed on %s field; "
+	       "printing as binary.  Error: %s", str_type, tmp_err);
+      
+      cur_quoted = quote_buffer(datap, len, common_special_chars);
+    }
+    else
+      cur_quoted = quote_string(ascii, common_special_chars);
+    free(ascii);
+    if(cur_quoted == NULL)
+    {
+      *error_msg = (char*)malloc(27+1);
+      if(*error_msg != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted.");
+    }
+    return cur_quoted;
+    break;
+
+  case REG_DWORD:
+    ascii_max = sizeof(char)*(8+2+1);
+    ascii = malloc(ascii_max);
+    if(ascii == NULL)
+      return NULL;
+
+    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
+	     datap[3], datap[2], datap[1], datap[0]);
+    return ascii;
+    break;
+
+  case REG_DWORD_BE:
+    ascii_max = sizeof(char)*(8+2+1);
+    ascii = malloc(ascii_max);
+    if(ascii == NULL)
+      return NULL;
+
+    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
+	     datap[0], datap[1], datap[2], datap[3]);
+    return ascii;
+    break;
+
+  case REG_QWORD:
+    ascii_max = sizeof(char)*(16+2+1);
+    ascii = malloc(ascii_max);
+    if(ascii == NULL)
+      return NULL;
+
+    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
+	     datap[7], datap[6], datap[5], datap[4],
+	     datap[3], datap[2], datap[1], datap[0]);
+    return ascii;
+    break;
+    
+
+  /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
+   *      redone with fewer malloc calls and better string concatenation.
+   *      Also, gives lame output when "\0\0" is the string.
+   */
+  case REG_MULTI_SZ:
+    ascii_max = sizeof(char)*(len*4+1);
+    cur_str_max = sizeof(char)*(len+1);
+    cur_str = malloc(cur_str_max);
+    cur_ascii = malloc(cur_str_max);
+    ascii = malloc(ascii_max);
+    if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
+      return NULL;
+
+    /* Reads until it reaches 4 consecutive NULLs, 
+     * which is two nulls in unicode, or until it reaches len, or until we
+     * run out of buffer.  The latter should never happen, but we shouldn't
+     * trust our file to have the right lengths/delimiters.
+     */
+    asciip = ascii;
+    num_nulls = 0;
+    str_rem = ascii_max;
+    cur_str_rem = cur_str_max;
+    cur_str_len = 0;
+
+    for(i=0; (i < len) && str_rem > 0; i++)
+    {
+      *(cur_str+cur_str_len) = *(datap+i);
+      if(*(cur_str+cur_str_len) == 0)
+	num_nulls++;
+      else
+	num_nulls = 0;
+      cur_str_len++;
+
+      if(num_nulls == 2)
+      {
+	ret_err = uni_to_ascii(cur_str, cur_ascii, cur_str_len-1, cur_str_max);
+	if(ret_err < 0)
+	{
+	  /* XXX: should every sub-field error be enumerated? */
+	  if(*error_msg == NULL)
+	  {
+	    tmp_err = strerror(-ret_err);
+	    *error_msg = (char*)malloc(90+strlen(tmp_err)+1);
+	    if(*error_msg == NULL)
+	    {
+	      free(cur_str);
+	      free(cur_ascii);
+	      free(ascii);
+	      return NULL;
+	    }
+	    sprintf(*error_msg, "Unicode conversion failed on at least one "
+		    "MULTI_SZ sub-field; printing as binary.  Error: %s",
+		    tmp_err);
+	  }
+	  cur_quoted = quote_buffer(cur_str, cur_str_len-1, 
+				    subfield_special_chars);
+	}
+	else
+	  cur_quoted = quote_string(cur_ascii, subfield_special_chars);
+
+	alen = snprintf(asciip, str_rem, "%s", cur_quoted);
+	asciip += alen;
+	str_rem -= alen;
+	free(cur_quoted);
+
+	if(*(datap+i+1) == 0 && *(datap+i+2) == 0)
+	  break;
+	else
+	{
+	  if(str_rem > 0)
+	  {
+	    asciip[0] = '|';
+	    asciip[1] = '\0';
+	    asciip++;
+	    str_rem--;
+	  }
+	  memset(cur_str, 0, cur_str_max);
+	  cur_str_len = 0;
+	  num_nulls = 0;
+	  /* To eliminate leading nulls in subsequent strings. */
+	  i++;
+	}
+      }
+    }
+    *asciip = 0;
+    free(cur_str);
+    free(cur_ascii);
+    return ascii;
+    break;
+
+  /* XXX: Dont know what to do with these yet, just print as binary... */
+  default:
+    /* XXX: It would be really nice if this message somehow included the
+     *      name of the current value we're having trouble with, since
+     *      stderr/stdout don't always sync nicely.
+     */
+    fprintf(stderr, "WARNING: Unrecognized registry data type (0x%.8X); quoting as binary.\n", type);
+    
+  case REG_NONE:
+  case REG_RESOURCE_LIST:
+  case REG_FULL_RESOURCE_DESCRIPTOR:
+  case REG_RESOURCE_REQUIREMENTS_LIST:
+
+  case REG_BINARY:
+    return quote_buffer(datap, len, common_special_chars);
+    break;
+  }
+
+  return NULL;
+}
Index: releases/0.9.0/src/reglookup-recover.c
===================================================================
--- releases/0.9.0/src/reglookup-recover.c	(revision 123)
+++ releases/0.9.0/src/reglookup-recover.c	(revision 123)
@@ -0,0 +1,864 @@
+/*
+ * This program attempts to recover deleted data structures in a registry hive.
+ *
+ * Copyright (C) 2008 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 <sysexits.h>
+
+#include "../include/regfi.h"
+#include "../include/range_list.h"
+#include "../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 offset, uint32 length)
+{
+  uint8* buf;
+  char* quoted_buf;
+  uint32 len;
+
+  if((lseek(fd, offset, SEEK_SET)) == -1)
+    return NULL;
+
+  buf = (uint8*)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;
+}
+
+
+void printKey(REGF_FILE* f, REGF_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 = nt_time_to_unix(&nk->mtime);
+  tmp_time_s = gmtime(tmp_time);
+  strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+
+  quoted_name = quote_string(nk->keyname, key_special_chars);
+  if (quoted_name == NULL)
+  {
+    quoted_name = malloc(1*sizeof(char));
+    if(quoted_name == NULL)
+      bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+    quoted_name[0] = '\0';
+
+    fprintf(stderr, "WARNING: 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);
+}
+
+
+void printValue(REGF_FILE* f, const REGF_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;
+  uint32 size = vk->data_size;
+
+  /* Microsoft's documentation indicates that "available memory" is 
+   * the limit on value sizes.  Annoying.  We limit it to 1M which 
+   * should rarely be exceeded, unless the file is corrupt or 
+   * malicious. For more info, see:
+   *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
+   */
+  /* XXX: Should probably do something different here for this tool.
+   *      Also, It would be really nice if this message somehow included the
+   *      name of the current value we're having trouble with, since
+   *      stderr/stdout don't always sync nicely.
+   */
+  if(size > VK_MAX_DATA_LENGTH)
+  {
+    fprintf(stderr, "WARNING: value data size %d larger than "
+	    "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
+    size = VK_MAX_DATA_LENGTH;
+  }
+  
+  quoted_name = quote_string(vk->valuename, key_special_chars);
+  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(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+    quoted_name[0] = '\0';
+  }
+
+  quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
+  if(quoted_value == NULL)
+  {
+    quoted_value = malloc(1*sizeof(char));
+    if(quoted_value == NULL)
+      bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+    quoted_value[0] = '\0';
+
+    if(conv_error == NULL)
+      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
+	      "Memory allocation failure likely.\n", prefix, quoted_name);
+    else if(print_verbose)
+      fprintf(stderr, "WARNING: 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, "VERBOSE: 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(REGF_FILE* f, REGF_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(REGF_FILE* f, uint32 offset)
+{
+  char* quoted_buf;
+  uint32 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.  Also, piecing the path 
+ *      together is slow and redundant.
+ */
+char* getParentPath(REGF_FILE* f, REGF_NK_REC* nk)
+{
+  void_stack* path_stack = void_stack_new(REGF_MAX_DEPTH);
+  REGF_HBIN* hbin;
+  REGF_NK_REC* cur_ancestor;
+  char* ret_val;
+  char* path_element;
+  char* tmp_str;
+  uint32 virt_offset, i, stack_size, ret_val_size, ret_val_left, element_size;
+  uint32 max_length;
+
+  /* The path_stack size limit should guarantee that we don't recurse forever. */
+  virt_offset = nk->parent_off;
+  while(virt_offset != REGF_OFFSET_NONE)
+  {  
+    hbin = regfi_lookup_hbin(f, virt_offset);
+    if(hbin == NULL)
+      virt_offset = REGF_OFFSET_NONE;
+    else
+    {
+      max_length = hbin->block_size + hbin->file_off 
+	- (virt_offset+REGF_BLOCKSIZE);
+      cur_ancestor = regfi_parse_nk(f, virt_offset+REGF_BLOCKSIZE, 
+				    max_length, true);
+      if(cur_ancestor == NULL)
+	virt_offset = REGF_OFFSET_NONE;
+      else
+      {
+	if(cur_ancestor->key_type == NK_TYPE_ROOTKEY)
+	  virt_offset = REGF_OFFSET_NONE;
+	else
+	  virt_offset = cur_ancestor->parent_off;
+	
+	path_element = quote_string(cur_ancestor->keyname, key_special_chars);
+	if(path_element == NULL || !void_stack_push(path_stack, path_element))
+	{
+	  free(cur_ancestor->keyname);
+	  free(cur_ancestor);
+	  void_stack_free_deep(path_stack);
+	  return NULL;
+	}
+
+	regfi_key_free(cur_ancestor);
+      }
+    }
+  }
+  
+  stack_size = void_stack_size(path_stack);
+  ret_val_size = 16*stack_size;
+  if(ret_val_size == 0)
+    ret_val_size = 1;
+  ret_val_left = ret_val_size;
+  ret_val = malloc(ret_val_size);
+  if(ret_val == NULL)
+  {
+    void_stack_free_deep(path_stack);
+    return NULL;
+  }
+  ret_val[0] = '\0';
+
+  for(i=0; i<stack_size; i++)
+  {
+    path_element = void_stack_pop(path_stack);
+    element_size = strlen(path_element);
+    if(ret_val_left < element_size+2)
+    {
+      ret_val_size += element_size+16;
+      ret_val_left += element_size+16;
+      tmp_str = (char*)realloc(ret_val, ret_val_size);
+      if(tmp_str == NULL)
+      {
+	free(ret_val);
+	void_stack_free_deep(path_stack);
+	return NULL;
+      }
+      ret_val = tmp_str;
+    }
+
+    ret_val_left -= snprintf(ret_val+ret_val_size-ret_val_left,ret_val_left, "/%s", path_element);
+    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 offset, uint32 length)
+{
+  int32 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;
+}
+
+
+/* NOTE: unalloc_keys should be an empty range_list. */
+int extractKeys(REGF_FILE* f, 
+		range_list* unalloc_cells, 
+		range_list* unalloc_keys)
+{
+  const range_list_element* cur_elem;
+  REGF_NK_REC* key;
+  uint32 i, j;
+
+  for(i=0; i < range_list_size(unalloc_cells); i++)
+  {
+    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);
+      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");
+	  return 20;
+	}
+	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))
+      return 30;
+  }
+
+  return 0;
+}
+
+
+int extractValueLists(REGF_FILE* f,
+		      range_list* unalloc_cells,
+		      range_list* unalloc_keys)
+{
+  REGF_NK_REC* nk;
+  REGF_HBIN* hbin;
+  const range_list_element* cur_elem;
+  uint32 i, j, num_keys, off, values_length, max_length;
+
+  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!=REGF_OFFSET_NONE))
+    {
+      hbin = regfi_lookup_hbin(f, nk->values_off);
+      
+      if(hbin != NULL)
+      {
+	off = nk->values_off + REGF_BLOCKSIZE;
+	max_length = hbin->block_size + hbin->file_off - off;
+	/* XXX: This is a hack.  We parse all value-lists, VK records,
+	 *      and data records without regard for current allocation status.  
+	 *      On the off chance that such a record correctly parsed but is 
+	 *      actually a reallocated structure used by something else, we 
+	 *      simply prune it after the fact.  Would be faster to check this
+	 *      up front somehow.
+	 */
+	nk->values = regfi_load_valuelist(f, off, nk->num_values, max_length,
+					  false);
+	values_length = (nk->num_values+1)*sizeof(uint32);
+	if(values_length != (values_length & 0xFFFFFFF8))
+	  values_length = (values_length & 0xFFFFFFF8) + 8;
+
+	if(nk->values != NULL)
+	{
+	  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. 
+	     */
+	    for(j=0; j<nk->num_values; j++)
+	    {
+	      if(nk->values[j] != NULL)
+	      {
+		if(nk->values[j]->data != NULL)
+		  free(nk->values[j]->data);
+		free(nk->values[j]);
+	      }
+	    }
+	    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->num_values; j++)
+	    {
+	      if(nk->values[j] != NULL)
+	      {
+		if(!range_list_has_range(unalloc_cells, nk->values[j]->offset, 
+					 nk->values[j]->cell_size))
+		{ /* We've parsed a value which isn't in the unallocated list,
+		   * so prune it.
+		   */
+		  if(nk->values[j]->data != NULL)
+		    free(nk->values[j]->data);
+		  free(nk->values[j]);
+		  nk->values[j] = NULL;
+		}
+		else
+		{
+		  /* A VK record was recovered.  Remove from unalloc_cells
+		   * and inspect data.
+		   */
+		  if(!removeRange(unalloc_cells, nk->values[j]->offset,
+				  nk->values[j]->cell_size))
+		    return 21;
+
+		  /* Don't bother pruning or removing from unalloc_cells if 
+		   * there is no data, or it is stored in the offset.
+		   */
+		  if(nk->values[j]->data != NULL && !nk->values[j]->data_in_offset)
+		  {
+		    off = nk->values[j]->data_off+REGF_BLOCKSIZE;
+		    if(!range_list_has_range(unalloc_cells, off, 
+					     nk->values[j]->data_size))
+		    { /* We've parsed a data cell which isn't in the unallocated 
+		       * list, so prune it.
+		       */
+		      free(nk->values[j]->data);
+		      nk->values[j]->data = NULL;
+		    }
+		    else
+		    { /*A data record was recovered. Remove from unalloc_cells.*/
+		      if(!removeRange(unalloc_cells, off, 
+				      nk->values[j]->data_size))
+			return 22;
+		    }
+		  }
+		}
+	      }
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  return 0;
+}
+
+
+/* NOTE: unalloc_values should be an empty range_list. */
+int extractValues(REGF_FILE* f,
+		  range_list* unalloc_cells,
+		  range_list* unalloc_values)
+{
+  const range_list_element* cur_elem;
+  REGF_VK_REC* vk;
+  uint32 i, j, off;
+
+  for(i=0; i < range_list_size(unalloc_cells); i++)
+  {
+    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);
+      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;
+  }
+
+  /* Now see if the data associated with each value is intact */
+  for(i=0; i<range_list_size(unalloc_values); i++)
+  {
+    cur_elem = range_list_get(unalloc_values, i);
+    vk = (REGF_VK_REC*)cur_elem->data;
+    if(vk == NULL)
+      return 40;
+
+    if(vk->data != NULL && !vk->data_in_offset)
+    {
+      off = vk->data_off+REGF_BLOCKSIZE;
+      if(!range_list_has_range(unalloc_cells, off, vk->data_size))
+      { /* We've parsed a data cell which isn't in the unallocated 
+	 * list, so prune it.
+	 */
+	free(vk->data);
+	vk->data = NULL;
+      }
+      else
+      { /*A data record was recovered. Remove from unalloc_cells.*/
+	if(!removeRange(unalloc_cells, off, vk->data_size))
+	  return 50;
+      }
+    }
+  }
+
+  return 0;
+}
+
+
+/* NOTE: unalloc_sks should be an empty range_list. */
+int extractSKs(REGF_FILE* f, 
+	       range_list* unalloc_cells,
+	       range_list* unalloc_sks)
+{
+  const range_list_element* cur_elem;
+  REGF_SK_REC* sk;
+  uint32 i, j;
+
+  for(i=0; i < range_list_size(unalloc_cells); i++)
+  {
+    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);
+      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;
+	}
+	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)
+{ 
+  REGF_FILE* f;
+  const range_list_element* cur_elem;
+  range_list* unalloc_cells;
+  range_list* unalloc_keys;
+  range_list* unalloc_values;
+  range_list* unalloc_sks;
+  char** parent_paths;
+  char* tmp_name;
+  char* tmp_path;
+  REGF_NK_REC* tmp_key;
+  REGF_VK_REC* tmp_value;
+  uint32 argi, arge, i, j, ret, num_unalloc_keys;
+  /* uint32 test_offset;*/
+  
+  /* Process command line arguments */
+  if(argc < 2)
+  {
+    usage();
+    bailOut(EX_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(EX_USAGE, "");
+    }
+  }
+  /*test_offset = strtol(argv[argi++], NULL, 16);*/
+
+  if((registry_file = strdup(argv[argi])) == NULL)
+    bailOut(EX_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(EX_NOINPUT, "");
+  }
+
+  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_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);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractValueLists() failed with %d.\n", ret);
+    return ret;
+  }
+
+  /* Carve any orphan values and associated data */
+  ret = extractValues(f, unalloc_cells, unalloc_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractValues() 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 = (REGF_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 = (REGF_NK_REC*)cur_elem->data;
+
+    printKey(f, tmp_key, parent_paths[i]);
+    if(tmp_key->num_values > 0 && tmp_key->values != NULL)
+    {
+      tmp_name = quote_string(tmp_key->keyname, key_special_chars);
+      tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2);
+      if(tmp_path == NULL)
+	return 10;
+      sprintf(tmp_path, "%s/%s", parent_paths[i], tmp_name);
+      for(j=0; j < tmp_key->num_values; j++)
+      {
+	tmp_value = tmp_key->values[j];
+	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 = (REGF_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);
+    }
+  }
+
+  return 0;
+}
Index: releases/0.9.0/src/reglookup.c
===================================================================
--- releases/0.9.0/src/reglookup.c	(revision 123)
+++ releases/0.9.0/src/reglookup.c	(revision 123)
@@ -0,0 +1,615 @@
+/*
+ * A utility to read a Windows NT/2K/XP/2K3 registry file, using 
+ * Gerald Carter''s regfio interface.
+ *
+ * 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 <stdlib.h>
+#include <sysexits.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include "../include/regfi.h"
+#include "../include/void_stack.h"
+
+/* Globals, influenced by command line parameters */
+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 */
+REGF_FILE* f;
+
+
+/* XXX: A hack to share some functions with reglookup-recover.c.
+ *      Should move these into a properly library at some point.
+ */
+#include "common.c"
+
+
+void printValue(const REGF_VK_REC* vk, char* prefix)
+{
+  char* quoted_value = NULL;
+  char* quoted_name = NULL;
+  char* conv_error = NULL;
+  const char* str_type = NULL;
+  uint32 size = vk->data_size;
+
+  /* Microsoft's documentation indicates that "available memory" is 
+   * the limit on value sizes.  Annoying.  We limit it to 1M which 
+   * should rarely be exceeded, unless the file is corrupt or 
+   * malicious. For more info, see:
+   *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
+   */
+  if(size > VK_MAX_DATA_LENGTH)
+  {
+    fprintf(stderr, "WARNING: value data size %d larger than "
+	    "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
+    size = VK_MAX_DATA_LENGTH;
+  }
+  
+  quoted_name = quote_string(vk->valuename, key_special_chars);
+  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(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+    quoted_name[0] = '\0';
+  }
+
+  quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
+  if(quoted_value == NULL)
+  {
+    if(conv_error == NULL)
+      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
+	      "Memory allocation failure likely.\n", prefix, quoted_name);
+    else if(print_verbose)
+      fprintf(stderr, "WARNING: 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, "VERBOSE: While quoting value for '%s/%s', "
+	    "warning returned: %s\n", prefix, quoted_name, conv_error);
+
+  str_type = regfi_type_val2str(vk->type);
+  if(print_security)
+  {
+    if(str_type == NULL)
+      printf("%s/%s,0x%.8X,%s,,,,,\n", prefix, quoted_name,
+	     vk->type, quoted_value);
+    else
+      printf("%s/%s,%s,%s,,,,,\n", prefix, quoted_name,
+	     str_type, quoted_value);
+  }
+  else
+  {
+    if(str_type == NULL)
+      printf("%s/%s,0x%.8X,%s,\n", prefix, quoted_name,
+	     vk->type, quoted_value);
+    else
+      printf("%s/%s,%s,%s,\n", prefix, quoted_name,
+	     str_type, quoted_value);
+  }
+
+  if(quoted_value != NULL)
+    free(quoted_value);
+  if(quoted_name != NULL)
+    free(quoted_name);
+  if(conv_error != NULL)
+    free(conv_error);
+}
+
+
+
+/* XXX: Each chunk must be unquoted after it is split out. 
+ *      Quoting syntax may need to be standardized and pushed into the API 
+ *      to deal with this issue and others.
+ */
+char** splitPath(const char* s)
+{
+  char** ret_val;
+  const char* cur = s;
+  char* next = NULL;
+  char* copy;
+  uint32 ret_cur = 0;
+
+  ret_val = (char**)malloc((REGF_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(EX_OSERR, "ERROR: Memory allocation problem.\n");
+	  
+      memcpy(copy, cur, next-cur);
+      copy[next-cur] = '\0';
+      ret_val[ret_cur++] = copy;
+      if(ret_cur < (REGF_MAX_DEPTH+1+1))
+	ret_val[ret_cur] = NULL;
+      else
+	bailOut(EX_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 < (REGF_MAX_DEPTH+1+1))
+      ret_val[ret_cur] = NULL;
+    else
+      bailOut(EX_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
+  }
+
+  return ret_val;
+}
+
+
+void freePath(char** path)
+{
+  uint32 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 */
+/* XXX: Some way should be found to integrate this into regfi's API 
+ *      The problem is that the escaping is sorta reglookup-specific.
+ */
+char* iter2Path(REGFI_ITERATOR* i)
+{
+  const REGFI_ITER_POSITION* cur;
+  uint32 buf_left = 127;
+  uint32 buf_len = buf_left+1;
+  uint32 name_len = 0;
+  uint32 grow_amt;
+  char* buf;
+  char* new_buf;
+  char* name;
+  const char* cur_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)
+      cur_name = i->cur_key->keyname;
+    else
+      cur_name = cur->nk->keyname;
+
+    buf[buf_len-buf_left-1] = '/';
+    buf_left -= 1;
+    name = quote_string(cur_name, key_special_chars);
+    name_len = strlen(name);
+    if(name_len+1 > buf_left)
+    {
+      grow_amt = (uint32)(buf_len/2);
+      buf_len += name_len+1+grow_amt-buf_left;
+      if((new_buf = realloc(buf, buf_len)) == NULL)
+      {
+	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* i, char* prefix)
+{
+  const REGF_VK_REC* value;
+
+  value = regfi_iterator_first_value(i);
+  while(value != NULL)
+  {
+    if(!type_filter_enabled || (value->type == type_filter))
+      printValue(value, prefix);
+    value = regfi_iterator_next_value(i);
+  }
+}
+
+
+void printKey(REGFI_ITERATOR* i, char* full_path)
+{
+  static char empty_str[1] = "";
+  char* owner = NULL;
+  char* group = NULL;
+  char* sacl = NULL;
+  char* dacl = NULL;
+  char mtime[20];
+  time_t tmp_time[1];
+  struct tm* tmp_time_s = NULL;
+  const REGF_SK_REC* sk;
+  const REGF_NK_REC* k = regfi_iterator_cur_key(i);
+
+  *tmp_time = nt_time_to_unix(&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(i)))
+  {
+    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;
+
+    printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime, 
+	   owner, group, sacl, dacl);
+
+    if(owner != empty_str)
+      free(owner);
+    if(group != empty_str)
+      free(group);
+    if(sacl != empty_str)
+      free(sacl);
+    if(dacl != empty_str)
+      free(dacl);
+  }
+  else
+    printf("%s,KEY,,%s\n", full_path, mtime);
+}
+
+
+void printKeyTree(REGFI_ITERATOR* iter)
+{
+  const REGF_NK_REC* root = NULL;
+  const REGF_NK_REC* cur = NULL;
+  const REGF_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);
+  
+  if(root == NULL)
+    bailOut(EX_DATAERR, "ERROR: root cannot be NULL.\n");
+  
+  do
+  {
+    if(print_this)
+    {
+      path = iter2Path(iter);
+      if(path == NULL)
+	bailOut(EX_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))
+	  bailOut(EX_DATAERR, "ERROR: could not traverse iterator upward.\n");
+	
+	cur = regfi_iterator_cur_key(iter);
+	if(cur == NULL)
+	  bailOut(EX_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))
+	bailOut(EX_DATAERR, "ERROR: could not traverse iterator downward.\n");
+
+      cur = sub;
+      sub = regfi_iterator_first_subkey(iter);
+      print_this = true;
+    }
+  } while(!((cur == root) && (sub == NULL)));
+
+  if(print_verbose)
+    fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
+}
+
+
+/* XXX: what if there is BOTH a value AND a key with that 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)
+{
+  const REGF_VK_REC* value;
+  char* tmp_path_joined;
+  const char** tmp_path;
+  uint32 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**)*(REGF_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 < REGF_MAX_DEPTH+1+1);
+      i++)
+    tmp_path[i] = path[i];
+
+  tmp_path[i] = NULL;
+
+  if(print_verbose)
+    fprintf(stderr, "VERBOSE: Attempting to retrieve specified path: %s\n",
+	    path_filter);
+
+  /* Special check for '/' path filter */
+  if(path[0] == NULL)
+  {
+    if(print_verbose)
+      fprintf(stderr, "VERBOSE: Found final path element as root key.\n");
+    return 2;
+  }
+
+  if(!regfi_iterator_walk_path(iter, tmp_path))
+  {
+    free(tmp_path);
+    return 0;
+  }
+
+  if(regfi_iterator_find_value(iter, path[i]))
+  {
+    if(print_verbose)
+      fprintf(stderr, "VERBOSE: Found final path element as value.\n");
+
+    value = regfi_iterator_cur_value(iter);
+    tmp_path_joined = iter2Path(iter);
+
+    if((value == NULL) || (tmp_path_joined == NULL))
+      bailOut(EX_OSERR, "ERROR: Unexpected error before printValue.\n");
+
+    if(!type_filter_enabled || (value->type == type_filter))
+      printValue(value, tmp_path_joined);
+
+    free(tmp_path);
+    free(tmp_path_joined);
+    return 1;
+  }
+  else if(regfi_iterator_find_subkey(iter, path[i]))
+  {
+    if(print_verbose)
+      fprintf(stderr, "VERBOSE: Found final path element as key.\n");
+
+    if(!regfi_iterator_down(iter))
+      bailOut(EX_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
+
+    return 2;
+  }
+
+  if(print_verbose)
+    fprintf(stderr, "VERBOSE: 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, "\n");
+}
+
+
+int main(int argc, char** argv)
+{
+  char** path = NULL;
+  REGFI_ITERATOR* iter;
+  int retr_path_ret;
+  uint32 argi, arge;
+
+  /* Process command line arguments */
+  if(argc < 2)
+  {
+    usage();
+    bailOut(EX_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(EX_USAGE, "ERROR: '-p' option requires parameter.\n");
+      }
+      if((path_filter = strdup(argv[argi])) == NULL)
+	bailOut(EX_OSERR, "ERROR: Memory allocation problem.\n");
+
+      path_filter_enabled = true;
+    }
+    else if (strcmp("-t", argv[argi]) == 0)
+    {
+      if(++argi >= arge)
+      {
+	usage();
+	bailOut(EX_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(EX_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
+    {
+      usage();
+      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
+      bailOut(EX_USAGE, "");
+    }
+  }
+  if((registry_file = strdup(argv[argi])) == NULL)
+    bailOut(EX_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(EX_NOINPUT, "");
+  }
+
+  iter = regfi_iterator_new(f);
+  if(iter == NULL)
+    bailOut(EX_OSERR, "ERROR: Couldn't create registry iterator.\n");
+
+  if(print_header)
+  {
+    if(print_security)
+      printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL\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);
+    freePath(path);
+
+    if(retr_path_ret == 0)
+      fprintf(stderr, "WARNING: specified path not found.\n");
+    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(EX_DATAERR,"ERROR: Unknown error occurred in retrieving path.\n");
+    }
+  }
+  else
+    printKeyTree(iter);
+
+  regfi_iterator_free(iter);
+  regfi_close(f);
+
+  return 0;
+}
